记利用crypto-js插件进行加解密之踩坑记录

项目需求:前端需要调用后端登录接口login,然后这个接口后端进行了加密。所以前端需要利用crypto-js插件实现DES-CBC加解密方法

首先观察项目原先的AES-ECB加解密方法

import CryptoJS from "crypto-js";

const Key = "*******";//密钥

export default {
  /**
   * 加密
   * @param word
   * @param keyStr
   */
  encrypt(word) {
    var key = CryptoJS.enc.Utf8.parse(Key);//密钥进行Utf8编码,此步骤必须
    var message = CryptoJS.enc.Utf8.parse(word);
    var encryptStr = CryptoJS.AES.encrypt(message, key, {
      mode: CryptoJS.mode.ECB,//AES加密模式
      padding: CryptoJS.pad.Pkcs7//定义填充方式
    });
    //注意encryptStr.ciphertext才是加密后的数据
    var encryptHexStr = encryptStr.ciphertext.toString().toUpperCase();
    //输出的数据再进行Hex编码
    return CryptoJS.enc.Hex.parse(encryptHexStr).toString();
  },
  //解密
  decrypt(word) {
    var key = CryptoJS.enc.Utf8.parse(Key);//密钥进行Utf8编码,此步骤必须
    //注意加密时的返回数据进行了CryptoJS.enc.Hex.parse编码,解密时也同样需要
    var contentHexStr = CryptoJS.enc.Hex.parse(word);
    var srcs = CryptoJS.enc.Base64.stringify(contentHexStr);
    var decryptStr = CryptoJS.AES.decrypt(srcs, key, {
      mode: CryptoJS.mode.ECB,//AES加密模式
      padding: CryptoJS.pad.Pkcs7//定义填充方式
    });
    var decryptedStr = decryptStr.toString(CryptoJS.enc.Utf8);
    var value = decryptedStr.toString();
    return value;
  }

实现的DES-CBC加解密方法如下:

import CryptoJS from "crypto-js";
const cmiotKey = "CMIOTAPP";
const keyHex = CryptoJS.enc.Utf8.parse(cmiotKey);//定义编码后的密钥
export default {
  // 加密方法
  encryptDES(word) {
    var encrypt = CryptoJS.DES.encrypt(word, keyHex, {
      iv: CryptoJS.enc.Base64.parse('*******'),
      mode: CryptoJS.mode.CBC,
      padding: CryptoJS.pad.Pkcs7
    });
    return encrypt.toString();
  },
  // 解密方法
  decryptDES(ciphertext) {
    //首先解密字符串不能存在空格,否则会报错(Malformed UTF-8 data)或者解密为空
    // DES解密时,如果加密数据不是8的整数倍就会报错(Malformed UTF-8 data)
    //所以需要解码前先进行一次base64解码
    var srcs = CryptoJS.enc.Base64.parse(ciphertext)
    let decrypted = CryptoJS.DES.decrypt({ ciphertext: srcs }, keyHex, {
      iv: CryptoJS.enc.Base64.parse('*******'),
      mode: CryptoJS.mode.CBC,
      padding: CryptoJS.pad.Pkcs7
    });
    var decryptedStr = decrypted.toString(CryptoJS.enc.Utf8)
    var value = decryptedStr.toString();
    return value;
  },
};

⚠️: DES-CBC模式必须有偏移量iv,iv前后端需要一致。由于后端设置的iv为字节数组(private static byte[] iv ={1,2,3,4,5,6,7,8};),前端并没有字节的概念。上面解决方法是可以先将后端iv进行base64编码成字符串,前端再进行解码获得iv

经过百度还有另外一种方法可以获得iv。方法如下

import CryptoJS from "crypto-js";

const cmiotKey = "********";
const keyHex = CryptoJS.enc.Utf8.parse(cmiotKey);
var iv = [1, 2, 3, 4, 5, 6, 7, 8];
//将byte数组转化成字符串的方法
//byteToString([1, 2, 3, 4, 5, 6, 7, 8])返回结果为'\x01\x02\x03\x04\x05\x06\x07\b'
function byteToString(arr) {
  if (typeof arr === 'string') {
    return arr;
  }
  var str = '', _arr = arr;
  for (var i = 0; i < _arr.length; i++) {
    var one = _arr[i].toString(2),
      v = one.match(/^1+?(?=0)/);
    if (v && one.length == 8) {
      var bytesLength = v[0].length;
      var store = _arr[i].toString(2).slice(7 - bytesLength);
      for (var st = 1; st < bytesLength; st++) {
        store += _arr[st + i].toString(2).slice(2);
      }
      str += String.fromCharCode(parseInt(store, 2));
      i += bytesLength - 1;
    } else {
      str += String.fromCharCode(_arr[i]);
    }
  }
  return str;
}

export default {
  // 加密方法
  encryptDES(word) {
    var ivString = byteToString(iv);
    var ivHex = CryptoJS.enc.Utf8.parse(ivString);
    var encrypt = CryptoJS.DES.encrypt(word, keyHex, {
      iv: ivHex,
      mode: CryptoJS.mode.CBC,
      padding: CryptoJS.pad.Pkcs7
    });
    return encrypt.toString();
  },
  // 解密方法
  decryptDES(ciphertext) {
    var srcs = CryptoJS.enc.Base64.parse(ciphertext)
    var ivString = byteToString(iv);//获得字节数组转化后的字符串
    var ivHex = CryptoJS.enc.Utf8.parse(ivString);//字符串进行Utf8编码
    let decrypted = CryptoJS.DES.decrypt({ ciphertext: srcs }, keyHex, {
      iv: ivHex,
      mode: CryptoJS.mode.CBC,
      padding: CryptoJS.pad.Pkcs7
    });
    var decryptedStr = decrypted.toString(CryptoJS.enc.Utf8)
    var value = decryptedStr.toString();
    return value;
  },
};

⚠️:为什么初始化的iv设置成[1, 2, 3, 4, 5, 6, 7, 8],首先了解下java中的byte数组的不同写法。下面三者是等价的:

  • byte [] aa = {00010110, 01010010,10111000};//二进制
  • byte [] aa = {0x16, 0x52, 0xB8};//十六进制
  • byte [] aa ={22, 82, 184};//十进制

所以得出结论var iv=[1, 2, 3, 4, 5, 6, 7, 8]的写法和java中的byte [] iv ={1, 2, 3,4, 5, 6, 7, 8}是等价的
⚠️:String.fromCharCode()属于静态方法,只能够通过全局String对象进行调用,不能通过String对象的实例进行调用。方法的返回值为String类型,其返回值为Unicode数值所表示的字符串

参考链接:java代码中的byte数组介绍
在线DES-CBC加解密工具代码介绍

你可能感兴趣的:(js,javascript,前端,vue.js)