美文网首页
pos 8583报文我的解决方案

pos 8583报文我的解决方案

作者: 华先生_a70a | 来源:发表于2017-06-08 13:22 被阅读0次

    最近公司在做pos业务,以前直接是pos接我们后台c,后台c去对接支付pos支付通道,现在打算用android系统pos机器,完成原pos机的业务,下面出2个图
    传统pos和android系统pos


    相信对支付业务了解的同学,都知道8583报文,我这个业务也是都是用是8583报文交互的,我们先来看看8583报文的组成

    1,报文头
    12,交易类型
    3,位图
    4,报文域
    8583报文讲解网上一堆,我这主要是针对,8583组装和解析,已经常见的c传过来的数据转换位java字符串
    8583报文是这样的下面举个例子:

    60000000006016011603240210702406C022C09A111662257575445226840000000000000001000000102012051000100006326225757544522684D201220111172543433138383331303935323838383132303135383131303030303135365CB9C8D88C010844260000000000000001309F2608C26F69BE24700A5E9F2701809F101307010103A0B802010A010000000000BB1B528B9F37047A4DFFAC9F36020094950500880470009A031706089C01009F02060000000001005F2A02015682027C009F1A0201569F03060000000000009F3303E0E9C89F34034203009F3501229F1E0830303030303930359F4104000000100014220000010006204533383936303641

    首先我们知道报文是16进制的一段数字串,可是通道c和我们前台java是scoket通信,scoket通信数据交互是byte数组,所以首先我们先要有byte[]
    转化为16进制字符串的方法
    * @param b 10进制byte数组
    * @return 返回16进制的字符串
    */
    public static String byte2Hex(byte[] b) {
    String hs = "";
    String stmp = "";
    for (int n = 0; n < b.length; n++) {
    stmp = (Integer.toHexString(b[n] & 0XFF));
    if (stmp.length() == 1) {
    hs = hs + "0" + stmp;
    } else {
    hs = hs + stmp;
    }
    if (n < b.length - 1)
    hs = hs;
    }
    return hs.toUpperCase();
    }

    我们pos交易,常见的都是2个步骤

    • 初始化-获取终端主密钥(此密钥受传输密钥保护)
      -签到-获得工作密钥(此密钥受终端主密钥保护)
      密钥是为了获得ping密钥来加密52数据(加密咱们银行卡交易密码)
      mac密钥是来获得mac数据(64域数据)
      先来讲解如何组装报文和解析报文,密钥的加解密穿插在里面讲,继续往下看
      首先我先贴出我组装报文的方法
      /**
      * 组装报文(只支持位图为64的报文组装)
      * @param data 数据
      * @param type 交易类型
      * @param bitmap String位图
      public static byte[] makeBW(LinkedHashMap<String, String> data, String type, String bitmap) {
      LogUtils.d("拼接报文每个域数据" + data.toString());
      List<byte[]> result = new ArrayList<>();
      //tpdu+MSGHEAD +type+bitmap
      //Common.Messagehead=tpdu+MSGHEAD
      String msghead = Common.Messagehead + type + PartsUtils.GroupOfBitMap(bitmap);
      byte[] bytes1 = PartsUtils.GroupOfBCD(msghead);
      result.add(bytes1);
      for (Map.Entry<String, String> entry : data.entrySet()) {
      String key = entry.getKey();
      String value = entry.getValue();
      String yuType = map1().get(key);
      String yuLength = map2().get(key);
      byte[] bytes = null;
      if (yuLength.startsWith("LLVAR")) {
      if (yuType.startsWith("BCD")) {
      bytes = PartsUtils.GroupOfLLVar(value, Common.BCD);
      } else {
      bytes = PartsUtils.GroupOfLLVar(value, Common.ASCII);
      }
      } else if (yuLength.startsWith("LLLVAR")) {
      if (yuType.startsWith("BCD")) {
      bytes = PartsUtils.GroupOfLLLVar(value, Common.BCD);
      } else {
      bytes = PartsUtils.GroupOfLLLVar(value, Common.ASCII);
      }
      } else {
      if (yuType.startsWith("BCD")) {
      bytes = PartsUtils.GroupOfBCD(value);
      } else {
      bytes = PartsUtils.GroupOfASCII(value);
      }
      }
      result.add(bytes);
      }
      byte[] bw_result = PartsUtils.ArrayMerge(result);
      Log.d("BWTools", "报文组装数据:" + byte2Hex(bw_result));
      return bw_result;
      }
      由于Messagehead一般不会变我就写在定义在成常量不;
      相信大家都看到了很多类型什么LLLVAR和BCD之类的数据类型,这些都是报文64域中每一域的数据结构
      我们组装报文肯定希望直接传个map就可以了key对应的是多少域,value直接就是该域的值,顺这个思路我的报文组装方法就是这样的,首先我们需要知道报文一共几种类型,就建立几种方法,其实就是把字符串转化为16进制的lllvar或者bcd类似的数据
      下面是转换方法
      生成位图的方法
      /**
      * @param bitmap 位图域
      * @ 生成位图
      /
      public static String GroupOfBitMap(String bitmap) {
      int length = bitmap.length() / 4;
      StringBuffer sb = new StringBuffer();
      for (int i = 0; i < length; i++) {
      String sub = bitmap.substring(i * 4, i * 4 + 4);
      int d = Integer.parseInt(sub, 2);
      sb.append(Integer.toHexString(d));
      }
      return sb.toString().toUpperCase();
      }
      传入位图生成报文所需要的位图如传入
      //消费位图 64域中有数的域就置1没有数据置0
      public final static String consume_bitmap =
      "0111" + "0000" + "0010" + "0100" //1-16
      + "0000" + "0110" + "1100" + "0000" //17-32
      + "0010" + "0000" + "1100" + "0000" //33-48
      + "1001" + "1010" + "0001" + "0001";//49-64
      把数据转化为BCD的报文
      /
      *
      * @param data 需要转换的数据
      * @ BCD类型的报文
      /
      public static byte[] GroupOfBCD(String data) {
      StringBuffer sb = new StringBuffer(data);
      if (sb.length() % 2 != 0)
      sb.insert(sb.length(), "0");
      //确定长度
      int length = sb.length() / 2;
      //创建报文体
      byte[] msg = new byte[length];
      //组装报文
      for (int i = 0; i < length; i++) {
      String sub = sb.substring(i * 2, i * 2 + 2);
      msg[i] = Integer.valueOf(sub, 16).byteValue();
      }
      return msg;
      }
      把数据转化为BCD的ascii
      /
      *
      * @param data 需要转换的数据
      * @ ASCII类型的报文
      /
      public static byte[] GroupOfASCII(String data) {
      return data.getBytes();
      }
      把数据转化为长度为LLLVar的报文
      /
      *
      * @param data 需要转换的数据
      * @ type类型 bcd或者ascii
      /
      public static byte[] GroupOfLLLVar(String data, String Type) {
      StringBuffer sb = new StringBuffer();
      if (Type.equals(Common.BCD)) {
      if (data.length() > 255) {
      sb.append("0" + data.length() / 2);
      } else {
      int[] length = {data.length() / 256, data.length() % 256};
      for (int i = 0; i < length.length; i++) {
      StringBuffer s = new StringBuffer("0");
      String str = length[i] < 10 ? s.append(length[i]).toString()
      : String.valueOf(length[i]);
      sb.append(str);
      }
      }
      sb.append(data);
      return GroupOfBCD(sb.toString());
      } else {
      return ArrayMerge(GroupOfASCII(data)); //长度算错
      }
      }
      把数据转化为长度为LLVar的报文
      /
      *
      * @param data 需要转换的数据
      * @ type类型 bcd或者ascii
      /
      public static byte[] GroupOfLLVar(String data, String Type) {
      if (Type.equals(Common.BCD)) {
      int length = data.length();
      StringBuffer sb = new StringBuffer();
      String mStr = length < 10 ? new StringBuffer("0").append(length).toString() : String.valueOf(length);
      sb.append(mStr);
      sb.append(data);
      return GroupOfBCD(sb.toString());
      } else {
      return ArrayMerge(GroupOfASCII(data));
      }
      }
      把每一域的数组值合并
      /
      *
      * @param data 需要合并的数据,可变长度的参数列表
      * @ 合并所有转换后的数组,组装报文。
      /
      public static byte[] ArrayMerge(byte[]... data) {
      //计算长度
      int length = 0;
      for (byte[] t : data) {
      length += t.length;
      }
      //计算报文长度
      byte[] mlength = length(length);
      //建立目标数组
      byte[] result = new byte[length + mlength.length];
      //导入报文长度
      System.arraycopy(mlength, 0, result, 0, mlength.length);
      //设置导入位置
      int begin = mlength.length;
      for (byte[] t : data) {
      System.arraycopy(t, 0, result, begin, t.length);
      begin += t.length;
      }
      return result;
      }
      传入参数为list集合的数组组装方法
      public static byte[] ArrayMerge( List<byte[]> data) {
      //计算长度
      int length = 0;
      for (byte[] t : data) {
      length += t.length;
      }
      //计算报文长度
      byte[] mlength = length(length);
      //建立目标数组
      byte[] result = new byte[length + mlength.length];
      //导入报文长度
      System.arraycopy(mlength, 0, result, 0, mlength.length);
      //设置导入位置
      int begin = mlength.length;
      for (byte[] t : data) {
      System.arraycopy(t, 0, result, begin, t.length);
      begin += t.length;
      }
      return result;
      }
      组装mac计算需要的数组集合
      /
      *
      * @param data 需要合并的数据,可变长度的参数列表
      * @ 合并所有转换后的数组,组装报文。
      */
      public static byte[] ArrayMerge64(byte[]... data) {
      //计算长度
      int length = 0;
      for (byte[] t : data) {
      length += t.length;
      }
      //建立目标数组
      byte[] result = new byte[length];

        //设置导入位置
        int begin = 0;
        for (byte[] t : data) {
            System.arraycopy(t, 0, result, begin, t.length);
            begin += t.length;
        }
        return result;
      }
      public static byte[]  ArrayMerge64(List<byte[]> data) {
        //计算长度
        int length = 0;
        for (byte[] t : data) {
            length += t.length;
        }
        //建立目标数组
        byte[] result = new byte[length];
      
        //设置导入位置
        int begin = 0;
        for (byte[] t : data) {
            System.arraycopy(t, 0, result, begin, t.length);
            begin += t.length;
        }
        return result;
      }
      

    计算报文的长度
    /**
    * @param length 传入报文长度
    * @ 计算报文长度转换为byte格式
    */
    private static byte[] length(int length) {
    byte[] result = new byte[2];
    int[] key = {length / 256, length % 256};
    for (int i = 0; i < key.length; i++) {
    StringBuffer sb = new StringBuffer("0");
    String str = key[i] < 10 ? sb.append(key[i]).toString() :
    String.valueOf(key[i]);
    result[i] = Integer.valueOf(str).byteValue();
    }
    return result;
    }

    通过上面的方法我们可以轻松组装一个报文,下面举个例子

                    LinkedHashMap<String, String> data_consumer = new LinkedHashMap<String, String>();
                    data_consumer.put("2", cardBean.getCardNo());
                    data_consumer.put("3", "数据");
                    data_consumer.put("4", 数据);  //金额  55域9f02相等
                    data_consumer.put("11", 数据);
                    data_consumer.put("11", "数据");
                    data_consumer.put("14", "数据");
                    data_consumer.put("22", "数据");  
                    data_consumer.put("23", 数据);
                    data_consumer.put("25", "数据");
                    data_consumer.put("26", "数据");
                    data_consumer.put("35", 数据);
                    data_consumer.put("41", 数据);
                    data_consumer.put("42", 数据);
                    data_consumer.put("49", "数据")
                    data_consumer.put("52", "密码加密数据");
                    data_consumer.put("53", "数据");
                    data_consumer.put("55", 数据)
                    data_consumer.put("60", "数据");
                    String yu64 = BWTools.get64yu(数据)));
                    data_consumer.put("64", 数据);
                    byte[] xf_consumer = BWTools.makeBW(data_consumer, Common.Consumer, Common.consume_bitmap);
                一个list集合传入对应的数据直接是string就行,会最后通道我们的makeBW方法返回一个byte[]数组可以直接用scoket传给通道,  
    }
    

    下面讲解通道返回的byte[]如何转换为我们看的懂得数据即解析8583报文

    public static LinkedHashMap<String, String> analyzeBW(String bwdata) {
         LinkedHashMap<String, String> filedMap = new LinkedHashMap<>();
        String bwString = bwdata;
        String temp1 = bwString.substring(0, 2);
        String temp2 = bwString.substring(2, 4);
        int int1 = Integer.parseInt(temp1, 16);
        int int2 = Integer.parseInt(temp2, 16);
        int data = int1 * 256 + int2;
        String bw_result = bwString.substring(4, data * 2 + 4);
        Log.d("serve返回的报文不含长度", bw_result);
        String bitmap = bw_result.substring(26, 42);
        String yu_data = bw_result.substring(42);
        String bitMap128Str = hexString2binaryString(bitmap);
        for (int i = 1; i < bitMap128Str.length(); i++) {
            if (bitMap128Str.charAt(i) == '1') {
                String filedValue = "";// 字段值
                String filedName = "" + (i + 1);// FIELD005
                // 获取域定义信息
                String defLen = map2().get(filedName);
                boolean isFixLen = true;// 是否是变成
                if (defLen.startsWith("LL")) {
                    isFixLen = false;
                }
                // 截取域信息
                if (!isFixLen) {// 变长域
                    int defLen1 = 0;
                    int start_point = 0;
                    if (defLen.startsWith("LLVAR")) {
                        defLen = yu_data.substring(0, 2);
                        defLen1 = Integer.valueOf(defLen);
                        start_point = 2;
                    }
                    if (defLen.startsWith("LLLVAR")) {
                        defLen = yu_data.substring(0, 4);
                        int temp = Integer.parseInt(defLen);
                        String temp3 = defLen.substring(0, 2);
                        String temp4 = defLen.substring(2, 4);
                        int int3 = Integer.parseInt(temp3);
                        int int4 = Integer.parseInt(temp4);
                        //                        defLen1 = int3 * 256 + int4;
                        defLen1 = temp;
                        start_point = 4;
                        //报文长度是长度x2,bcd不需要乘以2
                    }
                    String type = map1().get(filedName);
                    if (filedName.equals("55")) {
                        //此处55域
                        filedValue = yu_data.substring(start_point, start_point + defLen1 * 2);
                            yu_data = yu_data.substring(start_point + defLen1 * 2);
                    } else if (type.startsWith("BCD")) {
                        filedValue = yu_data.substring(start_point, start_point + defLen1);
                        if (defLen1 % 2 != 0) {
                            yu_data = yu_data.substring(start_point + defLen1 + 1);
                        } else {
                            yu_data = yu_data.substring(start_point + defLen1);
                        }
                    } else {
                        filedValue = hexTolLetter(yu_data.substring(start_point, start_point + defLen1 * 2));
                        yu_data = yu_data.substring(start_point + defLen1 * 2);
                    }
                } else {// 不变长域
                    int defLen2 = Integer.parseInt(map2().get(filedName));
                    String type2 = map1().get(filedName);
                    if (filedName.equals("64")) {//64不关心类型 2位变一位  同理ascii
                        filedValue = hexTolLetter(yu_data.substring(0, defLen2 * 2));
                    } else if (type2.startsWith("BCD")) {
                        filedValue = yu_data.substring(0, defLen2);
                        if (defLen2 % 2 != 0) {
                            yu_data = yu_data.substring(defLen2 + 1);
                        } else {
                            yu_data = yu_data.substring(defLen2);
                        }
                    } else {
                        filedValue = hexTolLetter(yu_data.substring(0, defLen2 * 2));
                        yu_data = yu_data.substring(defLen2 * 2);
                    }
                }
                filedMap.put(filedName, filedValue);
            }
        }
        return filedMap;
    }
    

    方法有点吧通告放回的byte[]数组转化为16进制的8583报文string类型作为参数传入即可,其实就是一个循环判断便利报文根据类型解析的方法,这个方法需要确定报文每个域的类型和长度所有需要2个配置集合

    private static HashMap<String, String> map1() {
        HashMap<String, String> map1 = new HashMap<>();
        map1.put("head", "BCD");
        map1.put("type", "BCD");
        map1.put("map", "BCD");
        map1.put("2", "BCD");
        map1.put("3", "BCD");
        map1.put("4", "BCD");
        map1.put("11", "BCD");
        map1.put("12", "BCD");
        map1.put("13", "BCD");
        map1.put("14", "BCD");
        map1.put("15", "BCD");
        map1.put("22", "BCD");
        map1.put("23", "BCD");
        map1.put("25", "BCD");
        map1.put("26", "BCD");
        map1.put("32", "BCD");
        map1.put("35", "BCD");
        map1.put("36", "BCD");
        map1.put("37", "ASCII");
        map1.put("38", "ASCII");
        map1.put("39", "ASCII");
        map1.put("41", "ASCII");
        map1.put("42", "ASCII");
        map1.put("44", "ASCII");
        map1.put("49", "ASCII");
        map1.put("52", "BCD");
        map1.put("53", "BCD");
        map1.put("55", "BCD");
        map1.put("54", "ASCII");
        map1.put("60", "BCD");
        map1.put("61", "BCD");
        map1.put("62", "ASCII");
        map1.put("63", "ASCII");
        map1.put("64", "ASCII");  //判断是binary
        return map1;
    }
    private static HashMap<String, String> map2() {
        HashMap<String, String> map2 = new HashMap<>();
        map2.put("head", "22");
        map2.put("type", "4");
        map2.put("map", "16");
        map2.put("2", "LLVAR019");
        map2.put("3", "6");
        map2.put("4", "12");
        map2.put("5", "12");
        map2.put("6", "12");
        map2.put("7", "10");
        map2.put("9", "8");
        map2.put("10", "8");
        map2.put("11", "6");
        map2.put("12", "6");
        map2.put("13", "4");
        map2.put("14", "4");
        map2.put("15", "4");
        map2.put("16", "4");
        map2.put("18", "4");
        map2.put("19", "3");
        map2.put("22", "3");
        map2.put("23", "3");
        map2.put("25", "2");
        map2.put("26", "2");
        map2.put("32", "LLVAR011");
        map2.put("33", "LLVAR011");
        map2.put("35", "LLVAR037");
        map2.put("36", "LLLVAR104");
        map2.put("37", "12");
        map2.put("38", "6");
        map2.put("39", "2");
        map2.put("41", "8");
        map2.put("42", "15");
        map2.put("43", "40");
        map2.put("44", "LLVAR25");
        map2.put("45", "LLVAR79");
        map2.put("48", "LLLVAR512");
        map2.put("49", "3");
        map2.put("50", "3");
        map2.put("51", "3");
        map2.put("52", "16"); // 64bit的二进制数
        map2.put("53", "16");
        map2.put("55", "LLLVAR255");
        map2.put("54", "LLLVAR040");
        map2.put("60", "LLLVAR100");
        map2.put("61", "LLLVAR29");
        map2.put("62", "LLLVAR200");
        map2.put("63", "LLLVAR63");
        map2.put("64", "8");
        return map2;
    }
    

    map1确定报文的类型map2确定报文的长度,最终返回一个list的集合 key是域的地址,value就是string明文数据
    下面贴出几个上面用得方法

    /**
     * 位图操作
     * <p>
     * 把16位图的字节数组转化成128位01字符串
     */
    private static String get16BitMapStr(byte[] bitMap16) {
        String bitMap128 = "";
        // 16位图转2进制位图128位字符串
        for (int i = 0; i < bitMap16.length; i++) {
            int bc = bitMap16[i];
            bc = (bc < 0) ? (bc + 256) : bc;
            String bitnaryStr = Integer.toBinaryString(bc);// 二进制字符串
            // 左补零,保证是8位
            String rightBitnaryStr = strCopy("0", Math.abs(8 - bitnaryStr.length())) + bitnaryStr;// 位图二进制字符串
            // 先去除多余的零,然后组装128域二进制字符串
            bitMap128 += rightBitnaryStr;
        }
        return bitMap128;
    }
    
    /**
     * 复制字符
     *
     * @param str
     * @param count
     * @return
     */
    private static String strCopy(String str, int count) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < count; i++) {
            sb.append(str);
        }
        return sb.toString();
    }
    
    // 返回字段号码,例如005
    public static String getNumThree(int i) {
        String len = "";
        String iStr = String.valueOf(i);
        len = strCopy("0", 3 - iStr.length()) + iStr;
        return len;
    }
    
    /**
     * 16进制转化为字母
     *
     * @param hex 要转化的16进制数,用逗号隔开 如:536861646f77
     * @return
     */
    private static String hexTolLetter(String hex) {
        StringBuilder sb = new StringBuilder();
        while (hex.length() > 0) {
            String temp = hex.substring(0, 2);
            int i = Integer.parseInt(temp, 16);
            sb.append((char) i);
            hex = hex.substring(2);
        }
        return sb.toString();
    }
    
    /**
     * ascii
     * 字符串中每个字母转化为16进制
     *
     * @param letter
     * @return
     */
    public static String letterToH(String letter) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < letter.length(); i++) {
            char c = letter.charAt(i);
            String s = Integer.toHexString(c);
            if (s.length() == 1) {
                s = "0" + s;
            }
            sb.append(s);
            System.out.println(s);
        }
        //        sb.deleteCharAt(sb.length() - 2);
        return sb.toString();
    }
    
    /**
     * 16进制转二进制
     *
     * @param hexString
     * @return
     */
    public static String hexString2binaryString(String hexString) {
        if (hexString == null || hexString.length() % 2 != 0)
            return null;
        String bString = "", tmp;
        for (int i = 0; i < hexString.length(); i++) {
            tmp = "0000" + Integer.toBinaryString(Integer.parseInt(hexString.substring(i, i + 1), 16));
            bString += tmp.substring(tmp.length() - 4);
        }
        return bString;
    }
    
    /**
     * 二进制转16进制
     *
     * @param bString
     * @return
     */
    private static String binaryString2hexString(String bString) {
        if (bString == null || bString.equals("") || bString.length() % 8 != 0)
            return null;
        StringBuffer tmp = new StringBuffer();
        int iTmp = 0;
        for (int i = 0; i < bString.length(); i += 4) {
            iTmp = 0;
            for (int j = 0; j < 4; j++) {
                iTmp += Integer.parseInt(bString.substring(i + j, i + j + 1)) << (4 - j - 1);
            }
            tmp.append(Integer.toHexString(iTmp));
        }
        return tmp.toString();
    }
    

    下面讲讲密钥的加解密,没有ping和mac是组装不了52和64的
    下面讲讲,tmk终端主密钥,主密钥由传输密钥保护,所以需要先拿到传输密钥一般pos的初始化都是拿主密钥,不过初始化可能会分几步,我拿我们平台举例,初始化分3步
    初始化——握手(第一步)
    62域返回一次性的临时随机数Ans…6(LLLVAR) 6位的随机数RANDOM-NUM。
    初始化——获取临时密钥(第二步)
    62域
    返回一次性的临时密钥TMPKEY(单倍长密钥)的密文ans…12(LLLVAR);前8个字节是密文,后4个字节是checkvalue。
    该密钥使用随机数Ans…6(LLLVAR) 6位的随机数RANDOM-NUM后补00,对TMPKEY做DES加密产生。
    初始化——获取POS主密钥(第三步)
    62域
    返回POS主密钥MASTERKEY(双倍长密钥)的密文ans…16(LLLVAR);
    该密钥使用主密钥受TMPKEY保护,做DES加密产生。
    那我们就根据要去一步一步获取吧
    首先先通道scoket拿到random_num和tmpkey
    开始解析传输密钥密文下面是代码逻辑

                    byte[] bytes_handData2 = ParseTMPKEY(rando_num);
                    byte[] tempkey = new byte[8];//建立一个长度为8的数组放传输密钥密文
                    System.arraycopy(bytes_handData2, 0, tempkey, 0, 8);
                    byte[] decrypt_tmpkey = DesUtils.decrypt(tempkey, radom);  //临时密钥解密后的数据
                    byte[] encrypt = DesUtils.encrypt(ParseTMPKEY("0000000000000000"),decrypt_tmpkey);//密钥     明文都是16进制的,此次的16个0为16进制
                    String check1 = BWTools.byte2Hex(encrypt).substring(0, 8);//传输密钥明文对16个0做des加密的前8位和checkvalue对比一样说checkvalue校验正确
                    String check2= handData2.substring(16, 24);
                    LogUtils.i("初始化第二步校验checkvalue——1,计算得到",check1);
                    LogUtils.i("初始化第二步校验checkvalue——2==16进制",check2);
                    int i = check1.compareToIgnoreCase(check2);
                    if (i == 0) {
                        Utils.runOnUIThread(new Runnable() {
                            @Override
                            public void run() {
                                ToastUtil.showToast("checkvalue检查正确");
                            }
                        });
                    }
    

    下面解密主密钥和校验checkvalue

                    final byte[] masterkey = ParseTMPKEY(主密钥密文);
                    byte[] masterkey1 = new byte[8];
                    byte[] masterkey2 = new byte[8];
                    System.arraycopy(masterkey, 0, masterkey1, 0, 8);
                    System.arraycopy(masterkey, 8, masterkey2, 0, 8);
                    byte[] decrypt_masterkey1 = DesUtils.decrypt(masterkey1, decrypt_tmpkey);
                    byte[] decrypt_masterkey2 = DesUtils.decrypt(masterkey2, decrypt_tmpkey);
                    byte[] result_master = new byte[16];
                    System.arraycopy(decrypt_masterkey1, 0, result_master, 0, 8);
                    System.arraycopy(decrypt_masterkey2, 0, result_master, 8, result_master.length - 8);
                    String master_key = byte2Hex(result_master);//获得终端主密钥的明文
    

    就是根据文档做加解密
    下面是签到交易获取工作密钥的密文

              String s = BWTools.analyzeBW(byte2Hex(d)).get("62");//获取工作密钥密文
                    String result = BWTools.letterToH(s);//密文转化为ascii转16进制
                    LogUtils.d("签到原始", result);
                    final int resultlength = result.length();
                    LogUtils.d("goRegister", "resultlength:" + resultlength);
                    if (resultlength < 72) {
                        Utils.runOnUIThread(new Runnable() {
                            @Override
                            public void run() {
                                ToastUtil.showToast("签到62域长度问题,长度为" + resultlength);
                            }
                        });
                        return;
                    }
                    String ping = result.substring(0, 32);
                    String ping_checkvalue = result.substring(32, 40);
                    Log.d("ping", ping);
                    String mac = result.substring(40, 72);
                    String mac_checkvalue = result.substring(72, 80);
                    byte[] bytes_ping = DesUtils.ThreeDecrypt(result_master_bytes, ParseTMPKEY(ping));
                    byte[] bytes_mac = DesUtils.ThreeDecrypt(result_master_bytes, ParseTMPKEY(mac));
                    String ping_des = zm(bytes_ping, "ping密钥解密后");
                    String mac_des = zm(bytes_mac, "mac密钥解密后");
                    String ping_checkvalue_js = (BWTools.byte2Hex(DesUtils.ThreeEncryptt(ParseTMPKEY(ping_des),ParseTMPKEY("00000000000000000000000000000000")))).substring(0,8);
                    LogUtils.d("ping_checkvalue1",ping_checkvalue);
                    LogUtils.d("ping_checkvalue2",ping_checkvalue_js);
                    String mac_checkvalue_js = (BWTools.byte2Hex(DesUtils.encrypt(ParseTMPKEY("0000000000000000"),ParseTMPKEY(mac_des)))).substring(0,8);
                    LogUtils.d("mac_checkvalue1",mac_checkvalue);
                    LogUtils.d("mac_checkvalue2",mac_checkvalue_js);
                    int i = ping_checkvalue.compareToIgnoreCase(ping_checkvalue_js);
                    int i1 = mac_checkvalue.compareToIgnoreCase(mac_checkvalue_js);
                    if (i == 0 && i1 == 0) {
                        Utils.runOnUIThread(new Runnable() {
                            @Override
                            public void run() {
                                ToastUtil.showToast("工作密钥checkvalue检测正确");
                            }
                        });
                    }
    

    下面贴出部分方法

    /**
     * ascii
     * 字符串中每个字母转化为16进制
     *
     * @param letter
     * @return
     */
    public static String letterToH(String letter) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < letter.length(); i++) {
            char c = letter.charAt(i);
            String s = Integer.toHexString(c);
            if (s.length() == 1) {
                s = "0" + s;
            }
            sb.append(s);
            System.out.println(s);
        }
        //        sb.deleteCharAt(sb.length() - 2);
        return sb.toString();
    }
    

    到此就获得了ping和mac明文下面可以愉快的加密52和64啦
    先讲52,52域很简单 首先需要银行卡号,我的方法是直接传入35域数据,根据自己需要可以改为直接传银行卡
    银行卡从倒数第2位先前截取13个前面不0000和("06"+密码+"FFFFFFFF")做异或,异或的结果用ping密文做3des加密下面是方法
    /**
    * 52yu获取
    * @param yu35data
    * @param ping_des
    * @param pwd
    * @return
    /
    public static String get52yu(String yu35data, String ping_des, String pwd) {
    String[] ds = yu35data.split("D");
    String s1 = "0000"+ds[0].substring(ds[0].length()-13,ds[0].length()-1);
    String s2 = "06"+pwd+"FFFFFFFF";
    String xor = Tools.xor(s1, s2);
    Log.d("Tools52_s1", s1);
    Log.d("Tools52_s2", s2);
    Log.d("Tools52_xor", xor);
    byte[] threeEncryptt = DesUtils.ThreeEncryptt(ParseTMPKEY(ping_des),ParseTMPKEY(xor+"0000000000000000"));
    Log.d("Tools3des加密后的数值", byte2Hex(threeEncryptt).substring(0, 16));
    return byte2Hex(threeEncryptt).substring(0,16);
    }
    下面讲解64域mac的值计算方法
    mac比较复杂,用伪代码解释下
    将type_63域的报文数据(16进制数据)分成8位1组,前后异或下去等到的结果是16位ascii转换32位(16进制字符)
    加入前16位a1后后16位a2,a1和mac密钥做des加密(或者3des看通道要求)得到的结果 b1,然后b1和a2做异或运算得到c,最后c和mac做des加密或者3des加密,结果就是mac
    下面是方法
    /
    *
    * 获取64域mac
    * @param bw type-63域报文
    * @param mac mac密钥
    * @return
    /
    public static String getMac(String bw, String mac) {
    // type-63域报文先分成16个一组前后异或下去
    String xor_result = Tools.xor_result(bw);
    //将 报文每8位结果异或结果的16位转32位(将ASC字符串转16进制字符)
    String xor_result_2_hex = Tools.xor_result_2_hex(xor_result);
    //System.out.println(xor_result_2_hex);
    String result1 = xor_result_2_hex.substring(0, 16);
    String result2 = xor_result_2_hex.substring(16);
    //System.out.println(result1);
    // System.out.println(result2);
    //byte[] bytes = DesUtils.ThreeEncryptt(ParseUtils.ParseTMPKEY(mac), ParseUtils.ParseTMPKEY(result1 + "0000000000000000")); 3des
    byte[] bytes = DesUtils.encrypt(ParseTMPKEY(result1 + "0000000000000000"), ParseTMPKEY(mac.substring(0,16)));
    String s = byte2Hex(bytes);
    String substring = s.substring(0, 16);
    // System.out.println(substring);
    String xor = Tools.xor(substring, result2);
    // System.out.println(xor);
    //byte[] bytes2 = DesUtils.ThreeEncryptt(ParseUtils.ParseTMPKEY(mac), ParseUtils.ParseTMPKEY(xor + "0000000000000000")); 3des
    byte[] bytes2 = DesUtils.encrypt(ParseTMPKEY(xor + "0000000000000000"), ParseTMPKEY(mac.substring(0,16)));
    String s2 = byte2Hex(bytes2);
    Log.d("mac__64为截取", s2);
    return s2.substring(0, 8);
    }
    下面贴出部分方法
    /
    *将报文每8位结果异或结果的16转32
    * 将ASC字符串转16进制字符
    *
    * @param asc
    * @return
    */
    private static String xor_result_2_hex(String asc) {
    StringBuffer hex = new StringBuffer();
    try {
    byte[] bs = asc.toUpperCase().getBytes("UTF-8");
    for (byte b : bs) {
    hex.append(Integer.toHexString(new Byte(b).intValue()));
    }
    } catch (UnsupportedEncodingException e) {
    e.printStackTrace();
    }
    return hex.toString();
    }

    //报文异或结果
    private static String xor_result(String bw) {
        List<String> bw_sub = sub(bw);
        //System.out.println(bw_sub.toString());
        String temp = bw_sub.get(0);
        for (int i = 1; i < bw_sub.size(); i++) {
            temp = xor(temp, bw_sub.get(i));
            //System.out.println("第"+i+"次"+temp);
        }
        return temp;
    }
    
    //string截取为16位一组的数据 不足补0
    private static List<String> sub(String bw) {
        List<String> listdata = new ArrayList();
        while (bw.length() > 16) {
            String substring = bw.substring(0, 16);
            listdata.add(substring);
            bw = bw.substring(16);
        }
        if (bw.length() == 16
                ) {
            listdata.add(bw);
        }
        if (bw.length() < 16) {
            int i = 16 - bw.length();
            for (int i1 = 0; i1 < i; i1++) {
                bw = bw + "0";
            }
            listdata.add(bw);
        }
        return listdata;
    }
    
    //2个16进制的string异或
    public static String xor(String string, String string2) {
        int[] i = toInterger(string);
        int[] j = toInterger(string2);
        int length = i.length == j.length ? i.length : 0;
        int[] result = new int[length];
        StringBuffer sb = new StringBuffer();
        for (int k = 0; k < result.length; k++) {
            int data = i[k] ^ j[k];
            sb.append(Integer.toHexString(data));
        }
        return sb.toString().toUpperCase();
    }
    
    //16进制string转int
    private static int[] toInterger(String string) {
    
        int length = string.length();
        int[] result = new int[length];
        for (int i = 0; i < length; i++) {
            String str = string.substring(i, i + 1);
            int k = Integer.valueOf(str, 16);
            result[i] = k;
        }
        return result;
    }
    
    public static String getBankCardNum(String cardNum) {
        StringBuilder sb = new StringBuilder();
        sb.append(cardNum.substring(0, 6));
        sb.append("*******");
        sb.append(cardNum.substring(cardNum.length() - 4));
        return sb.toString();
    }
    

    最后贴出我的64域获取方法,可根据需求自定义修改
    /**
    * 获取64mac
    *
    * @param data type-63域
    * @param mac_des
    * @return
    */
    public static String get64yu(LinkedHashMap<String, String> data, String type, String bitmap, String mac_des) {
    List<byte[]> result = new ArrayList<>();
    //tpdu+MSGHEAD +type+bitmap
    //Common.Messagehead=tpdu+MSGHEAD
    String msghead = type + PartsUtils.GroupOfBitMap(bitmap);
    byte[] bytes1 = PartsUtils.GroupOfBCD(msghead);
    Log.d("BWTools", "bytes1:" + Arrays.toString(bytes1));
    result.add(bytes1);
    for (Map.Entry<String, String> entry : data.entrySet()) {
    String key = entry.getKey();
    String value = entry.getValue();
    String yuType = map1().get(key);
    String yuLength = map2().get(key);
    byte[] bytes = null;
    if (yuLength.startsWith("LLVAR")) {//变长
    if (yuType.startsWith("BCD")) {
    bytes = PartsUtils.GroupOfLLVar(value, Common.BCD);
    } else {
    bytes = PartsUtils.GroupOfLLVar(value, Common.ASCII);
    }
    } else if (yuLength.startsWith("LLLVAR")) {
    if (yuType.startsWith("BCD")) {
    bytes = PartsUtils.GroupOfLLLVar(value, Common.BCD);
    } else {
    bytes = PartsUtils.GroupOfLLLVar(value, Common.ASCII);
    }
    } else {//定长
    if (yuType.startsWith("BCD")) {
    bytes = PartsUtils.GroupOfBCD(value);
    } else {
    bytes = PartsUtils.GroupOfASCII(value);
    }
    }
    result.add(bytes);

        }
        byte[] bw_result = PartsUtils.ArrayMerge64(result);
        String s = byte2Hex(bw_result);
        String mac = Tools.getMac(s, mac_des);
        return mac;
    }
    

    任然不明白的,可以留言!大家一起进步

    相关文章

      网友评论

          本文标题:pos 8583报文我的解决方案

          本文链接:https://www.haomeiwen.com/subject/trmzfxtx.html