javax.smartcardio: ¿cómo enviar comandos nativos a la tarjeta Desfire?

Estoy creando una aplicación java que se comunica con una tarjeta Mifare DESFire a través de un lector sin contacto de PC / SC y la API javax.smartcardio. Logro enviar APDU ISO 7816 regulares (CLA, INS, P1-P2, Lc, datos de comando, Le).

He leído en el blog de Ridrix que las tarjetas DESFire (al menos la versión EV1 que estoy usando) son compatibles con APDU y comandos nativos, donde la mayoría de los comandos solo tienen 1 byte de longitud.

Por ejemplo, el comando ” Obtener versión “:

Command: 60 Response: af 04 01 01 00 02 18 05 

Probé ese comando con el progtwig PC / SC Diag de SpringCard ( disponible aquí ) y obtengo una respuesta correcta.

Pero no puedo enviar este comando con javax.smartcardio: esta API parece haber sido creada para APDU reales y, por lo tanto, no permite comandos de 1 byte de longitud.

Aquí esta lo que hice:

 public static void main(String[] args){ TerminalFactory factory = TerminalFactory.getDefault(); CardTerminals terminalList = factory.terminals(); try { CardTerminal ct = terminalList.list().get(0); ct.waitForCardPresent(0); Card card = ct.connect("*"); CardChannel channel = card.getBasicChannel(); byte[] command = { 0x60 }; channel.transmit(new CommandAPDU(command)); } catch (CardException e) { e.printStackTrace(); } } 

Me da el siguiente error:

 Exception in thread "main" java.lang.IllegalArgumentException: apdu must be at least 4 bytes long at javax.smartcardio.CommandAPDU.parse(Unknown Source) at javax.smartcardio.CommandAPDU.(Unknown Source) 

Probé la única forma (AFAIK) de enviar un comando:

  ByteBuffer command = ByteBuffer.allocate(1); command.put((byte) 0x60); ByteBuffer response = ByteBuffer.allocate(512); channel.transmit(command, response); 

y obtener un error similar:

 Exception in thread "main" java.lang.IllegalArgumentException: Command APDU must be at least 4 bytes long at sun.security.smartcardio.ChannelImpl.checkManageChannel(Unknown Source) at sun.security.smartcardio.ChannelImpl.doTransmit(Unknown Source) at sun.security.smartcardio.ChannelImpl.transmit(Unknown Source) 

¿Conoces alguna forma de enviar este tipo de comando usando javax.smartcardio o alguna otra cosa?

Sé que es posible envolver estos comandos, pero preferiría usar los comandos nativos (más simples).

Gracias.

Casi 4 años después, pero en caso de que alguien tropiece con esta pregunta, encontré una respuesta a esto. Hoy en día, muchos lectores admiten el ajuste de marcos APDU Desfire en un comando ISO 7816-4. Descubrí una limitación por la cual los datos no pueden exceder los 55 bytes.

Consulte la página 23 de este documento para obtener información completa: http://neteril.org/files/M075031_desfire.pdf

Esto significa que puede especificar lo siguiente para envolver el marco APDU

 CLA = 0x90 INC = {Your Desfire Command eg 0x60 - Get Version} P1 = 0 P2 = 0 Data = 1st byte = length of data followed by byte data. Terminate data with a 0x00 byte 

La respuesta también se envuelve de la siguiente manera:

 SW1 = 0x91 SW2 = Result Status Data = Response Data 

Así se puede usar el siguiente código

 public static byte CMD_WRAP_START = (byte)0x90; public static byte CMD_WRAP_END = (byte)0x00; private CommandAPDU wrapAPDUFrameUsingISO7816_4(byte[] apdu) throws CardException { if (apdu.length > 55){ throw new CardException("The length of the wrapped DESFire command must not be longer than 55 bytes, checksum included."); } boolean hasData = apdu.length > 1; byte[] result; if (hasData) { result = new byte[apdu.length + 5]; } else { result = new byte[apdu.length + 4]; } result[0] = CMD_WRAP_START; // CLA result[1] = apdu[0]; // DESFIRE CMD CODE result[2] = 0; // P1 result[3] = 0; // P2 if (hasData) { result[4] = (byte) (apdu.length - 1); // Length of wrapped data, ONLY IF DATA EXISTS System.arraycopy(apdu,1,result,5,apdu.length-1); // DESFIRE Command data } result[result.length-1] = CMD_WRAP_END; return new CommandAPDU(result); } private static byte [] unwrapFromISO7816_4(byte[] wrapped) throws CardException { if (wrapped.length<2){ throw new CardException("Expected at least 2 bytes for ISO 7816-4 wrapped response: " + String.valueOf(Hex.encodeHex(wrapped, false))); } if (wrapped[wrapped.length-2]!=(byte)0x91){ throw new CardException("Expected 0x91 in SW1 for ISO 7816-4 wrapped response: " + String.valueOf(Hex.encodeHex(wrapped, false))); } byte[] result = new byte[wrapped.length-1]; System.arraycopy(wrapped,0,result,1,wrapped.length-2); // The DESFIRE response result[0] = wrapped[wrapped.length-1]; // The DESFIRE Status return result; } 

javax.smartcardio es una API escrita para usar los comandos ISO 7816-4. Por lo tanto, no es posible enviar comandos “nativos”. Básicamente, los comandos nativos pueden ser cualquier cosa, por lo que sería difícil admitirlos.

O vuelve a JNI o ​​puede intentar encontrar algo que use transmitControlCommand . Pero me temo que no hay una forma real de usar DESFire sin una biblioteca adicional.

Personalmente creo que es mucho más fácil usar la capa de envoltura.

Aquí tiene la respuesta: el comando APDU debe tener al menos 4 bytes.

  * case 1 : |CLA|INS|P1 |P2 | len = 4 * case 2s: |CLA|INS|P1 |P2 |LE | len = 5 * case 3s: |CLA|INS|P1 |P2 |LC |...BODY...| len = 6..260 * case 4s: |CLA|INS|P1 |P2 |LC |...BODY...|LE | len = 7..261 * * (Extended length is not currently supported) * case 2e: |CLA|INS|P1 |P2|00 |LE1|LE2| len = 7 * case 3e: |CLA|INS|P1 |P2 |00|LC1|LC2|...BODY...| len = 8..65542 * case 4e: |CLA|INS|P1 |P2 |00|LC1|LC2|...BODY...|LE1|LE2| len =10..65544 * * EMV