I'm trying to load a public key using the cryptography module, this is what the key looks like:
>>> print(pubkey)
-----BEGIN RSA PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+6gvHdCUCjnc4hSMwbdIIspk4
69pVAzjjb8tDJsCH/QpiK9vXe4nDZ7p9kiw2ACw0fkWaPnApKBwXNB9Nd9Sf+XFt
cIzdqKKBcAqZZCu2pA729amNRug9DoZdkstaBG+VfTxXhdzQRSTxxqJQWgdV8ejK
kt4D1M6pAiTkAyD0eQIDAQAB
-----END RSA PUBLIC KEY-----
I'm trying to load it using the load_pem_public_key()
method:
>>> from cryptography.hazmat.backends import default_backend
>>> from cryptography.hazmat.primitives.serialization import load_pem_public_key
>>> load_pem_public_key(pubkey, default_backend())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/elias/.virtualenvs/ckpypkg/local/lib/python2.7/site-packages/cryptography/hazmat/primitives/serialization.py", line 24, in load_pem_public_key
return backend.load_pem_public_key(data)
File "/home/elias/.virtualenvs/ckpypkg/local/lib/python2.7/site-packages/cryptography/hazmat/backends/multibackend.py", line 285, in load_pem_public_key
return b.load_pem_public_key(data)
File "/home/elias/.virtualenvs/ckpypkg/local/lib/python2.7/site-packages/cryptography/hazmat/backends/openssl/backend.py", line 1376, in load_pem_public_key
self._handle_key_loading_error()
File "/home/elias/.virtualenvs/ckpypkg/local/lib/python2.7/site-packages/cryptography/hazmat/backends/openssl/backend.py", line 1595, in _handle_key_loading_error
raise ValueError("Could not unserialize key data.")
ValueError: Could not unserialize key data.
Am I doing something wrong? Is there something wrong with this key? Why can't it be unserialized?
OpenSSL version:
$ openssl version
OpenSSL 1.0.1f 6 Jan 2014
UPDATE: I just tested the same code with a different key (the same one from this other SO question) and it worked, which makes this yet more puzzling: why it works for that key and not for mine?
Importing an RSA Key Container You can use the Aspnet_regiis.exe tool with the –pi switch to import an RSA key container from an XML file. You must also specify whether the imported key container is a machine-level or user-level key container.
Python-RSA is an RSA implementation written entirely in Python. According to PKCS#1 version 1.5, it provides encryption and decryption, signing and checking signatures, and key creation. It can be used both on the command line and as a Python library.
Long story short, apparently your PEM has the header and footer of PKCS#1 format(-----BEGIN RSA PUBLIC KEY-----
and -----END RSA PUBLIC KEY-----
) but contains the DER sequence for PKCS#8 format, because of that load_pem_public_key
cannot unserialize the pem properly, because it expects an PKCS#1 DER format, but receives an PKCS#8 format. Quick fix by replacing header and footer with those corresponding to PKCS#8 format.
In your pem file replace -----BEGIN RSA PUBLIC KEY-----
with -----BEGIN PUBLIC KEY-----
, and -----END RSA PUBLIC KEY-----
with -----END PUBLIC KEY-----
Your public key should look like:
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+6gvHdCUCjnc4hSMwbdIIspk4
69pVAzjjb8tDJsCH/QpiK9vXe4nDZ7p9kiw2ACw0fkWaPnApKBwXNB9Nd9Sf+XFt
cIzdqKKBcAqZZCu2pA729amNRug9DoZdkstaBG+VfTxXhdzQRSTxxqJQWgdV8ejK
kt4D1M6pAiTkAyD0eQIDAQAB
-----END PUBLIC KEY-----
otherwise the cryptography module will not be able to parse it.
EDIT
-----BEGIN RSA PUBLIC KEY-----
is for PKCS#1, and -----BEGIN PUBLIC KEY-----
is for PKCS#8
You can check out your DER format by doing the following:
from Crypto.Util.asn1 import DerSequence
public_key_der = DerSequence()
public_key_der.decode('MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+6gvHdCUCjnc4hSMwbdIIspk469pVAzjjb8tDJsCH/QpiK9vXe4nDZ7p9kiw2ACw0fkWaPnApKBwXNB9Nd9Sf+XFtcIzdqKKBcAqZZCu2pA729amNRug9DoZdkstaBG+VfTxXhdzQRSTxxqJQWgdV8ejKkt4D1M6pAiTkAyD0eQIDAQAB'.decode('base64'));
for k,v in enumerate(public_key_der):
print k, v
you will notice that your public_key_der[0]
is another DER Sequence(you can actually decode it again: public_key_der.decode(public_key_der[0])
), and represents the AlgorithmIdentifier sequence from PKCS#8 DER format, if it were PKCS#1 public_key_der[0]
should have an INTEGER which represents the modulus.
more info about PKCS#8 vs PKCS#1 formats can be found here: https://tls.mbed.org/kb/cryptography/asn1-key-structures-in-der-and-pem
You are trying to treat the key like a PEM encoded RSA key, but in fact what you have is public key using PKCS#1 format. The data between -----BEGIN RSA PUBLIC KEY-----
and -----END RSA PUBLIC KEY-----
is actually just base-64 encoded DER data. There may be a library function to get at this (I started looking through the cryptography
documentation and my eyes started to glaze over), but the following will work...
We start with your key data:
>>> print pubkey
-----BEGIN RSA PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+6gvHdCUCjnc4hSMwbdIIspk4
69pVAzjjb8tDJsCH/QpiK9vXe4nDZ7p9kiw2ACw0fkWaPnApKBwXNB9Nd9Sf+XFt
cIzdqKKBcAqZZCu2pA729amNRug9DoZdkstaBG+VfTxXhdzQRSTxxqJQWgdV8ejK
kt4D1M6pAiTkAyD0eQIDAQAB
-----END RSA PUBLIC KEY-----
We drop the BEGIN
and END
lines:
>>> b64data = '\n'.join(pubkey.splitlines()[1:-1])
>>> print b64data
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+6gvHdCUCjnc4hSMwbdIIspk4
69pVAzjjb8tDJsCH/QpiK9vXe4nDZ7p9kiw2ACw0fkWaPnApKBwXNB9Nd9Sf+XFt
cIzdqKKBcAqZZCu2pA729amNRug9DoZdkstaBG+VfTxXhdzQRSTxxqJQWgdV8ejK
kt4D1M6pAiTkAyD0eQIDAQAB
And then we base64 decode the data:
>>> derdata = base64.b64decode(b64data)
Now we have the DER encoded public key, so we can feed this to
load_der_public_key
:
>>> from cryptography.hazmat.backends import default_backend
>>> from cryptography.hazmat.primitives.serialization import load_der_public_key
>>> key = load_der_public_key(derdata, default_backend())
>>> print key
<cryptography.hazmat.backends.openssl.rsa._RSAPublicKey object at 0x7fe590ea6d10>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With