AES Applet return 不同卡上的不同结果
AES Applet return different result on different cards
我写了下面的小程序在CBC和ECB模式下做AES加解密:
package cryptoPack;
import javacard.framework.*;
import javacard.security.AESKey;
import javacard.security.CryptoException;
import javacard.security.KeyBuilder;
import javacardx.crypto.Cipher;
public class CryptoAES extends Applet {
// Abbreviations
private static final boolean NO_EXTERNAL_ACCESS = false;
// AES Cipher AND its required key
Cipher cipher;
AESKey AESkey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, NO_EXTERNAL_ACCESS);
// A fixed Initial vector for AES CBC mode.
public byte[] IV = { (byte) 0x00, (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44, (byte) 0x55, (byte) 0x66,
(byte) 0x77, (byte) 0x88, (byte) 0x99, (byte) 0xAA, (byte) 0xBB, (byte) 0xCC, (byte) 0xDD, (byte) 0xEE,
(byte) 0xFF };
// Defining switch case variables for supported instructions ::: INS in APDU
// command
final byte SET_KEY = (byte) 0xC0;
final byte DO_CRYPTO = (byte) 0xC2;
// Defining switch case variables for cipher algorithms ::: P1 in APDU
// command
final byte AES_BLOCK_128_CBC_NOPAD = (byte) 0x00;
final byte AES_BLOCK_128_ECB_NOPAD = (byte) 0x01;
public static void install(byte[] bArray, short bOffset, byte bLength) {
new CryptoAES();
}
protected CryptoAES() {
register();
}
public void process(APDU apdu) {
if (selectingApplet()) {
return;
}
byte[] buffer = apdu.getBuffer();
// Analyzing the command.
try {
switch (buffer[ISO7816.OFFSET_INS]) {
case SET_KEY:
setKeyAndInit(apdu);
break;
case DO_CRYPTO:
do_crypto(apdu);
break;
default:
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
}
} catch (CryptoException e) {
ISOException.throwIt(((CryptoException) e).getReason());
}
}
public void setKeyAndInit(APDU apdu) throws ISOException {
byte[] buffer = apdu.getBuffer();
if (buffer[ISO7816.OFFSET_LC] == 16) {
AESkey.setKey(buffer, (short) ISO7816.OFFSET_CDATA);
} else {
ISOException.throwIt(ISO7816.SW_DATA_INVALID);
}
switch (buffer[ISO7816.OFFSET_P1]) {
case AES_BLOCK_128_CBC_NOPAD:
cipher = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, NO_EXTERNAL_ACCESS);
break;
case AES_BLOCK_128_ECB_NOPAD:
cipher = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_ECB_NOPAD, NO_EXTERNAL_ACCESS);
break;
}
}
public void do_crypto(APDU apdu) throws ISOException {
byte[] buffer = apdu.getBuffer();
short datalen = apdu.setIncomingAndReceive();
if ((datalen % 16) != 0) {
ISOException.throwIt(ISO7816.SW_DATA_INVALID);
}
byte[] out_data = JCSystem.makeTransientByteArray((short) 16, JCSystem.CLEAR_ON_DESELECT);
switch (buffer[ISO7816.OFFSET_P1]) {
case AES_BLOCK_128_CBC_NOPAD:
if(buffer[ISO7816.OFFSET_P2]== 0x00){
cipher.init(AESkey, Cipher.MODE_DECRYPT, IV, (short) 0x00, (short) 0x10);
}else{
cipher.init(AESkey, Cipher.MODE_ENCRYPT, IV, (short) 0x00, (short) 0x10);
}
break;
case AES_BLOCK_128_ECB_NOPAD:
if(buffer[ISO7816.OFFSET_P2]== 0x00){
cipher.init(AESkey, Cipher.MODE_DECRYPT);
}else{
cipher.init(AESkey, Cipher.MODE_ENCRYPT);
}
break;
default:
break;
}
short out_data_len = cipher.doFinal(buffer, (short) ISO7816.OFFSET_CDATA, datalen, out_data,
(short) 0);
Util.arrayCopyNonAtomic(out_data, (short) 0, buffer, (short) 0, out_data_len);
apdu.setOutgoingAndSend((short) 0, out_data_len);
}
}
在三个不同的卡上安装上面的小程序后,我有以下结果:
NXP JCOP v2.4.2 r3 - T=1 TPDU:
Send: 00 A4 04 00 06 01 02 03 04 07 01 00
Recv: 90 00
Time used: 15.000 ms
Send: 00 C0 00 00 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 00
Recv: 90 00
Time used: 123.000 ms
Send: 00 C2 00 00 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 00
Recv: C6 11 20 8A 02 37 B4 21 82 80 BC 62 CB 14 6C 46 90 00
Time used: 102.000 ms
Send: 00 C2 00 01 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 00
Recv: BB E0 95 7A 71 0E 04 4B FB 6B 5B 81 04 F6 7A A1 90 00
Time used: 72.000 ms
Send: 00 C2 01 00 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 00
Recv: C6 00 02 B9 46 62 D2 56 0A 19 16 D9 07 C9 82 B9 90 00
Time used: 73.000 ms
Send: 00 C2 01 01 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 00
Recv: E5 6E 26 F5 60 8B 8D 26 8F 25 56 E1 98 A0 E0 1B 90 00
Time used: 73.000 ms
两张其他卡片 - T=0 TPDU:
Send: 00 A4 04 00 06 01 02 03 04 07 01 00
Recv: 90 00
Time used: 675.000 ms
Send: 00 C0 00 00 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 00
Recv: 90 00
Time used: 57.000 ms
Send: 00 C2 00 00 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 00
Recv: A8 03 C8 BC 28 C3 C9 AD EB 56 82 55 9B 7A 68 1E 90 00
Time used: 88.000 ms
Send: 00 C2 00 01 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 00
Recv: 74 3F B8 61 66 76 1C E0 B3 85 A3 AF E7 55 D1 29 90 00
Time used: 80.000 ms
Send: 00 C2 01 00 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 00
Recv: A8 12 EA 8F 6C 96 AF DA 63 CF 28 EE 57 A7 86 E1 90 00
Time used: 86.000 ms
Send: 00 C2 01 01 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 00
Recv: FF A3 C7 ED 04 71 0B 98 06 7D AE 68 15 E2 75 1F 90 00
Time used: 83.000 ms
正如您在上面看到的,结果是不同的。我将以上结果与 this online tool 进行了比较。看来我的 NXP JCOP 卡工作正常。另外两张卡有什么问题?
在访问 INS == 0xC0 (setKeyAndInit)
的 APDU 缓冲区之前未调用 setIncomingAndReceive
方法。
The applet receives the APDU instance to process from the Java Card
runtime environment in the Applet.process(APDU) method, and the first
five header bytes [ CLA, INS, P1, P2, P3 ] are available in the APDU
buffer.
在调用 setIncomingAndReceive
之前 APDU 缓冲区中没有保证数据部分,您不应该访问它。但是,实际行为通常取决于特定的 Java 卡实现,这就是为什么您的小程序在 NXP 卡上正常工作的原因。
我写了下面的小程序在CBC和ECB模式下做AES加解密:
package cryptoPack;
import javacard.framework.*;
import javacard.security.AESKey;
import javacard.security.CryptoException;
import javacard.security.KeyBuilder;
import javacardx.crypto.Cipher;
public class CryptoAES extends Applet {
// Abbreviations
private static final boolean NO_EXTERNAL_ACCESS = false;
// AES Cipher AND its required key
Cipher cipher;
AESKey AESkey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, NO_EXTERNAL_ACCESS);
// A fixed Initial vector for AES CBC mode.
public byte[] IV = { (byte) 0x00, (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44, (byte) 0x55, (byte) 0x66,
(byte) 0x77, (byte) 0x88, (byte) 0x99, (byte) 0xAA, (byte) 0xBB, (byte) 0xCC, (byte) 0xDD, (byte) 0xEE,
(byte) 0xFF };
// Defining switch case variables for supported instructions ::: INS in APDU
// command
final byte SET_KEY = (byte) 0xC0;
final byte DO_CRYPTO = (byte) 0xC2;
// Defining switch case variables for cipher algorithms ::: P1 in APDU
// command
final byte AES_BLOCK_128_CBC_NOPAD = (byte) 0x00;
final byte AES_BLOCK_128_ECB_NOPAD = (byte) 0x01;
public static void install(byte[] bArray, short bOffset, byte bLength) {
new CryptoAES();
}
protected CryptoAES() {
register();
}
public void process(APDU apdu) {
if (selectingApplet()) {
return;
}
byte[] buffer = apdu.getBuffer();
// Analyzing the command.
try {
switch (buffer[ISO7816.OFFSET_INS]) {
case SET_KEY:
setKeyAndInit(apdu);
break;
case DO_CRYPTO:
do_crypto(apdu);
break;
default:
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
}
} catch (CryptoException e) {
ISOException.throwIt(((CryptoException) e).getReason());
}
}
public void setKeyAndInit(APDU apdu) throws ISOException {
byte[] buffer = apdu.getBuffer();
if (buffer[ISO7816.OFFSET_LC] == 16) {
AESkey.setKey(buffer, (short) ISO7816.OFFSET_CDATA);
} else {
ISOException.throwIt(ISO7816.SW_DATA_INVALID);
}
switch (buffer[ISO7816.OFFSET_P1]) {
case AES_BLOCK_128_CBC_NOPAD:
cipher = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, NO_EXTERNAL_ACCESS);
break;
case AES_BLOCK_128_ECB_NOPAD:
cipher = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_ECB_NOPAD, NO_EXTERNAL_ACCESS);
break;
}
}
public void do_crypto(APDU apdu) throws ISOException {
byte[] buffer = apdu.getBuffer();
short datalen = apdu.setIncomingAndReceive();
if ((datalen % 16) != 0) {
ISOException.throwIt(ISO7816.SW_DATA_INVALID);
}
byte[] out_data = JCSystem.makeTransientByteArray((short) 16, JCSystem.CLEAR_ON_DESELECT);
switch (buffer[ISO7816.OFFSET_P1]) {
case AES_BLOCK_128_CBC_NOPAD:
if(buffer[ISO7816.OFFSET_P2]== 0x00){
cipher.init(AESkey, Cipher.MODE_DECRYPT, IV, (short) 0x00, (short) 0x10);
}else{
cipher.init(AESkey, Cipher.MODE_ENCRYPT, IV, (short) 0x00, (short) 0x10);
}
break;
case AES_BLOCK_128_ECB_NOPAD:
if(buffer[ISO7816.OFFSET_P2]== 0x00){
cipher.init(AESkey, Cipher.MODE_DECRYPT);
}else{
cipher.init(AESkey, Cipher.MODE_ENCRYPT);
}
break;
default:
break;
}
short out_data_len = cipher.doFinal(buffer, (short) ISO7816.OFFSET_CDATA, datalen, out_data,
(short) 0);
Util.arrayCopyNonAtomic(out_data, (short) 0, buffer, (short) 0, out_data_len);
apdu.setOutgoingAndSend((short) 0, out_data_len);
}
}
在三个不同的卡上安装上面的小程序后,我有以下结果:
NXP JCOP v2.4.2 r3 - T=1 TPDU:
Send: 00 A4 04 00 06 01 02 03 04 07 01 00
Recv: 90 00
Time used: 15.000 ms
Send: 00 C0 00 00 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 00
Recv: 90 00
Time used: 123.000 ms
Send: 00 C2 00 00 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 00
Recv: C6 11 20 8A 02 37 B4 21 82 80 BC 62 CB 14 6C 46 90 00
Time used: 102.000 ms
Send: 00 C2 00 01 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 00
Recv: BB E0 95 7A 71 0E 04 4B FB 6B 5B 81 04 F6 7A A1 90 00
Time used: 72.000 ms
Send: 00 C2 01 00 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 00
Recv: C6 00 02 B9 46 62 D2 56 0A 19 16 D9 07 C9 82 B9 90 00
Time used: 73.000 ms
Send: 00 C2 01 01 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 00
Recv: E5 6E 26 F5 60 8B 8D 26 8F 25 56 E1 98 A0 E0 1B 90 00
Time used: 73.000 ms
两张其他卡片 - T=0 TPDU:
Send: 00 A4 04 00 06 01 02 03 04 07 01 00
Recv: 90 00
Time used: 675.000 ms
Send: 00 C0 00 00 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 00
Recv: 90 00
Time used: 57.000 ms
Send: 00 C2 00 00 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 00
Recv: A8 03 C8 BC 28 C3 C9 AD EB 56 82 55 9B 7A 68 1E 90 00
Time used: 88.000 ms
Send: 00 C2 00 01 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 00
Recv: 74 3F B8 61 66 76 1C E0 B3 85 A3 AF E7 55 D1 29 90 00
Time used: 80.000 ms
Send: 00 C2 01 00 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 00
Recv: A8 12 EA 8F 6C 96 AF DA 63 CF 28 EE 57 A7 86 E1 90 00
Time used: 86.000 ms
Send: 00 C2 01 01 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 00
Recv: FF A3 C7 ED 04 71 0B 98 06 7D AE 68 15 E2 75 1F 90 00
Time used: 83.000 ms
正如您在上面看到的,结果是不同的。我将以上结果与 this online tool 进行了比较。看来我的 NXP JCOP 卡工作正常。另外两张卡有什么问题?
在访问 INS == 0xC0 (setKeyAndInit)
的 APDU 缓冲区之前未调用 setIncomingAndReceive
方法。
The applet receives the APDU instance to process from the Java Card runtime environment in the Applet.process(APDU) method, and the first five header bytes [ CLA, INS, P1, P2, P3 ] are available in the APDU buffer.
在调用 setIncomingAndReceive
之前 APDU 缓冲区中没有保证数据部分,您不应该访问它。但是,实际行为通常取决于特定的 Java 卡实现,这就是为什么您的小程序在 NXP 卡上正常工作的原因。