x509Chain.build fails, certutil -verify passes

0

I have a root certificate and a leaf. The leaf has a CRL URL OID extension which points to a valid online location. Doing this:

certutil -verify .\leaf.cer

fails with

ERROR: Verifying leaf certificate revocation status returned The revocation function was unable to check revocation because the revocation server was offline. 0x80092013 (-2146885613 CRYPT_E_REVOCATION_OFFLINE)

If I do this:

certutil -verify .\leaf.cer .\root.cer

Then verification passes, and I see the CRL getting pulled from online in Fiddler.

In my C# code, I do this:

X509Chain childCertChain = new X509Chain();
childCertChain.ChainPolicy.ExtraStore.Add(rootCert);
childCertChain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
childCertChain.ChainPolicy.UrlRetrievalTimeout = TimeSpan.FromSeconds(10);
if (!childCertChain.Build(childCert))
        {
            // The root cert is not in the windows certificate store, that is fine
            if (childCertChain.ChainStatus.Length != 1 || childCertChain.ChainStatus.First().Status != X509ChainStatusFlags.UntrustedRoot)
            {
                throw new Exception("Certificate validation error.");
            }
        }

This will hit my exception, and even though chainElements will be correctly filled with the 2 certs, ChainStatus will show:

OfflineRevocation, RevocationStatusUnknown

I also will not see any web requests in Fiddler. I can programmatically download the CRL given the URL so it's not my debug environment AFAIK. Any ideas how to get x509Chain.Build to succeed?

c#
x509certificate
x509
certificate-revocation
certutil
asked on Stack Overflow Apr 8, 2020 by serkan

1 Answer

0

I've read your code from PC and figured what's up: your root certificate is not trusted on your system. Windows CryptoAPI throws CERT_E_UNTRUSTEDROOT when root certificate is not trusted. Along with this, CryptoAPI terminates revocation checking and throw two addition errors: CRYPT_E_NO_REVOCATION_CHECK and CERT_E_UNTRUSTEDROOT, because revocation checking was skipped. In addition with untrusted root, CryptoAPI skips other checks (such as constraints, for example), but do not report them. Everything is correct and as expected.

You are trying to allow untrusted root, but do it incorrectly, because this way you skip other checks (as mentioned in previous paragraph) and your logic is vulnerable.

You can exclude untrusted root exception in chain settings and force CryptoAPI to continue validation and return success if no other errors found, but I strongly recommend to not do that, because then you are open to any kind of MITM.

You have two options:

  1. make root certificate trusted by a machine (local system). This may not be possible.
  2. call CertCreateCertificateChainEngine function directly and put your root certificate in hRestrictedTrust member of CERT_CHAIN_ENGINE_CONFIG structure.

If you are on Windows, 2nd approach is the most secure and reliable. As of now, .NET's X509Chain doesn't implement this functionality. It requires extra coding, but there are no much alternatives.

answered on Stack Overflow Apr 8, 2020 by Crypt32

User contributions licensed under CC BY-SA 3.0