欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

POS 终端 mac 状态保密(SM4)算法(Java 实现)

最编程 2024-07-10 16:26:43
...

概念理解

mac算法是(Message Authentication Codes 消息认证码算法),是含有密钥散列函数算法。主要通过异或运算,再配合其他加密算法实现mac值的运算,用于校验。

Demo项目下载地址 :https://github.com/Cats-eat-fish/-sm4-MAC-

实现过程


  1. 将需要加密计算的字符串转换为16进制字符串

    例如:
    密钥:"12345678901234567890123456789012"
    待加密数据(字符串形式):
    6000112018111411003020170930101010120171025000755000000005553

    转化为16进制:
    36303030 31313230 31383131 31343131 30303330 32303137 30393330 31303130 31303132 30313731 30323530 30303735 35303030 30303030 30353535 33

  2. 将转换过的16字符串进行补位分组

    部位后的数据(部位规则:字符长度%32!=0补位)
    36303030 31313230 31383131 31343131 30303330 32303137 30393330 31303130 31303132 30313731 30323530 30303735 35303030 30303030 30353535 33000000

    分组后的数据
    第 0组明文:36303030 31313230 31383131 31343131
    第 1组明文:30303330 32303137 30393330 31303130
    第 2组明文:31303132 30313731 30323530 30303735
    第 3组明文:35303030 30303030 30353535 33000000

  3. 将分组过的字符串数组逐步进行异或运算

    异或流程
    第 0组明文: 36303030 31313230 31383131 31343131
    第 1组明文: 30303330 32303137 30393330 31303130
    第 1组异或结果:06000300 03010307 01010201 00040001
    第 2组明文:31303132 30313731 30323530 30303735
    第 2组异或结果:37303232 33303436 31333731 30343734
    第3组明文:35303030 30303030 30353535 33000000
    与第2组密文异或:02000202 03000406 01060204 03343734

  4. 将最后异或结果转化为16进制字符串

    转换为16进制
    30323030 30323032 30333030 30343036 30313036 30323034 30333334 33373334

  5. 取前16位和后16位,前16位进行sm4加密,加密结果与后16位异或后再加密

    前16位
    30323030 30323032 30333030 30343036

    后16位
    30313036 30323034 30333334 33373334

    前16位加密结果
    B038A2B2 F3FFE3C8 AC60B377 C70C2DB1

    与后16位异或结果
    80099284 C3CDD3FC 9C538043 F43B1E85

    再加密结果
    1E80F373 40B0FEE6 2C81F356 AFB20BF1

  6. 取前16个字符作为mac值

    mac值:1E80F37340B0FEE6


具体实现过程

准备工作

  1. 数据转换工具类 TransferUtils.java

    • 字符串转换为16进制字符串
    public static String StringToHexString(String str) {
        String data = bytesToHexString(str.getBytes(), 32);
        return data;
    } 
    
    • 字节数组转换为16进制字符串
    /**
     * 字节数组转化为十六进制字符串 并按照len的倍数进行补"0"
     * @param bytes 字节数据
     * @param len   部位原则  不够len倍数补"0"
     * @return
     */
    public static String bytesToHexString(byte[] bytes, int len) {
        StringBuffer hexStr = new StringBuffer();
        for (byte b : bytes) {
            hexStr.append(String.format("%02x", new Integer(b & 0xff)));
        }
        //长度不满32的整数倍 在后边添加 "0"
        while (hexStr.length() % len != 0) {
            hexStr.append("0");
        }
    
        return hexStr.toString();
    }
    
  • 将字符串str 按照长度len 进行分组
    /**
     * 将字符串str 按照长度len 进行分组
     * @param str 字符串
     * @param len 每组字符长度
     * @return
     */
    public static String[] dataGrouping(String str, int len) {
        int lenth = str.length() % len == 0 ? str.length() / len : str.length() / len + 1;
        String[] data = new String[lenth];
        for (int i = 0; i < lenth; i++) {
            data[i] = str.substring(i * len, i * len + len);
        }
        return data;
    }
    
  • 将字符串数组进行异或运算
    public static String handleXOrStringArr(String[] strs){
        String result = "";
        for (int i = 1;i < strs.length;i++){
            if (i == 1){
                result = xOr(strs[0],strs[1]);
            }else {
                result = xOr(strs[i],result);
            }
        }
        return result;
    }
    
  • 异或运算
    /**
     * 异或运算
     * @param s1
     * @param s2
     * @return
     */
    public static String xOr(String s1, String s2) {
        String data = IntArr2String(xOr(String2IntArr(s1),String2IntArr(s2)));
        return data;
    }
    public static int [] xOr(int[] i1,int[] i2){
        int[] xor = new int[i1.length];
        for (int i = 0;i < i1.length & i < i2.length;i++){
            xor[i] = i1[i]^i2[i];
        }
        return xor;
    }
    
  1. 加密工具类
    • sm4加密
  public static byte[] encodeSMS4(byte[] plaintext, byte[] key) {
      byte[] ciphertext = new byte[plaintext.length];

      int k = 0;
      int plainLen = plaintext.length;
      while (k + 16 <= plainLen) {
          byte[] cellPlain = new byte[16];
          for (int i = 0; i < 16; i++) {
              cellPlain[i] = plaintext[k + i];
          }
          byte[] cellCipher = encode16(cellPlain, key);
          for (int i = 0; i < cellCipher.length; i++) {
              ciphertext[k + i] = cellCipher[i];
          }

          k += 16;
      }

      return ciphertext;
  }

