3

I have a java card applet which will generate a RSA private public key pair (512 bits each). And it will send the public key modulus and exponent (modulus is 64 bytes)

In the host application (java) i need to re build the rsa public key using the same exponent and modulus, but when i try to reconstruct using the following code i am getting an error.

Java card code:

// this one to create the key pair
  rsa_KeyPair = new KeyPair(KeyPair.ALG_RSA_CRT, KeyBuilder.LENGTH_RSA_512);
  rsa_KeyPair.genKeyPair();
  rsa_PublicKey = (RSAPublicKey) rsa_KeyPair.getPublic();
  rsa_PrivateCrtKey 0= (RSAPrivateCrtKey) rsa_KeyPair.getPrivate();
  cipherRSA = Cipher.getInstance(Cipher.ALG_RSA_PKCS1, false);

//this is to send the modulus
  byte[] buffer = apdu.getBuffer();
  rsa_PublicKey.getModulus(buffer, ISO7816.OFFSET_CDATA);
  apdu.setOutgoing();
  apdu.setOutgoingLength((short) 64);
  apdu.sendBytesLong(buffer, ISO7816.OFFSET_CDATA, (short) 64);

This part of the code is working fine. i am able to send the modulus perfectly to the host side.

Java code for host application below:

//command for retrieving modulus
resp = channel.transmit(new CommandAPDU(cmdMod));
BigInteger modulus = new BigInteger(resp.getData());

I am getting the 64 byte modulus as expected but when i make a big integer out of it its showing a large negative value.

//command for retrieving exponent
resp = channel.transmit(new CommandAPDU(cmdExp)); 
BigInteger modulus = new BigInteger(resp.getData());
byte[] input = { (byte) 0x92, (byte) 0x84, (byte) 0x3B,
        (byte) 0xD3, (byte) 0x5D, (byte) 0x8A, (byte) 0x6B,
        (byte) 0x56, (byte) 0xDA, (byte) 0xEA, (byte) 0xE0,
        (byte) 0x2F, (byte) 0x6D, (byte) 0xAA, (byte) 0x62,
        (byte) 0x4B, (byte) 0x38, (byte) 0xCE, (byte) 0xD4,
        (byte) 0x70, (byte) 0xA2, (byte) 0x16, (byte) 0x35,
        (byte) 0xCC, (byte) 0xEE, (byte) 0xB8, (byte) 0x31,
        (byte) 0x13, (byte) 0x37, (byte) 0x40, (byte) 0xBE,
        (byte) 0xA1, (byte) 0xCD, (byte) 0x84, (byte) 0xD9,
        (byte) 0xF3, (byte) 0xE6, (byte) 0xCE, (byte) 0x26,
        (byte) 0x0A, (byte) 0xC1, (byte) 0x40, (byte) 0xED,
        (byte) 0x20, (byte) 0x8F, (byte) 0x3D, (byte) 0x9F,
        (byte) 0x0D, (byte) 0xE7, (byte) 0x19, (byte) 0xC8,
        (byte) 0x87, (byte) 0x96, (byte) 0x29, (byte) 0xF2,
        (byte) 0x63, (byte) 0x34, (byte) 0x6D, (byte) 0x10,
        (byte) 0xB9, (byte) 0xFB, (byte) 0xB4, (byte) 0x75,
        (byte) 0xE9 };

RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA")
        .generatePublic(new RSAPublicKeySpec(modulus, exponent));

Cipher cipher = null;

cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, pubKey);

byte[] cipherText = cipher.doFinal(input);

Error:

javax.crypto.BadPaddingException: Message is larger than modulus
    at sun.security.rsa.RSACore.parseMsg(Unknown Source)
    at sun.security.rsa.RSACore.crypt(Unknown Source)
    at sun.security.rsa.RSACore.rsa(Unknown Source)
    at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:355)
    at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:389)
    at javax.crypto.Cipher.doFinal(Cipher.java:2121)
    at testAuth.main(testAuth.java:150)

I checked the reponse from the card. i am getting all the 64 bytes of the modulus correctly. but when i make the Big Integer i am getting a large negative value. what should i do?

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
Arjun
  • 197
  • 3
  • 15

1 Answers1

4

The issue is that BigInteger by default encodes to a signed big endian representation. If you decode the bytes using the constructor it does the opposite, i.e. it expects a signed value. Now most cryptography is performed on (large) unsigned integers. This is because the calculations are performed within a mathematical group (modulus calculations). These calculations are always performed on positive numbers, and RSA is no exception to this rule.

Now the size of the modulus is equal to the key size (not the key strength) of the RSA key. This means that an RSA key of 512 bit has a modulus of exactly 512 bits when encoded as a unsiged, big endian number. For numbers this means that the most significant bit is always set to '1'. However, that bit is used to indicate the sign bit for unsigned numbers encoded as a two-complement value. In other words, any modulus that has a key size dividable by 8 will be negative when interpreted as signed value.

The solution is of course to use the constructor where you can indicate the sign bit yourself:

BigInteger(int signum, byte[] magnitude)

where magnitude is the unsigned representation, in your case:

new BigInteger(1, resp.getData());

Java Card uses unsigned representations in the API, as it is more cryptographically oriented. No need for complicated methods in there.


Note that the reverse - creating a statically sized byte array out of an encoded signed BigInteger is even trickier, see this answer for information about how to perform that particular conversion.

Community
  • 1
  • 1
Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • I found this answer while reviewing some of my answers. It is as yet unaccepted. Did this solve your issue? If it did, accept, if it didn't please indicate where you are stuck. – Maarten Bodewes Dec 19 '14 at 19:46
  • yes. this solved the issue. thank you very much. and sorry for the delay. PS: i can't vote up because i am a new user and i dont have enough reputation for that. but anyways thank you verymuch.. :) – Arjun Jan 15 '15 at 10:50
  • That's ok, the accept is more important as it shows that the question has been answered. I've got enough reputation anyway. – Maarten Bodewes Jan 15 '15 at 12:04