Ссылка, которую вы даете, полностью верна. Вам потребуется реализовать COM-объект в NET, реализующий следующие два COM-интерфейса (как минимум): ICredentialProvider, ICredentialProviderCredential.
Сначала вам нужно будет открыть их в .NET, чтобы вы могли ссылаться на них. По моему опыту, проще всего это сделать через IDL в Windows SDK. Файл idl, который вам понадобится, будет \Program Files (x86)\Windows Kits\10\Include\10.0.15063.0\um\credentialprovider.idl
.
Уловка №1: вам нужно обернуть все определения в оператор library CredentialProviders { ... }
, иначе только некоторые типы получат экспорт)
Откройте собственные инструменты VS и скомпилируйте их с помощью midl
: midl "C:\Program Files (x86)\Windows Kits\10\Include\10.0.15063.0\um\credentialprovider.idl"
Это создаст библиотеку типов (файл tlb), которую затем может использовать .NET для преобразования типов в типы C#. Теперь вы можете использовать утилиту tlbimp
для создания DLL-библиотеки взаимодействия, которую затем можно использовать в .NET: tlbimp.exe credentialprovider.tlb /out:CredentialProvider.Interop.dll
Уловка №2. Компиляция с tlbimp
, к сожалению, удаляет типы возвращаемых данных для вызова метода (HRESULT
) и предполагает использование подсистемы исключений .NET. В этом случае это не сработает, так как winlogon (или хост-приложение credUI) повторно выдает исключения и завершает процесс. Решение заключается в использовании утилиты под названием tlbimp2
. На данный момент он размещен в SVN на codeplex — доступен только код. Мне пришлось загрузить код и перекомпилировать инструмент в VS2017 (я загрузил артефакты в прикрепленном репозитории). Итак, нам нужно запустить это, чтобы скомпилировать для winlogon: tlbImp2.exe credentialprovider.tlb /out:CredentialProvider.Interop.dll /unsafe /verbose /preservesig
Теперь мы можем запустить библиотеку, которую мы можем реализовать из COM-объекта. Запустите проект dll в .NET framework. Отредактируйте проект и убедитесь, что установлен флажок «Зарегистрироваться для COM-взаимодействия». Ссылка на скомпилированную библиотеку Interop.
Как было сказано ранее, нам понадобятся два реализованных интерфейса: ICredentialProvider, ICredentialProviderCredential. Код должен выглядеть так:
[ComVisible(true)]
[Guid("<random-guid>")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ITestWindowsCredentialProvider: ICredentialProvider
{
}
Вы можете сделать то же самое для интерфейса ICredentialProviderCredential.
По реализации:
[ComVisible(true)]
[Guid("<another-unique-id>")] // <-- This is what we are going to use for registration
[ClassInterface(ClassInterfaceType.None)]
public class TestWindowsCredentialProvider : ITestWindowsCredentialProvider
{
private const int E_NOTIMPL = unchecked((int) 0x80004001);
...
public int SetSerialization(ref _CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION pcpcs)
{
return E_NOTIMPL;
}
...
}
Возврат из всех методов E_NOTIMPL
и предоставление значений во всех out
параметрах. Теперь у нас есть базовый объект, с которого мы можем начать. Вы можете отложить реализацию ICredentialProviderCredential до реализации метода ICredentialProvider::GetCredentialAt.
Вы можете зарегистрировать этого поставщика учетных данных, добавив ключ в параметр реестра HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers
, названный в соответствии с руководством по реализации компонента (помните здесь фигурные скобки).
Когда вы это сделаете, служба входа в систему и вызовы credUI будут загружать и запрашивать ваш компонент. Однако имейте в виду: любое исключение приведет к сбою winlogon, из-за которого вы не сможете войти в систему. Лучше всего использовать виртуальную машину.
Теперь, после всех этих шагов, нам все еще нужно реализовать поставщика учетных данных. Это лучше всего описано в документе под названием Опыт входа в Windows на основе поставщика учетных данных. В конечном итоге вам нужно будет организовать пользовательский интерфейс с любыми возможными вещами, которые вам нужно будет делать в фоновом режиме, и сопоставить их с пользователем.
Вы сможете войти в систему пользователя, если правильно сериализуете информацию о пользователе в реализации метода ICredentialProviderCredential::GetSerialization.
Example
Я сделал пример этого, и вы можете найти его здесь: https://github.com/phaetto/windows-credentials-provider
person
Alexander Mantzoukas
schedule
25.08.2017