使用PC/SC读卡器进行Ultralight EV1身份验证

3
我有一个问题,尝试使用Java通过PC/SC阅读器(具体来说是ACR1222L)验证Ultralight EV1卡。我能够使用ISO 14443-3标签的相应APDU在未受保护的标签上进行写入和读取。然而,由于PWD_AUTH不是14443-3标准的一部分(或者任何原生命令),因此我找不到运行PWD_AUTH命令的方法。是否可能运行此命令(或任何原生命令)?
我已尝试发送以下APDU {e0 00 00 24 07 1b ff ff ff ff 63 00},其中1b是原生命令,ff ff ff ff是密码,63 00是命令加密码的CRC_A。我还尝试过不带CRC,交换参数顺序等,但到目前为止我无法使其工作。
我也尝试过包装APDU(如https://stackoverflow.com/a/41729534/3613883中所述)。我已成功地与Desfire EV1卡配合使用,但与Ultralight EV1不起作用(因为显然不支持ISO7816-4)。
那么,有没有办法使用PC/SC阅读器验证Ultralight EV1卡?
1个回答

4
首先,MIFARE Ultralight EV1不使用APDU通信协议,而是直接基于ISO/IEC 14443-3定义的帧结构来发送指令。由于ISO/IEC 14443-3只定义了帧结构和防冲突/枚举命令,因此在其之上的任何协议(例如MIFARE Ultralight/NTAG指令集)都是专有的。
使用密码FF FF FF FF进行密码验证的正确指令应该是:
byte[] tagCommand = new byte[] { (byte)0x1B, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF };

注意,CRC通常由非接触前端芯片处理,因此您无需手动添加。

使用ACR1222L,有多种不同的方式来交换此类专有命令:

  1. You can use PC_to_RDR_Escape (note that that's only available if you installed the original ACR driver package for the reader). Assuming that you are using the Java Smartcard IO API, you would do that using the method Card.transmitControlCommand():

    byte[] response = card.transmitControlCommand(SCARD_CTL_CODE(3500), command);
    

    The definition of the method SCARD_CTL_CODE can be found in this post.

    The command needs to be a byte array that contains an APDU header for the pseudo-APDU that passes raw commands to the contactless frontend chip and the actual command for the contactless frontend chip. Since the ACR1222L is based on an NXP PN532(?), the command for the contactless frontend chip would be the InDataExchange command (see the user manual):

    byte[] interfaceCommandHeader = new byte[] { (byte)0xD4, (byte)0x40, (byte)0x01 };
    byte[] interfaceCommand = Arrays.copyOf(interfaceCommandHeader, interfaceCommandHeader.length + tagCommand.length);
    System.arraycopy(tagCommand, 0, interfaceCommand, interfaceCommandHeader.length, tagCommand.length);
    

    Depending on how the reader actually activates the card, you might need to use the InCommunicateThru command instead of InDataExchange:

    byte[] interfaceCommandHeader = new byte[] { (byte)0xD4, (byte)0x42 };
    byte[] interfaceCommand = Arrays.copyOf(interfaceCommandHeader, interfaceCommandHeader.length + tagCommand.length);
    System.arraycopy(tagCommand, 0, interfaceCommand, interfaceCommandHeader.length, tagCommand.length);
    

    The pseudo APDU header can be added by:

    byte[] commandHeader = new byte[] { (byte)0xE0, (byte)0x00, (byte)0x00, (byte)0x24, (byte)0x00 };
    byte[] command = Arrays.copyOf(commandHeader, commandHeader.length + interfaceCommand.length);
    System.arraycopy(interfaceCommand, 0, command, commandHeader.length, interfaceCommand.length);
    command[4] = (byte)(interfaceCommand.length & 0x0FF);  // update Lc field
    
  2. Another option is to send commands directly using PC_to_RDR_XfrBlock. This maps to CardChannel.transmit() in the Java Smartcard IO API:

    ResponseAPDU responseApdu = cardChannel.transmit(commandAPDU);
    

    The manual of your reader is not quite clear if the same pseudo APDU header can be used over that interface. However, if you look into appendix H, you'll find a different header from wrapping into a pseudo APDU (the ACR122U legacy mode). So you could use the following:

    CommandAPDU commandAPDU = new CommandAPDU(0xFF, 0x00, 0x00, 0x00, interfaceCommand);
    

    Note that, again, you have to wrap the tag command into the InDataExchange command for the contactless frontend chip.


1
嗨,使用这两个选项,我能够运行读写命令,即tagCommand = new byte[] { (byte)0x30, (byte)0x04}和tagCommand = new byte[] { (byte)0xA2, (byte)0x04, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00}。但是当我尝试运行其他命令(例如{(byte)0x1B, ...}进行身份验证,或{(byte) 0x60}获取版本),我得到63 00作为响应,这意味着通信中出现了错误。我是否遗漏了什么? 顺便说一下,选项1的最后一行应该是command[4] = (byte)(interfaceCommand.length & 0x0FF);。 非常感谢。 - jesm00
1
如果我使用InCommunicateThru命令进行包装(byte [] interfaceCommandHeader = new byte [] {(byte) 0xD4,(byte) 0x40};),GET_VERSION和PWD_AUTH命令都可以工作(尽管我不知道为什么:))。非常感谢您的帮助。 - jesm00
抱歉,我犯了一个错误,InCommunicateThru 包装器应该是 byte[] interfaceCommandHeader = new byte[] { (byte)0xD4, (byte)0x42};。 - jesm00
1
@jesm00 感谢您的测试和澄清。我已将其整合到我的答案中。希望这对您来说没问题。 - Michael Roland

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接