使用 PC/SC reader 验证 Ultralight EV1

Authenticating Ultralight EV1 with PC/SC reader

我在 Java 中尝试使用 PC/SC reader(特别是 ACR1222L)验证 Ultralight EV1 卡时遇到问题。我能够使用 ISO 14443-3 标签的相应 APDU 在不受保护的标签上进行读写。但是,我找不到 运行 PWD_AUTH 命令的方法,因为它不是 14443-3 标准(或与此相关的任何本机命令)的一部分。是否可以 运行 此命令(或与此相关的任何本机命令)?

我尝试发送以下 APDU {e0 00 00 24 07 1b ff ff ff ff 63 00} 其中 1b 是本机命令,ff ff ff ff 是密码,63 00 是 CRC_A命令加上密码。我也尝试过不使用CRC,切换参数顺序等,但到目前为止我无法正常工作。

我还尝试包装 APDU(如 中所述)。我在 Desfire EV1 卡上使用它,但它不适用于 ultralight EV1(因为它显然不支持 ISO7816-4)。

那么,有没有办法使用 PC/SC reader 来验证 Ultralight EV1 卡?

首先,MIFARE Ultralight EV1 不支持 APDU。相反,它使用直接基于 ISO/IEC 14443-3 中定义的框架的命令。由于 ISO/IEC 14443-3 仅定义了帧和 anti-collision/enumeration 命令,因此之上的任何协议(例如 MIFARE Ultralight/NTAG 命令集)都是专有的。

使用密码 FF FF FF FF 进行密码验证的正确命令是:

byte[] tagCommand = new byte[] { (byte)0x1B, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF };

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

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

  1. 您可以使用 PC_to_RDR_Escape(请注意,仅当您为 reader 安装了原始 ACR driver 软件包时才可用)。假设您正在使用 Java 智能卡 IO API,您可以使用方法 Card.transmitControlCommand():

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

    方法的定义SCARD_CTL_CODE可以找到in this post

    command 需要是包含 APDU header 的字节数组 pseudo-APDU 将原始命令传递给非接触式前端芯片和非接触式前端的实际命令芯片。由于 ACR1222L 基于 NXP PN532(?),非接触式前端芯片的命令将是 InDataExchange 命令(参见 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);
    

    根据 reader 实际激活卡的方式,您可能需要使用 InCommunicateThru 命令而不是 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);
    

    伪 APDU header 可以通过以下方式添加:

    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. 另一种选择是直接使用PC_to_RDR_XfrBlock发送命令。这映射到 Java 智能卡 IO API:

    中的 CardChannel.transmit()
    ResponseAPDU responseApdu = cardChannel.transmit(commandAPDU);
    

    如果可以在该接口上使用相同的伪 APDU header,manual of your reader 不是很清楚。但是,如果您查看附录 H,您会发现 header 与包装到伪 APDU(ACR122U 传统模式)不同。所以你可以使用以下内容:

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

    请再次注意,您必须将标签命令包装到非接触式前端芯片的 InDataExchange 命令中。