我有一个Java卡小程序,它将生成RSA私钥(每个512位)。并将发送公钥模数和指数(模数为64字节)
在主机应用程序(java)中,我需要使用相同的指数和模数来重建rsa公钥,但是当我尝试使用以下代码进行重建时,却遇到了错误。
Java卡代码:
// 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);
这部分代码工作正常。我能够将模数完美地发送到主机端。
主机应用程序的Java代码如下:
//command for retrieving modulus
resp = channel.transmit(new CommandAPDU(cmdMod));
BigInteger modulus = new BigInteger(resp.getData());
我得到了预期的64字节模数,但是当我用它做成一个大整数时,它显示出很大的负值。
//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);
错误:
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)
我检查了卡片上的回复。我正确地获取了所有64个字节的模数。但是当我制作Big Integer时,我得到了很大的负值。我该怎么办?
最佳答案
问题在于,默认情况下BigInteger
编码为带符号的大字节序表示。如果您使用构造函数对字节进行解码,则会执行相反的操作,即,期望带符号的值。现在,大多数加密是对(大)无符号整数执行的。这是因为计算是在数学组内执行的(模量计算)。这些计算始终对正数执行,RSA也不例外。
现在,模数的大小等于RSA密钥的密钥大小(而不是密钥强度)。这意味着,当将512位的RSA密钥编码为无符号大字节序数时,其模数恰好为512位。对于数字,这意味着最高有效位始终设置为“1”。但是,该位用于指示编码为两个补码值的无符号数字的符号位。换句话说,键大小可除以8的任何模数在解释为带符号值时都将为负数。
解决方案当然是使用构造函数,您可以在其中自己指示符号位:
BigInteger(int signum, byte[] magnitude)
在这种情况下,大小是无符号表示形式:
new BigInteger(1, resp.getData());
Java Card在API中使用未签名的表示形式,因为它更加面向密码。那里不需要复杂的方法。
请注意,相反的做法是-从编码的带符号
BigInteger
中创建一个静态大小的字节数组更加棘手,有关如何执行特定转换的信息,请参见this answer。