UWP Windows Store 应用中的 TLS 客户端证书认证

3

我正在尝试连接到一个使用TLS和客户端证书验证的服务器。以下是代码片段:

async Task TestClientCertAuth()
{
    int iWinInetError = 0;
    Uri theUri = new Uri("http://xxx-xxx");
    try
    {
        using (HttpBaseProtocolFilter baseProtocolFilter = new HttpBaseProtocolFilter())
        {
            // Task<Certificate> GetClientCertificate() displays a UI with all available 
            // certificates with and returns the user selecter certificate.  An 
            // oversimplified implementation is included for completeness.
            baseProtocolFilter.ClientCertificate = await GetClientCertificate();
            baseProtocolFilter.AllowAutoRedirect = false;
            baseProtocolFilter.AllowUI = false;
            using (HttpClient httpClient = new HttpClient(baseProtocolFilter))
            using (HttpRequestMessage httpRequest = new HttpRequestMessage(HttpMethod.Get, theUri))
            using (HttpResponseMessage httpResponse = await httpClient.SendRequestAsync(httpRequest))
            {
                httpResponse.EnsureSuccessStatusCode();

                // Further HTTP calls using httpClient based on app logic.
            }
        }
    }
    catch (Exception ex)
    {
        iWinInetError = ex.HResult & 0xFFFF;
        LogMessage(ex.ToString() + " Error code: " + iWinInetError);
        throw;
    }
}

// Task<Certificate> GetClientCertificate() displays a UI with all available 
// certificates with and returns the user selecter certificate.  An 
// oversimplified implementation is included for completeness.
private async Task<Certificate> GetClientCertificate()
{
    IReadOnlyList<Certificate> certList = await CertificateStores.FindAllAsync();
    Certificate clientCert = null;
    // Always choose first enumerated certificate.  Works so long as there is only one cert
    // installed and it's the right one.
    if ((null != certList) && (certList.Count > 0))
    {
        clientCert = certList.First();
    }
    return clientCert;
}

SendRequestAsync调用抛出HRESULT 0x80072F7D的异常 - 我相信这意味着ERROR_INTERNET_SECURITY_CHANNEL_ERROR。服务器证书信任没有问题,客户端证书已安装在应用程序本地存储中,并且我可以使用CertificateStores.FindAllAsync检索它。查看SSL跟踪,我发现客户端证书未被发送。
如果将HttpBaseProtocolFilter.AllowUI设置为true,则不会发生上述问题。在这种情况下,SendRequestAsync调用会导致显示一个UI,询问是否同意使用客户端证书。一旦在此对话框上选择“允许”,我就可以在跟踪中看到客户端证书和证书验证消息被发送,并且连接成功建立。
问题:应用程序代码已经处理了用户的证书选择。我想知道是否有办法以编程方式指定同意使用客户端证书。因为启用AllowUI会导致其他副作用 - 例如,如果服务器返回带有WWW-Authenticate: Basic标头的401 HTTP代码,则基础协议过滤器会弹出自己的UI以接受用户凭据,而不给调用方处理它的机会。由于我已经使用自己的UI选择了客户端证书并获得了用户凭据,所以希望避免上述两个UI。谢谢。
2个回答

3

这篇微软博客文章介绍了当AllowUI为false时此错误发生的原因,并提供了一种解决方法。无论如何,证书同意UI都不能被绕过,所以EU必须经历这个流程。而且,在Windows Phone上的行为似乎不同。尝试了这个解决方案,似乎在桌面和平板电脑上能够工作。总体思路是通过尝试访问私钥来“启动”当前应用程序会话中较低级别API所使用的证书。在这种情况下,我们只是尝试签名一些虚拟数据。一旦EU授予对证书的访问权限,TLS会话建立就能成功进行。需要检查这在Windows Phone上的表现如何。

    private async Task<bool> RequestCertificateAccess(Certificate cert)
    {
        bool signOK = false;

        try
        {
            IBuffer data = CryptographicBuffer.ConvertStringToBinary("ABCDEFGHIJKLMNOPQRSTUVWXYZ012345656789", 
                BinaryStringEncoding.Utf8);

            CryptographicKey key = await PersistedKeyProvider.OpenKeyPairFromCertificateAsync(cert,
                HashAlgorithmNames.Sha1, CryptographicPadding.RsaPkcs1V15);

            IBuffer sign = await CryptographicEngine.SignAsync(key, data);

            signOK = CryptographicEngine.VerifySignature(key, data, sign);
        }
        catch (Exception ex)
        {
            LogMessage(ex.ToString(), "Certificate access denied or sign/verify failure.");
            signOK = false;
        }
        return signOK;
    }

在设置客户端证书的基本协议过滤器之前,可以调用RequestClientCertificateAccess。

@Tomas Karban,谢谢您的回复。我没有使用sharedUserCertificates,因此如果我理解正确,我可以枚举的任何证书都必须在应用程序的证书存储中。如果您还没有看过我分享的链接,它可能对您的情况有所帮助。


0

我的观点是,您没有将证书存储在应用程序自己的证书存储中。如果设置 HttpBaseProtocolFilter.AllowUI = true 并确认对话框,应用程序将获得使用用户存储中的私钥的权限。如果没有 UI 确认,应用程序只能使用自己证书存储中的私钥。

Windows 10 Mobile 上的情况甚至更糟 -- 据我所知,您无法将 HttpBaseProtocolFilter.AllowUI 设置为 true(请参见我的问题 Cannot set HttpBaseProtocolFilter.AllowUI to true on Windows 10 Mobile)。这只留下一个选项,就是使用应用程序自己的证书存储。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接