项目需求:前端需要调用后端登录接口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加解密工具代码介绍