Reputation: 821
Which is the best way to parse with python a binary file with X509 Certificate in DER format to extract public key.
Upvotes: 12
Views: 30163
Reputation: 431
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
Upvotes: 21
Reputation: 821
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.
Upvotes: 5
Reputation: 1415
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.
Upvotes: 13