Как записать файл на смарт-карту с помощью Java-карты - PullRequest
2 голосов
/ 23 октября 2019

Здравствуйте, я новичок в использовании javacard! Я хочу записать файл (размером менее 1024 КБ) на свою смарт-карту, чтобы преобразовать файл в массив байтов (byte []), например, у меня есть массив длиной 638! Мой код для записи и чтения данных работает нормально (без ошибок), но когда я читаю данные со смарт-карты и сравниваю их с массивом по умолчанию, они не совпадают (одинаковой длины, но различного содержимого). Я прочитал много тем здесь, но я не смог найти ту, которая может решить мою проблему! Спасибо за помощь

Я использую JCDK 2.2.2

Мой апплет для чтения и записи данных:

.....
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;
    }
}
.....

Код, который я использую для отправки данных на смарт-карту(apduio пакет 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;
     }
}

.....

Ответы [ 2 ]

2 голосов
/ 24 октября 2019

Похоже, вы смешиваете 1 и 3 команды ISO. Случай ISO 1 означает, что CDATA или RDATA отсутствуют, а случай ISO 3 означает только CDATA, а не RDATA. Как правило, лучше всего указывать команду только в одном из 4 возможных случаев (случай 2 - это просто RDATA, а случай 4 - и CDATA, и RDATA. Например, вы можете создать команду CREATE RECORD или что-то подобное.

Вы должны вызвать setIncomingAndReceive прямо перед копированием данных в массив. Если вы этого не сделаете, в буфере может присутствовать только часть данных. Обратите внимание, что setIncomingAndReceive - это удобный метод, и вы можетечтобы найти лучшие методы для обработки больших объемов данных.

Обратите внимание, что ISO 7816-4 уже определяет файловую систему на основе карт, копирование части этого существующего стандарта может быть более полезным, чем создание собственного с нуля. что вы должны предпочесть APDU#getOffsetCdata, а не константу. Этот метод совместим с APDU расширенной длины, и вы можете захотеть повысить его до тех, что в будущем.

0 голосов
/ 30 октября 2019

Я предполагаю, что причина, по которой вы получаете разные данные от той, которую вы записали на карту, заключается в следующей строке кода:

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

В отличие от вашего кода для записи данных на карту, здесьвы передаете смещение данных в параметре P1. Помните, что P1 - это всего один байт, и он будет переполнен при i = 3, поэтому невозможно кодировать смещения массива длиной 638 байт в P1.

Самое быстрое решение вашей проблемы - использоватьтот же подход, что и в вашем коде написания, то есть:

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

и умножение в коде JavaCard:

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

Это решение должно работать, но оно не идеально, как вашКод JavaCard поддерживает произвольную длину чтения / записи (P2 - длина копии), но смещения фиксируются на 0, 100, 200 и т. Д. Это означает, что он работает только до тех пор, пока вы отправляете P2 = 100 на карту, которой является ваш текущий кодделает. Но тогда зачем отправлять этот параметр в первую очередь, если он зафиксирован на одном значении?

Я согласен с Мартеном Бодевесом, что было бы лучше следовать ISO / IEC 7816-4 и использовать указанный там синтаксис команды,Команды UPDATE BINARY и READ BINARY, описанные в стандарте, должны соответствовать вашим потребностям.

...