Getting certificate chain with Python 3.3 SSL module

后端 未结 3 868
萌比男神i
萌比男神i 2021-01-12 04:53

I can get the standard certificate information for an SSL connection in Python 3.3 via the getpeercert() method on the SSL socket. However, it doesn\'t seem to provide the c

相关标签:
3条回答
  • 2021-01-12 05:34

    The answer above did not work out of the box.

    After going through many options, I found this to be the simplest approach which requires minimum 3rd party libraries.

    pip install pyopenssl certifi

    import socket
    from OpenSSL import SSL
    import certifi
    
    hostname = 'www.google.com'
    port = 443
    
    
    context = SSL.Context(method=SSL.TLSv1_METHOD)
    context.load_verify_locations(cafile=certifi.where())
    
    conn = SSL.Connection(context, socket=socket.socket(socket.AF_INET, socket.SOCK_STREAM))
    conn.settimeout(5)
    conn.connect((hostname, port))
    conn.setblocking(1)
    conn.do_handshake()
    conn.set_tlsext_host_name(hostname.encode())
    for (idx, cert) in enumerate(conn.get_peer_cert_chain()):
        print(f'{idx} subject: {cert.get_subject()}')
        print(f'  issuer: {cert.get_issuer()})')
        print(f'  fingerprint: {cert.digest("sha1")}')
    
    conn.close()
    

    Here is a link to the original idea https://gist.github.com/brandond/f3d28734a40c49833176207b17a44786

    Here is a reference which brought me here How to get response SSL certificate from requests in python?

    0 讨论(0)
  • 2021-01-12 05:41

    I'm not sure, but I think that part of the OpenSSL API just isn't available in Python's ssl-module.

    It seems that the function SSL_get_peer_cert_chain is used to access the certificate chain in OpenSSL. See, for example, the section of openssl s_client that prints the output you included. On the other hand, grepping the source of Python's ssl-module for SSL_get_peer_cert_chain yields no matches.

    M2Crypto and pyOpenSSL both seem to include a get_peer_cert_chain function, if you're willing to look at other (and non-stdlib) libraries. I can't vouch for them personally, though, since I haven't used them much.

    0 讨论(0)
  • 2021-01-12 05:46

    Thanks to the contributing answer by Aleksi, I found a bug/feature request that already requested this very thing: http://bugs.python.org/issue18233. Though the changes haven't been finalized, yet, they do have a patch that makes this available:

    This is the test code which I've stolen from some forgotten source and reassembled:

    import socket
    
    from ssl import wrap_socket, CERT_NONE, PROTOCOL_SSLv23
    from ssl import SSLContext  # Modern SSL?
    from ssl import HAS_SNI  # Has SNI?
    
    from pprint import pprint
    
    def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None,
                        ca_certs=None, server_hostname=None,
                        ssl_version=None):
        context = SSLContext(ssl_version)
        context.verify_mode = cert_reqs
    
        if ca_certs:
            try:
                context.load_verify_locations(ca_certs)
            # Py32 raises IOError
            # Py33 raises FileNotFoundError
            except Exception as e:  # Reraise as SSLError
                raise SSLError(e)
    
        if certfile:
            # FIXME: This block needs a test.
            context.load_cert_chain(certfile, keyfile)
    
        if HAS_SNI:  # Platform-specific: OpenSSL with enabled SNI
            return (context, context.wrap_socket(sock, server_hostname=server_hostname))
    
        return (context, context.wrap_socket(sock))
    
    hostname = 'www.google.com'
    print("Hostname: %s" % (hostname))
    
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((hostname, 443))
    
    (context, ssl_socket) = ssl_wrap_socket(s,
                                           ssl_version=2, 
                                           cert_reqs=2, 
                                           ca_certs='/usr/local/lib/python3.3/dist-packages/requests/cacert.pem', 
                                           server_hostname=hostname)
    
    pprint(ssl_socket.getpeercertchain())
    
    s.close()
    

    Output:

    Hostname: www.google.com
    ({'issuer': ((('countryName', 'US'),),
                 (('organizationName', 'Google Inc'),),
                 (('commonName', 'Google Internet Authority G2'),)),
      'notAfter': 'Sep 11 11:04:38 2014 GMT',
      'notBefore': 'Sep 11 11:04:38 2013 GMT',
      'serialNumber': '50C71E48BCC50676',
      'subject': ((('countryName', 'US'),),
                  (('stateOrProvinceName', 'California'),),
                  (('localityName', 'Mountain View'),),
                  (('organizationName', 'Google Inc'),),
                  (('commonName', 'www.google.com'),)),
      'subjectAltName': (('DNS', 'www.google.com'),),
      'version': 3},
     {'issuer': ((('countryName', 'US'),),
                 (('organizationName', 'GeoTrust Inc.'),),
                 (('commonName', 'GeoTrust Global CA'),)),
      'notAfter': 'Apr  4 15:15:55 2015 GMT',
      'notBefore': 'Apr  5 15:15:55 2013 GMT',
      'serialNumber': '023A69',
      'subject': ((('countryName', 'US'),),
                  (('organizationName', 'Google Inc'),),
                  (('commonName', 'Google Internet Authority G2'),)),
      'version': 3},
     {'issuer': ((('countryName', 'US'),),
                 (('organizationName', 'Equifax'),),
                 (('organizationalUnitName',
                   'Equifax Secure Certificate Authority'),)),
      'notAfter': 'Aug 21 04:00:00 2018 GMT',
      'notBefore': 'May 21 04:00:00 2002 GMT',
      'serialNumber': '12BBE6',
      'subject': ((('countryName', 'US'),),
                  (('organizationName', 'GeoTrust Inc.'),),
                  (('commonName', 'GeoTrust Global CA'),)),
      'version': 3},
     {'issuer': ((('countryName', 'US'),),
                 (('organizationName', 'Equifax'),),
                 (('organizationalUnitName',
                   'Equifax Secure Certificate Authority'),)),
      'notAfter': 'Aug 22 16:41:51 2018 GMT',
      'notBefore': 'Aug 22 16:41:51 1998 GMT',
      'serialNumber': '35DEF4CF',
      'subject': ((('countryName', 'US'),),
                  (('organizationName', 'Equifax'),),
                  (('organizationalUnitName',
                    'Equifax Secure Certificate Authority'),)),
      'version': 3})
    
    0 讨论(0)
提交回复
热议问题