Which is the best way to parse with python a binary file with X509 Certificate in DER format to extract public key.
-
here's [code examples on how to convert certificates to json using any of: asn1crypto, pyOpenSSL, M2Crypto, cryptography, pyasn1 packages (text in Russian, code in Python)](https://ru.stackoverflow.com/a/464445/23044). Here's [a stdlib variant (`ssl._ssl._test_decode_cert`) and `ctypes` variant for comparison (not recommended) -- serial number is extracted in the examples](https://ru.stackoverflow.com/a/574662/23044) – jfs Apr 06 '20 at 16:34
3 Answers
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

- 351
- 2
- 11
-
the modulus print command is throwing error as ```ValueError: Error parsing asn1crypto.x509.Certificate - tag should have been 16, but 13 was found``` Why is it so? – Bogota Jun 17 '20 at 03:51
-
1This can happen if you're trying to read a PEM-formatted certificate. The above code works with a DER-encoded one. (See [here](https://serverfault.com/questions/9708/what-is-a-pem-file-and-how-does-it-differ-from-other-openssl-generated-key-file) for more information on certificate formats.) Conversion from PEM to DER should be a matter of just stripping the header/footer and base64-decoding the middle part. – De117 Jun 19 '20 at 10:27
-
@Bogota It looks like the `asn1cypto.pem` module will do the conversion for you -- see [this documentation page](https://github.com/wbond/asn1crypto/blob/master/docs/pem.md). If using `cryptography.x509`, there is the `load_pem_x509_certificate` function, which loads PEM-formatted certificates. `asn1crypto.pem` offers a `detect` function which lets you [look before you leap](https://stackoverflow.com/questions/11360858/what-is-the-eafp-principle-in-python), whereas with `cryptography.x509` functions you just try one and then the other. – De117 Jul 30 '22 at 16:16
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.

- 1,365
- 10
- 10
-
exist any way to print pretty all the parsed certificated (just as pyasn1.printPretty()) or at least to print pubkey as hexstring. – David A Sep 15 '13 at 03:07
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.

- 763
- 1
- 9
- 22