Why does CheckCertificateRevocationList=true fails http requests behind a proxy server with ssl inspection? #114625
-
I have a Windows app that sends an https request with HttpClient when it starts with the following code:
According to the doc, CheckCertificateRevocationList does some extra check to see if the client cert is on the Certificate Revocation List (CRL). This works perfectly in normal network envs (with extra security), however, I recently found this request fails in some users' network envs that have a proxy server with ssl traffic inspection enabled. In these envs, the client devices typically have their proxy server's root CA trusted, so the proxy server acts as a perfect man-in-the-middle to decrypt and inspect ssl traffic between the client device and the actual destination. With some debugging with my user, it seems when Removing I'm wondering as part of the CRL check, where does .Net HttpClient actually checks against? I'm more assuming the user's proxy cert is unlikely to be on the CRL since it's a private one that's only used in their enterprise network. |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments
-
If the relevant CRL isn't already cached, yes.
The CRL fetch is done within the Windows CertGetCertificateChain function. I don't know if it has any logging or not. It, presumably uses WinHTTP to fetch the CRL/OCSP status (when needed). I don't know if WinHTTP has a good logging mechanism, either.
It is most likely that those proxies are using a TLS certificate that either doesn't support revocation (resulting in an "I don't know how you want me to check it..." kind of error) or that just use an endpoint that is unreachable given the client's firewall and proxy settings. (Revocation data isn't served over HTTPS -- it creates a chicken and egg problem, and the data is all signed, so there's no risk of data tampering) If you use SocketsHttpHandler you can specify a custom validator to "fail open" when it can't get the CRL data, like handler.SslOptions.RemoteCertificateValidationCallback = (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) =>
{
// If RemoteCertificateChainErrors is the only flag set, try with CRL fail-open.
if (sslPolicyErrors == SslPolicyErrors.RemoteCertificateChainErrors)
{
chain.VerificationFlags |= X509VerificationFlags.IgnoreEndRevocationUnknown |
X509VerificationFlags.IgnoreCertificateAuthorityRevocationUnknown |
X509VerificationFlags.IgnoreRootRevocationUnknown;
return chain.Build(certificate);
}
// Otherwise, everything has to be perfect, or fail.
return sslPolicyErrors == SslPolicyErrors.None;
}; You can improve the above by 1) calling Dispose on all of the certificates in the chain.ChainElements collection (both before and after calling chain.Build -- nothing is going to use them after this callback returns, they're just going off to the finalizer thread, and/or 2) only asking for the rebuild if any of the chain elements actually has a RevocationStatusUnknown error flag set. But I went with the KISS principle here so it was easier to understand. Be aware that fail-open changes the security model of revocation. It improves "availability" by avoiding the problem you're seeing, but allows a network MITM to just block access to hide that they're using a revoked certificate. It's a judgment call you have to make. |
Beta Was this translation helpful? Give feedback.
-
Thanks heaps @bartonjs , it makes a lot of sense now. Will work with our security team to decide 👍 |
Beta Was this translation helpful? Give feedback.
If the relevant CRL isn't already cached, yes.
The CRL fetch is done within the Windows CertGetCertificateChain function. I don't know if it has any logging or not. It, presumably uses WinHTTP to fetch the CRL/OCSP status (when needed). I don't know if WinHTTP has a good logging mechanism, either.
It is most likely that those proxies are using a TLS certificate that either doe…