3

I'm currently messing around with the Requests module in python, which allows you to specify a SSL cert to use in your request, using the following command

url = r'https://www.google.com'
cert_path = r'C:\mystuff\google.crt'
requests.get(url, verify=cert_path)

Requests stores its trusted CA's in a PEM file located at python/Lib/site-packages/requests/cacert.pem. If you specify 'verify=True', then it will search cacert.pem.

I'm viewing the Cert for https://www.google.com through Chrome, and it has the following cert path.

GeoTrust Global CA -> Google Internet Authority G2 -> www.google.com

What I find strange is that when I pull out all of the GeoTrust related SSL Certs out of the cacert.pem store, put them into their own GeoTrust.pem, and point requests at that file, the handshake fails.

However, through trial and error, if I remove the very first SSL cert from cacerts.pem, which is "Equifax Secure CA", put it into it's own cert file, and point at that, the request works perfect.

In essence, why is a request using a GeoTrust cert being denied against a url setup with a GeoTrust cert? And why would it work when specifying the Equifax root?

I have also attempted to copy directly from google through chrome the GeoTrust root certificate as Base64, and add that to GeoTrust.pem, but the handshake still fails.

I'm new to this, so any help would be greatly appreciated

disflux
  • 133
  • 1
  • 4
  • From what you explain, looks like the cacerts still is being used, but the file you point not (since you mention you remove the files from the original cacerts). This makes sense? – CristianTM Jan 08 '16 at 19:07
  • 1
    Can you give more detail which of the GeoTrust certificates you extracted? It's especially important if [any of these](https://de.ssl-tools.net/subjects/b1b439179016b797795011f160b9d4a23cdbedee) was among the GeoTrust certificates you tried to use. As for the Equifax - there is an alternative trust path which ends with the Equifax certificate because the GeoTrust in the chain is signed by the Equifax. – Steffen Ullrich Jan 08 '16 at 19:13
  • @CristianTM The code is just an example of how one would point to either a cert file or a pem store. Once I removed the GeoTrust certs from the Requests pem, I added them to my own pem store, which I labeled GeoTrust.pem. Telling the request to use the GeoTrust.pem fails. – disflux Jan 08 '16 at 19:50
  • @SteffenUllrich GeoTrust Global CA, GeoTrust Primary Certification Authority, GeoTrust Primary Certification Authority -G3, GeoTrust Primary Certification Authority -G2 are all the ones in the cacerts.pem that I pulled out and tried. How can you tell that the GeoTrust root was signed by Equifax? – disflux Jan 08 '16 at 19:54
  • see the [SSLLabs report](https://www.ssllabs.com/ssltest/analyze.html?d=google.com&s=216.58.192.4&hideResults=on) and look for the issuer of certificate#3: "Equifax / Equifax Secure Certificate Authority". But it is still strange that verification fails if your CA file includes the GeoTrust Global CA. – Steffen Ullrich Jan 08 '16 at 20:23
  • @SteffenUllrich Yea, I'm not sure. I pulled the GeoTrust Global CA out into its own cert. It looks to be the same exact as google's root ca. Same public key, preferred alg, subject key. But, the handshake fails. – disflux Jan 08 '16 at 20:33

1 Answers1

5

I'm new to this, so any help would be greatly appreciated

This is actually an issue were not only newbies despair. The issue is complex and depends on specific implementations and bugs in the TLS libraries involved. Thus it is not really your fault that it does not work as expected. I hope the following explanation can be understood:

There are actually several trust path to a trusted certificate. What the server sends as certificates is this:

#0 CN=www.google.com SAN=DNS:www.google.com
#1 CN=Google Internet Authority G2
#2 CN=GeoTrust Global CA

The last certificate is signed by #3 OU=Equifax Secure Certificate Authority which is not included in the chain sent by the server.

The NSS library used in Chrome and Firefox starts at the top and checks for each certificate if it can find a trusted CA which signed this certificate, i.e. a certificate which exists in the local trust store. It will stop with the validation at certificate #1, because it has a local certificate for GeoTrust Global CA which can be used to validate certificate #1. Thus it is done with validation and ignores certificate #2.

OpenSSL up to version 1.0.1 (at least up to 1.0.1p) works different. It will look at the whole chain and try to find a trust anchor for the last certificate in the chain. Since often servers send the root certificate inside the chain (which is wrong) it will eliminate all self-signed certificates from the chain before it will look for the trust anchor (since root certificates are usually self-signed).

In this case the certificate #2 for "GeoTrust Global CA" is not a self-signed certificate (signed by Equifax) and will not be removed. It will thus try to find the issuer for this certificate #2 "GeoTrust Global CA" in the trust store to built the trust chain. If you included the certificate #3 (Equifax) in the trust store (file name in verify parameter) then all is fine and the validation succeeds. If you only include the "GeoTrust Global CA" in the trust store the validation fails because this is not the correct trust anchor for certificate #2. OpenSSL 1.0.1 is too dumb to realize that this would be the perfect trust anchor for certificate #1 and thus the validation fails.

The situation was changed finally (more than 3 years after I reported this kind of bug) in OpenSSL 1.0.2. If the first attempt to validate the chain fails it will remove the last certificate from the chain (certificate #2) and retry. In this case it will succeed. That means if you would use a version of Python built with OpenSSL 1.0.2 then it should be fine. For more details about the different ways of validating the trust path see the last comment at OpenSSL bug#3621.

Steffen Ullrich
  • 190,458
  • 29
  • 381
  • 434