Java 卡小程序中的 RSA 加密 returns "ILLEGAL_USE"
RSA Cryptography returns "ILLEGAL_USE" in Java Card applet
我编写了以下小程序来在我的 NXP JCOP 卡中执行 RSA 加密操作:
package testPack;
import javacard.framework.*;
import javacard.security.CryptoException;
import javacard.security.KeyBuilder;
import javacard.security.KeyPair;
import javacard.security.RSAPrivateKey;
import javacard.security.RSAPublicKey;
import javacardx.crypto.Cipher;
public class Test extends Applet {
RSAPrivateKey myRSAPriKey;
RSAPublicKey myRSAPubKey;
Cipher myCipher;
KeyPair myKeyPair;
byte[] input;
byte[] result;
private static final byte INS_GEN_KEYPAIR = (byte) 0x10;
private static final byte INS_INIT_CIPHER_ENC = (byte) 0x20;
private static final byte INS_ENC = 0x21;
private static final byte INS_INIT_CIPHER_DEC = (byte) 0x30;
private static final byte INS_DEC = (byte) 0x31;
private static final byte P1_CHAIN_APDU = (byte) 0x00;
private static final byte P1_LAST_APDU = (byte) 0x01;
public static void install(byte[] bArray, short bOffset, byte bLength) {
new Test();
}
protected Test() {
myRSAPriKey = (RSAPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PRIVATE, KeyBuilder.LENGTH_RSA_2048, false);
myRSAPubKey = (RSAPublicKey) KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PUBLIC, KeyBuilder.LENGTH_RSA_2048, false);
myKeyPair = new KeyPair(myRSAPubKey, myRSAPriKey);
myCipher = Cipher.getInstance(Cipher.ALG_RSA_PKCS1, false);
input = JCSystem.makeTransientByteArray((short) 256, JCSystem.CLEAR_ON_RESET);
result = JCSystem.makeTransientByteArray((short) 256, JCSystem.CLEAR_ON_RESET);
register();
}
public void process(APDU apdu) {
if (selectingApplet()) {
return;
}
byte[] buff = apdu.getBuffer();
byte ins = buff[ISO7816.OFFSET_INS];
byte p1 = buff[ISO7816.OFFSET_P1];
short lc = (short) (buff[ISO7816.OFFSET_LC] & 0x00FF);
short dataOffset = ISO7816.OFFSET_CDATA;
switch (ins) {
case INS_GEN_KEYPAIR:
myKeyPair.genKeyPair();
break;
case INS_INIT_CIPHER_ENC:
myCipher.init(myRSAPubKey, Cipher.MODE_ENCRYPT);
break;
case INS_ENC:
apdu.setIncomingAndReceive();
if (p1 == P1_CHAIN_APDU) {
Util.arrayCopyNonAtomic(buff, dataOffset, input, (short) 0x00, lc);
} else if (p1 == P1_LAST_APDU) {
Util.arrayCopyNonAtomic(buff, dataOffset, input, (short) 128, lc);
try {
myCipher.doFinal(input, (short) 0x00, (short) 256, result, (short) 0x00);
} catch (CryptoException e) {
short reason = e.getReason();
ISOException.throwIt((short) ((short) 0x6B00 | reason));
}
apdu.setOutgoing();
apdu.setOutgoingLength((short) 256);
apdu.sendBytesLong(result, (short) 0x00, (short) 256);
}
break;
case INS_INIT_CIPHER_DEC:
myCipher.init(myRSAPriKey, Cipher.MODE_DECRYPT);
break;
case INS_DEC:
apdu.setIncomingAndReceive();
if (p1 == P1_CHAIN_APDU) {
Util.arrayCopyNonAtomic(buff, dataOffset, input, (short) 0x00, lc);
} else if (p1 == P1_LAST_APDU) {
Util.arrayCopyNonAtomic(buff, dataOffset, input, (short) 128, lc);
try {
myCipher.doFinal(input, (short) 0x00, (short) 256, result, (short) 0x00);
} catch (CryptoException e) {
short reason = e.getReason();
ISOException.throwIt((short) ((short) 0x6B00 | reason));
}
apdu.setOutgoing();
apdu.setOutgoingLength((short) 256);
apdu.sendBytesLong(result, (short) 0x00, (short) 256);
}
break;
}
}
}
问题是我在 doFinal()
方法上收到 0x0005
CryptoException 原因代码:
Select Applet begin...
Select Applet successful.
Send: 00 10 00 00 00
Recv: 90 00
Send: 00 20 00 00 00
Recv: 90 00
Send: 00 21 00 00 80 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00
Recv: 90 00
Send: 00 21 01 00 80 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00
Recv: 6B 05
问题:
- 如您所知,原因码
0x0005
表示ILLEGAL_USE。但是为什么?
- 如何在密码对象上使用
update()
方法从我的小程序中删除瞬态字节数组?
答案 1: 如 Cipher.ALG_RSA_PKCS1
文档中所述:
This algorithm is only suitable for messages of limited length. The
total number of input bytes processed during encryption may not be
more than k-11, where k is the RSA key's modulus size in bytes.
您尝试加密的邮件不遵循此规则,因为您正在加密 256 字节的邮件。由于模数大小为 256 字节,因此您可以加密的最大消息为 245 字节 (k-11)。您必须考虑将添加到消息中的额外填充字节。
答案 2: 您不能同时删除输入和输出缓冲区,因为您需要一个来存储部分结果。
case INS_INIT_CIPHER_ENC:
myCipher.init(myRSAPubKey, Cipher.MODE_ENCRYPT);
cipher_result_len = (short) 0x00;
break;
case INS_ENC:
apdu.setIncomingAndReceive();
if (p1 == P1_CHAIN_APDU) {
cipher_result_len += myCipher.update(buff, dataOffset, lc, result, cipher_result_len);
} else if (p1 == P1_LAST_APDU) {
try {
cipher_result_len += myCipher.doFinal(buff, dataOffset, lc, result, cipher_result_len);
} catch (CryptoException e) {
short reason = e.getReason();
ISOException.throwIt((short) ((short) 0x6B00 | reason));
}
apdu.setOutgoing();
apdu.setOutgoingLength(cipher_result_len);
apdu.sendBytesLong(result, (short) 0x00, cipher_result_len);
}
break;
cipher_result_len
是短数据,必须存储在临时缓冲区中。
我编写了以下小程序来在我的 NXP JCOP 卡中执行 RSA 加密操作:
package testPack;
import javacard.framework.*;
import javacard.security.CryptoException;
import javacard.security.KeyBuilder;
import javacard.security.KeyPair;
import javacard.security.RSAPrivateKey;
import javacard.security.RSAPublicKey;
import javacardx.crypto.Cipher;
public class Test extends Applet {
RSAPrivateKey myRSAPriKey;
RSAPublicKey myRSAPubKey;
Cipher myCipher;
KeyPair myKeyPair;
byte[] input;
byte[] result;
private static final byte INS_GEN_KEYPAIR = (byte) 0x10;
private static final byte INS_INIT_CIPHER_ENC = (byte) 0x20;
private static final byte INS_ENC = 0x21;
private static final byte INS_INIT_CIPHER_DEC = (byte) 0x30;
private static final byte INS_DEC = (byte) 0x31;
private static final byte P1_CHAIN_APDU = (byte) 0x00;
private static final byte P1_LAST_APDU = (byte) 0x01;
public static void install(byte[] bArray, short bOffset, byte bLength) {
new Test();
}
protected Test() {
myRSAPriKey = (RSAPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PRIVATE, KeyBuilder.LENGTH_RSA_2048, false);
myRSAPubKey = (RSAPublicKey) KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PUBLIC, KeyBuilder.LENGTH_RSA_2048, false);
myKeyPair = new KeyPair(myRSAPubKey, myRSAPriKey);
myCipher = Cipher.getInstance(Cipher.ALG_RSA_PKCS1, false);
input = JCSystem.makeTransientByteArray((short) 256, JCSystem.CLEAR_ON_RESET);
result = JCSystem.makeTransientByteArray((short) 256, JCSystem.CLEAR_ON_RESET);
register();
}
public void process(APDU apdu) {
if (selectingApplet()) {
return;
}
byte[] buff = apdu.getBuffer();
byte ins = buff[ISO7816.OFFSET_INS];
byte p1 = buff[ISO7816.OFFSET_P1];
short lc = (short) (buff[ISO7816.OFFSET_LC] & 0x00FF);
short dataOffset = ISO7816.OFFSET_CDATA;
switch (ins) {
case INS_GEN_KEYPAIR:
myKeyPair.genKeyPair();
break;
case INS_INIT_CIPHER_ENC:
myCipher.init(myRSAPubKey, Cipher.MODE_ENCRYPT);
break;
case INS_ENC:
apdu.setIncomingAndReceive();
if (p1 == P1_CHAIN_APDU) {
Util.arrayCopyNonAtomic(buff, dataOffset, input, (short) 0x00, lc);
} else if (p1 == P1_LAST_APDU) {
Util.arrayCopyNonAtomic(buff, dataOffset, input, (short) 128, lc);
try {
myCipher.doFinal(input, (short) 0x00, (short) 256, result, (short) 0x00);
} catch (CryptoException e) {
short reason = e.getReason();
ISOException.throwIt((short) ((short) 0x6B00 | reason));
}
apdu.setOutgoing();
apdu.setOutgoingLength((short) 256);
apdu.sendBytesLong(result, (short) 0x00, (short) 256);
}
break;
case INS_INIT_CIPHER_DEC:
myCipher.init(myRSAPriKey, Cipher.MODE_DECRYPT);
break;
case INS_DEC:
apdu.setIncomingAndReceive();
if (p1 == P1_CHAIN_APDU) {
Util.arrayCopyNonAtomic(buff, dataOffset, input, (short) 0x00, lc);
} else if (p1 == P1_LAST_APDU) {
Util.arrayCopyNonAtomic(buff, dataOffset, input, (short) 128, lc);
try {
myCipher.doFinal(input, (short) 0x00, (short) 256, result, (short) 0x00);
} catch (CryptoException e) {
short reason = e.getReason();
ISOException.throwIt((short) ((short) 0x6B00 | reason));
}
apdu.setOutgoing();
apdu.setOutgoingLength((short) 256);
apdu.sendBytesLong(result, (short) 0x00, (short) 256);
}
break;
}
}
}
问题是我在 doFinal()
方法上收到 0x0005
CryptoException 原因代码:
Select Applet begin...
Select Applet successful.
Send: 00 10 00 00 00
Recv: 90 00
Send: 00 20 00 00 00
Recv: 90 00
Send: 00 21 00 00 80 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00
Recv: 90 00
Send: 00 21 01 00 80 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00
Recv: 6B 05
问题:
- 如您所知,原因码
0x0005
表示ILLEGAL_USE。但是为什么? - 如何在密码对象上使用
update()
方法从我的小程序中删除瞬态字节数组?
答案 1: 如 Cipher.ALG_RSA_PKCS1
文档中所述:
This algorithm is only suitable for messages of limited length. The total number of input bytes processed during encryption may not be more than k-11, where k is the RSA key's modulus size in bytes.
您尝试加密的邮件不遵循此规则,因为您正在加密 256 字节的邮件。由于模数大小为 256 字节,因此您可以加密的最大消息为 245 字节 (k-11)。您必须考虑将添加到消息中的额外填充字节。
答案 2: 您不能同时删除输入和输出缓冲区,因为您需要一个来存储部分结果。
case INS_INIT_CIPHER_ENC:
myCipher.init(myRSAPubKey, Cipher.MODE_ENCRYPT);
cipher_result_len = (short) 0x00;
break;
case INS_ENC:
apdu.setIncomingAndReceive();
if (p1 == P1_CHAIN_APDU) {
cipher_result_len += myCipher.update(buff, dataOffset, lc, result, cipher_result_len);
} else if (p1 == P1_LAST_APDU) {
try {
cipher_result_len += myCipher.doFinal(buff, dataOffset, lc, result, cipher_result_len);
} catch (CryptoException e) {
short reason = e.getReason();
ISOException.throwIt((short) ((short) 0x6B00 | reason));
}
apdu.setOutgoing();
apdu.setOutgoingLength(cipher_result_len);
apdu.sendBytesLong(result, (short) 0x00, cipher_result_len);
}
break;
cipher_result_len
是短数据,必须存储在临时缓冲区中。