2016-02-14 142 views
16

前言:如何在沒有卡片的情況下向智能卡讀卡器(而不是智能卡)發送命令?

我有了一些擴展能力(比發送APDU命令到卡和接收APDU響應其他)雙界面智能卡讀卡器。

例如它的文件中被提到,你可以使用下面的命令你的讀者的固件版本:

GET_FIRMWARE_VERSION:FF 69 44 42 05 68 92 00 05 00

在它的工具,有此功能的按鈕,它的工作原理精細:

enter image description here

我甚至聞USB端口,看看有什麼在我的電腦,我的這個函數讀取器之間的連接正好交換:

命令: enter image description here

響應: enter image description here

問題:

我想使用其他工具或通過代碼來讓我的讀者版本(也許發送其他擴展命令)但我必須在讀卡器中插入一張卡才能發送指令,否則我會收到No Card Present異常,而我不想發送指令給卡! (讀者工具答案成功地GET_FIRMWARE_VERSION沒有任何卡在讀卡器的插槽,可使用)

我做了什麼至今:

1.I嘗試了一些工具,包括OpenSCToolPyAPDUTool和另一位讀者的工具。 2.我寫了下面的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如何擴展命令發送給讀寫器雖然沒有卡可用?

2-爲什麼我在嗅探數據中看不到命令​​頭? (請注意,由於標題是所有擴展命令的預先指定的固定值,因此我認爲閱讀工具不會使用GET_FIRMWARE_VERSION命令發送標題,並且它只發送數據!但它是如何工作的?)


更新:

使用試驗和錯誤,我發現一些有用的東西。

假設:

  • 僞APDU的固定報頭= FF 69 44 42
  • 爲GET_READER_FIRMWARE_VERSION = 68 92 00 04 00
  • 爲CHANGE_SAM_SLOT僞-APDU的數據字段= 68 92 01 00 03 XX 00 00 (我的讀取器具有兩個僞-APDU的數據字段SAM插槽,所以XX可以是0102
  • 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.transmitControlCommandcardChannel.transmit()方法我的讀者。關鍵是,所有使用第一種方法發送給閱讀器的命令都假定爲僞APDU命令,我不應該爲它們使用Psedo-APDU標頭!所有使用第二種方法發送給閱讀器的命令都假定爲常規APDU命令,所以如果需要通過第二種方法發送僞APDU命令,我必須將僞APDU頭添加到它。

讓我們看到輸出的接觸式讀取器:

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,上述程序只是爲它的第一次運行工作正常。我的意思是,如果我停止運行並重新運行它,第二種方法拋出一個異常:

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) 

正如你看到的上面,我不能用第二種方法了,我需要斷電的讀卡器和電源上它再次使它再次正常工作。

2-接觸界面(我的意思是SAM閱讀器)總是拋出以前的異常!我的意思是第二種方法根本不起作用(不是第一次運行,也不是第二次和第三次......)

請注意,我嘗試了不同的讀者,似乎這不僅限於此讀者。一些ACS閱讀器也有類似問題或者重新運行的問題完全相同

有沒有人有任何想法?

作爲一個側面的問題,Python是否有任何平等的方法來發送像Java一樣的僞APDU?

最後,從Reader的角度來看,connection.transmitControlCommandcardChannel.transmit()方法有什麼區別?

+0

不能肯定地說,但是通常讀卡器在PC上安裝了讀卡器專用庫,它可以與PCSC的SCID驅動程序並行使用。在這種情況下,讀卡器製造商的GUI很可能取決於這些庫,因爲它們可能會提供更多的讀卡器特定功能。我不確定您的傳遞問題是由於智能卡讀卡器的驅動程序還是由於PCSC堆棧本身造成的。不過,閱讀器製造商應該知道。 –

+0

您應該能夠使用依賴查看器檢查哪些庫被不同的工具使用。 –

+1

@MaartenBodewes謝謝親愛的Bodewes先生。我更新了這個問題。關於讀者特定的圖書館,我認爲這不是我的觀點。我讀者擁有的所有工具都是可移植的可執行文件(沒有任何.sys文件或.dll庫)。任何我在Windows命令行中使用'tasklist \ M'命令的方式,它都會返回這個可執行文件進程的'ntdll.dll,wow64.dll,wow64win.dll,wow64cpu.dll'。 – Abraham

回答

1

當您停止「智能卡」服務時,該工具是否仍然返回固件版本?如果是,那麼該工具可能使用原始IOCTL命令(DeviceIoControl)與驅動程序進行通信。


另請參閱this question。作者說,你必須設置SCARD_PROTOCOL_UNDEFINED爲協議參數:

SCardConnect(hSC, 
      readerState.szReader, 
      SCARD_SHARE_DIRECT, 
      SCARD_PROTOCOL_UNDEFINED, 
      &hCH, 
      &dwAP 
      ); 

我只是嘗試它,它似乎爲Windows 10,至少工作。通信是可能的,沒有插入卡。雖然我沒有爲其他Windows版本進行測試。

+0

親愛的阿明,不幸的是,我沒有更多訪問讀者來檢查它。 :) – Abraham

+0

哦,真可惜。 – arminb

+1

@Abraham我更新了我的答案。通過傳遞'SCARD_PROTOCOL_UNDEFINED'作爲協議參數,我能夠成功地與讀卡器通信,而無需插入卡。 – arminb