如何用 java 卡在智能卡中写入文件
How to write a file in a smart card with java card
您好,我是 javacard 的初学者!我想在我的智能卡中写入一个文件(小于 1024kb),所以我要做的是将文件转换为字节数组(byte[]),例如我有一个长度为 638 的数组!我写入和读取数据的代码工作正常(没有错误)但是当我从智能卡读取数据并将其与默认数组进行比较时,它们不相同(长度相同但内容不同)。我在这里阅读了很多主题,但找不到可以解决我问题的主题!谢谢你的帮助
我使用 JCDK 2.2.2
我的读写数据的小程序:
.....
public void process(APDU apdu) {
byte[] buffer = apdu.getBuffer();
switch(buffer[ISO7816.OFFSET_INS]) {
case INS_SET_DATA:
// byte bufferDataLength = (byte)(apdu.setIncomingAndReceive());
//We want to start data copy
if (buffer[ISO7816.OFFSET_P1] == 0x00 && buffer[ISO7816.OFFSET_P2] == 0x00) {
data = new byte[(short) 638];
} else {
// copying the apdu data into byte array Data
// array copy: (src, offset, target, offset,copy size)
Util.arrayCopyNonAtomic(buffer, (short) ISO7816.OFFSET_CDATA, data,
(short) ((short) buffer[ISO7816.OFFSET_P1] * 100),
(short) buffer[ISO7816.OFFSET_P2]
);
}
break;
case INS_GET_DATA:
// array copy: (src, offset, target, offset,copy size)
short t = (short)(buffer[ISO7816.OFFSET_P2] & 0xFF);
short o = (short)(buffer[ISO7816.OFFSET_P1] & 0xFF);
Util.arrayCopyNonAtomic(data, o, buffer, ISO7816.OFFSET_CDATA, t);
apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, t);
break;
}
}
.....
我用来将数据发送到智能卡的代码(JCDK 2.2.2 的 apduio 包)
.....
switch(choice) {
case 8:
byte[] configData = fileToByteArray("D:\jcard\config.dat");
byte[] chunk = new byte[100];
System.out.println("Config Length: " + configData.length); // Got 638
int configLength = configData.length;
double round = Math.floor((double)configLength / 100);
int lastPart = configLength % 100;
System.out.println("Round : " + round); // Got 6
System.out.println("Last Part : " + lastPart); // Got 38
double partToSend = lastPart == 0 ? round : round + 1;
//Initialize the array of byte in my applet to have the length of data i want to write in
apdu.command[Apdu.INS] = MyAppletClient.INS_SET_DATA;
apdu.command[Apdu.P1] = 0x00;
apdu.command[Apdu.P2] = 0x00;
cad.exchangeApdu(apdu);
handleResponse(apdu);
boolean allPartSend = true;
for (int i = 0; i < round; i++) {
//array copy: (src, offset, target, offset, copy size)
System.arraycopy(configData, (i * 100), chunk, 0, 100);
System.out.println("Data Length: " + chunk.length); // Got 100
apdu.command[Apdu.P1] = (byte) i;
apdu.command[Apdu.P2] = 100;
apdu.setDataIn(Data);
cad.exchangeApdu(apdu);
if (apdu.getStatus() != 0x9000) {
System.out.println("["+i+"] An error occurred with status: " + apdu.getStatus());
allPartSend = false;
break;
}
}
if(allPartSend) {
byte[] finalPart = new byte[lastPart];
System.arraycopy(configData, (int) (round * 100), finalPart, 0, lastPart);
apdu.command[Apdu.P1] = (byte) round;
apdu.command[Apdu.P2] = (byte) lastPart;
apdu.setDataIn(finalPart);
cad.exchangeApdu(apdu);
if (apdu.getStatus() != 0x9000) {
System.out.println("An error occurred with status: " + apdu.getStatus());
break;
} else {
System.out.println("OK");
}
} else {
System.out.println("Fail to send all data");
}
break;
case 9:
int cfgLength = 638; //Because i know the array has that length
byte[] res = new byte[cfgLength];
double rnd = Math.floor((double)cfgLength / 100);
int last = fpLength % 100, readLength = 0;
boolean allSend = true;
for(int i = 0; i < rnd; i++) {
apdu.command[Apdu.INS] = MyAppletClient.INS_GET_DATA;
apdu.command[Apdu.P1] = (byte) (i * 100);
apdu.command[Apdu.P2] = 100;
cad.exchangeApdu(apdu);
if (apdu.getStatus() != 0x9000) {
System.out.println("An error occurred with status: " + apdu.getStatus());
} else {
readLength += apdu.dataOut.length;
// System.out.println("Datalength : " + apdu.dataOut.length); // Got 100
//array copy: (src, offset, target, offset, copy size)
System.arraycopy(apdu.dataOut, 0, res, (i*100), apdu.dataOut.length);
}
}
if(allSend) {
apdu.command[Apdu.INS] = MyAppletClient.INS_GET_DATA;
apdu.command[Apdu.P1] = (byte) ((int)rnd * 100);
apdu.command[Apdu.P2] = (byte) last;
cad.exchangeApdu(apdu);
if (apdu.getStatus() != 0x9000) {
System.out.println("An error occurred with status: " + apdu.getStatus());
break;
} else {
readLength += apdu.dataOut.length;
//array copy: (src, offset, target, offset, copy size)
System.arraycopy(apdu.dataOut, 0, res, (int)(rnd * 100 ), apdu.dataOut.length);
}
} else {
System.out.println("Fail to get all the part");
}
byte[] cfgData = fileToByteArray("D:\jcard\config.dat");
System.out.println("ReadLength : " + readLength); // Got 638
System.out.println("Array equals : " + Arrays.equals(cfgData, res)); // Got false but expected true
break;
}
}
.....
您似乎在混合使用 ISO case 1 和 3 命令。 ISO 情况 1 意味着不存在 CDATA 或 RDATA,而 ISO 情况 3 意味着只有 CDATA,没有 RDATA。通常,最好让命令只是可能的 4 种情况之一(情况 2 只是 RDATA,情况 4 是 CDATA 和 RDATA。例如,您可以创建 CREATE RECORD 命令或类似命令。
您应该在将数据复制到数组之前立即调用 setIncomingAndReceive
。如果您不这样做,那么缓冲区中可能只存在部分数据。请注意,setIncomingAndReceive
是一种方便的方法,您也许可以找到更好的方法来处理大量数据。
请注意,ISO 7816-4 已经定义了基于文件的卡片系统;复制现有标准的一部分可能比从头开始创建自己的标准更有用。另请注意,您应该更喜欢 APDU#getOffsetCdata
而不是常量。该方法与扩展长度的 APDU 兼容,您可能希望在将来升级到这些 APDU。
我的猜测是,您收到的数据与写入卡的数据不同的原因是这行代码:
switch(choice) {
...
case 9:
...
for(int i = 0; i < rnd; i++) {
...
apdu.command[Apdu.P1] = (byte) (i * 100);
与将数据写入卡的代码不同,此处您在 P1 参数中传递数据偏移量。请记住,P1 只有一个字节,当 i=3 时它会溢出,因此无法在 P1 中编码 638 字节长数组的偏移量。
解决问题的最快方法是使用与编写代码时相同的方法,即:
switch(choice) {
...
case 9:
...
for(int i = 0; i < rnd; i++) {
...
apdu.command[Apdu.P1] = (byte) i;
并在 JavaCard 代码中进行乘法运算:
short o = (short) ((short) buffer[ISO7816.OFFSET_P1] * 100);
这个解决方案应该可行,但它并不完美,因为您的 JavaCard 代码支持 read/write 的任意长度(P2 - 复制长度),但偏移量固定为 0、100、200 等。这意味着只有当您将 P2=100 发送到卡片时,它才有效,您当前的代码就是这样做的。但是如果这个参数固定为一个值,为什么要首先发送这个参数呢?
我同意 Maarten Bodewes 的观点,最好遵循 ISO/IEC 7816-4 并使用其中指定的命令语法。标准中描述的 UPDATE BINARY 和 READ BINARY 命令应该符合您的需要。
您好,我是 javacard 的初学者!我想在我的智能卡中写入一个文件(小于 1024kb),所以我要做的是将文件转换为字节数组(byte[]),例如我有一个长度为 638 的数组!我写入和读取数据的代码工作正常(没有错误)但是当我从智能卡读取数据并将其与默认数组进行比较时,它们不相同(长度相同但内容不同)。我在这里阅读了很多主题,但找不到可以解决我问题的主题!谢谢你的帮助
我使用 JCDK 2.2.2
我的读写数据的小程序:
.....
public void process(APDU apdu) {
byte[] buffer = apdu.getBuffer();
switch(buffer[ISO7816.OFFSET_INS]) {
case INS_SET_DATA:
// byte bufferDataLength = (byte)(apdu.setIncomingAndReceive());
//We want to start data copy
if (buffer[ISO7816.OFFSET_P1] == 0x00 && buffer[ISO7816.OFFSET_P2] == 0x00) {
data = new byte[(short) 638];
} else {
// copying the apdu data into byte array Data
// array copy: (src, offset, target, offset,copy size)
Util.arrayCopyNonAtomic(buffer, (short) ISO7816.OFFSET_CDATA, data,
(short) ((short) buffer[ISO7816.OFFSET_P1] * 100),
(short) buffer[ISO7816.OFFSET_P2]
);
}
break;
case INS_GET_DATA:
// array copy: (src, offset, target, offset,copy size)
short t = (short)(buffer[ISO7816.OFFSET_P2] & 0xFF);
short o = (short)(buffer[ISO7816.OFFSET_P1] & 0xFF);
Util.arrayCopyNonAtomic(data, o, buffer, ISO7816.OFFSET_CDATA, t);
apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, t);
break;
}
}
.....
我用来将数据发送到智能卡的代码(JCDK 2.2.2 的 apduio 包)
.....
switch(choice) {
case 8:
byte[] configData = fileToByteArray("D:\jcard\config.dat");
byte[] chunk = new byte[100];
System.out.println("Config Length: " + configData.length); // Got 638
int configLength = configData.length;
double round = Math.floor((double)configLength / 100);
int lastPart = configLength % 100;
System.out.println("Round : " + round); // Got 6
System.out.println("Last Part : " + lastPart); // Got 38
double partToSend = lastPart == 0 ? round : round + 1;
//Initialize the array of byte in my applet to have the length of data i want to write in
apdu.command[Apdu.INS] = MyAppletClient.INS_SET_DATA;
apdu.command[Apdu.P1] = 0x00;
apdu.command[Apdu.P2] = 0x00;
cad.exchangeApdu(apdu);
handleResponse(apdu);
boolean allPartSend = true;
for (int i = 0; i < round; i++) {
//array copy: (src, offset, target, offset, copy size)
System.arraycopy(configData, (i * 100), chunk, 0, 100);
System.out.println("Data Length: " + chunk.length); // Got 100
apdu.command[Apdu.P1] = (byte) i;
apdu.command[Apdu.P2] = 100;
apdu.setDataIn(Data);
cad.exchangeApdu(apdu);
if (apdu.getStatus() != 0x9000) {
System.out.println("["+i+"] An error occurred with status: " + apdu.getStatus());
allPartSend = false;
break;
}
}
if(allPartSend) {
byte[] finalPart = new byte[lastPart];
System.arraycopy(configData, (int) (round * 100), finalPart, 0, lastPart);
apdu.command[Apdu.P1] = (byte) round;
apdu.command[Apdu.P2] = (byte) lastPart;
apdu.setDataIn(finalPart);
cad.exchangeApdu(apdu);
if (apdu.getStatus() != 0x9000) {
System.out.println("An error occurred with status: " + apdu.getStatus());
break;
} else {
System.out.println("OK");
}
} else {
System.out.println("Fail to send all data");
}
break;
case 9:
int cfgLength = 638; //Because i know the array has that length
byte[] res = new byte[cfgLength];
double rnd = Math.floor((double)cfgLength / 100);
int last = fpLength % 100, readLength = 0;
boolean allSend = true;
for(int i = 0; i < rnd; i++) {
apdu.command[Apdu.INS] = MyAppletClient.INS_GET_DATA;
apdu.command[Apdu.P1] = (byte) (i * 100);
apdu.command[Apdu.P2] = 100;
cad.exchangeApdu(apdu);
if (apdu.getStatus() != 0x9000) {
System.out.println("An error occurred with status: " + apdu.getStatus());
} else {
readLength += apdu.dataOut.length;
// System.out.println("Datalength : " + apdu.dataOut.length); // Got 100
//array copy: (src, offset, target, offset, copy size)
System.arraycopy(apdu.dataOut, 0, res, (i*100), apdu.dataOut.length);
}
}
if(allSend) {
apdu.command[Apdu.INS] = MyAppletClient.INS_GET_DATA;
apdu.command[Apdu.P1] = (byte) ((int)rnd * 100);
apdu.command[Apdu.P2] = (byte) last;
cad.exchangeApdu(apdu);
if (apdu.getStatus() != 0x9000) {
System.out.println("An error occurred with status: " + apdu.getStatus());
break;
} else {
readLength += apdu.dataOut.length;
//array copy: (src, offset, target, offset, copy size)
System.arraycopy(apdu.dataOut, 0, res, (int)(rnd * 100 ), apdu.dataOut.length);
}
} else {
System.out.println("Fail to get all the part");
}
byte[] cfgData = fileToByteArray("D:\jcard\config.dat");
System.out.println("ReadLength : " + readLength); // Got 638
System.out.println("Array equals : " + Arrays.equals(cfgData, res)); // Got false but expected true
break;
}
}
.....
您似乎在混合使用 ISO case 1 和 3 命令。 ISO 情况 1 意味着不存在 CDATA 或 RDATA,而 ISO 情况 3 意味着只有 CDATA,没有 RDATA。通常,最好让命令只是可能的 4 种情况之一(情况 2 只是 RDATA,情况 4 是 CDATA 和 RDATA。例如,您可以创建 CREATE RECORD 命令或类似命令。
您应该在将数据复制到数组之前立即调用 setIncomingAndReceive
。如果您不这样做,那么缓冲区中可能只存在部分数据。请注意,setIncomingAndReceive
是一种方便的方法,您也许可以找到更好的方法来处理大量数据。
请注意,ISO 7816-4 已经定义了基于文件的卡片系统;复制现有标准的一部分可能比从头开始创建自己的标准更有用。另请注意,您应该更喜欢 APDU#getOffsetCdata
而不是常量。该方法与扩展长度的 APDU 兼容,您可能希望在将来升级到这些 APDU。
我的猜测是,您收到的数据与写入卡的数据不同的原因是这行代码:
switch(choice) {
...
case 9:
...
for(int i = 0; i < rnd; i++) {
...
apdu.command[Apdu.P1] = (byte) (i * 100);
与将数据写入卡的代码不同,此处您在 P1 参数中传递数据偏移量。请记住,P1 只有一个字节,当 i=3 时它会溢出,因此无法在 P1 中编码 638 字节长数组的偏移量。
解决问题的最快方法是使用与编写代码时相同的方法,即:
switch(choice) {
...
case 9:
...
for(int i = 0; i < rnd; i++) {
...
apdu.command[Apdu.P1] = (byte) i;
并在 JavaCard 代码中进行乘法运算:
short o = (short) ((short) buffer[ISO7816.OFFSET_P1] * 100);
这个解决方案应该可行,但它并不完美,因为您的 JavaCard 代码支持 read/write 的任意长度(P2 - 复制长度),但偏移量固定为 0、100、200 等。这意味着只有当您将 P2=100 发送到卡片时,它才有效,您当前的代码就是这样做的。但是如果这个参数固定为一个值,为什么要首先发送这个参数呢?
我同意 Maarten Bodewes 的观点,最好遵循 ISO/IEC 7816-4 并使用其中指定的命令语法。标准中描述的 UPDATE BINARY 和 READ BINARY 命令应该符合您的需要。