Loading a PKCS#1 public key using M2Crypto

M

Marc Aymerich

Hi,
I've been trying very, very hard to load an RSA key using M2Crypto but without any success.

basically this is what I'm trying to do:.... MIIBCgKCAQEApwotnfHT9RAmxnuaGEMdI3lYPYE4aaqSD9v4KbTh1E7Le3GNJQb7
.... wCpmDe8+n8S5Kp/gBEpWiYuvsVA/T4KseoX7NMcacP+DJMwjmNd9U58USn2vLz0Z
.... TMtXpc/FUhW5PZdgCiuNzw6IFgGn9ZCCv85jjUIW3KD8fUNdrUfVSv4olDoL9NkR
.... dTRg3Os/znC6l0gv/mqnLaqj2bJ/tx47kUmj6Oq13JuEq34T+DVmsUCFVundQnRp
.... c/vVEqQot7Rvj9UmSvTi4WKt/qxiAnyZf3gXOdrXvxfVTGzD5I/Xg+By+a4C2JwB
.... A5RGvZP3fyfhkCnnhFDpfws5lc20FA6ryQIDAQAB
.... -----END RSA PUBLIC KEY-----"""Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.7/dist-packages/M2Crypto/RSA.py", line 422, in load_pub_key_bio
rsa_error()
File "/usr/lib/python2.7/dist-packages/M2Crypto/RSA.py", line 302, in rsa_error
raise RSAError, m2.err_reason_error_string(m2.err_get_error())
M2Crypto.RSA.RSAError: no start line


Reading all whats in Internet about this problem it seems that my key is in PKCS#1 format but M2Crypto only reads X.501 RSA keys.

I know that python-crypto doesn't have any problem loading this key, but I'll prefer to stick with M2Crypto because I already have lots code using M2Crypto.

So my question is, is there any way to load this key using M2Crypto? Can I convert somehow the key to X.501?

I'll be very, very grateful if someone can come up with an interesting idea!
thanks a lot!!
Marc
 
P

Piet van Oostrum

Marc Aymerich said:
Hi,
I've been trying very, very hard to load an RSA key using M2Crypto but without any success.

basically this is what I'm trying to do:
... MIIBCgKCAQEApwotnfHT9RAmxnuaGEMdI3lYPYE4aaqSD9v4KbTh1E7Le3GNJQb7
... wCpmDe8+n8S5Kp/gBEpWiYuvsVA/T4KseoX7NMcacP+DJMwjmNd9U58USn2vLz0Z
... TMtXpc/FUhW5PZdgCiuNzw6IFgGn9ZCCv85jjUIW3KD8fUNdrUfVSv4olDoL9NkR
... dTRg3Os/znC6l0gv/mqnLaqj2bJ/tx47kUmj6Oq13JuEq34T+DVmsUCFVundQnRp
... c/vVEqQot7Rvj9UmSvTi4WKt/qxiAnyZf3gXOdrXvxfVTGzD5I/Xg+By+a4C2JwB
... A5RGvZP3fyfhkCnnhFDpfws5lc20FA6ryQIDAQAB
... -----END RSA PUBLIC KEY-----"""
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.7/dist-packages/M2Crypto/RSA.py", line 422, in load_pub_key_bio
rsa_error()
File "/usr/lib/python2.7/dist-packages/M2Crypto/RSA.py", line 302, in rsa_error
raise RSAError, m2.err_reason_error_string(m2.err_get_error())
M2Crypto.RSA.RSAError: no start line


Reading all whats in Internet about this problem it seems that my key is in PKCS#1 format but M2Crypto only reads X.501 RSA keys.

I know that python-crypto doesn't have any problem loading this key, but I'll prefer to stick with M2Crypto because I already have lots code using M2Crypto.

So my question is, is there any way to load this key using M2Crypto? Can I convert somehow the key to X.501?
Converting to X.501 isn't difficult (assuming this is a 2048 bit key):
Get rid of the 'RSA' in header and trailer
Prepend X.501 header 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A' to the data
Reformat the lines to 64 characters.

from M2Crypto import BIO, RSA

pubkey="""-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEApwotnfHT9RAmxnuaGEMdI3lYPYE4aaqSD9v4KbTh1E7Le3GNJQb7
wCpmDe8+n8S5Kp/gBEpWiYuvsVA/T4KseoX7NMcacP+DJMwjmNd9U58USn2vLz0Z
TMtXpc/FUhW5PZdgCiuNzw6IFgGn9ZCCv85jjUIW3KD8fUNdrUfVSv4olDoL9NkR
dTRg3Os/znC6l0gv/mqnLaqj2bJ/tx47kUmj6Oq13JuEq34T+DVmsUCFVundQnRp
c/vVEqQot7Rvj9UmSvTi4WKt/qxiAnyZf3gXOdrXvxfVTGzD5I/Xg+By+a4C2JwB
A5RGvZP3fyfhkCnnhFDpfws5lc20FA6ryQIDAQAB
-----END RSA PUBLIC KEY-----
"""

