12

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?

Community
  • 1
  • 1
Elias Dorneles
  • 22,556
  • 11
  • 85
  • 107

2 Answers2

17

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>
larsks
  • 277,717
  • 41
  • 399
  • 399
  • And in this answer, we're doing pretty much the same thing as in the first answer in the [question to which you linked](http://stackoverflow.com/questions/10569189/how-to-read-a-rsa-public-key-in-pem-pkcs1-format)... – larsks Sep 27 '15 at 03:31
11

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

godvsdeity
  • 969
  • 8
  • 15
  • uh, are you sure? because that would be so weird! can you point to any source confirming that behavior? – Elias Dorneles Sep 27 '15 at 03:12
  • This can't be the reason, I just tested with another key (from [this question](http://stackoverflow.com/questions/10569189/how-to-read-a-rsa-public-key-in-pem-pkcs1-format) and it worked. – Elias Dorneles Sep 27 '15 at 03:14
  • 1
    -----BEGIN RSA PUBLIC KEY----- is for PKCS#1 standard, and -----BEGIN PUBLIC KEY----- is for PKCS#8 standard. I decoded your public key and it's der sequence fits that of PKCS#8. You can find more info about how DER sequence differs from PKCS#1 vs PKCS#8 here: https://tls.mbed.org/kb/cryptography/asn1-key-structures-in-der-and-pem. Btw, I did test my answer before posting it, and it worked. You should've at least try it before giving up. – godvsdeity Sep 27 '15 at 03:57
  • Thanks for that reference -- yeah, I had tried it and noticed that it didn't fail, but was not sure it had actually done the right thing, so I was hoping for an explanation which you've now given, so thank you. :) – Elias Dorneles Sep 27 '15 at 04:07
  • Great, it's much clear now, upvoted! Yeah, I suppose I should tell Travis-CI folks, I'm getting the PEM from their API with: `curl https://api.travis-ci.org/repos/scrapinghub/skinfer/key` – Elias Dorneles Sep 27 '15 at 04:31