Search code examples
c#x509certificate2

Make X509Certificate2 accessible after validation via ServerCertificateCustomValidationCallback


I am using a HttpClientHandler with a custom ServerCertificateCustomValidationCallback.

I need, or at least would like to have, access to the certificate chain and the server certificate after the request is done. At the moment I just put both of them in class variables for access later.

Is this possible?

The behavior I'm seeing now is such that both objects cant be accessed after the validation. I currently get a System.Security.Cryptography.CryptographicException when I try to inspect the server certificate in my debugger. The chain appears to be empty.


Solution

  • The problem you face is caused because objects are disposed after leaving callback. You can copy certificate to local variable as follows:

    class MyClass {
        static X509Certificate2 localCert; // must be static
        void MyMethod() {
            ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, error) =>
            {
                localCert = new X509Certificate2(cert.RawData);
                // continue validation and return value
               ...
            };
            // do TLS connection
            ...
        }
    }
    

    chain copy is a bit more complex, because you cannot serialize it. However, you can try to use p/invoke and duplicate chain handle by calling CertDuplicateCertificateChain function and passing existing chain handle as a parameter. That is, create another static field of type of X509Chain and assign it this way inside callback function:

    localChain = new X509Chain(Win32.CertDuplicateCertificateChain(chain.ChainContext));
    

    function definition may be like this:

    static class Win32 {
        [DllImport("crypt32.dll", SetLastError = true)]
        public static extern IntPtr CertDuplicateCertificateChain(IntPtr pChainContext);
        [DllImport("crypt32.dll", SetLastError = true)]
        public static extern void CertFreeCertificateChain(IntPtr pChainContext);
    }
    

    do not forget to manually dispose your local chain instance to avoid memory leak. I haven't tested this approach to copy chain, but this is what I would try first.