如何用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).

卡驱动中的结构块:

  1. 结构块为(ICC,IC,Application_Identificacion.......):

    表头:"file 2(0501h, 0502h ....) bytes"+"type(data-0 or signed-1) 1 bytes"+"length 2 bytes"+数据(读卡):

  2. ICC,IC 和 Card_donwload 不需要签名,所以 0501 到 0522(不是 050e),签名块文件是授权所必需的,但读取字节文件和创建二进制文件不是必需的.

  3. 读取 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();
            }
        }
    }
    
  4. 读取数据:

    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里面的文档报错