如何使用Java卡在智能卡中写入文件

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何使用Java卡在智能卡中写入文件相关的知识,希望对你有一定的参考价值。

你好,我是使用javacard的初学者!我想在智能卡中写入一个文件(小于1024kb),所以我要做的就是将文件转换为字节数组(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;
    }
}
.....

我用于将数据发送到智能卡的代码(JCDK 2.2.2的apduio软件包)

.....

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

.....
答案

我的猜测是,您收到的数据与写到卡上的数据不同的原因是此行代码:

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

[与您向卡中写入数据的代码不同,这里您在P1参数中传递数据偏移量。请记住,P1只有一个字节,并且在i = 3时会溢出,因此无法在P1中编码638字节长数组的偏移量。

解决问题的最快方法是使用与编写代码相同的方法,即:

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时,您当前的密码就会执行此操作。但是,如果它固定为一个值,为什么要首先发送此参数呢?

我同意Maarten Bodewes的观点,最好遵循ISO / IEC 7816-4并使用此处指定的命令语法。标准中描述的UPDATE BINARY和READ BINARY命令应符合您的需求。

另一答案

似乎您正在混合使用ISO情况1和3命令。 ISO情况1表示不存在CDATA或RDATA,ISO情况3意味着仅CDATA,没有RDATA。通常,最好让命令仅是可能的4种情况之一(情况2只是RDATA,情况4既是CDATA又是RDATA。例如,您可以创建CREATE RECORD命令或类似的命令。

您应在将数据复制到阵列之前立即调用setIncomingAndReceive。如果不这样做,则缓冲区中可能仅存在部分数据。请注意,尽管setIncomingAndReceive是一种方便的方法,但您也许能够找到更好的方法来处理大量数据。

请注意,ISO 7816-4已经定义了基于文件的卡系统;复制现有标准的一部分可能比重新创建自己的标准更为有用。另请注意,您应该更喜欢APDU#getOffsetCdata而不是常量。该方法与扩展长度的APDU兼容,并且您将来可能希望升级到这些。

以上是关于如何使用Java卡在智能卡中写入文件的主要内容,如果未能解决你的问题,请参考以下文章

Android:使用选项卡在不同片段之间切换

如何将这个 Objective-C 代码片段写入 Swift?

使用java如何直接往word文件中写入内容最好有详细的介绍和源代码

java代码 如何向TXT文件写入内容?

在 Java 中将数据写入 NFC 智能卡

如何以智能的方式将 Java.util.Map 写入包裹?