Luke Harrison
Luke Harrison

Reputation: 21

How to load an RSA512 public key and exponent from a .bin file into python

I'm having trouble loading an RSA512 public key from a .bin file into python. The issue mainly stems from the fact that I don't know what format the key is stored in. This is the only description of the file I was given.

"key.bin - Raw binary bytes of RSA 512 bit public key and exponent. Used to verify signature of incoming packets."

I don't know if this is helpful but here are bytes printed in python of the .bin file.

9902c4a66b1ff76392919e7bbc35d51a5128b9da03e131b489d5ed01c1d075fc4c139a9952e9a3b040d984219a4aef0d421f6b8f9c79e1c3c35a218ecba54dc9010001

The goal of the actual challenge is to build a udp server that verifies the digital signature and integrity of an incoming packet. Currently i'm using python 2.7 with the cryptography library. Documentation can be found below. https://cryptography.io/en/latest/hazmat/primitives/asymmetric/rsa/?highlight=rsa%20512

I have already tried the code below but I get the same error for the first two formats and a slightly different one for the third.

with open("key.bin", "rb") as key_file:
    private_key = serialization.load_der_public_key(key_file.read(), backend=default_backend())

ValueError: Could not deserialize key data.

with open("key.bin", "rb") as key_file:
    private_key = serialization.load_pem_public_key(key_file.read(), backend=default_backend())

ValueError: Could not deserialize key data.

with open("key.bin", "rb") as key_file:
    private_key = serialization.load_ssh_public_key(key_file.read(), backend=default_backend())

ValueError: Key is not in the proper format or contains extra data.

Also the hashing algorithm used for verification is SHA256 but that is probably irrelevant.

Upvotes: 2

Views: 2336

Answers (2)

Maarten Bodewes
Maarten Bodewes

Reputation: 93948

Your key is not encoded in a known standard. You need to extract the modulus and exponent, and then build the public key out of them.

The modulus defines the RSA key size and is therefore 512 bits or 64 bytes as unsigned big endian value. The public exponent may have any size, but is usually small. The most used exponent value is 010001 in hexadecimals, which is the fifth prime of Fermat (also called F4, zero based index). Better however to simply get the first 64 bytes and assume the rest encodes is the public exponent.

So you can use RSAPublicNumbers to create the values from the modulus n and exponent e. Trick is to make sure that you create the modulus as positive value instead of a negative value.


Let's say that data is the binary data read from the file. Then you can get the public key in the following way.

You may want to use 'little' instead of 'big' if the following doesn't work (big endian is the RSA default, but you never know). However, in your case the little endian value is dividable by e.g. 11, so that's not a likely modulus value (the prime values should be close to half the key size in bits to be secure).

modsize = 512 // 8

modBytes = data[slice(0, modsize)]
mod = int.from_bytes(modBytes, byteorder='big')

expBytes = data[slice(modsize, None)]
exp = int.from_bytes(expBytes, byteorder='big')

pubkey = RSAPublicNumbers(exp, mod).public_key(default_backend())

Note that from_bytes has only been added in Python 3.2. RSAPublicNumbers is a bit weird in the sense that it takes the exponent parameter before the modulus. Every other API that I've seen takes the modulus before the exponent.

Upvotes: 4

key.bin - Raw binary bytes of RSA 512 bit public key and exponent. Used to verify signature of incoming packets.

In a RSA-512 key, the modulus is a 512-bit number, which fits in 64 bytes or 128 hexadecimal digits. Your file is represented by 134 hex digits, so it's likely that 128 of these digits are the modulus and the rest is the public exponent and possibly metadata.

The public exponent is almost always 3 or 65537=0x010001. Given that key.bin ends with 010001 in hex, a reasonable guess is that those last 3 bytes are the public exponent, and the first 64 bytes are the modulus.

with open("key.bin", "rb") as key_file:
    n_bytes = key_file.read(64)
    e_bytes = key_file.read(3)

You now need to figure out whether the encoding is little-endian or big-endian. You can't tell from the public exponent because it's palindromic. So try both possibilities:

n = int(n_bytes.encode('hex'), 16)

or

n = int(reversed(n_bytes).encode('hex'), 16)

Since you have the key in an ad hoc format, rather than a standard format that is used in real life, you're probably meant to use arithmetic primitives rather than a cryptography library to work with the key.

Upvotes: 2

Related Questions