Java 使用 AES 的卡加密 returns 6F00
Java card encryption using AES returns 6F00
我是 Java 卡片编程的新手。我看到了一个关于 Java 卡的 AES 加密方法的示例,并且能够创建和安装小程序。当我尝试与小程序通信时,它 returns 出现 6F00
错误消息。我做错了什么?
这是我的代码示例:
private void SendData(APDU apdu)
{
aesKeyTrial= (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false);
byte[] outBuffer;
outBuffer = new byte[16];
byte[] trialKey;
trialKey = new byte[16];
byte[] input = {(byte)0x44,(byte)0x22,(byte)0x33,(byte)0x44,(byte)0x55,(byte)0x66,(byte)0x77,(byte)0x88,(byte)0x99,0x10,(byte)0xA2, 0x35, (byte)0x5E,0x15,0x16,0x14};
byte[] key = {0x2d, 0x2a, 0x2d, 0x42, 0x55, 0x49, 0x4c, 0x44, 0x41, 0x43, 0x4f, 0x44, 0x45, 0x2d, 0x2a, 0x2d};
byte[] buffer = apdu.getBuffer();
short len = (short) input.length;
aesKeyTrial.setKey(trialKey,(short)0);
if(len<=0||len%16!=0)
{
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
}
aesCipher = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, false);
aesCipher.init(aesKeyTrial, Cipher.MODE_ENCRYPT);
aesCipher.doFinal(input, ISO7816.OFFSET_CDATA, len, outBuffer, (short)0);
Util.arrayCopyNonAtomic(input, (short)0, buffer, (short)0, (short)len);
apdu.setOutgoing();
apdu.setOutgoingLength(len);
apdu.sendBytes((short) 0, (short)len);
}
我要加密的是 "input"。
嗨,我发现我的错误是什么或者我的代码有什么问题。我误解了 doFinal 函数,它弄乱了卡中要安装的内容。
这是新的干净代码:
Cipher aesCipher;
AESKey aesKeyTrial;
aesKeyTrial= (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES_TRANSIENT_DESELECT, KeyBuilder.LENGTH_AES_128, false);
byte[] buffer = apdu.getBuffer();
byte[] aesKey;
aesKey = new byte[16];
byte[] input = {(byte)0x11,(byte)0x22,(byte)0x33,(byte)0x44,(byte)0x55,(byte)0x66,(byte)0x77,(byte)0x88,(byte)0x99,0x10,(byte)0xA2, 0x35, (byte)0x5E,0x15,0x16,0x14};
byte[] key = {0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26};
short len = (short) input.length;
if (len <= 0 || len % 16 != 0)
{
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
}
aesKeyTrial.setKey(key,(short)0);
aesCipher = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_ECB_NOPAD, false);
aesCipher.init(aesKeyTrial, Cipher.MODE_ENCRYPT);
aesCipher.doFinal(input, (short)0, len, buffer, (short)0);
apdu.setOutgoingAndSend((short)0, len);
正如您在回答中所说,您的代码的问题是您向 doFinal (byte[] inBuff, short inOffset, short inLength,byte[] outBuff, short outOffset)
方法传递了错误的参数。
但是,您使用的示例代码似乎很糟糕。我称它写得不好是有原因的:
1- 它在多次调用的方法中使用了动态变量。动态变量是使用 new
关键字创建的变量。这些变量将分配在 EEPROM 中。在 EEPROM 中写入真的比 RAM 慢,而且它之前的擦写操作也有限 wear out,因此强烈建议在不需要共享数据的情况下使用 RAM 而不是 EEPROM不同的 CAD (Card Reader) 会话。又因为Java Card中没有mandatory/automatic垃圾收集器,所以不要重复定义不同的动态变量,用一个全局变量代替(class变量或实例变量代替局部变量)。
2- 由于int
数据类型在Java卡片中是可选的,你必须尽量使用short
和byte
并且你还需要将所有值转换为这些类型以防止 signed/unsigned 问题。
最后,我为您提供了您程序的改进版本:
package soQusetion;
import javacard.framework.*;
import javacard.security.AESKey;
import javacard.security.KeyBuilder;
import javacardx.crypto.Cipher;
public class SOQ1 extends Applet {
Cipher aesCipher;
AESKey aesKey;
//Two different types of memory for different usage. each one has 16 (= 0x10) byte capacity.
private byte[] volatileMem;
private byte[] nonVolatileMem;
//INS value for APDU command
public static final byte INS_SET_KEY = 0x10;
public static final byte INS_ENCRYPT = 0x20;
public static final byte INS_DECRYPT = 0x30;
public static void install(byte[] bArray, short bOffset, byte bLength) {
new SOQ1();
}
protected SOQ1() {
volatileMem = JCSystem.makeTransientByteArray((short) 0x10, JCSystem.CLEAR_ON_DESELECT);
nonVolatileMem = new byte[(short) 0x10];
aesCipher = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_ECB_NOPAD, false);
aesKey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false);
register();
}
public void process(APDU apdu) {
if (selectingApplet()) {
return;
}
byte[] buffer = apdu.getBuffer();
apdu.setIncomingAndReceive();
if (buffer[ISO7816.OFFSET_LC] != (byte) 0x10) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
}
switch (buffer[ISO7816.OFFSET_INS]) {
case INS_SET_KEY:
aesKey.setKey(buffer, ISO7816.OFFSET_CDATA);
break;
case INS_ENCRYPT:
aesCipher.init(aesKey, Cipher.MODE_ENCRYPT);
aesCipher.doFinal(buffer, ISO7816.OFFSET_CDATA, (short) 0x10, volatileMem, (short) 0x00);
Util.arrayCopyNonAtomic(volatileMem, (short) 0x00, buffer, (short) 0x00, (short) 0x10);
apdu.setOutgoingAndSend((short) 0x00, (short) 0x10);
break;
case INS_DECRYPT:
aesCipher.init(aesKey, Cipher.MODE_DECRYPT);
aesCipher.doFinal(buffer, ISO7816.OFFSET_CDATA, (short) 0x10, volatileMem, (short) 0x00);
Util.arrayCopyNonAtomic(volatileMem, (short) 0x00, buffer, (short) 0x00, (short) 0x10);
apdu.setOutgoingAndSend((short) 0x00, (short) 0x10);
break;
default:
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
}
}
}
它的工作原理如下:
Select Applet begin...
Select Applet successful.
//Loading AES Key APDU Command
Send: 00 10 00 00 10 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 00
Recv: 90 00
//Encrypt APDU Command
Send: 00 20 00 00 10 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 00
Recv: 62 F6 79 BE 2B F0 D9 31 64 1E 03 9C A3 40 1B B2 90 00
//Decrypt APDU Command
Send: 00 30 00 00 10 62 F6 79 BE 2B F0 D9 31 64 1E 03 9C A3 40 1B B2 00
Recv: 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 90 00
欢迎提出任何使上述程序更高效的建议。
我是 Java 卡片编程的新手。我看到了一个关于 Java 卡的 AES 加密方法的示例,并且能够创建和安装小程序。当我尝试与小程序通信时,它 returns 出现 6F00
错误消息。我做错了什么?
这是我的代码示例:
private void SendData(APDU apdu)
{
aesKeyTrial= (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false);
byte[] outBuffer;
outBuffer = new byte[16];
byte[] trialKey;
trialKey = new byte[16];
byte[] input = {(byte)0x44,(byte)0x22,(byte)0x33,(byte)0x44,(byte)0x55,(byte)0x66,(byte)0x77,(byte)0x88,(byte)0x99,0x10,(byte)0xA2, 0x35, (byte)0x5E,0x15,0x16,0x14};
byte[] key = {0x2d, 0x2a, 0x2d, 0x42, 0x55, 0x49, 0x4c, 0x44, 0x41, 0x43, 0x4f, 0x44, 0x45, 0x2d, 0x2a, 0x2d};
byte[] buffer = apdu.getBuffer();
short len = (short) input.length;
aesKeyTrial.setKey(trialKey,(short)0);
if(len<=0||len%16!=0)
{
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
}
aesCipher = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, false);
aesCipher.init(aesKeyTrial, Cipher.MODE_ENCRYPT);
aesCipher.doFinal(input, ISO7816.OFFSET_CDATA, len, outBuffer, (short)0);
Util.arrayCopyNonAtomic(input, (short)0, buffer, (short)0, (short)len);
apdu.setOutgoing();
apdu.setOutgoingLength(len);
apdu.sendBytes((short) 0, (short)len);
}
我要加密的是 "input"。
嗨,我发现我的错误是什么或者我的代码有什么问题。我误解了 doFinal 函数,它弄乱了卡中要安装的内容。
这是新的干净代码:
Cipher aesCipher;
AESKey aesKeyTrial;
aesKeyTrial= (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES_TRANSIENT_DESELECT, KeyBuilder.LENGTH_AES_128, false);
byte[] buffer = apdu.getBuffer();
byte[] aesKey;
aesKey = new byte[16];
byte[] input = {(byte)0x11,(byte)0x22,(byte)0x33,(byte)0x44,(byte)0x55,(byte)0x66,(byte)0x77,(byte)0x88,(byte)0x99,0x10,(byte)0xA2, 0x35, (byte)0x5E,0x15,0x16,0x14};
byte[] key = {0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26};
short len = (short) input.length;
if (len <= 0 || len % 16 != 0)
{
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
}
aesKeyTrial.setKey(key,(short)0);
aesCipher = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_ECB_NOPAD, false);
aesCipher.init(aesKeyTrial, Cipher.MODE_ENCRYPT);
aesCipher.doFinal(input, (short)0, len, buffer, (short)0);
apdu.setOutgoingAndSend((short)0, len);
正如您在回答中所说,您的代码的问题是您向 doFinal (byte[] inBuff, short inOffset, short inLength,byte[] outBuff, short outOffset)
方法传递了错误的参数。
但是,您使用的示例代码似乎很糟糕。我称它写得不好是有原因的:
1- 它在多次调用的方法中使用了动态变量。动态变量是使用 new
关键字创建的变量。这些变量将分配在 EEPROM 中。在 EEPROM 中写入真的比 RAM 慢,而且它之前的擦写操作也有限 wear out,因此强烈建议在不需要共享数据的情况下使用 RAM 而不是 EEPROM不同的 CAD (Card Reader) 会话。又因为Java Card中没有mandatory/automatic垃圾收集器,所以不要重复定义不同的动态变量,用一个全局变量代替(class变量或实例变量代替局部变量)。
2- 由于int
数据类型在Java卡片中是可选的,你必须尽量使用short
和byte
并且你还需要将所有值转换为这些类型以防止 signed/unsigned 问题。
最后,我为您提供了您程序的改进版本:
package soQusetion;
import javacard.framework.*;
import javacard.security.AESKey;
import javacard.security.KeyBuilder;
import javacardx.crypto.Cipher;
public class SOQ1 extends Applet {
Cipher aesCipher;
AESKey aesKey;
//Two different types of memory for different usage. each one has 16 (= 0x10) byte capacity.
private byte[] volatileMem;
private byte[] nonVolatileMem;
//INS value for APDU command
public static final byte INS_SET_KEY = 0x10;
public static final byte INS_ENCRYPT = 0x20;
public static final byte INS_DECRYPT = 0x30;
public static void install(byte[] bArray, short bOffset, byte bLength) {
new SOQ1();
}
protected SOQ1() {
volatileMem = JCSystem.makeTransientByteArray((short) 0x10, JCSystem.CLEAR_ON_DESELECT);
nonVolatileMem = new byte[(short) 0x10];
aesCipher = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_ECB_NOPAD, false);
aesKey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false);
register();
}
public void process(APDU apdu) {
if (selectingApplet()) {
return;
}
byte[] buffer = apdu.getBuffer();
apdu.setIncomingAndReceive();
if (buffer[ISO7816.OFFSET_LC] != (byte) 0x10) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
}
switch (buffer[ISO7816.OFFSET_INS]) {
case INS_SET_KEY:
aesKey.setKey(buffer, ISO7816.OFFSET_CDATA);
break;
case INS_ENCRYPT:
aesCipher.init(aesKey, Cipher.MODE_ENCRYPT);
aesCipher.doFinal(buffer, ISO7816.OFFSET_CDATA, (short) 0x10, volatileMem, (short) 0x00);
Util.arrayCopyNonAtomic(volatileMem, (short) 0x00, buffer, (short) 0x00, (short) 0x10);
apdu.setOutgoingAndSend((short) 0x00, (short) 0x10);
break;
case INS_DECRYPT:
aesCipher.init(aesKey, Cipher.MODE_DECRYPT);
aesCipher.doFinal(buffer, ISO7816.OFFSET_CDATA, (short) 0x10, volatileMem, (short) 0x00);
Util.arrayCopyNonAtomic(volatileMem, (short) 0x00, buffer, (short) 0x00, (short) 0x10);
apdu.setOutgoingAndSend((short) 0x00, (short) 0x10);
break;
default:
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
}
}
}
它的工作原理如下:
Select Applet begin...
Select Applet successful.
//Loading AES Key APDU Command
Send: 00 10 00 00 10 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 00
Recv: 90 00
//Encrypt APDU Command
Send: 00 20 00 00 10 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 00
Recv: 62 F6 79 BE 2B F0 D9 31 64 1E 03 9C A3 40 1B B2 90 00
//Decrypt APDU Command
Send: 00 30 00 00 10 62 F6 79 BE 2B F0 D9 31 64 1E 03 9C A3 40 1B B2 00
Recv: 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 90 00
欢迎提出任何使上述程序更高效的建议。