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

时间:2017-01-25 12:20:50

标签: java smartcard apdu smartcard-reader pcsc

我正在尝试更改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();
}

但这会导致以下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

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

要么我完全失明了,要么我的电脑没有正确设置。我只能说,我已经使用这款阅读器成功地从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);

但这会导致“未知错误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

2 个答案:

答案 0 :(得分:5)

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

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

    执行此操作
    Card card = getCard("*");
    

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

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

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

    Card card = getCard("DIRECT");
    

    然后,您可以使用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;
        }
    }
    

    请注意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);
    

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

    要可靠地支持阅读器的所有功能(包括转义命令),您需要使用ACS on their website提供的“PC / SC驱动程序”包。

答案 1 :(得分:1)

尝试使用

card.transmitControlCommand(int controlCode, byte[] command)

而不是传输。根据第5.8节(您链接到的pdf的第41页) controlcode是3500,虽然我不清楚,如果是十六进制或int,所以如果你能够,则与SCARD_CTL_CODE比较。至少,我解释了这个文档。

通常你使用transmitControlCommand与读者交谈并发送与卡谈话。

修正了ControlCode中的拼写错误。感谢Torhan Bartel告诉我。