How to proper validate SSL certificate with X509Ce

2019-07-28 03:24发布

问题:

I have to validate several SSL certificates in a none-browser application for HttpRequests and websocket connections which should run on IOS, Android and Linux. When a connection via HTTPS happens I receive an array of X509Certificate2 objects where the bottom one is the server certificate and the most top one the root CA (hopefully). As an example, when I connect to https://google.com I receive 3 X509Certificate2 with the following SubjectName.Name:

  • 0 : "CN=google.com, O=Google Inc, L=Mountain View, S=California, C=US"
  • 1 : "CN=Google Internet Authority G2, O=Google Inc, C=US"
  • 2 : "CN=GeoTrust Global CA, O=GeoTrust Inc., C=US"

I now need to verify the certificate validation with the given information and the following verifications:

  • Chain-of-trust verification
  • Hostname verification
  • Certificate revocation verification

What I tried and not understood and failed:

When I call the X509Certificate2.Verify() method on each certificate independently it returns false everytime. I also do not understand why it can return anything else then false because the verification happens independently. Instead the complete chain, meaning all certificates, should be checked as far as I understood the theory.

I then used the X509Chain class:

foreach (X509Certificate2 cert in allthreecerts)
    {
        X509Chain chain = new X509Chain();
        X509ChainPolicy chainPolicy = new X509ChainPolicy()
        {
            RevocationMode = X509RevocationMode.Offline,
            RevocationFlag = X509RevocationFlag.EntireChain
        };
        chain.ChainPolicy = chainPolicy;
        if (!chain.Build(cert))
        {
            foreach (X509ChainElement chainElement in chain.ChainElements)
            {
                foreach (X509ChainStatus chainStatus in chainElement.ChainElementStatus)
                {
                    Debug.WriteLine(chainStatus.StatusInformation);
                }
            }
        }
    }            

This prints out RevocationStatusUnknown and OfflineRevocation for each certificate.

Again, I do not understand why this should work since a chain is build which each certificate independently. Should not be the parent certificate be the Issue of the child certificate all down to the root CA?

What I think I need somehow but do not know how.

In order to verify the certificate revocation all clients need to provide a certificate revocation list. Where do I get such a list and where to I load it and tell the chain to use this local list?

The same problem is the Chain-of-trust verification since the last certification should be a root certification and must be one of the clients trusted root CA's. So I have to load, for example, all root CA which are shipped with Firefox and check if the root CA is one of them? What is the best way to do that?

[Update] I made a copy paste mistake in the google.com example where I pasted two identical certificate subject names.

回答1:

When a connection via HTTPS happens I receive an array of X509Certificate2 objects where the bottom one is the server certificate and the most top one the root CA (hopefully).

If you really did get root ca certificate from SSL server then the SSL server is not configured correctly. It should return its server certificate and the whole chain Except the root CA certificate.

When I call the X509Certificate2.Verify() method on each certificate independently it returns false everytime.

As stated in the documentation of X509Certificate2.Verify method This method builds a simple chain for the certificate and applies the base policy to that chain. Now what is the base policy I do not know. But on of the default values of ChainPolicy is RevocationMode = X509RevocationMode.Online. As for Mono the source of this method can be found here. The source of Mono implementation of X509Chain can be found here.

This prints out RevocationStatusUnknown and OfflineRevocation for each certificate.

What else should it print when you specified RevocationMode = X509RevocationMode.Offline and there are no CRLs or OCSP responses in cache (probably)?

In order to verify the certificate revocation all clients need to provide a certificate revocation list. Where do I get such a list and where to I load it and tell the chain to use this local list?

Each certificate (well except root ca certificate) in the chain contains a link (or links) to CRL. .NET and most likely Mono too has an implementation that will find link to CRL in the certificate, will download it and will check if the certificate is not revoked.

The same problem is the Chain-of-trust verification since the last certification should be a root certification and must be one of the clients trusted root CA's. So I have to load, for example, all root CA which are shipped with Firefox and check if the root CA is one of them? What is the best way to do that?

No, RevocationFlag = X509RevocationFlag.EntireChain will do that for you using some store that mono uses. I don't know if it is the Firefox store but on Linux it has its own store and you can import root ca certificates from Firefox store. Check ChainElements and see for yourself what certificates did it find.

I would suggest that you build the chain with SSL server certificate only (as this will check all certs in the chain upwards) with RevocationMode = X509RevocationMode.Online and RevocationFlag = X509RevocationFlag.EntireChain. I would also try to set ExtraStore property of X509Chain to a list of certificate you got from SSL server. After the Build method I would chceck ChainStatus property of X509Chain object which is an array of statuses. I would pick all statuses that have not set X509ChainStatus.Status to NoError. If there will be any such status I would throw and log each X509ChainStatus.Status and X509ChainStatus.StatusInformation.

HTH