javax.smartcardio:如何将本机命令发送到 Desfire 卡?
Posted
技术标签:
【中文标题】javax.smartcardio:如何将本机命令发送到 Desfire 卡?【英文标题】:javax.smartcardio: how to send native commands to Desfire card? 【发布时间】:2012-07-27 10:25:41 【问题描述】:我正在创建一个通过 PC/SC 非接触式读卡器和 javax.smartcardio API 与 Mifare DESFire 卡通信的 java 应用程序。我设法发送常规 ISO 7816 APDU(CLA、INS、P1-P2、Lc、命令数据、Le)。
我在Ridrix's Blog 上读到 DESFire 卡(至少我正在使用的 EV1 版本)支持 APDU 和 本机命令,其中大多数命令只有 1 个字节长。 p>
例如,“获取版本”命令:
Command: 60
Response: af 04 01 01 00 02 18 05
我使用 SpringCard (available here) 的 PC/SC Diag 程序测试了该命令,得到了正确的响应。
但我无法使用 javax.smartcardio 发送此命令:此 API 似乎是为 real 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 或其他方式发送此类命令的任何方法吗?
我知道可以包装这些命令,但我更喜欢使用(更简单的)本机命令。
谢谢。
【问题讨论】:
【参考方案1】:javax.smartcardio
是为使用 ISO 7816-4 命令而编写的 API。因此,不可能发送“本机”命令。基本上,本机命令可以是任何东西,因此很难支持它们。
您要么恢复到 JNI,要么尝试找到使用 transmitControlCommand
的东西。但恐怕没有额外的库就没有真正使用 DESFire 的方法。
我个人认为使用包裹层要容易得多。
【讨论】:
这对您有帮助吗?你能跟进你的问题吗? 感谢 owlstead!我想我会使用包装命令。我相信 DESFire 卡的第一个版本只支持本地命令,但我不相信我会遇到这些。我尝试了transmitControlCommand
,但这个java函数失败了(此外,我不知道如何填写controlCorde
字段)。您能否更具体地说明在我的案例中 JNI 的使用?
使用 JNI 或多或少意味着创建一个包装类和.dll
,它使用 NXP 的 DESFire 特定库和头文件。 transmitControlCommand()
是特定于读者的,因此其使用受到限制。【参考方案2】:
将近 4 年后,但以防万一有人对这个问题有疑问,我确实找到了答案。今天的许多读者支持将 Desfire APDU 帧包装在 ISO 7816-4 命令中。我确实发现了数据不能超过 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;
【讨论】:
您确定这是阅读器的功能吗? AFAIK DESFire 卡本身支持此功能(因此它们是“框架”命令,与“本机”命令不同)。【参考方案3】:这里有答案:命令 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
【讨论】:
以上是关于javax.smartcardio:如何将本机命令发送到 Desfire 卡?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Android 上使用 javax.smartcardio 包?
如何使用 RFID 读卡器 (javax.smartcardio) 识别德国身份证?
通过 javax.smartcardio 连接到 micro SD
将 javax.smartcardio 用于 MIFARE Classic 和 Omnikey 5021 CL