How can I decode a SSL certificate using python?

前端 未结 5 1866
半阙折子戏
半阙折子戏 2020-12-02 14:42

How can I decode a pem-encoded (base64) certificate with Python? For example this here from github.com:

-----BEGIN CERTIFICATE-----
MIIHKjCCBhKgAwIBAgIQDnd2i         


        
相关标签:
5条回答
  • 2020-12-02 14:55

    You can use pyasn1 and pyasn1-modules packages to parse this kind of data. For instance:

    from pyasn1_modules import pem, rfc2459
    from pyasn1.codec.der import decoder
    
    substrate = pem.readPemFromFile(open('cert.pem'))
    cert = decoder.decode(substrate, asn1Spec=rfc2459.Certificate())[0]
    print(cert.prettyPrint())
    

    Read the docs for pyasn1 for the rest.

    0 讨论(0)
  • 2020-12-02 14:57

    This code dumps a cert file content:

    import OpenSSL.crypto
    
    cert = OpenSSL.crypto.load_certificate(
          OpenSSL.crypto.FILETYPE_PEM,
          open('/path/to/cert/file.crt').read()
    )
    
    print OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_TEXT, cert)
    

    Give it a go.

    0 讨论(0)
  • 2020-12-02 14:58

    Python's standard library, even in the latest version, does not include anything that can decode X.509 certificates. However, the add-on cryptography package does support this. Quoting an example from the documentation:

    >>> from cryptography import x509
    >>> from cryptography.hazmat.backends import default_backend
    >>> cert = x509.load_pem_x509_certificate(pem_data, default_backend())
    >>> cert.serial_number
    2
    

    Another add-on package that might be an option is pyopenssl. This is a thin wrapper around the OpenSSL C API, which means it will be possible to do what you want, but expect to spend a couple days tearing your hair out at the documentation.

    If you can't install Python add-on packages, but you do have the openssl command-line utility,

    import subprocess
    cert_txt = subprocess.check_output(["openssl", "x509", "-text", "-noout", 
                                        "-in", certificate])
    

    should produce roughly the same stuff you got from your web utility in cert_txt.

    Incidentally, the reason doing a straight-up base64 decode gives you binary gobbledygook is that there are two layers of encoding here. X.509 certificates are ASN.1 data structures, serialized to X.690 DER format and then, since DER is a binary format, base64-armored for ease of file transfer. (A lot of the standards in this area were written way back in the nineties when you couldn’t reliably ship anything but seven-bit ASCII around.)

    0 讨论(0)
  • 2020-12-02 15:02

    I am not Sure how you received it but another simple way for getting it installed is to just write it as a binary file and then run it using os

    import os
    
    cert= function_gives_binary_cert()
    with open('RecvdCert.der','wb') as file:
         file.write(cert)
    
    os.startfile('RecvdCert.der')
    

    Beware of running received binary from an unknown source. Just want to decode then use OpenSSL as mentioned in other answers.

    0 讨论(0)
  • 2020-12-02 15:09

    Notes:

    • Everything relies on (!!!undocumented!!!) ssl._ssl._test_decode_cert
      (present in Python 3(.7) / Python 2), no additional module(s) needed
    • Please, take a look at [SO]: Can't receive peer certificate in Python client using OpenSSL's ssl.SSLContext() (@CristiFati's answer), which addresses a wider problem

    Regarding the certificate (PEM) from the question:

    • Saved it in a file called q016899247.crt (in the script (code00.py) dir)
    • The end tag: ("-----END CERTIFICATE----") was missing a hyphen (-) at the end; corrected in Question @VERSION #4.)

    code00.py:

    #!/usr/bin/env python3
    
    import sys
    import os
    import ssl
    import pprint
    
    
    def main():
        cert_file_base_name = "q016899247.crt"
        cert_file_name = os.path.join(os.path.dirname(__file__), cert_file_base_name)
        try:
            cert_dict = ssl._ssl._test_decode_cert(cert_file_name)
        except Exception as e:
            print("Error decoding certificate: {0:}".format(e))
        else:
            print("Certificate ({0:s}) data:\n".format(cert_file_base_name))
            pprint.pprint(cert_dict)
    
    
    if __name__ == "__main__":
        print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
        main()
        print("\nDone.")
    

    Output:

    [cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q016899247]> "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\Scripts\python.exe" code00.py
    Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] 64bit on win32
    
    Certificate (q016899247.crt) data:
    
    {'OCSP': ('http://ocsp.digicert.com',),
     'caIssuers': ('http://www.digicert.com/CACerts/DigiCertHighAssuranceEVCA-1.crt',),
     'crlDistributionPoints': ('http://crl3.digicert.com/ev2009a.crl',
                               'http://crl4.digicert.com/ev2009a.crl'),
     'issuer': ((('countryName', 'US'),),
                (('organizationName', 'DigiCert Inc'),),
                (('organizationalUnitName', 'www.digicert.com'),),
                (('commonName', 'DigiCert High Assurance EV CA-1'),)),
     'notAfter': 'Jul 29 12:00:00 2013 GMT',
     'notBefore': 'May 27 00:00:00 2011 GMT',
     'serialNumber': '0E77768A5D07F0E57959CA2A9D5082B5',
     'subject': ((('businessCategory', 'Private Organization'),),
                 (('jurisdictionCountryName', 'US'),),
                 (('jurisdictionStateOrProvinceName', 'California'),),
                 (('serialNumber', 'C3268102'),),
                 (('countryName', 'US'),),
                 (('stateOrProvinceName', 'California'),),
                 (('localityName', 'San Francisco'),),
                 (('organizationName', 'GitHub, Inc.'),),
                 (('commonName', 'github.com'),)),
     'subjectAltName': (('DNS', 'github.com'), ('DNS', 'www.github.com')),
     'version': 3}
    
    Done.
    
    0 讨论(0)
提交回复
热议问题