for a class I'm looking at the TLS certificate chain for google.com
. When I click in the Chrome or Firefox browser, the root certificate is shown as GTS Root R1
with a validity up to 2036, self-signed, so it must be a root-certificate.
However, if I check the same in Python using the following code, I get a GTS Root R1
certificate with a validity of 2028, which is signed by GlobalSign nv-sa
, so this time it's NOT a root-certificate!
Is it possible that Google.com returns two different certificate chains, depending on which client does the request? If it supposes that the client accepts the GTS Root R1
as root certificate, it returns this one, else it returns one signed by GlobalSign nv-sa
?
If so, why?
The following is the chain with the certificates and their digest/sha256. Now when I look at the certificate chain in the browser, the first two have the same digest
/ sha-256
, but the third one has a different digest
. So I definitely think I'm getting a different chain depending on the client...
Certificate #0
Subject b'CN': b'*.google.com'
notBefore: b'20211101021952Z'
notAfter: b'20220124021951Z'
version:2
sigAlg: b'sha256WithRSAEncryption'
digest: b'E9:7C:86:18:34:DE:F4:11:4D:2D:5E:6F:1A:49:22:A1:04:EE:9E:7C:8D:CB:72:3F:6D:67:58:8F:7E:F3:4B:AB'
issuer: <X509Name object '/C=US/O=Google Trust Services LLC/CN=GTS CA 1C3'>
Certificate #1
Subject b'C': b'US'
Subject b'O': b'Google Trust Services LLC'
Subject b'CN': b'GTS CA 1C3'
notBefore: b'20200813000042Z'
notAfter: b'20270930000042Z'
version:2
sigAlg: b'sha256WithRSAEncryption'
digest: b'23:EC:B0:3E:EC:17:33:8C:4E:33:A6:B4:8A:41:DC:3C:DA:12:28:1B:BC:3F:F8:13:C0:58:9D:6C:C2:38:75:22'
issuer: <X509Name object '/C=US/O=Google Trust Services LLC/CN=GTS Root R1'>
Certificate #2
Subject b'C': b'US'
Subject b'O': b'Google Trust Services LLC'
Subject b'CN': b'GTS Root R1'
notBefore: b'20200619000042Z'
notAfter: b'20280128000042Z'
version:2
sigAlg: b'sha256WithRSAEncryption'
digest: b'3E:E0:27:8D:F7:1F:A3:C1:25:C4:CD:48:7F:01:D7:74:69:4E:6F:C5:7E:0C:D9:4C:24:EF:D7:69:13:39:18:E5'
issuer: <X509Name object '/C=BE/O=GlobalSign nv-sa/OU=Root CA/CN=GlobalSign Root CA'>
The python code I used to fetch the certificate:
from OpenSSL import SSL, crypto
import socket, certifi
def dump_cert(cert):
for component in cert.get_subject().get_components():
print("Subject %s: %s" % (component))
print("notBefore:", cert.get_notBefore())
print("notAfter:", cert.get_notAfter())
print("version:" + str(cert.get_version()))
print("sigAlg:", cert.get_signature_algorithm())
print("digest:", cert.digest('sha256'))
print("issuer:", cert.get_issuer())
print()
def get_connection_chain(host, port = 443):
dst = (str.encode(host), port)
ctx = SSL.Context(SSL.TLSv1_2_METHOD)
s = socket.create_connection(dst)
s = SSL.Connection(ctx, s)
s.set_connect_state()
s.set_tlsext_host_name(dst[0])
s.sendall(b'HEAD / HTTP/1.2\n\n')
s.recv(16)
return (s, s.get_peer_cert_chain())
def dump_chain(chain):
for pos, cert in enumerate(chain):
print("Certificate #" + str(pos))
dump_cert(cert)
conn, chain = get_connection_chain("google.ch")
dump_chain(chain)