I need to do a validation of a client certificate, and I am a bit uncertain if I am doing it the right way. My strategy is:
I do an Issuer
and a chain length check, because I did some unit tests, that did show that if I just sent in the public key part of either the
root certificate
orintermediate certificate
the chain was built successfully, so I couldn't rely on the chain build alone, and that is why I am doing some additional checks, but now I am not certain if this is the right way. I am not checking for revocation, because I know that the CA
does not provide that currently (It looks like their CRL is expired. More on that here).
Here is my code
public ChainValidatorStatus Validate(X509Certificate2 clientCertificateToBeValidated)
{
var chain = new X509Chain();
chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
chain.ChainPolicy.CustomTrustStore.Add(_options.CA);
chain.ChainPolicy.CustomTrustStore.Add(_options.Intermediate);
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
try
{
var buildValid = chain.Build(clientCertificateToBeValidated);
var status = new ChainValidatorStatus { BuildValid = buildValid };
if (buildValid)
{
var buildClientCert = chain.ChainElements.First();
// there needs to be three elements in the ChainElements. We expect there to be: root, intermediate, client certificate
var isThereThreeElementsInChainElements = chain.ChainElements.Count == 3;
// we expect the first cert in the built chain to have an issuer that is the same as the intermediate
var validIssuerInFirstCertificateInChain = buildClientCert.Certificate.Issuer == _options.Intermediate.Subject;
// we expect the incoming to have the issuer as the intermediate
var validIssuer = clientCertificateToBeValidated.Issuer == _options.Intermediate.Subject;
X509ChainElementCollection elements = chain.ChainElements;
bool isRootCertTheSame = elements[^1].Certificate.RawData.SequenceEqual(_options.CA.GetRawCertData());
status.ValidIssuerInFirstCertificateInChain = validIssuerInFirstCertificateInChain;
status.ValidIssuer = validIssuer;
status.IsThereThreeElementsInChainElements = isThereThreeElementsInChainElements;
status.IsRootCertTheSame = isRootCertTheSame;
}
// if we get here we know: the client cert is trusted, is signed by the intermediate, and is not the CA or the intermediate
return status;
}
catch (Exception exception)
{
return ChainValidatorStatus.NotValid;
}
}
Am I completely off here, or is this a valid way of making sure that:
Hope someone will be able to chip in.
X509Chain
performs a complete certificate chain validation logic (as described in RFC 5280 §6 and a little-bit more), so your logic in if (buildValid) {}
condition is redundant and potentially flawed. If you look into RFC, chain building and validation is very complex process and it is extremely unlikely you can replicate the same yourself with same degree of confidence. X509Chain
implements this for you. I agree with comments, do not roll your own crypto.
what you need to do in that condition block, is to perform actual authentication and authorization. That is, you have to examine leaf certificate, extract identify information and bind it to an account in your identity database. After that you can apply authorization rules to figure out what connected identity can do and what cannot.