pk = pubkey.split('\n')
pk = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A' + ''.join(pk[1:-2])
pk = [pk[i:i+64] for i in range(0, len(pk), 64)]
pk = '-----BEGIN PUBLIC KEY-----\n' + '\n'.join(pk) + '\n-----END PUBLIC KEY-----'

bio = BIO.MemoryBuffer(pk) # pk is ASCII, don't encode
key = RSA.load_pub_key_bio(bio)
 
M

Marc Aymerich

I've been trying very, very hard to load an RSA key using M2Crypto but without any success.

basically this is what I'm trying to do:
... MIIBCgKCAQEApwotnfHT9RAmxnuaGEMdI3lYPYE4aaqSD9v4KbTh1E7Le3GNJQb7
... wCpmDe8+n8S5Kp/gBEpWiYuvsVA/T4KseoX7NMcacP+DJMwjmNd9U58USn2vLz0Z
... TMtXpc/FUhW5PZdgCiuNzw6IFgGn9ZCCv85jjUIW3KD8fUNdrUfVSv4olDoL9NkR
... dTRg3Os/znC6l0gv/mqnLaqj2bJ/tx47kUmj6Oq13JuEq34T+DVmsUCFVundQnRp
... c/vVEqQot7Rvj9UmSvTi4WKt/qxiAnyZf3gXOdrXvxfVTGzD5I/Xg+By+a4C2JwB
... A5RGvZP3fyfhkCnnhFDpfws5lc20FA6ryQIDAQAB
... -----END RSA PUBLIC KEY-----"""
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.7/dist-packages/M2Crypto/RSA.py", line 422, in load_pub_key_bio

File "/usr/lib/python2.7/dist-packages/M2Crypto/RSA.py", line 302, in rsa_error
raise RSAError, m2.err_reason_error_string(m2.err_get_error())
M2Crypto.RSA.RSAError: no start line


Reading all whats in Internet about this problem it seems that my key is in PKCS#1 format but M2Crypto only reads X.501 RSA keys.

I know that python-crypto doesn't have any problem loading this key, but I'll prefer to stick with M2Crypto because I already have lots code using M2Crypto.

So my question is, is there any way to load this key using M2Crypto? Can I convert somehow the key to X.501?

Converting to X.501 isn't difficult (assuming this is a 2048 bit key):

Get rid of the 'RSA' in header and trailer

Prepend X.501 header 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A' to the data

Reformat the lines to 64 characters.



from M2Crypto import BIO, RSA



pubkey="""-----BEGIN RSA PUBLIC KEY-----

MIIBCgKCAQEApwotnfHT9RAmxnuaGEMdI3lYPYE4aaqSD9v4KbTh1E7Le3GNJQb7

wCpmDe8+n8S5Kp/gBEpWiYuvsVA/T4KseoX7NMcacP+DJMwjmNd9U58USn2vLz0Z

TMtXpc/FUhW5PZdgCiuNzw6IFgGn9ZCCv85jjUIW3KD8fUNdrUfVSv4olDoL9NkR

dTRg3Os/znC6l0gv/mqnLaqj2bJ/tx47kUmj6Oq13JuEq34T+DVmsUCFVundQnRp

c/vVEqQot7Rvj9UmSvTi4WKt/qxiAnyZf3gXOdrXvxfVTGzD5I/Xg+By+a4C2JwB

A5RGvZP3fyfhkCnnhFDpfws5lc20FA6ryQIDAQAB

-----END RSA PUBLIC KEY-----

