Sergio Morales
Sergio Morales

Reputation: 2640

Extracting RSA public key modulus and exponent stored in unsigned char arrays

So, I'm using a proprietary library that has its own implementation for the creation of RSA key pairs. The public key struct looks like this:

typedef struct
{
   unsigned int bits;                         //Length of modulus in bits
   unsigned char modulus[MAX_RSA_MOD_LEN];    //Modulus
   unsigned char exponent[MAX_RSA_MOD_LEN];   //Exponent
} RSA_PUB_KEY

I need to figure out a way to extract both the exponent and the module so I can send them to a server as part of a validation scheme. I guess that this is a pretty standard procedure (or so I hope). I've already read these two similar questions:

But so far I've had no luck. I'm also not sure of how to use if at all necessary the "bits" field to extract the modulus. In short what I have to do is be able to recreate this public key in Java:

BigInteger m = new BigInteger(MODULUS); 
BigInteger e = new BigInteger(EXPONENT);

RSAPublicKeySpec keySpec = new RSAPublicKeySpec(m, e);
KeyFactory fact = KeyFactory.getInstance("RSA");
PublicKey pubKey = fact.generatePublic(keySpec);

return pubKey;

Edit:

This is what I'm doing right now: (RSAPublic is a RSA_PUB_KEY struct as described above).

//RSAPublic.bits = length of modulus in bits                
log("Modulus length: "+std::to_string(RSAPublic.bits));
log("Key length: "+std::to_string(keyLengthInBits));

//Calculating buffer size for converted hexadec. representations
int modulusLengthInBytes = (RSAPublic.bits+7)/8 ;
int exponentLengthInBytes = (keyLengthInBits+7)/8;

char convertedMod[modulusLengthInBytes*2+1];
char convertedExp[exponentLengthInBytes*2+1];

//Conversion
int i;
for(i=0; i<modulusLengthInBytes ; i++){
  sprintf(&convertedMod[i*2], "%02X", RSAPublic.modulus[i]);
}
for(i=0; i<exponentLengthInBytes ; i++){
  sprintf(&convertedExp[i*2], "%02X", RSAPublic.exponent[i]);
}

//Print results
printf("Modulus: %s\n", convertedMod);  
printf("Exponent: %s\n", convertedExp); 

And this is the output:

Modulus length: 16
Key length: 512
Modulus: 0000
Exponent: 0A000200FFFFFFFFFFFF0000600007004DDA0100B01D0000AEC642017A4513000000000000000000000000000000000000000000000000000000000000000000

Upvotes: 2

Views: 4649

Answers (2)

Maarten Bodewes
Maarten Bodewes

Reputation: 93968

If the first bytes of the public exponent are all zero's then you are dealing with a big endian array. This is most common. In principle the public exponent can be as large as the modulus, but this is commonly not the case. Most common values are 65537, 17 and 3, maybe even 2 but the 3 and 2 are not such good values. Other 2-4 byte primes are also common.

Now if you know the endianness, you can have a look at the modulus. If the highest byte value is 00 then you are dealing with a signed representation of the modulus. Otherwise it is likely unsigned. The highest order byte of the modulus that contains bits should always be 80 or higher. The reason is that otherwise the key size would be smaller than the given key size. This is assuming that the key size is a multiple of 8 of course.

Java only works with big endian for BigInteger (and any other number representation). So if you have little endian encoding in C then you need to reverse the values in Java. It is probably the best to reverse the hexadecimal values in the string to accomplish that. Make sure you handle 2 hexadecimal characters at a time.

Then, as DrYap suggested, use the hexadecimal constructor of BigInteger. Note that if you end up using a byte array then you may want to use new BigInteger(1, MODULUS) as this makes sure you get a positive number regardless of the highest order bit value in the encoding.

Upvotes: 1

DrYap
DrYap

Reputation: 6647

I'm assuming that you can't just send binary data since you mention the hexadecimal conversion. The most compact way you can send the data as text would be with base 64 but this is more complex than hexadecimal.

Client side

Convert the unsigned char array to a hexadecimal string using a method from the links you have. The bits field will determine how many bytes from the array to use given by (bits+7)/8.

Depending on implementation you might have to explicitly select the overflow bits or the rest might be zeroed, this also depends on the endianness so since you are unsure on implementation details you might have to fiddle around with it a bit.

Once you have the encoded strings, send them to the server.

Server side

Read the encoded strings from the connection and then pass them to the BigInteger(String val, int radix) constructor using the radix of hexadecimal (16).

You will then have A BigInteger with the value you require.

Upvotes: 2

Related Questions