Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I retrieve the TLS/SSL peer certificate of a remote host using python?

I need to scan through a list of IPs and retrieve the common name from the certificate on that IP (for every IP that allows port 443 connections). I have been able to successfully do this using the sockets and ssl modules. It works for all IPs with valid, signed certificates but it isn't working for self-signed certificates.

If I use this method, it requires a valid cert that is verified by my CA-bundle:

from socket import socket import ssl  s = socket() c = ssl.wrap_socket(s,cert_reqs=ssl.CERT_REQUIRED, ca_certs='ca-bundle.crt') c.connect(('127.0.0.1', 443))  print c.getpeercert() 

If I remove the cert_reqs=ssl.CERT_REQUIRED then it connects but doesn't get the certificate at all.

How can I retrieve the common name for a certificate on an IP whether it validates against the ca-bundle or not?

like image 812
Dave Forgac Avatar asked Oct 07 '11 16:10

Dave Forgac


People also ask

How do I get an SSL certificate in Python?

To install certifi Python on Linux: Open the terminal or shell in your Linux OS. Type python3 -m pip install certifi, and hit Enter. If it doesn't work, try using this command: pip3 install certifi or python -m pip install certifi.

How can I get certificate issuer information in Python?

Use pyOpenSSL. You can also access additional components, e.g. organisation ( subject. O / issuer. O ), organisational unit ( subject.


Video Answer


2 Answers

The python ssl library seems like it only parses out the cert for you if it has a valid signature.

    """Returns a formatted version of the data in the     certificate provided by the other end of the SSL channel.     Return None if no certificate was provided, {} if a     certificate was provided, but not validated.""" 

You can still get the server certificate with the ssl.get_server_certificate() function, but it returns it in PEM format. (Alternatively, you could call c.getpeercert(True), which returns the cert in binary DER format, whether it's validated or not.)

>>> print ssl.get_server_certificate(('server.test.com', 443)) -----BEGIN CERTIFICATE----- MIID4zCCAsugAwIBA..... 

From here, I would use M2Crypto or OpenSSL to read the cert and get values:

# M2Crypto cert = ssl.get_server_certificate(('www.google.com', 443)) x509 = M2Crypto.X509.load_cert_string(cert) x509.get_subject().as_text() # 'C=US, ST=California, L=Mountain View, O=Google Inc, CN=www.google.com'  # OpenSSL x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert) x509.get_subject().get_components() #[('C', 'US'), # ('ST', 'California'), # ('L', 'Mountain View'), # ('O', 'Google Inc'), # ('CN', 'www.google.com')] 
like image 121
JimB Avatar answered Sep 21 '22 04:09

JimB


On Mac you need to install swig and M2Crypto

On terminal run:

brew install swig 

And then:

sudo pip install m2crypto 

Then you can run the code above:

from socket import socket import ssl import M2Crypto import OpenSSL  # M2Crypto cert = ssl.get_server_certificate(('www.google.com', 443)) x509 = M2Crypto.X509.load_cert_string(cert) print x509.get_subject().as_text() # 'C=US, ST=California, L=Mountain View, O=Google Inc, CN=www.google.com'  # OpenSSL x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert) print x509.get_subject().get_components() #[('C', 'US'), # ('ST', 'California'), # ('L', 'Mountain View'), # ('O', 'Google Inc'), # ('CN', 'www.google.com')] 
like image 44
Gil Allen Avatar answered Sep 21 '22 04:09

Gil Allen