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?

serkan
  • 5
  • 1
  • 3
  • How many elements you get in `chain.ChainElements`? – Crypt32 Apr 08 '20 at 06:07
  • it has 2 elements, so the chain building is working. It's only the verification portion that fails. – serkan Apr 08 '20 at 18:04
  • Then, probably, root is trusted by user, but not by machine. Or there are revocation checking issues. – Crypt32 Apr 08 '20 at 18:29
  • Yes, in my code above I allow untrustedRoot, but I get these other two errors in ChainStatus: OfflineRevocation, RevocationStatusUnknown – serkan Apr 08 '20 at 18:54
  • What says `certutil -verify -urlfetch cert.cer`. Check if all URLs are OK, or even better post certutil output. – Crypt32 Apr 08 '20 at 18:56
  • Yup in my question above I mention that certutil -verify .\leaf.cer fails but certutil -verify .\leaf.cer .\root.cer passes – serkan Apr 08 '20 at 19:01
  • With `-urlfetch` switch? – Crypt32 Apr 08 '20 at 19:03
  • same behaviour with -urlfetch, if I pass both leaf and root it'll pass.if I just pass leaf I see verified URLs for leaf but its also looking for the root URLs but I haven't passed the root cert in so it fails. Maybe the same is happening in my code, maybe the extraStore doesn't get checked when validating the URLs? – serkan Apr 08 '20 at 19:23

1 Answers1

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.

Crypt32
  • 12,850
  • 2
  • 41
  • 70
  • You are correct, installing the cert resolved the untrusted root error as well as the revocationUnknown errors. I will try to implement 2. or get approval for 1. Thanks! – serkan Apr 09 '20 at 17:58
  • 1st option opens a security vulnerability. You've been warned. – Crypt32 Apr 09 '20 at 18:09