如何在 C# 中验证来自特定根 CA 的证书链

How to validate a certificate chain from a specific root CA in C#

我有一个如下所示的证书链:root CA -> intermediate CA -> client certificate。如何验证收到的证书是由 "root CA" 明确创建的?

验证整个链是没有问题的。可以这样做:

X509Certificate2 rootCert = new X509Certificate2(rootCertFile);
X509Certificate2 intermediateCert = new X509Certificate2(intermediateCertFile);
X509Certificate2 clientCert = new X509Certificate2(clientCertFile);

chain.ChainPolicy.ExtraStore.Add(rootCert);
chain.ChainPolicy.ExtraStore.Add(intermediateCert);

if(chain.Build(clientCert))
{
    // ... chain is valid
}

这里的问题是证书针对 (Windows) 证书存储进行了验证,但我只想针对特定的根 CA 进行验证。

我还认为可以检查 chain.ChainElements 是否包含我预期的根 CA。但是,如果有人从不同的根 CA 向我发送了一个有效链,并且只是添加了我期望的根 CA 怎么办?

证书链 API 检查每个元素是否签署了前面的元素,所以没有人可以在最后添加你的根 CA(前提是你没有使用 384 位之类的东西)具有 MD5 签名的 RSA 密钥,在这种情况下他们可以伪造您的签名)。

您可以对您喜欢的任何额外检查进行编码,例如您知道 none 链的长度将超过 3(尽管您可以在根 CA 的 X509 基本约束扩展中对其进行编码)。

if (!chain.Build(cert))
{
    return false;
}

if (chain.ChainElements.Length > 3)
{
    return false;
}

X509Certificate2 chainRoot = chain.ChainElements[chain.ChainElements.Length - 1].Certificate;

return chainRoot.Equals(root);

如果您更喜欢最后一行可以是 return root.RawData.SequenceEquals(chainRoot.RawData);(确保它们具有相同的字节数)。

一些注意事项:

  • 当您调用 X509Chain.Build() 每个 X509Certificate2 对象时,它 returns 通过 X509ChainElement 是一个新对象。您可能希望处置您未返回的任何对象(可能是所有对象)。
  • 即使 chain.Build returns 为假,它也会填充 ChainElements 数组,因此您可以检查原因。
  • X509Chain 对象本身是一次性的,您可能想要处置它(您可能已经在您的代码片段之外这样做了)。
  • 处置链不会处置任何已创建的证书,因为您可能已经持有对象引用。