Page 1 of 1
Testing command success on JCIDE but failed on real card?
Posted: Thu Dec 03, 2015 6:47 am
by leonard
I develop an applet which return some simulate bytes data(6 bytes) when it receive Read Records command from host application. I use ACS ACR 38 card reader to access to card.
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.
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
Code: Select all
apdu.setOutgoingAndSend((short) 0, (short)6)
Could you explain to me what is wrong here?
Thanks
Re: Testing command success on JCIDE but failed on real card?
Posted: Thu Dec 03, 2015 10:05 pm
by leonard
Is anyone can help me out !
For more detail, here is my code, please try and let me know why it don't work on real card if you can.
Many thanks.
Code: Select all
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;
/*
// EF data FID Recordlen MaxRecordNum
private byte [][] dataEF1; // 4001 05 16
private byte [][] dataEF2; // 4002 08 08
private byte [][] dataEF3; // 4003 16 05
private byte [][] dataEF4; // 4004 05 16
private byte [][] dataEF5; // 4005 08 08
private byte [][] dataEF6; // 4006 16 05
private byte [][] dataEF7; // 4007 05 16
*/
private byte[] outBuffer;
// private byte currentEF;
private byte[] currentEF; // hirose 150127
//static byte currentEF;
/**
* Constructor
*/
private PTCRecords(byte[] bArray, short bOffset, byte bLength) {
currentEF = JCSystem.makeTransientByteArray((short)1, JCSystem.CLEAR_ON_DESELECT);
currentEF[0] = NO_EF0; // hirose 150127
// 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;
// waits();
}
}
private void waits( ) {
byte i;
for( i=0; i<0x20; i++){
}
}
}
Re: Testing command success on JCIDE but failed on real card?
Posted: Thu Dec 03, 2015 11:02 pm
by mabel
Is the communication protocol of your card the same as the simulator protocol in
JCIDE?
"6C xx" means you should tell cards the number of returned bytes(xx) you expected.
So you should append Le byte to the APDU command and resend it.
>> 00 B3 00 00 06
Note:
You'd better set the same simulator protocol in
JCIDE as the real card.
Re: Testing command success on JCIDE but failed on real card?
Posted: Fri Dec 04, 2015 4:59 am
by leonard
mabel wrote:Is the communication protocol of your card the same as the simulator protocol in
JCIDE?
"6C xx" means you should tell cards the number of returned bytes(xx) you expected.
So you should append Le byte to the APDU command and resend it.
>> 00 B3 00 00 06
Note:
You'd better set the same simulator protocol in
JCIDE as the real card.
Thanks for your answer,
The problem is solved as your explanation. Issue occur because of different protocol,
pyApdutool set protocol is T1 while card support T0 protocol.
I've changed the command by adding Le byte, then its work now.