如何用APDU读取和下载ddd行车记录仪文件?
How to read and download ddd tachograph file with APDU?
我正在尝试使用 javax.smartcardio
从行驶记录仪卡中读取数据。问题是我在盲目地做大部分事情,因为我在 APDU commands
.
上找不到简单的教程
目前我打印了卡片类型(card: PC/SC card in HID Global OMNIKEY 3x21 Smart Card Reader 0, protocol T=0, state OK
)并读取了卡片识别信息(选择文件0520
)。
如何下载行驶记录仪 .ddd 文件?
目前的代码如下:
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());
}
}
我设法识别并读取驱动程序 activity 文件 - 我不得不将标识符更改为 0504
,如下图所示:
现在我的问题是如何将我得到的字节数组转换成 .ddd 文件。
我在我的代码的更新版本中尝试过(见上文)但是 .ddd reader 告诉我文件已损坏(我使用第三方应用程序将 .ddd 转换为 .txt - 称为readesm).
卡驱动中的结构块:
结构块为(ICC,IC,Application_Identificacion.......):
表头:"file 2(0501h, 0502h ....) bytes"+"type(data-0 or signed-1) 1 bytes"+"length 2 bytes"+数据(读卡):
ICC,IC 和 Card_donwload 不需要签名,所以 0501 到 0522(不是 050e),签名块文件是授权所必需的,但读取字节文件和创建二进制文件不是必需的.
读取 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();
}
}
}
读取数据:
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根据switch里面的文档报错
我正在尝试使用 javax.smartcardio
从行驶记录仪卡中读取数据。问题是我在盲目地做大部分事情,因为我在 APDU commands
.
目前我打印了卡片类型(card: PC/SC card in HID Global OMNIKEY 3x21 Smart Card Reader 0, protocol T=0, state OK
)并读取了卡片识别信息(选择文件0520
)。
如何下载行驶记录仪 .ddd 文件?
目前的代码如下:
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());
}
}
我设法识别并读取驱动程序 activity 文件 - 我不得不将标识符更改为 0504
,如下图所示:
现在我的问题是如何将我得到的字节数组转换成 .ddd 文件。
我在我的代码的更新版本中尝试过(见上文)但是 .ddd reader 告诉我文件已损坏(我使用第三方应用程序将 .ddd 转换为 .txt - 称为readesm).
卡驱动中的结构块:
结构块为(ICC,IC,Application_Identificacion.......):
表头:"file 2(0501h, 0502h ....) bytes"+"type(data-0 or signed-1) 1 bytes"+"length 2 bytes"+数据(读卡):
ICC,IC 和 Card_donwload 不需要签名,所以 0501 到 0522(不是 050e),签名块文件是授权所必需的,但读取字节文件和创建二进制文件不是必需的.
读取 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(); } } }
读取数据:
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根据switch里面的文档报错