UWP Windows Store 应用上的 TLS 客户端证书身份验证
TLS client certificate authentication on UWP Windows Store app
我正在尝试连接到使用带有客户端证书身份验证的 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设置为真,则不会出现上述问题。在这种情况下,SendRequestAsync 调用会导致显示 UI 以请求同意使用客户端证书。在此对话框中选择 'Allow' 后,我可以看到在跟踪中发送了客户端证书和证书验证消息,并且连接已成功建立。
问题:应用程序代码已经处理了用户的证书选择。我想知道是否有任何方法可以指定同意以编程方式使用客户端证书。因为启用 AllowUI 会导致其他副作用 - 例如,如果服务器使用 WWW-Authenticate 重新运行 401 HTTP 代码:Basic header,基本协议过滤器会弹出它自己的 [=26] =] 接受用户凭据而不给调用者处理它的机会。想避免以上两种 UIs,因为我已经选择了客户端证书并从拥有自己的 UIs 的用户那里获得了凭据。谢谢
我认为您没有将证书存储在应用程序自己的证书存储区中。如果您设置 HttpBaseProtocolFilter.AllowUI = true
并确认对话框,应用程序将获得使用用户存储中的私钥的权限。如果没有 UI 确认,应用程序只能使用其自己的证书存储中的私钥。
Windows 10 Mobile 上的情况更糟——据我所知,您无法将 HttpBaseProtocolFilter.AllowUI
设置为 true(参见我的问题 )。这留下了使用应用程序自己的证书存储的唯一选项。
此 Microsoft Blog Entry 提供了有关当 AllowUI 为 false 时出现此错误的原因的信息,并提供了解决方法。证书同意 UI 在任何情况下都无法绕过,因此这是欧盟必须经历的事情。 Windows Phone 上的行为似乎也有所不同。尝试过此解决方案,它似乎适用于桌面和 Surface。一般的想法是 'prime' 通过尝试访问私钥来为当前应用程序会话中的较低级别 API 使用的证书。在这种情况下,我们只是尝试签署一些虚拟数据。一旦欧盟授予对证书的访问权限,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,所以如果我理解正确的话,我可以枚举的任何证书都必须在应用程序的证书存储中。如果您还没有看过我分享的 link,可能会对您的案例有所帮助。
我正在尝试连接到使用带有客户端证书身份验证的 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设置为真,则不会出现上述问题。在这种情况下,SendRequestAsync 调用会导致显示 UI 以请求同意使用客户端证书。在此对话框中选择 'Allow' 后,我可以看到在跟踪中发送了客户端证书和证书验证消息,并且连接已成功建立。
问题:应用程序代码已经处理了用户的证书选择。我想知道是否有任何方法可以指定同意以编程方式使用客户端证书。因为启用 AllowUI 会导致其他副作用 - 例如,如果服务器使用 WWW-Authenticate 重新运行 401 HTTP 代码:Basic header,基本协议过滤器会弹出它自己的 [=26] =] 接受用户凭据而不给调用者处理它的机会。想避免以上两种 UIs,因为我已经选择了客户端证书并从拥有自己的 UIs 的用户那里获得了凭据。谢谢
我认为您没有将证书存储在应用程序自己的证书存储区中。如果您设置 HttpBaseProtocolFilter.AllowUI = true
并确认对话框,应用程序将获得使用用户存储中的私钥的权限。如果没有 UI 确认,应用程序只能使用其自己的证书存储中的私钥。
Windows 10 Mobile 上的情况更糟——据我所知,您无法将 HttpBaseProtocolFilter.AllowUI
设置为 true(参见我的问题 )。这留下了使用应用程序自己的证书存储的唯一选项。
此 Microsoft Blog Entry 提供了有关当 AllowUI 为 false 时出现此错误的原因的信息,并提供了解决方法。证书同意 UI 在任何情况下都无法绕过,因此这是欧盟必须经历的事情。 Windows Phone 上的行为似乎也有所不同。尝试过此解决方案,它似乎适用于桌面和 Surface。一般的想法是 'prime' 通过尝试访问私钥来为当前应用程序会话中的较低级别 API 使用的证书。在这种情况下,我们只是尝试签署一些虚拟数据。一旦欧盟授予对证书的访问权限,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,所以如果我理解正确的话,我可以枚举的任何证书都必须在应用程序的证书存储中。如果您还没有看过我分享的 link,可能会对您的案例有所帮助。