How can I decode a pem-encoded (base64) certificate with Python? For example this here from github.com:
-----BEGIN CERTIFICATE-----
MIIHKjCCBhKgAwIBAgIQDnd2i
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.
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.
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.)
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.
Notes:
ssl._ssl._test_decode_cert
Regarding the certificate (PEM) from the question:
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.