javacard - ¿Cómo enviar comandos al lector de tarjetas inteligentes(y no a la tarjeta inteligente) mientras no haya una tarjeta presente?
apdu smartcard-reader (1)
Cuando detiene el servicio de "tarjeta inteligente", ¿la herramienta sigue devolviendo una versión de firmware? Si es así, entonces la herramienta podría usar comandos IOCTL
procesar ( DeviceIoControl ) para comunicarse con el controlador.
También eche un vistazo a esta pregunta . El autor dice que tienes que configurar SCARD_PROTOCOL_UNDEFINED
como parámetro de protocolo:
SCardConnect(hSC,
readerState.szReader,
SCARD_SHARE_DIRECT,
SCARD_PROTOCOL_UNDEFINED,
&hCH,
&dwAP
);
Acabo de probarlo y parece funcionar al menos para Windows 10. La comunicación fue posible sin tarjeta insertada. No probé para otras versiones de Windows sin embargo.
Prefacio:
Tengo un lector de tarjetas inteligentes de doble interfaz que tiene algunas capacidades extendidas (además de enviar comandos APDU a la tarjeta y recibir respuestas APDU).
Por ejemplo, en su documento se menciona que puede obtener la versión de firmware de su lector usando el siguiente comando:
GET_FIRMWARE_VERSION: FF 69 44 42 05 68 92 00 05 00
En su herramienta, hay un botón para esta función y funciona bien:
Incluso olfateé el puerto USB para ver qué fue exactamente lo que se intercambió en la conexión entre mi PC y mi lector para esta función:
Problema:
Quiero obtener mi versión de lector (y quizás enviar otros comandos extendidos) utilizando otras herramientas o mediante un código, pero debo insertar una tarjeta en el lector de tarjetas para poder enviar comandos, de lo contrario, recibiré la excepción No Card Present
, mientras que no t quiere enviar comandos a la tarjeta! (La herramienta del lector responde con éxito a GET_FIRMWARE_VERSION sin ninguna tarjeta disponible en las ranuras del lector)
Lo que hice hasta ahora:
1. Probé algunas herramientas, como OpenSCTool , PyAPDUTool y la herramienta de otro lector. 2. Escribí el siguiente script de Python para enviar comandos extendidos.
#--- 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()
Salida:
>>> ================================ 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)
¡Pero ambos tienen el problema mencionado!
Preguntas:
1- ¿Cómo enviar comandos extendidos al lector mientras no hay una tarjeta disponible?
2- ¿Por qué no puedo ver el encabezado del comando en los datos rastreados? (Tenga en cuenta que, como el encabezado es un valor fijo designado previamente para todos los comandos extendidos, creo que la herramienta del lector no envía el encabezado con el comando GET_FIRMWARE_VERSION y envía solo los datos. ¿Cómo funciona?)
Actualizar:
Usando prueba y error encontré algo útil.
Suposiciones
- Cabezal fijo de pseudo-APDU =
FF 69 44 42
- Campo de datos de pseudo-APDU para GET_READER_FIRMWARE_VERSION =
68 92 00 04 00
- Campo de datos de pseudo-APDU para CHANGE_SAM_SLOT =
68 92 01 00 03 XX 00 00
(Mi lector tiene dos ranuras SAM, por lo queXX
puede ser01
o02
) - Comando SELECT APDU =
00 A4 04 00 00
Ok, escribí el siguiente programa de 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;
}
}
En el programa anterior, puedo enviar comandos a mi lector usando los métodos connection.transmitControlCommand
y cardChannel.transmit()
. El punto es que, todos los comandos que se envían al lector utilizando el primer método, se asumen como comandos Pseudo-APDU y no debo usar el encabezado Psedo-APDU para ellos. Y todos los comandos que se envían al lector utilizando el segundo método, se asumen como comandos APDU regulares, por lo que si debo enviar comandos Pseudo-APDU a través del segundo método, debo agregar el encabezado Pseudo-APDU.
Veamos la salida para el lector sin contacto:
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)
¿Hay algún problema todavía?
Sí, dos problemas !:
1-el programa anterior funciona bien solo para su primera ejecución. Quiero decir, si dejo de correr y lo vuelvo a ejecutar, el segundo método lanza una excepción:
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)
Como se ve arriba, ya no puedo usar el segundo método y necesito apagar el lector y encenderlo nuevamente para que funcione bien otra vez.
2-¡La interfaz de contacto (me refiero al lector SAM) siempre lanza una excepción previa! Quiero decir que el segundo método no funciona en absoluto (ni la primera ejecución, ni la segunda y la tercera ...)
Tenga en cuenta que probé diferentes lectores, parece que esto no está limitado solo a este lector. Algunos lectores de ACS también tienen un problema similar o exactamente el mismo con la ejecución de la ejecución
¿Alguien tiene alguna idea?
Y como una pregunta complementaria, ¿Python tiene métodos iguales para enviar Pseudo-APDU como Java?
Y finalmente, desde el punto de vista de Reader, ¿cuál es la diferencia entre los métodos connection.transmitControlCommand
y cardChannel.transmit()
?