如何在没有卡的情况下向智能卡 reader(而不是智能卡)发送命令?
How to send commands to smart card reader (and not to the smart card) while no card present?
前言:
我有一张双界面智能卡 reader,它具有一些扩展功能(除了向卡发送 APDU 命令和接收 APDU 响应之外)。
例如在其文档中提到您可以使用以下命令获取 reader 的固件版本:
GET_FIRMWARE_VERSION: FF 69 44 42 05 68 92 00 05 00
在它的工具中,有一个用于此功能的按钮并且可以正常使用:
我什至嗅探了 USB 端口以查看在我的 PC 和我的 reader 之间的连接中为这个功能到底交换了什么:
命令:
回复:
问题:
我想使用其他工具或通过代码获取我的 reader 版本(并可能发送其他扩展命令),但我必须在卡中插入一张卡 reader 才能发送命令,否则我会收到 No Card Present
异常,而我不想向卡发送命令! (reader 工具成功回答 GET_FIRMWARE_VERSION 而 reader 的插槽中没有可用的卡)
到目前为止我做了什么:
1.I 尝试了一些工具,包括 OpenSCTool 、PyAPDUTool 和另一个 reader 的工具。
2.I 编写了以下 python 脚本来发送扩展命令。
#--- Importing required modules.
import sys
import time
sys.path.append("D:\PythonX\Lib\site-packages")
from smartcard.scard import *
import smartcard.util
from smartcard.System import readers
#---This is the list of commands that we want to send device
cmds =[[,0xFF,0x69,0x44,0x42,0x05,0x68,0x92,0x00,0x04,0x00],]
#--- Let's to make a connection to the card reader
r=readers()
print "Available Readers :",r
print
target_reader = input("--- Select Reader (0, 1 , ...): ")
print
while(True):
try:
print "Using :",r[target_reader]
reader = r[target_reader]
connection=reader.createConnection()
connection.connect()
break
except:
print "--- Exception occured! (Wrong reader or No card present)"
ans = raw_input("--- Try again? (0:Exit/1:Again/2:Change Reader)")
if int(ans)==0:
exit()
elif int(ans)==2:
target_reader = input("Select Reader (0, 1 , ...): ")
#--- An struct for APDU responses consist of Data, SW1 and SW2
class stru:
def __init__(self):
self.data = list()
self.sw1 = 0
self.sw2 = 0
resp = stru()
def send(cmds):
for cmd in cmds:
#--- Following 5 line added to have a good format of command in the output.
temp = stru() ;
temp.data[:]=cmd[:]
temp.sw1=12
temp.sw2=32
modifyFormat(temp)
print "req: ", temp.data
resp.data,resp.sw1,resp.sw2 = connection.transmit(cmd)
modifyFormat(resp)
printResponse(resp)
def modifyFormat(resp):
resp.sw1=hex(resp.sw1)
resp.sw2=hex(resp.sw2)
if (len(resp.sw2)<4):
resp.sw2=resp.sw2[0:2]+'0'+resp.sw2[2]
for i in range(0,len(resp.data)):
resp.data[i]=hex(resp.data[i])
if (len(resp.data[i])<4):
resp.data[i]=resp.data[i][0:2]+'0'+resp.data[i][2]
def printResponse(resp):
print "res: ", resp.data,resp.sw1,resp.sw2
send(cmds)
connection.disconnect()
输出:
>>> ================================ RESTART ================================
Available Readers : ['CREATOR CRT-603 (CZ1) CCR RF 0', 'CREATOR CRT-603 (CZ1) CCR SAM 0']
--- Select Reader (0, 1 , ...): 0
Using : CREATOR CRT-603 (CZ1) CCR RF 0
--- Exception occured! (Wrong reader or No card present)
--- Try again? (0:Exit/1:Again/2:Change Reader)
>>> ================================ RESTART ================================
Available Readers : ['CREATOR CRT-603 (CZ1) CCR RF 0', 'CREATOR CRT-603 (CZ1) CCR SAM 0']
--- Select Reader (0, 1 , ...): 1
Using : CREATOR CRT-603 (CZ1) CCR SAM 0
--- Exception occured! (Wrong reader or No card present)
--- Try again? (0:Exit/1:Again/2:Change Reader)
但是两者都有上述问题!
问题:
1- 如何在没有可用卡的情况下向 reader 发送扩展命令?
2- 为什么我在嗅探数据中看不到命令 header? (请注意,由于 Header 是所有扩展命令的 pre-designated 固定值,我认为 reader 工具不会发送带有 GET_FIRMWARE_VERSION 命令的 header而且它只发送数据!但它是如何工作的?)
更新:
通过反复试验,我发现了一些有用的东西。
假设:
- Pseudo-APDUs固定header=
FF 69 44 42
- Pseudo-APDU 数据字段 GET_READER_FIRMWARE_VERSION =
68 92 00 04 00
- Pseudo-APDU 数据字段 CHANGE_SAM_SLOT =
68 92 01 00 03 XX 00 00
(我的reader有两个SAM插槽,所以XX
可以是01
或02
)
- SELECT APDU 命令=
00 A4 04 00 00
好的,我写了下面的Java程序:
import java.util.List;
import java.util.Scanner;
import javax.smartcardio.Card;
import javax.smartcardio.CardChannel;
import javax.smartcardio.CardException;
import javax.smartcardio.CardTerminal;
import javax.smartcardio.CommandAPDU;
import javax.smartcardio.ResponseAPDU;
import javax.smartcardio.TerminalFactory;
import javax.xml.bind.DatatypeConverter;
public class TestPCSC {
public static void main(String[] args) throws CardException {
TerminalFactory tf = TerminalFactory.getDefault();
List< CardTerminal> terminals = tf.terminals().list();
System.out.println("Available Readers:");
System.out.println(terminals + "\n");
Scanner scanner = new Scanner(System.in);
System.out.print("Which reader do you want to send your commands to? (0 or 1 or ...): ");
String input = scanner.nextLine();
int readerNum = Integer.parseInt(input);
CardTerminal cardTerminal = (CardTerminal) terminals.get(readerNum);
Card connection = cardTerminal.connect("DIRECT");
CardChannel cardChannel = connection.getBasicChannel();
System.out.println("Write your commands in Hex form, without '0x' or Space charaters.");
System.out.println("\n---------------------------------------------------");
System.out.println("Pseudo-APDU Mode:");
System.out.println("---------------------------------------------------");
while (true) {
System.out.println("Pseudo-APDU command: (Enter 0 to send APDU command)");
String cmd = scanner.nextLine();
if (cmd.equals("0")) {
break;
}
System.out.println("Command : " + cmd);
byte[] cmdArray = hexStringToByteArray(cmd);
byte[] resp = connection.transmitControlCommand(CONTROL_CODE(), cmdArray);
String hex = DatatypeConverter.printHexBinary(resp);
System.out.println("Response : " + hex + "\n");
}
System.out.println("\n---------------------------------------------------");
System.out.println("APDU Mode:");
System.out.println("---------------------------------------------------");
while (true) {
System.out.println("APDU command: (Enter 0 to exit)");
String cmd = scanner.nextLine();
if (cmd.equals("0")) {
break;
}
System.out.println("Command : " + cmd);
byte[] cmdArray = hexStringToByteArray(cmd);
ResponseAPDU resp = cardChannel.transmit(new CommandAPDU(cmdArray));
byte[] respB = resp.getBytes();
String hex = DatatypeConverter.printHexBinary(respB);
System.out.println("Response : " + hex + "\n");
}
connection.disconnect(true);
}
public static int CONTROL_CODE() {
String osName = System.getProperty("os.name").toLowerCase();
if (osName.indexOf("windows") > -1) {
/* Value used by both MS' CCID driver and SpringCard's CCID driver */
return (0x31 << 16 | 3500 << 2);
} else {
/* Value used by PCSC-Lite */
return 0x42000000 + 1;
}
}
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i + 1), 16));
}
return data;
}
}
在上面的程序中,我可以使用 connection.transmitControlCommand
和 cardChannel.transmit()
方法向我的 reader 发送命令。关键是,所有使用第一种方法发送到 reader 的命令都被假定为 Pseudo-APDU 命令,我不应该为它们使用 Psedo-APDU header!所有使用第二种方法发送到 reader 的命令都被假定为常规 APDU 命令,因此如果我需要通过第二种方法发送 Pseudo-APDU 命令,我必须添加 Pseudo-APDU header 给它。
让我们看看 contact-less reader:
的输出
run:
Available Readers:
[PC/SC terminal ACS ACR122 0,
PC/SC terminal CREATOR CRT-603 (CZ1) CCR RF 0,
PC/SC terminal CREATOR CRT-603 (CZ1) CCR SAM 0]
Which reader do you want to send your commands to? (0 or 1 or ...): 1
Write your commands in Hex form, without '0x' or Space charaters.
---------------------------------------------------
Pseudo-APDU Mode:
---------------------------------------------------
Pseudo-APDU command: (Enter 0 to send APDU command)
00A4040000
Command : 00A4040000
Response : 6800
//Based on reader's documents, 0x6800 means "Class byte is not correct"
//As I have a regular java card in the RF field of my reader, I conclude that
//this response is Reader's response (and not card response)
Pseudo-APDU command: (Enter 0 to send APDU command)
6892000400
Command : 6892000400
Response : 433630335F435A375F425F31353038323100039000
Pseudo-APDU command: (Enter 0 to send APDU command)
FF694442056892000400
Command : FF694442056892000400
Response : 6800
//Pseudo-APDU commands doesn't work in Pseudo-APDU mode if I add the Pseudo-APDU header to them.
Pseudo-APDU command: (Enter 0 to send APDU command)
00A4040000
Command : 00A4040000
Response : 6800
Pseudo-APDU command: (Enter 0 to send APDU command)
0
---------------------------------------------------
APDU Mode:
---------------------------------------------------
APDU command: (Enter 0 to exit)
00A4040000
Command : 00A4040000
Response : 6F198408A000000018434D00A50D9F6E061291921101009F6501FF9000
APDU command: (Enter 0 to exit)
6892000400
Command : 6892000400
Response : 6E00
//This is the response of my card. I can't receive Firmware version in APDU mode using this command without Pseudo-APDU header.
APDU command: (Enter 0 to exit)
FF694442056892000400
Command : FF694442056892000400
Response : 433630335F435A375F425F31353038323100099000
//I successfully received Firmware version in APDU mode using the fixed Pseudo-APDU header.
APDU command: (Enter 0 to exit)
00A4040000
Command : 00A4040000
Response : 6F198408A000000018434D00A50D9F6E061291921101009F6501FF9000
APDU command: (Enter 0 to exit)
0
BUILD SUCCESSFUL (total time: 1 minute 36 seconds)
还有问题吗?
是的,两个问题!:
1-上面的程序只在第一个 运行 上运行良好。我的意思是,如果我停止 运行ning 并重新 运行 它,第二种方法会抛出异常:
run:
Available Readers:
[PC/SC terminal ACS ACR122 0, PC/SC terminal CREATOR CRT-603 (CZ1) CCR RF 0, PC/SC terminal CREATOR CRT-603 (CZ1) CCR SAM 0]
Which reader do you want to send your commands to? (0 or 1 or ...): 1
Write your commands in Hex form, without '0x' or Space charaters.
---------------------------------------------------
Pseudo-APDU Mode:
---------------------------------------------------
Pseudo-APDU command: (Enter 0 to send APDU command)
00A4040000
Command : 00A4040000
Response : 6800
Pseudo-APDU command: (Enter 0 to send APDU command)
FF694442056892000400
Command : FF694442056892000400
Response : 6800
Pseudo-APDU command: (Enter 0 to send APDU command)
6892000400
Command : 6892000400
Response : 433630335F435A375F425F31353038323100049000
Pseudo-APDU command: (Enter 0 to send APDU command)
00A4040000
Command : 00A4040000
Response : 6800
Pseudo-APDU command: (Enter 0 to send APDU command)
0
---------------------------------------------------
APDU Mode:
---------------------------------------------------
APDU command: (Enter 0 to exit)
00A4040000
Command : 00A4040000
Exception in thread "main" 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 TestPCSC.main(TestPCSC.java:58)
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
Java Result: 1
BUILD SUCCESSFUL (total time: 39 seconds)
正如你在上面看到的,我不能再使用第二种方法了,我需要 power-off 和 reader 并再次打开电源才能使其再次正常工作。
2-联系人界面(我指的是 SAM reader)总是抛出先前的异常!我的意思是第二种方法根本不起作用(第一种 运行,第二种和第三种 ....)
注意我尝试了不同的readers,看来这不仅限于此reader。一些ACS reader也有与re运行
类似或完全相同的问题
有人知道吗?
作为附带问题,Python 是否有像 Java 一样发送 Pseudo-APDU 的相同方法?
最后,从Reader的角度来看,connection.transmitControlCommand
和cardChannel.transmit()
方法有什么区别?
当您停止"Smart Card"服务时,该工具是否仍然是return固件版本?如果是,则该工具可能会使用原始 IOCTL
命令 (DeviceIoControl) 与驱动程序通信。
也看看this question。作者说你必须设置 SCARD_PROTOCOL_UNDEFINED
作为协议参数:
SCardConnect(hSC,
readerState.szReader,
SCARD_SHARE_DIRECT,
SCARD_PROTOCOL_UNDEFINED,
&hCH,
&dwAP
);
我刚刚试过了,它似乎至少对 Windows 10 有效。没有插入卡也可以通信。不过我没有测试其他 Windows 版本。
readers() 是可用读者的数组索引
reader = r[target_reader]
转换
reader = r[int(target_reader)]
输出
Available Readers : ['JAVACOS Virtual Contact Reader 0', 'JAVACOS Virtual Contactless Reader 1', 'OMNIKEY CardMan 3x21 0']
--- Select Reader (0, 1 , ...): 2
Using : OMNIKEY CardMan 3x21 0
ATR : 3B 9E 94 80 1F 47 80 31 A0 73 BE 21 13 66 86 88 02 14 4B 10 19
前言:
我有一张双界面智能卡 reader,它具有一些扩展功能(除了向卡发送 APDU 命令和接收 APDU 响应之外)。
例如在其文档中提到您可以使用以下命令获取 reader 的固件版本:
GET_FIRMWARE_VERSION: FF 69 44 42 05 68 92 00 05 00
在它的工具中,有一个用于此功能的按钮并且可以正常使用:
我什至嗅探了 USB 端口以查看在我的 PC 和我的 reader 之间的连接中为这个功能到底交换了什么:
命令:
回复:
问题:
我想使用其他工具或通过代码获取我的 reader 版本(并可能发送其他扩展命令),但我必须在卡中插入一张卡 reader 才能发送命令,否则我会收到 No Card Present
异常,而我不想向卡发送命令! (reader 工具成功回答 GET_FIRMWARE_VERSION 而 reader 的插槽中没有可用的卡)
到目前为止我做了什么:
1.I 尝试了一些工具,包括 OpenSCTool 、PyAPDUTool 和另一个 reader 的工具。 2.I 编写了以下 python 脚本来发送扩展命令。
#--- Importing required modules.
import sys
import time
sys.path.append("D:\PythonX\Lib\site-packages")
from smartcard.scard import *
import smartcard.util
from smartcard.System import readers
#---This is the list of commands that we want to send device
cmds =[[,0xFF,0x69,0x44,0x42,0x05,0x68,0x92,0x00,0x04,0x00],]
#--- Let's to make a connection to the card reader
r=readers()
print "Available Readers :",r
print
target_reader = input("--- Select Reader (0, 1 , ...): ")
print
while(True):
try:
print "Using :",r[target_reader]
reader = r[target_reader]
connection=reader.createConnection()
connection.connect()
break
except:
print "--- Exception occured! (Wrong reader or No card present)"
ans = raw_input("--- Try again? (0:Exit/1:Again/2:Change Reader)")
if int(ans)==0:
exit()
elif int(ans)==2:
target_reader = input("Select Reader (0, 1 , ...): ")
#--- An struct for APDU responses consist of Data, SW1 and SW2
class stru:
def __init__(self):
self.data = list()
self.sw1 = 0
self.sw2 = 0
resp = stru()
def send(cmds):
for cmd in cmds:
#--- Following 5 line added to have a good format of command in the output.
temp = stru() ;
temp.data[:]=cmd[:]
temp.sw1=12
temp.sw2=32
modifyFormat(temp)
print "req: ", temp.data
resp.data,resp.sw1,resp.sw2 = connection.transmit(cmd)
modifyFormat(resp)
printResponse(resp)
def modifyFormat(resp):
resp.sw1=hex(resp.sw1)
resp.sw2=hex(resp.sw2)
if (len(resp.sw2)<4):
resp.sw2=resp.sw2[0:2]+'0'+resp.sw2[2]
for i in range(0,len(resp.data)):
resp.data[i]=hex(resp.data[i])
if (len(resp.data[i])<4):
resp.data[i]=resp.data[i][0:2]+'0'+resp.data[i][2]
def printResponse(resp):
print "res: ", resp.data,resp.sw1,resp.sw2
send(cmds)
connection.disconnect()
输出:
>>> ================================ RESTART ================================
Available Readers : ['CREATOR CRT-603 (CZ1) CCR RF 0', 'CREATOR CRT-603 (CZ1) CCR SAM 0']
--- Select Reader (0, 1 , ...): 0
Using : CREATOR CRT-603 (CZ1) CCR RF 0
--- Exception occured! (Wrong reader or No card present)
--- Try again? (0:Exit/1:Again/2:Change Reader)
>>> ================================ RESTART ================================
Available Readers : ['CREATOR CRT-603 (CZ1) CCR RF 0', 'CREATOR CRT-603 (CZ1) CCR SAM 0']
--- Select Reader (0, 1 , ...): 1
Using : CREATOR CRT-603 (CZ1) CCR SAM 0
--- Exception occured! (Wrong reader or No card present)
--- Try again? (0:Exit/1:Again/2:Change Reader)
但是两者都有上述问题!
问题:
1- 如何在没有可用卡的情况下向 reader 发送扩展命令?
2- 为什么我在嗅探数据中看不到命令 header? (请注意,由于 Header 是所有扩展命令的 pre-designated 固定值,我认为 reader 工具不会发送带有 GET_FIRMWARE_VERSION 命令的 header而且它只发送数据!但它是如何工作的?)
更新:
通过反复试验,我发现了一些有用的东西。
假设:
- Pseudo-APDUs固定header=
FF 69 44 42
- Pseudo-APDU 数据字段 GET_READER_FIRMWARE_VERSION =
68 92 00 04 00
- Pseudo-APDU 数据字段 CHANGE_SAM_SLOT =
68 92 01 00 03 XX 00 00
(我的reader有两个SAM插槽,所以XX
可以是01
或02
) - SELECT APDU 命令=
00 A4 04 00 00
好的,我写了下面的Java程序:
import java.util.List;
import java.util.Scanner;
import javax.smartcardio.Card;
import javax.smartcardio.CardChannel;
import javax.smartcardio.CardException;
import javax.smartcardio.CardTerminal;
import javax.smartcardio.CommandAPDU;
import javax.smartcardio.ResponseAPDU;
import javax.smartcardio.TerminalFactory;
import javax.xml.bind.DatatypeConverter;
public class TestPCSC {
public static void main(String[] args) throws CardException {
TerminalFactory tf = TerminalFactory.getDefault();
List< CardTerminal> terminals = tf.terminals().list();
System.out.println("Available Readers:");
System.out.println(terminals + "\n");
Scanner scanner = new Scanner(System.in);
System.out.print("Which reader do you want to send your commands to? (0 or 1 or ...): ");
String input = scanner.nextLine();
int readerNum = Integer.parseInt(input);
CardTerminal cardTerminal = (CardTerminal) terminals.get(readerNum);
Card connection = cardTerminal.connect("DIRECT");
CardChannel cardChannel = connection.getBasicChannel();
System.out.println("Write your commands in Hex form, without '0x' or Space charaters.");
System.out.println("\n---------------------------------------------------");
System.out.println("Pseudo-APDU Mode:");
System.out.println("---------------------------------------------------");
while (true) {
System.out.println("Pseudo-APDU command: (Enter 0 to send APDU command)");
String cmd = scanner.nextLine();
if (cmd.equals("0")) {
break;
}
System.out.println("Command : " + cmd);
byte[] cmdArray = hexStringToByteArray(cmd);
byte[] resp = connection.transmitControlCommand(CONTROL_CODE(), cmdArray);
String hex = DatatypeConverter.printHexBinary(resp);
System.out.println("Response : " + hex + "\n");
}
System.out.println("\n---------------------------------------------------");
System.out.println("APDU Mode:");
System.out.println("---------------------------------------------------");
while (true) {
System.out.println("APDU command: (Enter 0 to exit)");
String cmd = scanner.nextLine();
if (cmd.equals("0")) {
break;
}
System.out.println("Command : " + cmd);
byte[] cmdArray = hexStringToByteArray(cmd);
ResponseAPDU resp = cardChannel.transmit(new CommandAPDU(cmdArray));
byte[] respB = resp.getBytes();
String hex = DatatypeConverter.printHexBinary(respB);
System.out.println("Response : " + hex + "\n");
}
connection.disconnect(true);
}
public static int CONTROL_CODE() {
String osName = System.getProperty("os.name").toLowerCase();
if (osName.indexOf("windows") > -1) {
/* Value used by both MS' CCID driver and SpringCard's CCID driver */
return (0x31 << 16 | 3500 << 2);
} else {
/* Value used by PCSC-Lite */
return 0x42000000 + 1;
}
}
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i + 1), 16));
}
return data;
}
}
在上面的程序中,我可以使用 connection.transmitControlCommand
和 cardChannel.transmit()
方法向我的 reader 发送命令。关键是,所有使用第一种方法发送到 reader 的命令都被假定为 Pseudo-APDU 命令,我不应该为它们使用 Psedo-APDU header!所有使用第二种方法发送到 reader 的命令都被假定为常规 APDU 命令,因此如果我需要通过第二种方法发送 Pseudo-APDU 命令,我必须添加 Pseudo-APDU header 给它。
让我们看看 contact-less reader:
的输出run:
Available Readers:
[PC/SC terminal ACS ACR122 0,
PC/SC terminal CREATOR CRT-603 (CZ1) CCR RF 0,
PC/SC terminal CREATOR CRT-603 (CZ1) CCR SAM 0]
Which reader do you want to send your commands to? (0 or 1 or ...): 1
Write your commands in Hex form, without '0x' or Space charaters.
---------------------------------------------------
Pseudo-APDU Mode:
---------------------------------------------------
Pseudo-APDU command: (Enter 0 to send APDU command)
00A4040000
Command : 00A4040000
Response : 6800
//Based on reader's documents, 0x6800 means "Class byte is not correct"
//As I have a regular java card in the RF field of my reader, I conclude that
//this response is Reader's response (and not card response)
Pseudo-APDU command: (Enter 0 to send APDU command)
6892000400
Command : 6892000400
Response : 433630335F435A375F425F31353038323100039000
Pseudo-APDU command: (Enter 0 to send APDU command)
FF694442056892000400
Command : FF694442056892000400
Response : 6800
//Pseudo-APDU commands doesn't work in Pseudo-APDU mode if I add the Pseudo-APDU header to them.
Pseudo-APDU command: (Enter 0 to send APDU command)
00A4040000
Command : 00A4040000
Response : 6800
Pseudo-APDU command: (Enter 0 to send APDU command)
0
---------------------------------------------------
APDU Mode:
---------------------------------------------------
APDU command: (Enter 0 to exit)
00A4040000
Command : 00A4040000
Response : 6F198408A000000018434D00A50D9F6E061291921101009F6501FF9000
APDU command: (Enter 0 to exit)
6892000400
Command : 6892000400
Response : 6E00
//This is the response of my card. I can't receive Firmware version in APDU mode using this command without Pseudo-APDU header.
APDU command: (Enter 0 to exit)
FF694442056892000400
Command : FF694442056892000400
Response : 433630335F435A375F425F31353038323100099000
//I successfully received Firmware version in APDU mode using the fixed Pseudo-APDU header.
APDU command: (Enter 0 to exit)
00A4040000
Command : 00A4040000
Response : 6F198408A000000018434D00A50D9F6E061291921101009F6501FF9000
APDU command: (Enter 0 to exit)
0
BUILD SUCCESSFUL (total time: 1 minute 36 seconds)
还有问题吗?
是的,两个问题!:
1-上面的程序只在第一个 运行 上运行良好。我的意思是,如果我停止 运行ning 并重新 运行 它,第二种方法会抛出异常:
run:
Available Readers:
[PC/SC terminal ACS ACR122 0, PC/SC terminal CREATOR CRT-603 (CZ1) CCR RF 0, PC/SC terminal CREATOR CRT-603 (CZ1) CCR SAM 0]
Which reader do you want to send your commands to? (0 or 1 or ...): 1
Write your commands in Hex form, without '0x' or Space charaters.
---------------------------------------------------
Pseudo-APDU Mode:
---------------------------------------------------
Pseudo-APDU command: (Enter 0 to send APDU command)
00A4040000
Command : 00A4040000
Response : 6800
Pseudo-APDU command: (Enter 0 to send APDU command)
FF694442056892000400
Command : FF694442056892000400
Response : 6800
Pseudo-APDU command: (Enter 0 to send APDU command)
6892000400
Command : 6892000400
Response : 433630335F435A375F425F31353038323100049000
Pseudo-APDU command: (Enter 0 to send APDU command)
00A4040000
Command : 00A4040000
Response : 6800
Pseudo-APDU command: (Enter 0 to send APDU command)
0
---------------------------------------------------
APDU Mode:
---------------------------------------------------
APDU command: (Enter 0 to exit)
00A4040000
Command : 00A4040000
Exception in thread "main" 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 TestPCSC.main(TestPCSC.java:58)
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
Java Result: 1
BUILD SUCCESSFUL (total time: 39 seconds)
正如你在上面看到的,我不能再使用第二种方法了,我需要 power-off 和 reader 并再次打开电源才能使其再次正常工作。
2-联系人界面(我指的是 SAM reader)总是抛出先前的异常!我的意思是第二种方法根本不起作用(第一种 运行,第二种和第三种 ....)
注意我尝试了不同的readers,看来这不仅限于此reader。一些ACS reader也有与re运行
类似或完全相同的问题有人知道吗?
作为附带问题,Python 是否有像 Java 一样发送 Pseudo-APDU 的相同方法?
最后,从Reader的角度来看,connection.transmitControlCommand
和cardChannel.transmit()
方法有什么区别?
当您停止"Smart Card"服务时,该工具是否仍然是return固件版本?如果是,则该工具可能会使用原始 IOCTL
命令 (DeviceIoControl) 与驱动程序通信。
也看看this question。作者说你必须设置 SCARD_PROTOCOL_UNDEFINED
作为协议参数:
SCardConnect(hSC,
readerState.szReader,
SCARD_SHARE_DIRECT,
SCARD_PROTOCOL_UNDEFINED,
&hCH,
&dwAP
);
我刚刚试过了,它似乎至少对 Windows 10 有效。没有插入卡也可以通信。不过我没有测试其他 Windows 版本。
readers() 是可用读者的数组索引
reader = r[target_reader]
转换
reader = r[int(target_reader)]
输出
Available Readers : ['JAVACOS Virtual Contact Reader 0', 'JAVACOS Virtual Contactless Reader 1', 'OMNIKEY CardMan 3x21 0']
--- Select Reader (0, 1 , ...): 2
Using : OMNIKEY CardMan 3x21 0
ATR : 3B 9E 94 80 1F 47 80 31 A0 73 BE 21 13 66 86 88 02 14 4B 10 19