2016-02-14 41 views
16

Vorwort:Wie kann ich Befehle an den Chipkartenleser (und nicht an die Chipkarte) senden, obwohl keine Karte vorhanden ist?

Ich habe ein Dual-Interface Chipkartenleser, den einige erweiterte Funktionen (außer Senden von APDU-Befehlen zu und Empfangen von Karten APDU Erwiderungen) aufweist.

Zum Beispiel in ihrem Dokument wird erwähnt, dass Sie Firmware-Version Ihrer Leser mit folgendem Befehl erhalten können:

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

In seinem Werkzeug, eine Taste für diese Funktion ist es und es funktioniert fein:

enter image description here

ich schnupperte sogar USB-Anschluss, um zu sehen, was genau in der Verbindung zwischen meinem PC und meinem Leser für diese Funktion ausgetauscht:

Befehl: enter image description here

Antwort: enter image description here

Problem:

ich meine Leser-Version erhalten möchten (und vielleicht auch andere erweiterte Befehle senden) mit anderen Werkzeugen oder über Code, aber ich muss eine Karte in den Kartenleser stecken, um Befehle senden zu können, sonst erhalte ich No Card Present Ausnahme, während ich keine Befehle an die Karte senden möchte! (Die Reader-Tool Antworten erfolgreich GET_FIRMWARE_VERSION ohne Karte in den Leser Slots)

Was ich bisher tat:

1.Ich versucht, einige Tools, einschließlich OpenSCTool, PyAPDUTool und ein anderes Leserwerkzeug. 2. Ich schrieb folgendes Python-Skript, um erweiterte Befehle zu senden.

#--- 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() 

Ausgang:

>>> ================================ 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) 

Aber beide haben das erwähnte Problem!

Fragen:

1- Wie erweiterte Befehle für den Leser zu senden, während es keine Karte verfügbar ist?

2- Warum kann ich den Befehl Header in den Sniffed-Daten nicht sehen? (Beachten Sie, dass, da Header ein vordefinierter fester Wert für alle erweiterten Befehle ist, das Reader-Tool den Header mit dem Befehl GET_FIRMWARE_VERSION nicht sendet und nur die Daten sendet! Aber wie funktioniert es?)


Update:

Mit Versuch und Irrtum fand ich etwas nützliches.

Annahmen:

  • Pseudo-APDUs Fest header = FF 69 44 42
  • Pseudo-APDU Datenfeld für GET_READER_FIRMWARE_VERSION = 68 92 00 04 00
  • Pseudo-APDU Datenfeld für CHANGE_SAM_SLOT = 68 92 01 00 03 XX 00 00 (Meine Leser zwei hat SAM-Steckplätze, so XX kann 01 oder 02 sein)
  • SELECT APDU-Befehl = 00 A4 04 00 00

Ok, ich habe das folgende Java-Programm:

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; 
    } 

} 

In dem obigen Programm, ich Befehle an meine Leser mit beiden connection.transmitControlCommand und cardChannel.transmit() Methoden senden. Der Punkt ist, dass alle Befehle, die mit der ersten Methode an den Leser gesendet werden, als Pseudo-APDU-Befehl angenommen werden und ich den Psedo-APDU-Header für sie nicht verwenden sollte! Und alle Befehle, die mit der zweiten Methode an den Leser gesendet werden, werden als reguläre APDU-Befehle angenommen. Wenn ich also Pseudo-APDU-Befehle über die zweite Methode senden muss, muss ich den Pseudo-APDU-Header hinzufügen.

Lassen siehe Ausgabe für den kontaktlosen Leser:

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) 

Gibt es noch ein Problem?

Ja, zwei Probleme !:

1-das obige Programm arbeitet für seinen ersten Lauf nur in Ordnung. Ich meine, wenn ich nicht mehr ausgeführt, und wiederholen Sie es, die zweite Methode eine Ausnahme aus:

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) 

Wie Sie oben sehen, kann ich nicht zweite Methode verwenden mehr und ich muss Ausschalt-Leser und Leistung auf es wieder, damit es wieder gut funktioniert.

2-Die Kontaktschnittstelle (ich meine der SAM-Leser) werfen vorher immer die Ausnahme! Ich meine, die zweite Methode funktioniert überhaupt nicht (weder erster Lauf, noch zweiter und dritter ....)

Beachten Sie, dass ich verschiedene Leser versuchte, scheint es, dass dies nicht nur auf diesen Leser beschränkt ist. Einige ACS-Leser haben auch ein ähnliches oder genau das gleiche Problem mit der Wiederholung

Hat jemand eine Idee?

Und als eine Nebenfrage, hat Python irgendwelche gleich Methoden zum Senden von Pseudo-APDU wie Java?

Und schließlich, aus der Sicht von Reader, was ist der Unterschied zwischen connection.transmitControlCommand und cardChannel.transmit() Methoden?

+0

Kann nicht sicher sagen, aber Leser haben oft Leser-spezifische Bibliotheken auf dem PC installiert, die parallel mit den SCID-Treibern für PCSC verwendet werden können. In diesem Fall hängt die GUI des Kartenleser-Herstellers wahrscheinlich von diesen Bibliotheken ab, da sie möglicherweise mehr kartenleserspezifische Funktionen aufweisen. Ich bin mir nicht sicher, wie sehr die Pass-Through-Probleme durch den Treiber des Smartcard-Lesers oder den PCSC-Stack selbst verursacht werden. Der Leserhersteller sollte es aber wissen. –

+0

Sie sollten mit einem Abhängigkeits-Viewer prüfen können, welche Bibliotheken von den verschiedenen Tools verwendet werden. –

+1

@MaartenBodewes Danke lieber Herr Bodewes. Ich habe die Frage aktualisiert. Über die Reader-spezifische Bibliothek denke ich, dass es nicht der Punkt in meinem Fall ist. Alle Tools, die mein Leser hat, ist eine portable ausführbare Datei (ohne .sys-Datei oder .dll-Bibliothek). Wie auch immer ich den 'tasklist \ M'-Befehl in der Windows-Befehlszeile verwendet habe und er gibt mir' ntdll.dll, wow64.dll, wow64win.dll, wow64cpu.dll' für diesen ausführbaren Dateiprozess zurück. – Abraham

Antwort

1

Wenn Sie den "Smart Card" Service beenden, gibt das Tool immer noch eine Firmwareversion zurück? Wenn ja, kann das Tool rohe IOCTL Befehle (DeviceIoControl) verwenden, um mit dem Treiber zu kommunizieren.


Werfen Sie auch einen Blick auf this question. Der Autor sagt, Sie SCARD_PROTOCOL_UNDEFINED als Protokollparameter zu setzen haben:

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

ich es gerade versucht, und es scheint, für Windows 10 zumindest zu arbeiten. Die Kommunikation war möglich, ohne dass eine Karte eingesetzt wurde. Ich habe jedoch nicht für andere Windows-Versionen getestet.

+0

Lieber Amin, leider habe ich keinen Zugriff mehr auf den Reader, um es zu überprüfen. :) – Abraham

+0

Oh, was für ein Jammer. – arminb

+1

@Abraham Ich aktualisierte meine Antwort. Ich konnte erfolgreich mit dem Leser kommunizieren, ohne eine Karte einzufügen, indem ich 'SCARD_PROTOCOL_UNDEFINED' als Protokollparameter übergab. – arminb