I develop an applet which return some simulate bytes data(6 bytes) when it receive Read Records command from host application. I test applet with JCIDE, it work well. I also test it with virtual card reader by using pyApdutool, it work well too.
But when I install applet on a card by using pyApdutool, use the same Read Records command, the card return "Unknown error" message. But when I don't set data in respond, the card return "90 00" message.
apdu.setOutgoingAndSend((short) 0, (short)0)
The applet was installed successful, using pyApdutool can select applet on the card.
Here is Read Records command: [0x00 0xB3 0x00 0xFF]
I use etOutgoingAndSend() method to send data out
apdu.setOutgoingAndSend((short) 0, (short)6)
Here is testing result with JCIDE
Here is testing result with PyApduTool on real card
And here is my code:
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;
}
}
}
please try and let me know why it don't work on real card if you could ? Thanks.
The problem is that your JCIDE simulator behaves as a T=1
card, but your real card is a T=0
card. T=0
and T=1
are transmission protocols defined by the ISO/IEC 7816-3 standard. T=0
is the older and simpler one.
T=1
returns all the data together with the status word independently on your Le
byte in APDU.
T=0
checks if the length of your output data is equal to Le
byte from the input (T=0
card expects your device has limited memory, so it responds with as many output bytes as asked to only). If not, it sends a status word 6CXX
, where the XX
is the correct Le
byte. You have to resend the command with 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
You don't have to worry about performance - the command is not processed again. Your output is ready from the first call, you just have to ask for it.
Use setIncomingAndReceive at the very beginning of the ReadRecors()
method. Like:
private void ReadRecord(APDU apdu) {
// ReadRecord
apdu.setIncomingAndReceive();
rest of your codes....
Calling this method indicates that this APDU has incoming data.
来源:https://stackoverflow.com/questions/34079805/read-records-failed-on-real-smart-card