If you use the OpenSSL
library in Python, you have get_peer_cert_chain
that you can apply on your connection object, it will give you a list of certificates as sent by the server, so the end certificate with all intermediates one if needed.
See https://pyopenssl.org/en/stable/api/ssl.html#connection-objects :
get_peer_cert_chain()
Retrieve the other side’s certificate (if any)
Returns: A list of X509 instances giving the peer’s certificate chain, or None if it does not have one.
Here is one crude example (without any error handling):
from OpenSSL import SSL
import socket
dst = ('www.google.com', 443)
ctx = SSL.Context(SSL.SSLv23_METHOD)
s = socket.create_connection(dst)
s = SSL.Connection(ctx, s)
s.set_connect_state()
s.set_tlsext_host_name(dst[0])
s.sendall('HEAD / HTTP/1.0\n\n')
s.recv(16)
certs = s.get_peer_cert_chain()
for pos, cert in enumerate(certs):
print "Certificate #" + str(pos)
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')
which gives:
Certificate #0
Subject C: US
Subject ST: California
Subject L: Mountain View
Subject O: Google LLC
Subject CN: www.google.com
notBefore:20180612133452Z
notAfter:20180821121300Z
version:2
sigAlg:sha256WithRSAEncryption
digest:06:C5:12:EB:3C:B1:7F:AB:18:E0:D5:22:E4:25:12:A7:30:AA:27:16:0B:3A:99:CB:3D:11:CF:12:EF:95:2E:41
Certificate #1
Subject C: US
Subject O: Google Trust Services
Subject CN: Google Internet Authority G3
notBefore:20170615000042Z
notAfter:20211215000042Z
version:2
sigAlg:sha256WithRSAEncryption
digest:BE:0C:CD:54:D4:CE:CD:A1:BD:5E:5D:9E:CC:85:A0:4C:2C:1F:93:A5:22:0D:77:FD:E8:8F:E9:AD:08:1F:64:1B
So you have the full detailed content of the certificate, see https://pyopenssl.org/en/stable/api/crypto.html#x509-objects for available info.
Then you have to_cryptography()
to be able to get its PEM version with something like: cert.to_cryptography().public_bytes(serialization.Encoding.PEM)
But take into account also that:
- it would be better to use a callback, see the
set_info_callback()
method, so that the diagnostics are run at the appropriate time (even type SSL.SSL_CB_HANDSHAKE_DONE
)
- I have observed you need to exchange some traffic (send/recv) before being able to call
get_peer_cert_chain()
(if you move it before sendall()
in my example, it returns None
)
From the link in your question, it seems you have the equivalent with getpeercertchain()
when just using ssl
and not OpenSSL
; however this still seems to be recorded as a bug with some patches available and may not be released.
In fact the latest documentation at https://docs.python.org/3.8/library/ssl.html does not list getpeercertchain()
.