How to write a file in a smart card with java card

tecogill :

Hello i'm a beginner on the use of javacard ! I want to write a file (less than 1024kb) in my smart card so what i do is to transform the file to an array of byte (byte[]), for example i have an array with the length of 638 ! My code to write and read the data works fine (no error) but when i read the data from the smart card and compare it to the default array, they are not same (same length but different content). I read a lot of topic here but i was unable to find one which can solve my problem ! Thank you for help

I use JCDK 2.2.2

My applet that read and write data:

.....
public void process(APDU apdu) {
    byte[] buffer = apdu.getBuffer();
    switch(buffer[ISO7816.OFFSET_INS]) {
        case INS_SET_DATA:
            // byte bufferDataLength = (byte)(apdu.setIncomingAndReceive());

            //We want to start data copy
            if (buffer[ISO7816.OFFSET_P1] == 0x00 && buffer[ISO7816.OFFSET_P2] == 0x00) {
                data = new byte[(short) 638];
            } else {
                // copying the apdu data into byte array Data
                // array copy: (src, offset, target, offset,copy size)
                Util.arrayCopyNonAtomic(buffer, (short) ISO7816.OFFSET_CDATA, data,
                            (short) ((short) buffer[ISO7816.OFFSET_P1] * 100),
                            (short) buffer[ISO7816.OFFSET_P2]
                    );
                }
            break;
        case INS_GET_DATA:
            // array copy: (src, offset, target, offset,copy size)
            short t = (short)(buffer[ISO7816.OFFSET_P2] & 0xFF);
            short o = (short)(buffer[ISO7816.OFFSET_P1] & 0xFF);

            Util.arrayCopyNonAtomic(data, o, buffer, ISO7816.OFFSET_CDATA, t);
            apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, t);
            break;
    }
}
.....

The code i use to send the data to the smartcard (apduio package of JCDK 2.2.2)

.....

switch(choice) {
   case 8:
       byte[] configData = fileToByteArray("D:\\jcard\\config.dat");
       byte[] chunk = new byte[100];

       System.out.println("Config Length: " + configData.length); // Got 638

       int configLength = configData.length;
       double round = Math.floor((double)configLength / 100);
       int lastPart = configLength % 100;

       System.out.println("Round : " + round); // Got 6
       System.out.println("Last Part : " + lastPart); // Got 38

       double partToSend = lastPart == 0 ? round : round + 1;

       //Initialize the array of byte in my applet to have the length of data i want to write in
       apdu.command[Apdu.INS] = MyAppletClient.INS_SET_DATA;
       apdu.command[Apdu.P1] = 0x00;
       apdu.command[Apdu.P2] = 0x00;

       cad.exchangeApdu(apdu);
       handleResponse(apdu);

       boolean allPartSend = true;
       for (int i = 0; i < round; i++) {
           //array copy: (src, offset, target, offset, copy size)
           System.arraycopy(configData, (i * 100), chunk, 0, 100);
           System.out.println("Data Length: " + chunk.length); // Got 100

           apdu.command[Apdu.P1] = (byte) i;
           apdu.command[Apdu.P2] = 100;

           apdu.setDataIn(Data);
           cad.exchangeApdu(apdu);
           if (apdu.getStatus() != 0x9000) {
               System.out.println("["+i+"] An error occurred with status: " + apdu.getStatus());
               allPartSend = false;
               break;
            }
        }

        if(allPartSend) {
            byte[] finalPart = new byte[lastPart];
            System.arraycopy(configData, (int) (round * 100), finalPart, 0, lastPart);

            apdu.command[Apdu.P1] = (byte) round;
            apdu.command[Apdu.P2] = (byte) lastPart;
            apdu.setDataIn(finalPart);
            cad.exchangeApdu(apdu);

            if (apdu.getStatus() != 0x9000) {
                System.out.println("An error occurred with status: " + apdu.getStatus());
                break;
            } else {
                System.out.println("OK");
            }
        } else {
            System.out.println("Fail to send all data");
        }
        break;
    case 9:
        int cfgLength = 638; //Because i know the array has that length
        byte[] res = new byte[cfgLength];
        double rnd = Math.floor((double)cfgLength / 100);
        int last = fpLength % 100, readLength = 0;

        boolean allSend = true;

        for(int i = 0; i < rnd; i++) {
            apdu.command[Apdu.INS] = MyAppletClient.INS_GET_DATA;
            apdu.command[Apdu.P1] = (byte) (i * 100);
            apdu.command[Apdu.P2] = 100;
            cad.exchangeApdu(apdu);

            if (apdu.getStatus() != 0x9000) {
                System.out.println("An error occurred with status: " + apdu.getStatus());
            } else {
                readLength += apdu.dataOut.length;
                // System.out.println("Datalength : " + apdu.dataOut.length); // Got 100
                //array copy: (src, offset, target, offset, copy size)
                System.arraycopy(apdu.dataOut, 0, res, (i*100), apdu.dataOut.length);
            }
         }

         if(allSend) {
             apdu.command[Apdu.INS] = MyAppletClient.INS_GET_DATA;
             apdu.command[Apdu.P1] = (byte) ((int)rnd * 100);
             apdu.command[Apdu.P2] = (byte) last;

             cad.exchangeApdu(apdu);
             if (apdu.getStatus() != 0x9000) {
                 System.out.println("An error occurred with status: " + apdu.getStatus());
                 break;
             } else {
                 readLength += apdu.dataOut.length;
                 //array copy: (src, offset, target, offset, copy size)
                 System.arraycopy(apdu.dataOut, 0, res, (int)(rnd * 100 ), apdu.dataOut.length);          
             }
         } else {
              System.out.println("Fail to get all the part");
         }

         byte[] cfgData = fileToByteArray("D:\\jcard\\config.dat");
         System.out.println("ReadLength : " + readLength); // Got 638
         System.out.println("Array equals : " + Arrays.equals(cfgData, res)); // Got false but expected true

         break;
     }
}

.....
Michal Iwanicki :

My guess is that the reason why you are receiving different data from the one you wrote to the card is this line of code:

switch(choice) {
...
    case 9:
    ...
        for(int i = 0; i < rnd; i++) {
        ...
            apdu.command[Apdu.P1] = (byte) (i * 100);

Unlike in your code for writing data to the card, here you are passing the data offset in P1 parameter. Please remember, that P1 is only one byte and it will overflow for i=3, so it is not possible to encode offsets of 638-bytes long array in P1.

The quickest solution of your problem is to use the same approach as in your writing code, i.e.:

switch(choice) {
...
    case 9:
    ...
        for(int i = 0; i < rnd; i++) {
        ...
            apdu.command[Apdu.P1] = (byte) i;

and do the multiplication in the JavaCard code:

short o = (short) ((short) buffer[ISO7816.OFFSET_P1] * 100);

This solution should work, but it is not perfect as your JavaCard code supports arbitrary length of read/write (P2 - copy length), but offsets are fixed to 0, 100, 200 etc. It means that it works only as long as you send P2=100 to the card, which your present code does. But then why sending this parameter in the first place if it is fixed to one value?

I agree with Maarten Bodewes that it would be best to follow the ISO/IEC 7816-4 and use the command syntax specified there. UPDATE BINARY and READ BINARY commands described in the standard should fit your needs.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324127&siteId=1