代码之家  ›  专栏  ›  技术社区  ›  Hello Lili

如何使用APDU读取和下载DDD行车记录仪文件?

  •  1
  • Hello Lili  · 技术社区  · 6 年前
    我试着用<代码> JavaX.StabyCaldio读取来自速度记录器卡的数据。问题是我盲目地做了大部分事情,因为我找不到关于 apdu commands>code>的简单教程。

    目前,我成功地打印了卡类型( card:pc/sc card in hid global omnikey 3x21 smart card reader 0,protocol t=0,state ok )并读取了卡标识信息(selected file 0520 )。

    如何下载转速表.ddd文件?

    这是目前为止的代码:

    public static void main(string[]args){
    
    尝试{
    terminalFactory factory=terminalFactory.getDefault();
    list<cardterminal>terminals=factory.terminals().list();
    system.out.println(“终端:”+终端);
    //获取第一个终端
    cardterminal terminal=终端。get(0);
    //与卡建立连接
    卡=终端连接(“T=0”);
    system.out.println(“卡:”+card);
    card channel channel=card.getbasicchannel();
    
    byte[]c1=hexstringtobytearray(stripcommandspace(“00 a4 04 0c 06 ff544143484f”));
    //byte[]c1=hexstringtobytearray(stripcommandspace(“ff ca 00 00 00 00”));
    responseapdu r1=channel.transmit(new commandapdu(c1));
    system.out.println(“响应:”+bytearraytohexstring(r1.getbytes());
    字节[]c2=hexstringtobytearray(stripcommandspace(“00 a4 02 0c 02 05 04”));
    responseapdu r2=channel.transmit(new commandapdu(c2));
    system.out.println(“响应:”+bytearraytohexstring(r2.getbytes());
    byte[]c3=hexstringtobytearray(stripcommandspace(“00 b0 00 01 128”));
    responseapdu r3=channel.transmit(new commandapdu(c3));
    system.out.println(“响应:”+bytearraytohexstring(r3.getbytes());
    system.out.println(“数据:”+bytearraytohexstring(r3.getdata());
    
    byte[]ceva=r3.getdata();
    string str=新字符串(ceva);
    system.out.println(“日期卡:”+str);
    
    string filename=“d:\\fisier\\test.ddd”;
    
    /*bufferedwriter writer=new bufferedwriter(new filewriter(filename));
    writer.write(str);
    
    writer.close();。*/
    fileoutputstream stream=new fileoutputstream(文件名);
    尝试{
    stream.write(ceva);
    }最后{
    Self();
    }
    
    / /断开
    卡。断开(错误);
    }catch(异常E){
    system.out.println(“exceptie”+e.getmessage());
    }
    }
    < /代码> 
    
    

    我设法识别并读取了驱动程序活动文件-我必须将标识符更改为0504like in the picture below:。

    现在我的问题是如何将我得到的字节数组转换为.ddd文件。

    我尝试了类似于更新版本的代码(见上文),但.ddd reader告诉我文件已损坏(我使用第三方应用程序,该应用程序可将.ddd转换为.txt-calledreadesm.)。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());
            }
        }
    

    我设法识别和读取驱动程序活动文件-我必须将标识符更改为0504如下图所示:

    enter image description here

    现在我的问题是如何将我得到的字节数组转换为.ddd文件.

    我试过更新版本的代码(见上文),但.ddd阅读器告诉我文件已损坏(我使用第三方应用程序将.ddd转换为.txt,调用读数)

    1 回复  |  直到 6 年前
        1
  •  1
  •   Andres Carmona    6 年前

    卡驱动程序中的结构块:

    1. 结构块为(ICC、IC、应用程序标识……):

      页眉:“文件2(0501h,0502h…)字节“+”类型(DATA0或SigNED-1)1字节“+”长度2字节“+数据(读卡):

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

    3. 读取FID的递归:

      (fid fid:fid.values())的{ system.out.println(fid.getid()); //未读取03F00和0500非文件 如果(!)fid.getid()。等于(“3f,00”)&!fid.getid().equals(“05,00”)){ b=读卡(r,channel,fid.getid()); //头块文件 byte[]htba=operationhelper.hextobytear(fid.getid()); byte[]sizebyte=bytebuffer.allocate(4).putint(b.length).array(); headerblock[0]=htba[0];//id文件字节1 headerblock[1]=htba[1];//id文件字节2 标题块〔2〕=0;//类型文件数据 headerblock[3]=size byte[2];//大小文件字节1 headerblock[4]=size byte[3];//大小文件字节2 尝试{ filetgd.write(headerblock); 写文件(b); //添加签名文件 如果(!)fid.getid()。等于(“00,02”)&&!fid.getid().equals(“00,05”)。 & & &!fid.getid()。等于(“c1,00”)&&!fid.getid().equals(“c1,08”)。 & & &!fid.getid().equals(“05,0e”)){ PerformHashFile(R,通道); B=签名(R,通道); SigByth= ByTeffSuff.Dead(4).PutIt(b长度).ARARY(); headerblock[0]=htba[0]; headerblock[1]=htba[1]; HeaderBlock[2]=1; headerblock[3]=大小字节[2]; HeaderBlock[4]=大小字节[3]; filetgd.write(headerblock); 写文件(b); } }catch(异常E){ e.printstacktrace(); } } }
      
          
    4. 读取数据:

      public static byte[]readcard(responseapdu r,cardchannel channel,string fid){
      bytearrayOutputstream dataResponseBuffer=new bytearrayOutputstream();
      尝试{
      //r=channel.transmit(new commandapdu(0x00,0x84,0x00,0x00,operationhelper.hextobytear)(“6f,07,00,00,00,00,00,00,00,a4,02,0c,02,05,04”));
      /**
      *获取参数
      *
      *标题
      *CLA - 00
      *INS—A4
      *P1—02
      *P2-0C
      *数据-00,02//6C,00,00,00,00,08,01,00,00,00
      *数据偏移- 00
      *数据长度-电车长度
      *
      */
      //r=channel.transmit(new commandapdu(0x00,0xA4,0x02,0x0C,operationhelper.hextobytear(“00,02”),0x00,0x02));
      如果(!)fid.equals(“00,02”)&!fid.等于(“00,05”))
      //选择仅限中频定位
      R=channel.transmit(new commandapdu(0x00,0xA4,0x04,0x0C,operationhelper.hextobytear)(“FF,54,41,43,48,4F”),0x00,0x06));
      //选择EF
      R=channel.transmit(new commandapdu(0x00,apducommand.select_file.getcommand(),0x02,0x0C,operationhelper.hextobytear(fid),0x00,0x02));
      布尔值结束=真;
      INT P1= 0x00;
      INT P2= 0x00;
      字节le=字节值((字节)255);
      int大小=0x00;
      做{
      R=channel.transmit(new commandapdu(0x00,apducommand.read_binary.getcommand(),p1,p2,(le<0)?乐和255;LE)
      开关(r.getsw1()){
      案例0x90:
      dataResponseBuffer.write(r.getData());
      大小+=(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=(偏移阵列[3]<0)?offsetarray[3]和255:offsetarray[3];
      断裂;
      案例0x67/,103年12月
      断裂;
      //正常进程xx=启用响应的字节数
      案例0x61:
      /*
      nuevalong=byte.valueof(codigoError[1]);
      */
      断裂;
      //参数不正确(超出ef)
      案例0x6a:
      如果(r.getsw2()==0x86)
      system.out.println(“参数p1-p2不正确”);
      断裂;
      /不正确长,SW2=精确长。如果不返回字段数据
      案例0X6C://108年12月
      //nuevalong=byte.valueof(codigoError[1]);
      如果(r.getsw2()==0x86)
      system.out.println(“参数p1-p2不正确”);
      断裂;
      判例0x69/(108年12月)
      结束=假;
      断裂;
      判例0x6B/(107年12月)
      结束=假;
      /*
      int div=(le<0)?le.intValue()&255:le.intValue();
      大小=div;
      le=byte.valueof((byte)(div/2));
      尺寸+= LE;
      如果(LE .ByTaleVe()= =(字节)0){
      le=byte.valueof((byte)1);
      结束=假;
      }
      offsetarray=bytebuffer.allocate(4).putint(size).array();
      enter=integer.valueof(bytearraysize4point(offsetarray));
      p1=(offsetarray[2]<0)?offsetarray[2]和255:offsetarray[2];
      p2=(偏移阵列[3]<0)?offsetarray[3]和255:offsetarray[3];
      */
      断裂;
      
      违约:
      断裂;
      }
      }(结尾);
      }捕获(cardexception e1){
      e1.printstacktrace();
      }捕获(IOException E){
      e.printstacktrace();
      }
      返回dataResponseBuffer.ToByTearray();
      < /代码> 
      
      

      }/P>

      5.根据开关内的文件进行错误处理。

    1. 结构块是(ICC、IC、应用程序标识……):

      头:“文件2(0501h,0502h…..)字节”+“类型(数据-0或有符号-1)1字节”+“长度2字节”+数据(读卡):

    2. ICC、IC卡和卡不需要签名,所以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.根据开关内的文件进行错误处理。