Reputation: 1587
I'm trying to read data from a tachograph card using javax.smartcardio
. The problem is I'm doing most of the things blindly because I can't find a simple tutorial on APDU commands
.
Currently I managed to print the card type (card: PC/SC card in HID Global OMNIKEY 3x21 Smart Card Reader 0, protocol T=0, state OK
) and read the card identification information (selected file 0520
).
How do I download the tachograph .ddd file?
Here's the code so far:
public static void main(String[] args) {
try {
TerminalFactory factory = TerminalFactory.getDefault();
List<CardTerminal> terminals = factory.terminals().list();
System.out.println("Terminals: " + terminals);
// get the first terminal
CardTerminal terminal = terminals.get(0);
// establish a connection with the card
Card card = terminal.connect("T=0");
System.out.println("card: " + card);
CardChannel channel = card.getBasicChannel();
byte[] c1 = hexStringToByteArray(stripCommandSpace("00 A4 04 0C 06 FF544143484F"));
//byte[] c1 = hexStringToByteArray(stripCommandSpace("FF CA 00 00 00"));
ResponseAPDU r1 = channel.transmit(new CommandAPDU(c1));
System.out.println("response: " + byteArrayToHexString(r1.getBytes()));
byte[] c2 = hexStringToByteArray(stripCommandSpace("00 a4 02 0c 02 05 04"));
ResponseAPDU r2 = channel.transmit(new CommandAPDU(c2));
System.out.println("response: " + byteArrayToHexString(r2.getBytes()));
byte[] c3 = hexStringToByteArray(stripCommandSpace("00 B0 00 01 128"));
ResponseAPDU r3 = channel.transmit(new CommandAPDU(c3));
System.out.println("response: " + byteArrayToHexString(r3.getBytes()));
System.out.println("data: " + byteArrayToHexString(r3.getData()));
byte[] ceva = r3.getData();
String str = new String(ceva);
System.out.println("Date card: " + str);
String fileName = "D:\\fisier\\test.ddd";
/*BufferedWriter writer = new BufferedWriter(new FileWriter(fileName));
writer.write(str);
writer.close();*/
FileOutputStream stream = new FileOutputStream(fileName);
try {
stream.write(ceva);
} finally {
stream.close();
}
// disconnect
card.disconnect(false);
} catch (Exception e) {
System.out.println("Exceptie " + e.getMessage());
}
}
I managed to identify and read the driver activity file - I had to change the identifier to 0504
like in the picture below:
Now my problem is how to convert the byte array I get into a .ddd file.
I tried like in the updated version of my code (see above) but the .ddd reader tells me the file is corrupt (I use a third party app which converts .ddd to .txt - called readesm).
Upvotes: 1
Views: 4662
Reputation: 71
Structure block in card driver:
structure block is (ICC, IC, Application_Identificacion ........):
header: "file 2(0501h, 0502h ....) bytes" + "type(data-0 or signed-1) 1 bytes" + "length 2 bytes" + data(read card):
ICC,IC and Card_donwload not necessary signed, so 0501 to 0522(not 050e), signed block file is necessary for authority but not necessary for read byte file and create binary file.
Read recursive for Fid:
for (Fid fid : Fid.values()) {
System.out.println(fid.getId());
// not read 03f00 and 0500 not files
if(!fid.getId().equals("3f,00") && !fid.getId().equals("05,00")){
b = readCard(r, channel, fid.getId());
// header block file
byte[] htba = OperationHelper.hexToByteAr(fid.getId());
byte[] sizeByte = ByteBuffer.allocate(4).putInt(b.length).array();
headerBlock[0] = htba[0]; // id file byte 1
headerBlock[1] = htba[1]; // id file byte 2
headerBlock[2] = 0; // type file data
headerBlock[3] = sizeByte[2]; // size file byte 1
headerBlock[4] = sizeByte[3]; // size file byte 2
try{
fileTGD.write(headerBlock);
fileTGD.write(b);
// add signature file
if (!fid.getId().equals("00,02") && !fid.getId().equals("00,05")
&& !fid.getId().equals("C1,00") && !fid.getId().equals("C1,08")
&& !fid.getId().equals("05,0E")){
performHashFile(r, channel);
b = signature(r, channel);
sizeByte = ByteBuffer.allocate(4).putInt(b.length).array();
headerBlock[0] = htba[0];
headerBlock[1] = htba[1];
headerBlock[2] = 1;
headerBlock[3] = sizeByte[2];
headerBlock[4] = sizeByte[3];
fileTGD.write(headerBlock);
fileTGD.write(b);
}
}catch (Exception e){
e.printStackTrace();
}
}
}
Read datas:
public static byte[] readCard( ResponseAPDU r, CardChannel channel, String fid){
ByteArrayOutputStream dataResponseBuffer = new ByteArrayOutputStream();
try {
//r = channel.transmit(new CommandAPDU(0x00, 0x84, 0x00, 0x00, OperationHelper.hexToByteAr("6f,07,00,00,00,08,00,00,00,00,00,a4,02,0c,02,05,04")));
/**
* GetParameters
*
* Headers
* CLA - 00
* INS - A4
* P1 - 02
* P2 - 0C
* DATA - 00,02 //6C,00,00,00,00,08,01,00,00,00
* dataOffset - 00
* dataLength - length of trama
*
*/
//r = channel.transmit(new CommandAPDU(0x00, 0xA4, 0x02, 0x0C, OperationHelper.hexToByteAr("00,02"), 0x00, 0x02));
if (!fid.equals("00,02") && !fid.equals("00,05"))
//select MF - Only positioning
r = channel.transmit(new CommandAPDU(0x00, 0xA4, 0x04, 0x0C, OperationHelper.hexToByteAr("ff,54,41,43,48,4f"), 0x00, 0x06));
// select EF
r = channel.transmit(new CommandAPDU(0x00, APDUCommand.SELECT_FILE.getCommand(), 0x02, 0x0C, OperationHelper.hexToByteAr(fid), 0x00, 0x02));
boolean end = true;
int p1 = 0x00;
int p2 = 0x00;
Byte le = Byte.valueOf((byte) 255);
int size = 0x00;
do {
r = channel.transmit(new CommandAPDU(0x00, APDUCommand.READ_BINARY.getCommand(), p1, p2, (le < 0) ? le & 255 : le));
switch (r.getSW1()) {
case 0x90:
dataResponseBuffer.write(r.getData());
size += (le < 0) ? le.intValue() & 255 : le.intValue();
byte[] offsetarray = ByteBuffer.allocate(4).putInt(size).array();
p1 = (offsetarray[2] < 0) ? offsetarray[2] & 255 : offsetarray[2];
p2 = (offsetarray[3] < 0) ? offsetarray[3] & 255 : offsetarray[3];
break;
case 0x67: // dec 103
break;
// normal process XX = number of bytes for response enabled
case 0x61:
/*
nuevaLong = Byte.valueOf(codigoError[1]);
*/
break;
// incorrect parameters (out of EF)
case 0x6a:
if (r.getSW2() == 0x86)
System.out.println("Parameters P1-P2 incorrects");
break;
// incorrect long, sw2 = exact long. If not return field data
case 0x6c: //dec 108
//nuevaLong = Byte.valueOf(codigoError[1]);
if (r.getSW2() == 0x86)
System.out.println("Parameter P1-P2 incorrects");
break;
case 0x69: //dec 108
end = false;
break;
case 0x6b: //dec 107
end = false;
/*
int div = (le < 0)? le.intValue() & 255: le.intValue() ;
size -= div;
le = Byte.valueOf((byte) (div / 2));
size += le;
if (le.byteValue() == (byte) 0) {
le = Byte.valueOf((byte) 1);
end = false;
}
offsetarray = ByteBuffer.allocate(4).putInt(size).array();
entero = Integer.valueOf(byteArraySize4ToInt(offsetarray) );
p1 = (offsetarray[2] < 0)? offsetarray[2] & 255: offsetarray[2];
p2 = (offsetarray[3] < 0)? offsetarray[3] & 255: offsetarray[3];
*/
break;
default:
break;
}
} while (end);
} catch (CardException e1) {
e1.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return dataResponseBuffer.toByteArray();
}
5.for making the errors according to the documentation inside the switch
Upvotes: 1