可以在 C# 中使用带有 CA 证书的 .p12 文件而不将它们导入证书存储 [英] Can a .p12 file with CA certificates be used in C# without importing them to certificate store

查看:64
本文介绍了可以在 C# 中使用带有 CA 证书的 .p12 文件而不将它们导入证书存储的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我得到了一个 .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"

Wireshark .net 核心:

解决方案

简短回答:否*.

Wordier 介绍:SslStreamClientCertificates 集合中挑选一个证书,使用由 TLS 服务器发送的数据(过去,但不再普遍)根(如果不适用,则它选择 HasPrivateKey 为真的第一件事).在选择过程中,每个候选证书都被单独检查,并要求系统解析链.在 Windows 上,然后将选定的证书发送到系统库,以用于我们现在正在执行 TLS",这 (IIRC) 是限制的来源.(macOS 和 Linux 构建的 .NET Core 只是试图保持行为对等)

一旦选择了证书,就会进行最后一次链式遍历来确定要在握手中包含哪些证书,此过程无需 ClientCertificates 集合中的任何其他内容的上下文即可完成.

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

此外,您不想要 PersistKeySet,而且可能也不想要 MachineKeySet:所有不同的 X509KeyStorageFlags 的基本原理是什么?

var handler = new HttpClientHandler();使用 (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.HasPrivateKey){handler.ClientCertificates.Add(cert);}别的{store.Add(cert);}}}var client = new HttpClient(handler);...

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

I got a .p12 certificate file with 3 certificates in it. 2 of them are CA certificates.

If I use curl (7.70 on Win10) I can do: 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 will use the CA certificates in the p12 file when connecting to the server.

On the other hand, if I try to do something similar in .net core (3.1) it fails with the error message "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);

Using Wireshark I saw that curl sends all three certificates from the p12 file whereas .net core only sends one. See images below.

If I install the CA certificates into "Personal certificate" for "Current User" then .net core also sends all three certificates and it works.

Question: Do I have to install the CA certificates (into the certificate store) when using .net core or is there a way to make it behave just like curl which uses the certificates from the p12 file?

Wireshark curl:

Wireshark .net core:

解决方案

Short answer: no*.

Wordier intro: SslStream picks one certificate out of the ClientCertificates collection, using data that (was historically, but no longer generally) is sent by the TLS server about appropriate roots (and if none is applicable then it picks the first thing where HasPrivateKey is true). During the selection process each candidate certificate is checked in isolation, and it asks the system to resolve the chain. On Windows, the selected certificate is then sent down to the system libraries for "we're doing TLS now", which (IIRC) is where the limitations come from. (macOS and Linux builds of .NET Core just try to maintain behavioral parity)

Once the certificate is selected, there's one last chain-walk to determine what certificates to include in the handshake, it's done without the context of anything else from the ClientCertificates collection.

If you know that your collection represents one chain, your best answer is to import the CA elements into your user CertificateAuthority store. That store does not impart any trust to the CA certificates, it's really just a cache that's used when building chains.

Also, you don't want PersistKeySet, and probably don't want MachineKeySet: What is the rationale for all the different X509KeyStorageFlags?

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);
...

* If your system already has the CA chain imported, it'll work. Alternatively, if the CA chain uses the Authority Information Access extension to publish a downloadable copy of the CA cert, the chain engine will find it, and everything will work.

这篇关于可以在 C# 中使用带有 CA 证书的 .p12 文件而不将它们导入证书存储的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