read records failed on real smart card

喜夏-厌秋 提交于 2019-12-08 08:32:40

问题


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.


回答1:


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.




回答2:


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!