I have a certificate chain that looks like this: root CA -> intermediate CA -> client certificate
. How can I validate that a received certificate is explicitly created by "root CA"?
To validate the whole chain is not a problem. This can be done like this:
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
}
The issue here is that the certificate gets validated against the (Windows) certificate store but I just want to validate it against a specific root CA.
I also thought that it would be possible to check if the chain.ChainElements
contains my expected root CA. But what if someone sends me a valid chain from a different root CA and just adds the my expected root CA?
The certificate chain API checks that each element signed the preceding element, so there's no way that someone can just tack your root CA on the end (provided you're not using something like a 384-bit RSA key with MD5 signatures, in which case they can just forge your signature).
You can encode any extra checks that you like, such as that you know none of your chains will exceed length 3 (though you could just have encoded that in your root CA's X509 Basic Constraints extension).
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);
If you prefer the last line could be return root.RawData.SequenceEquals(chainRoot.RawData);
(ensure they have the same bytes).
Some things of note: