.NET Core HttpClient使用ECDsa证书认证失败:服务器返回无证书
更新
经排查,问题出在SslStream(HttpClient基于该组件实现)上。在当前7.xx版本中,客户端发送请求时不会将证书链提交给服务器,这是一个已知问题。
我会保留这篇内容,因为下方的代码可能对他人有帮助(仅使用客户端证书发起请求时不会有问题)。
问题描述
我花费了大量时间排查.NET Core(7.0.100版本,也尝试过6.x版本)原生HttpClient使用基于ECDsa的证书进行客户端证书认证的问题,但始终无法正常运行。(顺带一提,使用相同方法处理RSA类型客户端证书时完全没有问题。)
出于安全要求,我必须使用ECDsa客户端证书及证书链。
我无法理解或找到相关说明解释为何该方案无法工作/不被支持,结果令我困惑。
加载证书和密钥并用于签名、验证数据时,所有测试均通过(见代码)。但最终客户端证书并未发送至服务器,导致SSL异常(我已用Python脚本测试过相同证书,完全正常)。
我无法想象(至少不希望如此)这类客户端证书不被支持。非常感谢任何帮助或替代解决方案的提示,此时切换到其他语言对我来说太糟糕了:/
参考代码
包含测试用证书和密钥供测试使用。示例中缺少证书链,可直接附加到证书字符串后
部分测试代码来自:Use X509Certificate2 to sign and validate ECDSA-SHA256 signatures
[Test] // Just some test to better understand whether the certificate and key is working and belong together public void TestEcdsaFunctionality() { var ecdsaCertificate = @"-----BEGIN CERTIFICATE----- MIIBgzCCASoCCQD+iUUbrH+BOzAKBggqhkjOPQQDAjBKMQswCQYDVQQGEwJQRDEL MAkGA1UECAwCQlcxEDAOBgNVBAcMB1BhbmRvcmExDzANBgNVBAoMBkFueU9yZzEL MAkGA1UEAwwCNDIwHhcNMjIxMTIxMTE0MjExWhcNMjMxMTIxMTE0MjExWjBKMQsw CQYDVQQGEwJQRDELMAkGA1UECAwCQlcxEDAOBgNVBAcMB1BhbmRvcmExDzANBgNV BAoMBkFueU9yZzELMAkGA1UEAwwCNDIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC AAT6vBU2iIcESep8UeQhfNFgfTArFYvtb2Pmlbk1+R9gdNaWEg1UK7dlt3/mH/X3 Mrg80JaTY3OPM92MY9e9gs7ZMAoGCCqGSM49BAMCA0cAMEQCIA3p2mMOYqGEzReY br7nYLsLdF0+dV6iZSZaG1iMHwblAiA5UaJlVr5CsCkG+j1ZJEICSOnVMyx4DjA5 oZuoMYa42w== -----END CERTIFICATE-----"; var ecdsaPrivateKey = @"MDECAQEEIM6BExC2G7P1KpViQmZ/Z65nukv8yQmvw6PqGGQcKn9boAoGCCqGSM49 AwEH"; var cert = X509Certificate2.CreateFromPem(ecdsaCertificate.ToCharArray()); var key = ECDsa.Create("ECDsa"); var keybytes = Convert.FromBase64String(ecdsaPrivateKey); key.ImportECPrivateKey(keybytes, out _); var helloBytes = Encoding.UTF8.GetBytes("Hello World"); // Sign data with the ECDsa key var signed = key.SignData(helloBytes, 0, helloBytes.Count(), HashAlgorithmName.SHA256); // Verify the data signature with the certificates public key var verified = cert.GetECDsaPublicKey().VerifyData(helloBytes, signed, HashAlgorithmName.SHA256); // Assume that everything went well and the data signature is valid Assert.That(verified, Is.EqualTo(true)); // Additional tests with the X509Certificate2 object type X509Certificate2 keyCert = ECDsaCertificateExtensions.CopyWithPrivateKey(cert, key); // Sing using the certificate that contains the private key using (ECDsa ecdsa = keyCert.GetECDsaPrivateKey()) { if (ecdsa == null) throw new ArgumentException("Cert must have an ECDSA private key", nameof(cert)); signed = ecdsa.SignData(helloBytes, HashAlgorithmName.SHA256); } // Verify signed data using the certificate that contains the private key using (ECDsa ecdsa = keyCert.GetECDsaPublicKey()) { if (ecdsa == null) throw new ArgumentException("Cert must be an ECDSA cert", nameof(cert)); Assert.That(ecdsa.VerifyData(helloBytes, signed, HashAlgorithmName.SHA256), Is.EqualTo(true)); } WorkshopRegistration(keyCert); } // This would be what I really want to use the client certificate for private void WorkshopRegistration(X509Certificate2 clientCert) { var payload = "{somepayload}"; var handler = new HttpClientHandler(); handler.ClientCertificateOptions = ClientCertificateOption.Manual; handler.SslProtocols = SslProtocols.Tls12; handler.ClientCertificates.Add(clientCert); handler.ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, cetChain, policyErrors) => { return true; }; var content = new StringContent(payload, System.Text.Encoding.UTF8, "application/json"); var client = new HttpClient(handler); client.DefaultRequestHeaders.Add("Accept", "application/json"); var result = client.PutAsync("https://someHostname.com/registration", content).GetAwaiter().GetResult(); if (result.StatusCode != HttpStatusCode.OK) throw new SuccessException("Registration failed with conent: " + result.Content.ToString()); }
内容的提问来源于stack exchange,提问作者hbertsch
相关产品推荐
相关产品推荐

