我开发了一个applet,它在从主机应用程序接收Read Records命令时返回一些模拟字节数据(6个字节)。 我用JCIDE测试applet,它运行良好。 我也使用pyApdutool用虚拟读卡器测试它,它也很好用。
但是当我使用pyApdutool在卡上安装applet时,使用相同的Read Records命令,卡返回" Unknown error"信息。 但是,当我没有设置响应数据时,卡片返回" 90 00"消息。
apdu.setOutgoingAndSend((short) 0, (short)0)
applet安装成功,使用pyApdutool可以选择卡上的applet。
这是Read Records命令: [0x00 0xB3 0x00 0xFF]
我使用etOutgoingAndSend()方法发送数据
apdu.setOutgoingAndSend((short) 0, (short)6)
Here is testing result with JCIDE
Here is testing result with PyApduTool on real card
这是我的代码:
package PTCRecords;
import javacard.framework.*;
public class PTCRecords extends Applet {
// class of instructions
private final static byte CLA = (byte) 0x00;
// instruction codes
private final static byte INS_SELECT = (byte) 0xA4; // select
private final static byte INS_READ_R = (byte) 0xB2; // read record
private final static byte INS_READ_RS = (byte) 0xB3; // read records
// instruction parameters
private final static byte P1_SEL_DF = (byte) 0x04; // select DF
private final static byte P2_SEL_DF = (byte) 0xC0; //
private final static byte P1_SEL_EF = (byte) 0x02; // select EF
private final static byte P2_SEL_EF = (byte) 0xC0; //
private final static byte P1_RD_R16 = (byte) 0x10; // read record #16
private final static byte P1_RD_R8 = (byte) 0x08; // read record #4
private final static byte P1_RD_R5 = (byte) 0x05; // read record #1
private final static byte P2_RD_R = (byte) 0x04; //
private final static byte P1_RD_RS = (byte) 0x00; // read records
private final static byte P2_RD_RS = (byte) 0xFF; //
// EF No.
private final static byte NO_EF0 = (byte) 0x00; // DF
private final static byte NO_EF1 = (byte) 0x01; // EF1
private final static byte NO_EF2 = (byte) 0x02; // EF2
private final static byte NO_EF3 = (byte) 0x03; // EF3
private final static byte NO_EF4 = (byte) 0x04; // EF4
private final static byte NO_EF5 = (byte) 0x05; // EF5
private final static byte NO_EF6 = (byte) 0x06; // EF6
private final static byte NO_EF7 = (byte) 0x07; // EF7
//only for test
private final static short ARRAY_SIZE = 256;
private byte[] outBuffer;
private byte[] currentEF;
/**
* Constructor
*/
private PTCRecords(byte[] bArray, short bOffset, byte bLength) {
currentEF = JCSystem.makeTransientByteArray((short)1, JCSystem.CLEAR_ON_DESELECT);
currentEF[0] = NO_EF0;
// create a transient buffer
outBuffer = new byte[ARRAY_SIZE];
}
public static void install(byte[] bArray, short bOffset, byte bLength)
{
new PTCRecords(bArray, bOffset, bLength).register(bArray, (short) (bOffset + 1), bArray[bOffset]);
}
public boolean select() {
return true;
} // select
public void process(APDU apdu) {
//Insert your code here
byte[] buf = apdu.getBuffer();
// the selectingApplet() is used in the applet process method to distinguish
// the SELECT APDU command, which selected this applet, from all other SELECT
// APDU commands. Returns true if this applet is being selected
if (selectingApplet()) {
currentEF[0] = NO_EF0; // hirose 150127
ISOException.throwIt(ISO7816.SW_NO_ERROR);
return;
}
// verify if the applet can accept this APDU message
if (buf[ISO7816.OFFSET_CLA] != CLA) {
ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
}
switch (buf[ISO7816.OFFSET_INS]) {
case INS_SELECT:
selectEF(apdu);
break;
case INS_READ_R: // 0xB2: read record
ReadRecord(apdu);
break;
case INS_READ_RS: // 0xB3: read records
//0x00 0xB3 0x00 0xFF
if((buf[ISO7816.OFFSET_P1] == P1_RD_RS)
&& (buf[ISO7816.OFFSET_P2] == P2_RD_RS))
{ // 0x00FF: ReadRecords
ReadRecors(apdu);
} else {
ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
}
break;
default:
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
}
}
private void selectEF(APDU apdu) {
// select
byte buffer[] = apdu.getBuffer();
currentEF[0] = buffer[ISO7816.OFFSET_CDATA+1];
ISOException.throwIt(ISO7816.SW_NO_ERROR);
}
private void ReadRecord(APDU apdu) {
// ReadRecord
byte buffer[] = apdu.getBuffer();
byte len=0;
byte record=0;
byte cnt=0;
len = buffer[ISO7816.OFFSET_LC];
record = buffer[ISO7816.OFFSET_P1];
switch(currentEF[0]){
case NO_EF1: // EF#1
case NO_EF4: // EF#4
case NO_EF7: // EF#7
if( record > P1_RD_R16){
ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
}
break;
case NO_EF2: // EF#2
case NO_EF5: // EF#5
if( record > P1_RD_R8){
ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
}
break;
case NO_EF3: // EF#3
case NO_EF6: // EF#6
if( record > P1_RD_R5){
ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
}
break;
}
// set data
getData( buffer, (byte)(0), (byte)len , currentEF[0]);
// send data
apdu.setOutgoingAndSend((short) 0, (short) len);
}
private void ReadRecors(APDU apdu) {
byte buffer[] = apdu.getBuffer();
byte lc = 0;
byte idxLen = 0;
byte len = 0;
byte cnt = 0;
if( buffer[ISO7816.OFFSET_CDATA] != 0x53){
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
}
lc = buffer[ISO7816.OFFSET_LC];
idxLen = buffer[ISO7816.OFFSET_CDATA+1];
if( lc != idxLen+2){
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
}
for( cnt = 0;cnt<idxLen;cnt+=4){
switch(buffer[(byte)(ISO7816.OFFSET_CDATA+cnt+3)]){
case NO_EF1: // EF#1
case NO_EF4: // EF#4
case NO_EF7: // EF#7
len += 5;
break;
case NO_EF2: // EF#2
case NO_EF5: // EF#5
len += 8;
break;
case NO_EF3: // EF#3
case NO_EF6: // EF#6
len += 16;
break;
}
}
getData( buffer, (byte)(0), (byte)5 , (byte)1);
getData( buffer, (byte)(5), (byte)8 , (byte)2);
getData( buffer, (byte)(5+8), (byte)16 , (byte)3);
getData( buffer, (byte)(5+8+16), (byte)5 , (byte)4);
getData( buffer, (byte)(5*2+8+16), (byte)8 , (byte)5);
getData( buffer, (byte)(5*2+8*2+16), (byte)16 , (byte)6);
getData( buffer, (byte)(5*2+8*2+16*2), (byte)5 , (byte)7);
apdu.setOutgoingAndSend((short)0, (short)6);
}
private void getData( byte buffer[], byte offset, byte len , byte val) {
byte cnt;
for( cnt = offset; cnt < offset + len ; cnt++){
buffer[cnt] = val;
}
}
}
如果可以,请尝试让我知道为什么它不能在真实卡上工作? 感谢。
答案 0 :(得分:2)
问题是您的JCIDE模拟器表现为T=1
卡,但您的真实卡是T=0
卡。 T=0
和T=1
是ISO / IEC 7816-3标准定义的传输协议。 T=0
是较旧且较简单的。
T=1
在APDU的Le
字节上独立返回所有数据和状态字。
T=0
检查输出数据的长度是否等于来自输入的Le
字节(T=0
卡,期望您的设备内存有限,因此它会响应尽可能多的输出字节按要求)。如果不是,则会发送状态字6CXX
,其中XX
是正确的Le
字节。您必须使用Le=XX
重新发送命令:
<- 00 A4 04 00 06 11 22 33 44 55 66 00
-> 90 00
<- 00 B3 00 FF
-> 6C 06
<- 00 B3 00 FF 06
-> 11 11 11 11 11 22 90 00
您不必担心性能 - 该命令不会再次处理。您的输出在第一次通话时已准备就绪,您只需要求它。
答案 1 :(得分:0)
在ReadRecors()
方法的最开头使用setIncomingAndReceive。像:
private void ReadRecord(APDU apdu) {
// ReadRecord
apdu.setIncomingAndReceive();
rest of your codes....
调用此方法表示此APDU有传入数据。