Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to load a RSA public key using Python's cryptography module

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?

like image 566
Elias Dorneles Avatar asked Sep 27 '15 02:09

Elias Dorneles


People also ask

How do I import my RSA key?

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.

What is RSA module in Python?

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.


2 Answers

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

like image 143
godvsdeity Avatar answered Oct 26 '22 13:10

godvsdeity


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>
like image 43
larsks Avatar answered Oct 26 '22 14:10

larsks