Hello Lili
Hello Lili

Reputation: 1587

How to read and download ddd tachograph file with APDU?

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:

enter image description here

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

Answers (1)

Andres Carmona
Andres Carmona

Reputation: 71

Structure block in card driver: enter image description here

  1. 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):

  2. 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.

  3. 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();
            }
        }
    }
    
  4. 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

Related Questions