Comment envoyer des commandes au lecteur de carte à puce (et non à la carte à puce) alors qu'aucune carte n'est présente?

Préface:

j'ai un lecteur de carte à puce à double interface qui possède certaines fonctionnalités étendues (autres que l'envoi de commandes APDU à la carte et la réception des réponses APDU).

Par exemple, dans son document, il est mentionné que vous pouvez obtenir la version du firmware de votre lecteur à l'aide de la commande suivante:

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

dans son outil, il y a un bouton pour cette fonction et il fonctionne amende:

enter image description here

j'ai même reniflé le port USB pour voir ce qui s'échangeait exactement dans la connexion entre mon PC et mon lecteur pour cette fonction:

Commande: enter image description here

Réponse: enter image description here

Problème:

je veux obtenir ma version de lecteur (et peut-être envoyer d'autres commandes étendues) en utilisant d'autres outils ou via code, mais je dois insérer une carte dans le lecteur de carte pour pouvoir envoyer des commandes, sinon je reçois No Card Present exception, alors que je ne veux pas envoyer de commandes à card! (L'outil reader répond avec succès à GET_FIRMWARE_VERSION sans aucune carte disponible dans les fentes du lecteur)

Ce que j'ai fait jusqu'à présent:

1.J'ai essayé quelques outils, y compris OpenSCTool, PyAPDUTool, et un autre lecteur de l'outil. 2.J'ai écrit en suivant python script pour envoyer des commandes étendues.

#--- Importing required modules.
import sys
import time
sys.path.append("D:PythonXLibsite-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()

Sortie:

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

mais les deux ont le problème mentionné!

Questions:

1-Comment envoyer des commandes étendues au lecteur alors qu'il n'y a pas de carte disponible?

2-Pourquoi Je ne vois pas l'en-tête de commande dans les données reniflées? (Notez que, comme Header est une valeur fixe pré-désignée pour toutes les commandes étendues, je pense que l'outil de lecteur n'envoie pas l'en-tête avec La commande GET_FIRMWARE_VERSION n'envoie que les données! mais comment ça fonctionne?)


mise à Jour:

en utilisant trial and error j'ai trouvé quelque chose d'utile.

suppositions:

  • Pseudo-unités apdu en-tête fixe = FF 69 44 42
  • Pseudo-champ de données APDU pour GET_READER_FIRMWARE_VERSION = 68 92 00 04 00
  • Pseudo-APDU data field for CHANGE_SAM_SLOT = 68 92 01 00 03 XX 00 00 (mon lecteur a deux SAM slots, donc XX peut être 01 ou 02)
  • SELECT APDU command = 00 A4 04 00 00

Ok, j'ai écrit le programme Java suivant:

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

}

dans le programme ci-dessus, je peux envoyer des commandes à mon lecteur en utilisant les deux connection.transmitControlCommand et cardChannel.transmit() méthodes. Le point est que, toutes les commandes qui envoient au lecteur en utilisant la première méthode, sont supposées comme commande Pseudo-APDU et je ne devrais pas utiliser L'en-tête Psedo-APDU pour eux! Et toutes les commandes envoyer au lecteur en utilisant la deuxième méthode, sont considérés comme des commandes APDU régulières, donc si je dois envoyer des commandes Pseudo-APDU via la deuxième méthode, je dois ajouter L'en-tête Pseudo-APDU à elle.

Laisser voir la sortie pour le contact lecteur:

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)

Est-il un problème?

Oui, deux problèmes!:

1 - le programme ci-dessus ne fonctionne bien que pour sa première exécution. Je veux dire, si j'arrête de courir et réexécutez, la deuxième méthode jeter une exception:

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)

comme vous le voyez ci-dessus, je ne peux plus utiliser la deuxième méthode et j'ai besoin de désactiver le lecteur et de l'activer à nouveau pour qu'il fonctionne à nouveau correctement.

2-l'interface de contact (je veux dire le lecteur SAM) jeter l'exception précédente toujours! Je veux dire que la deuxième méthode ne fonctionne pas du tout (ni la première, ni la deuxième et la troisième .... )

Notez que j'ai essayé différents lecteurs, il semble que ce n'est pas seulement limitée ti ce lecteur. Quelque Les lecteurs ACS ont aussi un problème similaire ou exactement le même avec la rediffusion

est-ce que quelqu'un a une idée?

et comme question secondaire, Python a-t-il des méthodes équivalentes pour envoyer des Pseudo-APDU comme Java?

et enfin, du point de vue du lecteur, Quelle est la différence entre connection.transmitControlCommand et cardChannel.transmit() méthodes?

16
demandé sur Abraham 2016-02-14 11:04:50

1 réponses

lorsque vous arrêtez le service "Carte À Puce", est-ce que l'outil renvoie toujours une version du micrologiciel? Si oui, alors l'outil peut utiliser des IOCTL commandes ( DeviceIoControl) pour communiquer avec le pilote.


regardez aussi cette question. L'auteur dit que vous avez à définir

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

je viens de l'essayer et il semble fonctionner pour Windows 10 au moins. Communication possible sans carte insérer. Je n'ai pas testé pour les autres versions de Windows.

1
répondu arminb 2017-05-23 12:17:58