智能卡读卡器访问时出现未知错误0x16

Tor*_*tel 3 java apdu smartcard pcsc smartcard-reader

我正在尝试更改ACR1252U上的蜂鸣器持续时间.

链接至API:http: //www.acs.com.hk/download-manual/6402/API-ACR1252U-1.09.pdf

根据API文档,我需要'E0000028010A'命令来更改蜂鸣器状态,其中'0A'将持续时间标记为0A*10ms(页44).

使用以下Java代码:

public static void main(String[] args) {
    try {
        byte[] send = new byte[6];

        send[0] = (byte) 0xE0; // Commandclass
        send[1] = (byte) 0x00; // Protocoll
        send[2] = (byte) 0x00; // Param 1
        send[3] = (byte) 0x28; // Param 2: Buzzerstatus
        send[4] = (byte) 0x01; // Change Flag
        send[5] = (byte) 0x0A; // Duration: 0A*10ms => 100ms

        Card card = getCard("DIRECT"); // Works!
        CardChannel channel = card.getBasicChannel(); // Works!
        CommandAPDU command = new CommandAPDU(send); // Works!
        channel.transmit(command); // EXCEPTION!
    } catch (Exception ex) {
        ex.printStackTrace();
    }
}

public static Card getCard(String target) throws Exception {
    TerminalFactory factory = TerminalFactory.getDefault();
    List<CardTerminal> terminals = factory.terminals().list();
    for (CardTerminal t : terminals) {
        if (t.getName().equals("ACS ACR1252 Dual Reader PICC 0")) {
            Card card = t.connect(target);
            return card;
        }
    }
    throw new Exception();
}
Run Code Online (Sandbox Code Playgroud)

但这导致以下stacktrace指示"未知错误0x16":

javax.smartcardio.CardException: sun.security.smartcardio.PCSCException: Unknown error 0x16
    at sun.security.smartcardio.ChannelImpl.doTransmit(ChannelImpl.java:219)
    at sun.security.smartcardio.ChannelImpl.transmit(ChannelImpl.java:90)
    at readerconfig.TagConfig.main(TagConfig.java:24)
Caused by: sun.security.smartcardio.PCSCException: Unknown error 0x16
    at sun.security.smartcardio.PCSC.SCardTransmit(Native Method)
    at sun.security.smartcardio.ChannelImpl.doTransmit(ChannelImpl.java:188)
    ... 2 more
Run Code Online (Sandbox Code Playgroud)

我花了好几个小时在这个方向寻找任何东西,但我找不到任何东西.我甚至尝试了另一个设备,它仍然产生了这个错误.

要么我完全失明了,要么我的电脑没有正确设置.我只能说,我已经使用这款阅读器成功地从NFC标签上书写和阅读.但我无法改变读者本身的配置.

编辑:

我也发现了这种发送命令的替代方法:

byte[] send = new byte[5];
send[0] = (byte) 0xE0;
send[1] = (byte) 0x0;
send[2] = (byte) 0x0;
send[3] = (byte) 0x18; // Tries to read firmware version
send[4] = (byte) 0x0;

Card card = CardUtils.getCard("DIRECT"); // Works!
card.transmitControlCommand(3500, send);
Run Code Online (Sandbox Code Playgroud)

但这会导致"未知错误0x1":

javax.smartcardio.CardException: transmitControlCommand() failed
    at sun.security.smartcardio.CardImpl.transmitControlCommand(CardImpl.java:236)
    at readerconfig.ReaderConfig.main(ReaderConfig.java:28)
Caused by: sun.security.smartcardio.PCSCException: Unknown error 0x1
    at sun.security.smartcardio.PCSC.SCardControl(Native Method)
    at sun.security.smartcardio.CardImpl.transmitControlCommand(CardImpl.java:232)
    ... 1 more
Run Code Online (Sandbox Code Playgroud)

Mic*_*and 5

有两种方法可以通过Java Smartcard IO API与此阅读器进行交互:

  1. 第一种是打开常规APDU传输通道(从PC/SC的角度来看,这映射到T = 0或T = 1协议).你可以使用

    Card card = getCard("*");
    
    Run Code Online (Sandbox Code Playgroud)

    但是,这将要求读者报告卡的存在.否则你无法以这种方式打开连接.

    然后,您可以将APDU命令发送到卡(在基本通道或逻辑通道上),您可以在基本通道上向阅读器发送特殊命令.这些特殊命令将其类字节设置为0xFF,以指示该命令旨在由读取器解释(而不是转发到卡).因此,这不适用于以0xE0开头的"外设控制"命令.

  2. 必须使用带有控制代码的控制命令将这些"外围设备控制"命令发送到阅读器SCARD_CTL_CODE(3500).与打开卡的连接一样,getCard("*")如果读卡器上有卡,则可以使用.但是,如果您希望能够将这些命令发送到阅读器,即使没有卡,也必须以"直接"模式打开连接:

    Card card = getCard("DIRECT");
    
    Run Code Online (Sandbox Code Playgroud)

    然后,您可以使用该方法发送控制命令card.transmitControlCommand().此方法将控制代码作为第一个参数,将命令(作为字节数组)作为第二个参数.在基本通道或任何逻辑通道上交换命令channel.transmit()通常不能在"直接"模式下工作(因此错误代码为0x16).

    控制代码计算为

    public static final int SCARD_CTL_CODE(int command) {
        boolean isWindows = System.getProperty("os.name").startsWith("Windows");
        if (isWindows) {
            return 0x00310000 | (command << 2);
        } else {
            return 0x42000000 | command;
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    请注意Windows和其他平台之间的区别.

    例如,要发送蜂鸣器控制命令,您将使用

    byte[] command = new byte[] { (byte)0xE0, (byte)0x00, (byte)0x00, (byte)0x28, (byte)0x01, (byte)0x0A };
    byte[] response = card.transmitControlCommand(SCARD_CTL_CODE(3500), command);
    
    Run Code Online (Sandbox Code Playgroud)

    最后,请注意,通过PC/SC发送IOCTL控制代码需要特殊的驱动程序支持.具体来说,Microsoft提供的标准CCID驱动程序默认情况下不支持发送转义命令(请参阅USB CCID类驱动程序详细信息).此驱动程序仅在通过注册表值"EscapeCommandEnable"启用它们后才支持转义命令.0x1您在问题中显示的错误是这个缺少对escape命令的支持的典型结果.

    要可靠地支持阅读器的所有功能(包括转义命令),您需要在其网站上使用ACS提供的"PC/SC驱动程序"包.