双向 SSL - X509Certificate2.CreateFromPemFile - WINHTTP_CALLBACK_STATUS_REQUEST_ERROR

问题描述 投票:0回答:2

我正在尝试调用需要双向 SSL (TLS 1.2) 的 API,并且我有以下代码:

var myCert = X509Certificate2.CreateFromPemFile(_publicFilename, _privateFilename);

using (var handler = new WinHttpHandler())
{
    handler.ClientCertificateOption = ClientCertificateOption.Manual;
    handler.ServerCertificateValidationCallback = (x, y, z, w) => true;
    handler.ClientCertificates.Add(myCert);
    handler.SslProtocols = SslProtocols.Tls12;

    using (var client = new HttpClient(handler))
    {
        var postData = new StringContent("", UnicodeEncoding.UTF8, "application/json");
        var response = await client.PostAsync("<API Endpoint>", postData);

        string responseString = await response.Content.ReadAsStringAsync();
        Console.WriteLine(responseString);
    }
}

但是,当我调用时,我收到以下 WinHttp 异常:

调用 WINHTTP_CALLBACK_STATUS_REQUEST_ERROR 时出现错误 12185,“否” 客户端证书中提供了凭据。'.

还在 Windows 事件查看器中,我收到以下错误消息:

TLS 客户端凭证的证书没有私钥 附加到它的信息属性。这种情况最常发生在以下情况: 证书备份不正确,然后又恢复。这 消息还可能表明证书注册失败。

无法弄清楚问题是什么。我在邮递员中使用完全相同的证书和密钥,它工作正常。

c# tls1.2 x509certificate2 winhttp
2个回答
2
投票

Windows 上的 TLS(WinHttpHandler、SslStream 或默认 HTTP 处理程序(使用 SslStream))要求证书具有指定的私钥。

您可以通过导入不带 PersistKeySet 的 PFX 来临时执行此操作...但是如何获取 PFX?嗯,很简单。

var myCert = X509Certificate2.CreateFromPemFile(_publicFilename, _privateFilename);

using (var tmpCert = new X509Certificate2(myCert.Export(X509ContentType.Pfx)))
using (var handler = new WinHttpHandler())
{
    ...
    handler.ClientCertificates.Add(tmpCert);
    ...
}

当 tmpCert 被 Dispose 时,指定的密钥将被删除。如果您的生命周期很复杂或很长,您可以不在 using 语句中创建证书。如果证书被垃圾收集并且进程保持活动状态足够长的时间以运行终结器,则密钥将被清理。


0
投票

我遇到了同样的问题,最终不得不转换证书类型(使用 iHttpClientFactor)。

soPemString
soKeyString
是来自 pem 和密钥文件的文本(作为字符串)。
soCertificate
本身给了我OP的错误,但转换后的
soCertificatePkcs
工作完美。

在 Web API 应用程序的 Startup.cs 中:

// using System.Security.Cryptography.X509Certificates;

// Http Client (for IHttpClientFactory) - StackOverflow
var soCertificate = X509Certificate2.CreateFromPem(soPemString.AsSpan(), soKeyString.AsSpan());
var soCertificatePkcs = new X509Certificate2(soCertificate.Export(X509ContentType.Pkcs12));
var soHandler = new HttpClientHandler();
soHandler.ClientCertificates.Add(soCertificatePkcs);
services.AddHttpClient("StackOverflow", client =>
{
    client.DefaultRequestHeaders.UserAgent.ParseAdd("StackOverflow");
    client.Timeout = TimeSpan.FromSeconds(90);
    client.DefaultRequestVersion = HttpVersion.Version20;
}).ConfigurePrimaryHttpMessageHandler(() => soHandler);
© www.soinside.com 2019 - 2024. All rights reserved.