在真正的智能卡上读取记录失败

时间:2015-12-04 02:14:24

标签: javacard

我开发了一个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;

        }
    }

}

如果可以,请尝试让我知道为什么它不能在真实卡上工作? 感谢。

2 个答案:

答案 0 :(得分:2)

问题是您的JCIDE模拟器表现为T=1卡,但您的真实卡是T=0卡。 T=0T=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有传入数据。