C#中的Windows凭据提供程序

时间:2016-04-05 11:37:24

标签: c# c++ windows winapi credential-providers

我正在开展一个学校项目,我应该修改Windows Logon UI中提供的凭据的方式。 经过一些搜索,我找到了着名的Vista RTM(Longhorn)样品和技术文档。我发现所有样本都是用C ++开发的。

由于我没有任何C / C ++经验,我认为自己是一个体面的C#程序员,我想知道是否有可能做这个C#。

我还需要与REST API交换数据以验证登录,因此C#会更友好。

我发现了这个https://stackoverflow.com/a/23496878/3626447,但@mageos提供的信息太过" raw"。

有人知道一些有用的资源吗?

1 个答案:

答案 0 :(得分:4)

您提供的链接完全正确。您需要在NET中实现一个COM对象,该对象实现以下两个COM接口(至少):ICredentialProvider,ICredentialProviderCredential

首先,您必须在.NET中公开它们,以便您可以引用它们。在我的经验中,更简单的方法是在Windows SDK中通过IDL。您需要的idl文件为\Program Files (x86)\Windows Kits\10\Include\10.0.15063.0\um\credentialprovider.idl

Catch#1:您需要将所有定义包含在library CredentialProviders { ... }语句中,否则只有某些类型的才能导出<) / p>

打开VS本机工具并使用midl进行编译: midl "C:\Program Files (x86)\Windows Kits\10\Include\10.0.15063.0\um\credentialprovider.idl"

这将创建一个类型库(tlb文件),然后.NET可以将其用于将类型转换为C#类型。您现在可以使用tlbimp实用程序创建一个可以在.NET中使用的interop dll: tlbimp.exe credentialprovider.tlb /out:CredentialProvider.Interop.dll

Catch#2:不幸的是,使用tlbimp编译剥离了方法调用(HRESULT)的返回类型,并希望您使用.NET异常子系统。在这种情况下,这不起作用,因为winlogon(或credUI主机应用程序)重新抛出异常并终止进程。解决方案是使用名为tlbimp2的实用程序。在撰写本文时,它在codeplex上托管了SVN - 只有代码可用。我必须下载代码并在VS2017中重新编译该工具(我已在附加的repo中上传了工件)。所以我们需要运行它来编译winlogon: tlbImp2.exe credentialprovider.tlb /out:CredentialProvider.Interop.dll /unsafe /verbose /preservesig

现在我们可以启动一个可以实现COM对象的库。在.NET框架中启动一个DLL项目。编辑项目并确保选中“Register for COM interop”标志。参考编译的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。

您可以通过在使用组件的实现guid命名的注册表设置HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers下添加密钥来注册此凭据提供程序(请记住此处的括号)。

当您这样做时,登录服务和credUI调用将加载和查询您的组件。但请注意:任何异常都会导致winlogon崩溃,导致您无法登录。使用VM作为最佳实践。

现在,在完成所有这些步骤之后,我们仍然需要实施凭据提供程序。最好在名为Credential Provider driven Windows Logon Experience的文档中对此进行描述。您最终需要使用在后台执行的任何可能的操作来编排UI,并将其映射到用户。

在正确序列化方法实现ICredentialProviderCredential :: GetSerialization上的用户信息时,您将能够登录用户。

我举了一个例子,您可以在这里找到它:https://github.com/phaetto/windows-credentials-provider