HttpClient 拒绝发送自签名客户端证书

Posted

技术标签:

【中文标题】HttpClient 拒绝发送自签名客户端证书【英文标题】:HttpClient refusing to send self-signed client certificate 【发布时间】:2021-05-26 23:38:57 【问题描述】:

我正在尝试使用HttpClient 使用以下代码发送自签名客户端证书

var handler = new HttpClientHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual; 
handler.ClientCertificates.Add(GetClientCertificate()); //Loads cert from a .pfx file
var client = new HttpClient(handler);
PerformRequest(client);

我看过十几个与此相关的 SO 帖子,但没有一个解决我的问题。

注意事项:

我已通过 Wireshark 验证服务器正在请求客户端证书,但 HttpClient 没有发送。

当我使用 Postman 执行请求时,发送完全相同的证书(使用相同的 .pfx 文件)

我已尝试通过handler.SslProtocols 将 TLS 版本强制为 1.0、1.1、1.2 -- 无济于事 从GetClientCertificate() 返回的X509Certificate2 有一个私钥 这是在 .NET Framework 4.7.2 上 -- 我无法更改

我已经对此进行了 3 天的故障排除,甚至通读了参考源,试图找出为什么我的证书不包括在内,但我找不到原因。 我怀疑在 HttpClient 实现的深处,我的证书被拒绝了,因为它是自签名的。

如何强制它发送我的证书? (服务器维护一个白名单,所以我不在乎它是否认为我的证书无效)。 或者,至少,我怎样才能从中获得任何类型的调试信息?有什么方法可以查明证书被拒绝的原因?


更新:

启用跟踪后,我发现 .NET 正确选择了我的证书:

System.Net Information: 0 : [23960] SecureChannel#64538993 - Selected certificate: <omitted>
System.Net Information: 0 : [23960] SecureChannel#64538993 - Left with 1 client certificates to choose from.
System.Net Information: 0 : [23960] SecureChannel#64538993 - Trying to find a matching certificate in the certificate store.
System.Net Information: 0 : [23960] SecureChannel#64538993 - Locating the private key for the certificate: <omitted>
System.Net Information: 0 : [23960] SecureChannel#64538993 - Certificate is of type X509Certificate2 and contains the private key.
System.Net Information: 0 : [23960] SecureChannel#64538993::.AcquireClientCredentials, new SecureCredential() (flags=(ValidateManual, NoDefaultCred, SendAuxRecord), m_ProtocolFlags=(Tls12Client), m_EncryptionPolicy=RequireEncryption)
System.Net Information: 0 : [23960] AcquireCredentialsHandle(package = Microsoft Unified Security Protocol Provider, intent  = Outbound, scc     = System.Net.SecureCredential)
System.Net Information: 0 : [23960] InitializeSecurityContext(credential = System.Net.SafeFreeCredential_SECURITY, context = 21c5cd98:21cd2108, targetName = <omitted>, inFlags = ReplayDetect, SequenceDetect, Confidentiality, AllocateMemory, InitManualCredValidation)
System.Net Information: 0 : [23960] InitializeSecurityContext(In-Buffers count=2, Out-Buffer length=100, returned code=ContinueNeeded).
... etc

但是,它仍然没有被发送。如果您有更多关于在哪里寻找的想法,我们将不胜感激。

【问题讨论】:

你在 postman 和 c# 中都看到了请求吗?如果发送请求,则 TLS 正在工作。不使用 TLS 发送证书。服务器发送一个带有证书名称的证书块。有关 TLS 协议,请参阅页面底部的 Wiki:en.wikipedia.org/wiki/Transport_Layer_Security 您可以通过在 System.Net 名称上启用 .NET 跟踪来获取更多诊断信息,其中包括“SSL 调试信息(无效证书、缺少颁发者列表和客户端证书错误)。 "见link。 @jdweng 我不确定我明白你在问什么。服务器在“Server Hello”期间发送“Certificate Request”。我确实看到了两者的请求,但区别在于邮递员使用客户端证书响应而 C# 没有。 @JohnWu 谢谢!这是一个很大的帮助。然而,现在我更加困惑了。跟踪显示它正在接受我的证书,那么为什么它没有通过网络发送? @PanagiotisKanavos : 为什么邮递员工作??? 【参考方案1】:

This answer 引导我找到解决方案。

X509Certificate2.PrivateKey 抛出了NotSupportedException,因为我使用 ECD 作为签名算法。我切换到 RSA,现在它可以正确发送证书。

奇怪的是,ECD 证书的跟踪显示没有问题,并且能够成功识别私钥。我不知道为什么会这样——对我来说这听起来像是一个错误。不过,RSA 将适用于我的用例,我已经厌倦了调试它。

【讨论】:

这不是一个错误,它记录在 prorperty docs An AsymmetricAlgorithm object, which is either an RSA or DSA cryptographic service provider. .NET 确实有 ECDsa 类。 您可以使用ECDsaCertificateExtensions.CopyWithPrivateKey(X509Certificate2, ECDsa) 来组合证书和私钥。该方法是在4.7.2中添加的 我知道PrivateKey 声明它不支持 ECDsa,这只是导致我更改签名算法的线索。但是我看不出为什么使用 ECDsa 的 X509Cert 不应该随我的请求一起发送。相同的证书适用于 Postman 的事实让我相信这确实是一个错误。 据记录,此属性不接受 ECDsa。这就是为什么它抛出 NotSupportedException 而不是其他一些随机错误 - 它被明确写入拒绝 ECDsa,even in .NET Core。您是否尝试过使用CopyWithPrivateKey 这一切都很好,但没有记录的是 ECDsa 签名的证书将默默地无法通过 HttpClient 发送。我不会再研究它了,我只会使用 RSA。

以上是关于HttpClient 拒绝发送自签名客户端证书的主要内容,如果未能解决你的问题,请参考以下文章

HttpClient 无法忽略或绕过自签名证书错误

使用 HttpClient 信任自签名证书

使用 Httpclient 信任自签名证书

httpclient 和自签名证书的问题

在 ktor httpClient(js) JS 引擎中忽略自签名证书的配置

Intranet 中的自签名 TLS 证书