您好,似乎这个问题的某种形式已经被问过一百万种不同的方式,但许多人没有答案,或者答案不适用于我。
我们有一个微不足道的小型 .NET 服务,可以调用目前仅支持 TLS 1.2 的第 3 方 API。
var requestHandler = new WebRequestHandler();
var clientCert = GetClientCert("THUMBPRINT");
requestHandler.ClientCertificates.Add(clientCert);
var encodedHeader = "FOO";
var httpClient = new HttpClient(requestHandler) { BaseAddress = new Uri("https://foo.bar.com/rest/api/") };
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", encodedHeader);
var httpResponse = httpClient.GetAsync("").Result;
var responseContent = httpResponse.Content.ReadAsStringAsync().Result;
当我将安全协议明确设置为 1.2 或以默认为 1.2 的框架为目标时,客户端证书不是发送到服务器的数据包的一部分。我在 Wireshark 中检查过它,证书在 1.2 版本中从未出现过。此时,服务器发送致命警报握手失败 (40),这会导致以下 .Net 异常:
The request was aborted: Could not create SSL/TLS secure channel.
我可以通过 Chrome 或 Postman(本机)进行相同的调用,并且效果很好。如果我将协议设置为 1.1,它就可以正常工作。但如果我通过 .NET 将其作为 1.2 运行,它每次都会失败。即使我故意根本不添加客户端证书,我也会在 Wireshark 中得到相同的错误和相同的流量。
我通过运行第 3 方端点https://www.ssllabs.com/ https://www.ssllabs.com/而且成绩很好。我在两边都看不到任何 MD5 签名。所有内容均通过 RSA 加密的 sha1 或 sha256 进行签名。
我启用了跟踪。以下输出似乎表明查找证书或其私钥没有问题:
System.Net Information: 0 : [19512] SecureChannel#41622463 - Certificate is of type X509Certificate2 and contains the private key.
System.Net Information: 0 : [19512] SecureChannel#41622463::.AcquireClientCredentials, new SecureCredential() (flags=(ValidateManual, NoDefaultCred, SendAuxRecord, UseStrongCrypto), m_ProtocolFlags=(Zero), m_EncryptionPolicy=RequireEncryption)
System.Net Information: 0 : [19512] AcquireCredentialsHandle(package = Microsoft Unified Security Protocol Provider, intent = Outbound, scc = System.Net.SecureCredential)
System.Net Information: 0 : [19512] InitializeSecurityContext(credential =
System.Net.SafeFreeCredential_SECURITY, context = 1ed7465db80:1f1d854c910, targetName = foo.bar.com, inFlags = ReplayDetect, SequenceDetect, Confidentiality, AllocateMemory, InitManualCredValidation)
System.Net Information: 0 : [19512] InitializeSecurityContext(In-Buffers count=2, Out-Buffer length=133, returned code=ContinueNeeded).
我还打开了 SCHANNEL 事件记录。在那里我可以看到它使用私钥获取客户端证书。
The TLS client credential's private key has the following properties:
CSP name: Microsoft Enhanced Cryptographic Provider v1.0
CSP type: 1
Key name: {some hex here}
Key Type: key exchange
Key Flags: 0x0
The attached data contains the certificate.
接下来是Wireshark中Alert包对应的错误:
A fatal alert was received from the remote endpoint. The TLS protocol defined fatal alert code is 40.
我完全没有主意了。感觉就像.Net 中的一个错误。我们一度认为这是一个与我们的客户端证书不完整的信任链,因为我们的证书包只有客户端证书和一个中间 CA。但我们从供应商处获得了根 CA,现在 Windows 表示该证书是合法的。
我在 Wireshark 中注意到的一件有趣的事情是,我们在成功调用时发送客户端证书和中间 CA。我不知道它是如何获取中间 CA 的,因为我只提取客户端证书来附加到请求。一旦我们有了根 CA,为什么它不也提交它呢?我的直觉告诉我问题可能仍然存在于某个地方,但我无法证明这一点。任何日志中都没有任何内容表明信任链发生故障。
我什至还连接了 ProcMon,看看是否能发现任何问题,但什么也没有跳出来。伙计,我花在这上面的时间本可以用 Java 重写整个事情。
我尝试过的其他一些方法不起作用:
针对 .Net 4.8
机器存储与用户存储
直接从 pfx 加载
HttpWebRequest 而不是 HttpClient
TcpClient 与 SslStream
较新的 Windows 10 版本
EDIT我刚刚读到一些内容,说 SHA-1 签名也与 MD5 一起从 TLS 1.2 中删除。我们的客户证书是用 SHA-1 签名的。我想可能就是这样,我们正在联系我们的供应商。