可以在 C# 中使用带有 CA 证书的 .p12 文件而不将它们导入证书存储吗

Can a .p12 file with CA certificates be used in C# without importing them to certificate store

我得到了一个 .p12 证书文件,里面有 3 个证书。其中2个是CA证书。

如果我使用 curl(Win10 上的 7.70),我可以: curl -s -S -i --cert Swish_Merchant_TestCertificate_1234679304.p12:swish --cert-type p12 --tlsv1.2 --header "Content-Type:application/json" https://mss.cpc.getswish.net/swish-cpcapi/api/v1/paymentrequests --data-binary @jsondata.json

Curl 在连接到服务器时将使用 p12 文件中的 CA 证书。

另一方面,如果我尝试在 .net core (3.1) 中做类似的事情,它会失败并显示错误消息 "The message received was unexpected or badly formatted."

var handler = new HttpClientHandler();
var certs = new X509Certificate2Collection();
certs.Import(@"Swish_Merchant_TestCertificate_1234679304.p12", "swish", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
foreach (var cert in certs)
{
    handler.ClientCertificates.Add(cert);
}
var client = new HttpClient(handler);
var url = "https://mss.cpc.getswish.net/swish-cpcapi/api/v1/paymentrequests";
var request = new HttpRequestMessage()
{
    RequestUri = new Uri(url),
    Method = HttpMethod.Post,
};
request.Content = new StringContent(System.IO.File.ReadAllText(@"jsondata.json"), Encoding.UTF8, "application/json");
request.Headers.Add("accept", "*/*");
var response = await client.SendAsync(request);

我使用 Wireshark 看到 curl 从 p12 文件发送所有三个证书,而 .net core 只发送一个。见下图。

如果我将 "Current User" 的 CA 证书安装到 "Personal certificate" 中,那么 .net 核心也会发送所有三个证书并且它可以工作。

问题:使用 .net core 时我是否必须安装 CA 证书(到证书存储中),或者有没有办法让它像 curl 一样使用p12 文件中的证书?

Wireshark 卷曲:

Wireshark .net 核心:

简答:否*。

更详细的介绍:SslStreamClientCertificates 集合中挑选一个证书,使用由 TLS 服务器发送的关于适当根的数据(过去是,但不再普遍)(如果none 是适用的然后它选择第一个 HasPrivateKey 是真的)。在选择过程中,每个候选证书都被隔离检查,并要求系统解析链。在 Windows,选定的证书随后被发送到 "we're doing TLS now" 的系统库,这 (IIRC) 是限制的来源。 (macOS 和 Linux 构建的 .NET Core 只是试图保持行为对等)

一旦选择了证书,就会进行最后一次链式遍历以确定要在握手中包含哪些证书,它是在没有来自 ClientCertificates 集合的任何其他内容的情况下完成的。

如果您知道您的集合代表一个链,您最好的答案是将 CA 元素导入您的用户 CertificateAuthority 存储区。该存储不会对 CA 证书赋予任何信任,它实际上只是构建链时使用的缓存。

此外,您不需要 PersistKeySet,也可能不需要 MachineKeySet:

var handler = new HttpClientHandler();

using (X509Store store = new X509Store(StoreName.CertificateAuthority, StoreLocation.CurrentUser))
{
    store.Open(OpenFlags.ReadWrite);

    var certs = new X509Certificate2Collection();
    certs.Import(@"Swish_Merchant_TestCertificate_1234679304.p12", "swish", X509KeyStorageFlags.DefaultKeySet);

    foreach (X509Certificate2 cert in certs)
    {
        if (cert.HasPrivateKey)
        {
            handler.ClientCertificates.Add(cert);
        }
        else
        {
            store.Add(cert);
        }
    }
}

var client = new HttpClient(handler);
...

* 如果您的系统已经导入了 CA 链,它就可以工作。或者,如果 CA 链使用授权信息访问扩展来发布 CA 证书的可下载副本,链引擎将找到它,并且一切正常。