javax.smartcardio:如何将本机命令发送到Desfire卡? [英] javax.smartcardio: how to send native commands to Desfire card?

查看:81
本文介绍了javax.smartcardio:如何将本机命令发送到Desfire卡?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am creating a java application communicating with a Mifare DESFire card through a PC/SC contactless reader and the javax.smartcardio API. I manage to send regular ISO 7816 APDUs (CLA, INS, P1-P2, Lc, Command data, Le).

我在 Ridrix的博客中已读过DESFire卡(至少是我正在使用的EV1版本)同时支持APDU和本地命令,其中大多数命令只有1个字节长.

I have read on Ridrix's Blog that DESFire cards (at least the EV1 version that I am using) support both APDUs and Native commands where most of the commands are only 1 byte long.

例如,"获取版本"命令:

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

我用来自 SpringCard PC/SC Diag 程序测试了该命令(

I tested that command with the PC/SC Diag program from SpringCard (available here) and I get a correct response.

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

But I cannot send this command with javax.smartcardio: this API seems to have been created for real APDUs and therefore does not allow 1 byte long commands.

这是我所做的:

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)其他发送命令的方式:

I tried the only (AFAIK) other way to send a command:

        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或其他方式发送这种命令的任何方法吗?

Do you know of any way to send this kind of command using javax.smartcardio or something else?

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

I know it is possible to wrap these commands but I would prefer to use the (simpler) native commands.

谢谢.

推荐答案

将近4年后,但以防万一有人在这个问题上胡思乱想,我确实找到了答案.如今,许多读者支持在ISO 7816-4命令中包装Desfire APDU帧.我确实发现了一个限制,即数据不能超过55个字节.

Nearly 4 years later but just in case someone stubbles across this question, I did find an answer to this. Many readers today support wrapping Desfire APDU frames in a ISO 7816-4 command. I did discover a limitation whereby the data cannot exceed 55 bytes.

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

Checkout page 23 in this doc for full info: http://neteril.org/files/M075031_desfire.pdf

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

This means that you can specify the following to wrap the APDU frame

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;
}

这篇关于javax.smartcardio:如何将本机命令发送到Desfire卡?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