Simple DER Cert Parsing in python

Question:

Which is the best way to parse with python a binary file with X509 Certificate in DER format to extract public key.

Asked By: David A

||

Answers:

Neither the built-in SSL module of Python nor PyOpenSSL have an API to extract the private key and access its information. M2Crypto is no longer maintained and doesn’t work with OpenSSL 1.0 and newer.

PyOpenSSL has a public key class but its features are limited:

>>> with open("cert.der", "rb") as f:
...     der = f.read()
... 
>>> import OpenSSL.crypto
>>> x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_ASN1, der)
>>> pkey = x509.get_pubkey()
>>> dir(pkey)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'bits', 'check', 'generate_key', 'type']
>>> pkey.bits()
4096L
>>> pkey.type() == OpenSSL.crypto.TYPE_RSA
True

Python 3.4 may get a X509 type that exposes more information like SPKI.

Answered By: Christian Heimes

It’s been a long time since I asked this question, though due to the number of views, I would like to describe how I managed to make it worked.

By using the OpenSSL API, we can easily print the DER certificate in a pretty readable way. Even though its features are very limited, this is an example.

print OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_TEXT,x509)

However, I wanted to have control over the keys in a var directly (it was needed to send it to other function), and in order to do that, I made the following function

def parse_asn1_der(derfile):
    from pyasn1_modules import  rfc2437,rfc2459
    from pyasn1.codec.der import decoder
    certType = rfc2459.Certificate(); 
    raw=derfile #the result of open(fname,"rb").read()
    cert,rest = decoder.decode(raw, asn1Spec=certType)
    RSAKEYSDATA=frombits(cert.getComponentByName("tbsCertificate").getComponentByName("subjectPublicKeyInfo").getComponentByName("subjectPublicKey"))
    SignatureCert=frombits(cert.getComponentByName("signatureValue")).encode("hex")
    rsaType=rfc2437.RSAPublicKey();
    rsadata,rsadata_rest = decoder.decode(RSAKEYSDATA, asn1Spec=rsaType)
    print "----"
    print "Certificate Plain Data"
    print "RSA Modulus: %X" %rsadata.getComponentByName("modulus")
    print "RSA Public exponent: %X" %rsadata.getComponentByName("publicExponent")
    print "Signature: %s"%SignatureCert
    return rsadata.getComponentByName("modulus")

Hope it helps anyone looking around.

Answered By: David A

The above answers are somewhat old (as of 2017).

You can use asn1crypto to do this in a nicer way:

from asn1crypto.x509 import Certificate

with open("mycert.der", "rb") as f:
    cert = Certificate.load(f.read())

n = cert.public_key.native["public_key"]["modulus"]
e = cert.public_key.native["public_key"]["public_exponent"]

print("{:#x}".format(n))    # prints the modulus (hexadecimal)
print("{:#x}".format(e))    # same, for the public exponent

It’s relatively new (from what I can see, mid-2015), provides a nicer interface than the libraries already mentioned, and is much faster than pyasn1 according to the author.

Update (2022-07-30): The cryptography package is another way to do it:

from cryptography import x509

with open("mycert.der", "rb") as f:
    cert = x509.load_der_x509_certificate(f.read())

modulus = cert.public_key().public_numbers().n
public_exponent = cert.public_key().public_numbers().e
Answered By: De117