首页 文章

javax.smartcardio:如何向Desfire卡发送本机命令?

提问于
浏览
2

我正在创建一个通过PC / SC非接触式读卡器和javax.smartcardio API与Mifare DESFire卡通信的Java应用程序 . 我设法定期发送ISO 7816 APDU(CLA,INS,P1-P2,Lc,Command数据,Le) .

我在Ridrix's Blog上读到DESFire卡(至少我使用的EV1版本)支持APDU和Native命令,其中大多数命令只有1个字节长 .

例如,“获取版本”命令:

Command: 60
Response: af 04 01 01 00 02 18 05

我使用SpringCard的PC / SC Diag程序(available here)测试了该命令,得到了正确的响应 .

但我无法使用javax.smartcardio发送此命令:此API似乎是为真正的APDU创建的,因此不允许1字节长的命令 .

这是我做的:

public static void main(String[] args){
    TerminalFactory factory = TerminalFactory.getDefault();
    CardTerminals terminalList = factory.terminals();

    try {
        CardTerminal ct = terminalList.list().get(0);       
        ct.waitForCardPresent(0);
        Card card = ct.connect("*");
        CardChannel channel = card.getBasicChannel();

        byte[] command = { 0x60 };

        channel.transmit(new CommandAPDU(command));
    } catch (CardException e) {
        e.printStackTrace();
    }
}

它给了我以下错误:

Exception in thread "main" java.lang.IllegalArgumentException: apdu must be at least 4 bytes long
    at javax.smartcardio.CommandAPDU.parse(Unknown Source)
    at javax.smartcardio.CommandAPDU.<init>(Unknown Source)

我尝试了唯一的(AFAIK)其他方式发送命令:

ByteBuffer command = ByteBuffer.allocate(1);
        command.put((byte) 0x60);

        ByteBuffer response = ByteBuffer.allocate(512);

        channel.transmit(command, response);

并得到一个类似的错误:

Exception in thread "main" java.lang.IllegalArgumentException: Command APDU must be at least 4 bytes long
    at sun.security.smartcardio.ChannelImpl.checkManageChannel(Unknown Source)
    at sun.security.smartcardio.ChannelImpl.doTransmit(Unknown Source)
    at sun.security.smartcardio.ChannelImpl.transmit(Unknown Source)

你知道使用javax.smartcardio或其他什么方式发送这种命令吗?

我知道可以包装这些命令,但我更喜欢使用(更简单的)本机命令 .

谢谢 .

3 回答

  • 3

    差不多4年后,但是为了防止这个问题有人发茬,我确实找到了答案 . 今天许多读者都支持在ISO 7816-4命令中包装Desfire APDU帧 . 我确实发现了数据不能超过55个字节的限制 .

    在本文档中查看第23页以获取完整信息:http://neteril.org/files/M075031_desfire.pdf

    这意味着您可以指定以下内容来包装APDU帧

    CLA = 0x90
    INC = {Your Desfire Command e.g. 0x60 - Get Version}
    P1 = 0
    P2 = 0
    Data = 1st byte = length of data followed by byte data. Terminate data with a 0x00 byte
    

    响应也包括如下:

    SW1 = 0x91
    SW2 = Result Status
    Data = Response Data
    

    因此可以使用以下代码

    public static byte CMD_WRAP_START = (byte)0x90;
    public static byte CMD_WRAP_END = (byte)0x00;
    
    private CommandAPDU wrapAPDUFrameUsingISO7816_4(byte[] apdu) throws CardException {
        if (apdu.length > 55){
            throw new CardException("The length of the wrapped DESFire command must not be longer than 55 bytes, checksum included.");
        }
        boolean hasData = apdu.length > 1;
        byte[] result;
        if (hasData) {
            result = new byte[apdu.length + 5];
        } else {
            result = new byte[apdu.length + 4];
        }
        result[0] = CMD_WRAP_START; // CLA
        result[1] = apdu[0];        // DESFIRE CMD CODE
        result[2] = 0;              // P1
        result[3] = 0;              // P2
        if (hasData) {
            result[4] = (byte) (apdu.length - 1);  // Length of wrapped data, ONLY IF DATA EXISTS
            System.arraycopy(apdu,1,result,5,apdu.length-1); // DESFIRE Command data
        }
        result[result.length-1] = CMD_WRAP_END;
        return new CommandAPDU(result);
    }
    
    private static byte [] unwrapFromISO7816_4(byte[] wrapped) throws CardException {
        if (wrapped.length<2){
            throw new CardException("Expected at least 2 bytes for ISO 7816-4 wrapped response: " + String.valueOf(Hex.encodeHex(wrapped, false)));
        }
        if (wrapped[wrapped.length-2]!=(byte)0x91){
            throw new CardException("Expected 0x91 in SW1 for ISO 7816-4 wrapped response: " + String.valueOf(Hex.encodeHex(wrapped, false)));
        }
        byte[] result = new byte[wrapped.length-1];
        System.arraycopy(wrapped,0,result,1,wrapped.length-2);  // The DESFIRE response
        result[0] = wrapped[wrapped.length-1];  // The DESFIRE Status
        return result;
    }
    
  • 1

    javax.smartcardio 是为使用ISO 7816-4命令而编写的API . 因此无法发送"native"命令 . 基本上,本机命令可以是任何东西,因此很难支持这些命令 .

    您要么恢复为JNI,要么尝试找到使用transmitControlCommand的内容 . 但是,如果没有额外的库,我担心没有真正的方法可以使用DESFire .

    我个人认为使用包装层要容易得多 .

  • 2

    这里有答案:命令APDU必须至少为4个字节 .

    * case 1 : |CLA|INS|P1 |P2 |                    len = 4 
         * case 2s: |CLA|INS|P1 |P2 |LE |                len = 5 
         * case 3s: |CLA|INS|P1 |P2 |LC |...BODY...|     len = 6..260 
         * case 4s: |CLA|INS|P1 |P2 |LC |...BODY...|LE | len = 7..261
         *
         * (Extended length is not currently supported) 
         * case 2e: |CLA|INS|P1 |P2|00 |LE1|LE2|                    len = 7 
         * case 3e: |CLA|INS|P1 |P2 |00|LC1|LC2|...BODY...|         len = 8..65542 
         * case 4e: |CLA|INS|P1 |P2 |00|LC1|LC2|...BODY...|LE1|LE2| len =10..65544
         *
         * EMV
    

相关问题