2

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
Community
  • 1
  • 1
Sergio Morales
  • 2,600
  • 6
  • 32
  • 40
  • 1
    Are the exponent and modulus byte arrays big or little endian? – DrYap Aug 09 '13 at 21:18
  • I unfortunately don't know. What I'd do is assume one, try if it works (if I can reconstruct it on the Java side, encrypt something, and decrypt it with the unmodified private key) and if it didn't try the other one. I know these are a lot of ifs =/ – Sergio Morales Aug 09 '13 at 21:35
  • 1
    This is not a standard procedure. The reason is that just sending the public key value does not indicate *trust*: how does the receiver know the public key is from the right sender? Normally the key is part of a certificate, which in turn has been signed by a *trusted certificate*. This is called Public Key Infrastructure or PKI. Normally public keys are send using ASN.1 DER encoding, possibly base 64 encoded or in PEM format (look them up). If they are send without separate structure then binary or base 64 is more commonly used. – Maarten Bodewes Aug 10 '13 at 00:29
  • From your edit, the modulus seems very small (both in bits and value) and the exponent as represented in the output is not prime which is wrong. I don't really know what to suggest other that trying different endianess but that wouldn't help with all the 0s. – DrYap Aug 12 '13 at 16:59
  • If I don't divide the numbers before converting, this is what I get (modulus) 00000000010000000100000001000000. I guess at this point I'll just try to ask the API vendor directly. – Sergio Morales Aug 12 '13 at 17:12
  • That initial structure cannot be correct. The length of the modulus is always the same size as the key size for RSA. – Maarten Bodewes Aug 12 '13 at 17:18

2 Answers2

2

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.

DrYap
  • 6,525
  • 2
  • 31
  • 54
  • The 'bits' attribute in the struct refers to the length of the modulus, but how do I figure out the size of the exponent? The public key constructor method has a parameter called bits that is only described as "key length" on the documentation, is it safe to assume that's the length of the exponent or is that something else altogether? – Sergio Morales Aug 12 '13 at 14:28
  • An RSA key is made up of a modulus and an exponent so I would hazard a guess that the exponent length would be the key length - modulus length but I am by no means certain. – DrYap Aug 12 '13 at 14:57
  • I've edited the original question with what I've done so far, maybe more info will help. – Sergio Morales Aug 12 '13 at 16:30
1

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.

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263