Криптография RSA возвращает "ILLEGAL_USE" в апплете Java Card
Я написал следующий апплет для выполнения операций шифрования RSA на моей карте NXP JCOP:
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;
}
}
}
Проблема в том, что я получаю 0x0005
Код причины CryptoException на doFinal()
метод:
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 ответ
Ответ 1: как указано в Cipher.ALG_RSA_PKCS1
документация:
Этот алгоритм подходит только для сообщений ограниченной длины. Общее количество входных байтов, обработанных во время шифрования, может быть не более k-11, где k - это размер модуля ключа RSA в байтах.
Сообщение, которое вы пытаетесь зашифровать, не соответствует этому правилу, поскольку вы шифруете 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
это короткие данные, которые должны храниться во временном буфере.