Search code examples
pythonsslm2crypto

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?


Solution

  • 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')]