How to verify X509 cert without importing root cert?

I opened an Issue on dotnet/corefx and they replied as follows:

If AllowUnknownCertificateAuthority is the only flag set then
chain.Build() will return true if

  • The chain correctly terminated in a self-signed certificate (via
    ExtraStore, or searched persisted stores)

  • None of the certificates are invalid per the requested revocation
    policy

  • All of the certificates are valid under the (optional)
    ApplicationPolicy or CertificatePolicy values

  • All of the certificates’ NotBefore values are at-or-before
    VerificationTime and all of the certificates’ NotAfter values are
    (at-or-)after VerificationTime.

If that flag is not specified then an additional constraint is added:

The self-signed certificate must be registered as trusted on the system (e.g. in the LM\Root store).

So, Build() returns true, you know that a time-valid non-revoked chain
is present. The thing to do at that point is read
chain.ChainElements[chain.ChainElements.Count - 1].Certificate and
determine if it is a certificate that you trust. I recommend comparing
chainRoot.RawData to a byte[] representing a certificate that you
trust as a root in context (that is, byte-for-byte compare rather than
using a thumbprint value).

(If other flags are set then other constraints are also relaxed)

So you should do it this way:

X509Chain chain = new X509Chain();
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy.ExtraStore.Add(root);
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
var isValid = chain.Build(cert);

var chainRoot = chain.ChainElements[chain.ChainElements.Count - 1].Certificate;
isValid = isValid && chainRoot.RawData.SequenceEqual(root.RawData);

Leave a Comment