智能卡 reader 访问中出现未知错误 0x16
Unkown error 0x16 on smartcard reader access
我正在尝试更改 ACR1252U 上的蜂鸣器持续时间。
Link 到 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();
}
但这会导致以下堆栈跟踪表明 "unkown error 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
我已经花了几个小时在这个方向上搜索任何东西,但是我找不到任何东西。我什至尝试了另一个设备,它仍然产生这个错误。
要么我完全失明了,要么我的电脑设置不正确。我只能说,我已经使用这个 reader 成功写入和读取 NFC 标签。但我无法更改 reader 本身的配置。
编辑:
我还找到了另一种发送命令的方法:
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);
但这会导致 "unknown error 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
尝试使用
card.transmitControlCommand(int controlCode, byte[] command)
而不是传输。根据第 5.8 节(您链接到的 pdf 的第 41 页)
controlcode 是 3500,虽然我不清楚它是十六进制还是整数,所以如果可以的话,请与 SCARD_CTL_CODE 进行比较。至少,我是这样解释文档的。
通常您使用 transmitControlCommand 与 reader 通信并传输与卡通信。
修复了 ControlCode 中的拼写错误。感谢 Torhan Bartel 告诉我。
有两种方法可以通过 Java 智能卡 IO API 与此 reader 交互:
首先是打开一个常规的APDU传输通道(从PC/SC的角度来看这映射到T=0或T=1协议)。您可以使用
Card card = getCard("*");
但是,这将需要 reader 报告卡的存在。否则你无法以这种方式打开连接。
然后您可以将 APDU 命令传输到卡(在基本通道或逻辑通道上),您可以在基本通道上向 reader 发送特殊命令。这些特殊命令的 class 字节设置为 0xFF,以指示该命令旨在由 reader 解释(而不是转发到卡)。所以这不适用于以 0xE0.
开头的 "peripherals control" 命令
那些 "peripherals control" 命令必须使用带有控制代码 SCARD_CTL_CODE(3500)
的控制命令发送到 reader。与打开与卡的连接一样,如果 reader 上存在卡,则可以使用 getCard("*")
。但是,如果即使没有卡也能将这些命令发送到 reader,则必须在 "direct" 模式下打开连接:
Card card = getCard("DIRECT");
然后您可以使用方法card.transmitControlCommand()
发送控制命令。此方法将控制代码作为第一个参数,将命令(作为字节数组)作为第二个参数。使用 channel.transmit()
在基本通道或任何逻辑通道上交换命令通常不会在 "direct" 模式下工作(因此错误代码为 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 控制代码需要特殊的驱动程序支持 。具体来说,微软提供的标准CCID驱动默认是不支持发送escape命令的(见USB CCID Class Driver Details)。此驱动程序仅在通过注册表值 "EscapeCommandEnable" 启用后才支持转义命令。您在问题中显示的错误 0x1
是缺少对转义命令的支持的典型结果。
要可靠地支持 reader 的所有功能(包括转义命令),您需要使用 ACS on their website 提供的 "PC/SC Drivers" 包。
我正在尝试更改 ACR1252U 上的蜂鸣器持续时间。
Link 到 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();
}
但这会导致以下堆栈跟踪表明 "unkown error 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
我已经花了几个小时在这个方向上搜索任何东西,但是我找不到任何东西。我什至尝试了另一个设备,它仍然产生这个错误。
要么我完全失明了,要么我的电脑设置不正确。我只能说,我已经使用这个 reader 成功写入和读取 NFC 标签。但我无法更改 reader 本身的配置。
编辑:
我还找到了另一种发送命令的方法:
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);
但这会导致 "unknown error 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
尝试使用
card.transmitControlCommand(int controlCode, byte[] command)
而不是传输。根据第 5.8 节(您链接到的 pdf 的第 41 页) controlcode 是 3500,虽然我不清楚它是十六进制还是整数,所以如果可以的话,请与 SCARD_CTL_CODE 进行比较。至少,我是这样解释文档的。
通常您使用 transmitControlCommand 与 reader 通信并传输与卡通信。
修复了 ControlCode 中的拼写错误。感谢 Torhan Bartel 告诉我。
有两种方法可以通过 Java 智能卡 IO API 与此 reader 交互:
首先是打开一个常规的APDU传输通道(从PC/SC的角度来看这映射到T=0或T=1协议)。您可以使用
Card card = getCard("*");
但是,这将需要 reader 报告卡的存在。否则你无法以这种方式打开连接。
然后您可以将 APDU 命令传输到卡(在基本通道或逻辑通道上),您可以在基本通道上向 reader 发送特殊命令。这些特殊命令的 class 字节设置为 0xFF,以指示该命令旨在由 reader 解释(而不是转发到卡)。所以这不适用于以 0xE0.
开头的 "peripherals control" 命令
那些 "peripherals control" 命令必须使用带有控制代码
SCARD_CTL_CODE(3500)
的控制命令发送到 reader。与打开与卡的连接一样,如果 reader 上存在卡,则可以使用getCard("*")
。但是,如果即使没有卡也能将这些命令发送到 reader,则必须在 "direct" 模式下打开连接:Card card = getCard("DIRECT");
然后您可以使用方法
card.transmitControlCommand()
发送控制命令。此方法将控制代码作为第一个参数,将命令(作为字节数组)作为第二个参数。使用channel.transmit()
在基本通道或任何逻辑通道上交换命令通常不会在 "direct" 模式下工作(因此错误代码为 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 控制代码需要特殊的驱动程序支持 。具体来说,微软提供的标准CCID驱动默认是不支持发送escape命令的(见USB CCID Class Driver Details)。此驱动程序仅在通过注册表值 "EscapeCommandEnable" 启用后才支持转义命令。您在问题中显示的错误
0x1
是缺少对转义命令的支持的典型结果。要可靠地支持 reader 的所有功能(包括转义命令),您需要使用 ACS on their website 提供的 "PC/SC Drivers" 包。