调用过程

  1. 将密钥转换为字节数组,将需要加密计算的字符串转换为16进制字符串并补位

    例如:
    密钥:"12345678901234567890123456789012"
    待加密数据(字符串形式):
    6000112018111411003020170930101010120171025000755000000005553

    //将key转换为字节数组
    byte[] keys = TransformUtils.HexStringToByteArr(key);
    //将加密数据转换为16进制字符串
    String hexData = TransformUtils.StringToHexString(data);
    
  2. 将转换过的16字符串进行分组

    //将数据分组 32位为一组
    String[] dataGroup = TransformUtils.dataGrouping(hexData, 32);
    
  3. 将分组过的字符串数组逐步进行异或运算

    //进行异或运算
    String xorData = TransformUtils.handleXOrStringArr(dataGroup);
    
  4. 将最后异或结果转化为16进制字符串

    //将异或结果转化为16进制字符串
    String hexOxrData = TransformUtils.StringToHexString(xorData);
    
  5. 取前16字节和后16字节,前16字节进行sm4加密,加密结果与后16字节异或后再加密

    //取前16字节和后16字节  两个16进制位表示一个字节
    String start32Bit = hexOxrData.substring(0,32);
    String end32Bit = hexOxrData.substring(hexOxrData.length() - 32,hexOxrData.length());
    //前16位进行首次加密
    byte[] encodeSMS4_1 = SMS4.encodeSMS4(TransformUtils.HexStringToByteArr(start32Bit), keys);
    //加密结果与后16位进行异或
    String xOrEnd = TransformUtils.xOr(TransformUtils.bytesToHexString(encodeSMS4_1, 16), end32Bit);
    //异或结果转成字节数组
    byte[] bytes = TransformUtils.HexStringToByteArr(xOrEnd);
    //进行二次加密
    byte[] resultByte = SMS4.encodeSMS4(bytes, keys);
    //将加密结果转换成16进制字符串 并取前八个字符作为mac值返回
    String result = TransformUtils.bytesToHexString(resultByte, 16);
    
  6. 取前16个字符作为mac值

    //取前8字节作为mac值返回
    result = result.substring(0,16);
    

后记

  此处是pos终端mac国密(sm4)算法,之前还有一个pos终端mac国际算法(双倍长 3des),两者的区别在于 分组的时候分组长度不太一样,国际是16个16进制字符(8字节)分组,国密是32个16进制字符(16字节)分组,再有不同的地方就是加密算法不同,国密是sm4加密算法,国际是双倍长3des加密算法,其他基本都是一样的,这个加密算法也是根据需求可以进行更改的 ,比如改为RSA 或AES也是可以的,这个就需要双方沟通了 统一加密算法就可以了