"""



pk = pubkey.split('\n')

pk = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A' + ''.join(pk[1:-2])

pk = [pk[i:i+64] for i in range(0, len(pk), 64)]

pk = '-----BEGIN PUBLIC KEY-----\n' + '\n'.join(pk) + '\n-----END PUBLIC KEY-----'



bio = BIO.MemoryBuffer(pk) # pk is ASCII, don't encode

key = RSA.load_pub_key_bio(bio)


Piet, that was really awesome, it seems so easy to do now :)
Thank you very, very much! really.
 
P

Piet van Oostrum

Piet van Oostrum said:
Converting to X.501 isn't difficult (assuming this is a 2048 bit key):
Get rid of the 'RSA' in header and trailer
Prepend X.501 header 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A' to the data
Reformat the lines to 64 characters.

This solution is a bit restricted as it only works if the key is 2048
bits and uses an exponent of 65537 (which is the default). Otherwise it
fails.

Here is a robust solution that works for all PKCS#1 keys. Instead of
using a fixed X.501 header it calculates the header. We could do a
complete ASN.1 encoding, but most of the parts are fixed. The only
variable parts are two length fields. So I just plug these into the
fixed stuff. This saves using one of the ASN.1 libraries. We do have to
work in binary (DER format) instead of base64, however.

from M2Crypto import BIO, RSA
import base64

def der_length(length):
"""DER encoding of a length"""
if length < 128:
return chr(length)
prefix = 0x80
result = ''
while length > 0:
result = chr(length & 0xff) + result
length >>= 8
prefix += 1
return chr(prefix) + result

pubkey="""-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEApwotnfHT9RAmxnuaGEMdI3lYPYE4aaqSD9v4KbTh1E7Le3GNJQb7
wCpmDe8+n8S5Kp/gBEpWiYuvsVA/T4KseoX7NMcacP+DJMwjmNd9U58USn2vLz0Z
TMtXpc/FUhW5PZdgCiuNzw6IFgGn9ZCCv85jjUIW3KD8fUNdrUfVSv4olDoL9NkR
dTRg3Os/znC6l0gv/mqnLaqj2bJ/tx47kUmj6Oq13JuEq34T+DVmsUCFVundQnRp
c/vVEqQot7Rvj9UmSvTi4WKt/qxiAnyZf3gXOdrXvxfVTGzD5I/Xg+By+a4C2JwB
A5RGvZP3fyfhkCnnhFDpfws5lc20FA6ryQIDAQAB
-----END RSA PUBLIC KEY-----
"""

pk = pubkey.split('\n')
pk = '\0' + base64.decodestring("".join(pk[1:-2]))
pk = '\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03' + \
der_length(len(pk)) + pk
pk = '\x30' + der_length(len(pk)) + pk
pk = '-----BEGIN PUBLIC KEY-----\n' + base64.encodestring(pk) + '-----END PUBLIC KEY-----'

bio = BIO.MemoryBuffer(pk)
key = RSA.load_pub_key_bio(bio)
 
M

Marc Aymerich

This solution is a bit restricted as it only works if the key is 2048

bits and uses an exponent of 65537 (which is the default). Otherwise it

fails.



Here is a robust solution that works for all PKCS#1 keys. Instead of

using a fixed X.501 header it calculates the header. We could do a

complete ASN.1 encoding, but most of the parts are fixed. The only

variable parts are two length fields. So I just plug these into the

fixed stuff. This saves using one of the ASN.1 libraries. We do have to

work in binary (DER format) instead of base64, however.

Thank you very much Piet,
I'm just starting to grasp these cryptography related concepts and your code is helping me a lot to understand how to handle these keys in a low level..

I'm updating my code incorporating your new contribution!

Just to let you know, during my previous research I had found a python-Crypto related solution that also uses DER and ASN.1 [1], but it uses a different approach (I guess). I suspect that this approach is also possible with M2Crypto because it has a method for constructing RSA keys [2].

[1] http://stackoverflow.com/a/10574723
[2] http://www.heikkitoivonen.net/m2crypto/api/M2Crypto.RSA-module.html#new_pub_key


Thanks again!
Marc

PS: Sorry for my email format, I'm using google groups and it seems to ignore any mailing best practice.
 
P

Piet van Oostrum

Marc Aymerich said:
Thank you very much Piet,
I'm just starting to grasp these cryptography related concepts and your code is helping me a lot to understand how to handle these keys in a low level.

I'm updating my code incorporating your new contribution!

Just to let you know, during my previous research I had found a python-Crypto related solution that also uses DER and ASN.1 [1], but it uses a different approach (I guess). I suspect that this approach is also possible with M2Crypto because it has a method for constructing RSA keys [2].

[1] http://stackoverflow.com/a/10574723
[2] http://www.heikkitoivonen.net/m2crypto/api/M2Crypto.RSA-module.html#new_pub_key
new_pub_key could be used but then you would have to do an ASN.1 parse
of the DER format of your key to get the n and e values. AFAICT M2Crypto
doesn't have methods to do this, so you would need to use one of the
python ASN.1 libraries (or write that part yourself).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,990
Messages
2,570,211
Members
46,796
Latest member
SteveBreed

Latest Threads

Top