加解密(aes,des)
前言
- 数据加密方式有很多种,每个人都有自己的选择,一旦要跟别人对接加密的数据,一定要先去了解下加密方式的类型和各种参数的设置要求,以下就是笔者在跟别人对接过程中遇到的坑,一方面是自己对加密了解不深,另一方面使用加密的一方自己本身也可能是个半桶水,从网上抄了一段代码可以成功运行后就以为搞定了,连加密的各种参数设置都不太清楚的情况下,很容易导致互相加解密失败
AES
- 首先,放上一个比较好用的工具:在线AES加密解密、AES在线加密解密、AES encryption and decryption–查错网
- 使用aes加解密前,双方一定要协商好aes位数(是128bit还是256bit等),加密模式(是ECB还是CBC等),偏移量(ECB没有偏移量,偏移量不同加密的结果是不一致的,即同一密钥的明文在不同偏移量下加密的结果不同),密钥(格式是什么,什么字符集,字节长度是多少,是16进制字符串,还是utf8字符串等)
- 上面提到的4个一定要双方确认清楚,否则这块的调试就能耗掉你许多时间
- 很多库会有意无意地屏蔽掉aes的配置,从而宣称其使用起来多方便,但是如果使用者不了解这些参数的配置,那么在跨平台时就一定会有各种各样的问题,因为每个平台的实现库的默认配置一般都不会相同,既然配置不同,怎么可能相互加解密
- 最后,提供一个js的加解密库 brix/crypto-js: JavaScript library of crypto standards.,crypto-js - npm
- 详细的api可以查看这里:https://cryptojs.gitbook.io/docs/
代码示例
js
var CryptoJS = require("../miniprogram_npm/crypto-js/index.js");
//加密设置,根据实际情况修改
var cfg={
mode: CryptoJS.mode.ECB,
// padding: CryptoJS.pad.Pkcs7 //pkcs7是默认的
padding: CryptoJS.pad.NoPadding //本套协议采用的nopadding
}
//var key = CryptoJS.enc.Hex.parse("bc7a3dc9e79563291d7bfa93df67d545");
var key = CryptoJS.enc.Hex.parse("db1c29b3a9093117f773295b3f07bf93ff");//秘钥(16进制字符串)
/**
* 设置aes秘钥,在加解密之前必须设置
*
* @param {string} key 秘钥,16进制字符串
*/
function SetHexKey(hexKey){
key = CryptoJS.enc.Hex.parse(hexKey)
}
/**
* aes加密方法,返回16进制字符串
*
* @param {string} hexWord 16进制字符串
*/
function AesEncrypt(hexWord) {
let srcs = CryptoJS.enc.Hex.parse(hexWord);
let encrypted = CryptoJS.AES.encrypt(srcs, key, cfg);
return encrypted.ciphertext.toString().toUpperCase();
}
/**
* aes解密方法,返回16进制字符串
* @param {string} hexWord 16进制字符串
*/
function AesDecrypt(hexWord) {
let encryptedHexStr = CryptoJS.enc.Hex.parse(hexWord);
let srcs = CryptoJS.enc.Base64.stringify(encryptedHexStr);
let decrypt = CryptoJS.AES.decrypt(srcs, key, cfg);
let decryptedStr = decrypt.toString(CryptoJS.enc.Hex);
return decryptedStr.toString();
}
/**
* 将字符转为base64编码
* @param {string} utf8Str 字符串,utf8编码
*/
function Base64Encode(utf8Str) {
let str = CryptoJS.enc.Utf8.parse(utf8Str);
let base64 = CryptoJS.enc.Base64.stringify(str);
return base64;
}
/**
* 还原base64编码,utf8编码
* @param {string} base64Str base64编码字符串
*/
function Base64Decode(base64Str) {
let words = CryptoJS.enc.Base64.parse(base64Str);
return words.toString(CryptoJS.enc.Utf8);
}
//暴露接口
module.exports = {
SetHexKey,
AesEncrypt,
AesDecrypt,
Base64Encode,
Base64Decode
}
3DES(TripleDES)
- 注意3des的秘钥长度是24字节,有些人发现js和java的加密结果不同,其中可能的原因就是密钥长度不同导致,因为不管是java还是js,如果密钥长度不足24个字节,它们都有各自的策略去补到24个字节,而策略不同,导致实际的密钥是不一样的,因此才会产生不同的结果
- 偏移量一般是8个字节
- 下面的java代码中(其他代码会帮你补齐还是报错我就不得而知了,感觉这里应该直接报错才好,查了老半天才知道是背地里偷偷帮我补齐到24个字节,但使用者完全不知道),如果密钥不足24个字节,比如只有16个字节,那么默认会将前8个字节补到后面凑齐24个字节,比如你在java里写的密钥是00112233445566778899AABBCCDDEEFF,那么实际上的密钥是00112233445566778899AABBCCDDEEFF0011223344556677,因此如果在web平台js里不能用00112233445566778899AABBCCDDEEFF当做密钥,要用00112233445566778899AABBCCDDEEFF0011223344556677去加解密,才能正确解出结果
- 当然会造成这些问题都是因为对3des不够熟悉造成的,如果熟悉就会用24个字节作为密钥,就不会出现这种需要补齐的问题,因此坑都是自己挖的,还得自己好好填
代码示例
java
import org.apache.commons.codec.binary.Base64;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.AlgorithmParameterSpec;
public class TripleDESUtil {
public static void main(String[] args) throws Exception{
encryptTest();//8FB06F38DCEBA75C3D2B3D6257EEAEB0
}
public static String encryptTest(){
try {
byte[] src = hexStringToByte("61207375706572206d616e2104040404");
byte[] key = hexStringToByte("00112233445566778899AABBCCDDEEFF1122334455667788");
byte[] ivs = new byte[] { 0x0, 0x0, 0x0,0x0, 0x0, 0x0, 0x0,0x0};
IvParameterSpec iv = new IvParameterSpec(ivs);
SecretKey deskey = new SecretKeySpec(key, "desede"); // 生成密钥21
Cipher c1 = Cipher.getInstance("DESede/CBC/NoPadding"); // 实例化负责加密/解密的Cipher工具类22
c1.init(Cipher.ENCRYPT_MODE, deskey, iv); // 初始化为加密模式23
return bytesToHexString(c1.doFinal(src));
} catch (java.security.NoSuchAlgorithmException e1) {
e1.printStackTrace();
} catch (javax.crypto.NoSuchPaddingException e2) {
e2.printStackTrace();
} catch (java.lang.Exception e3) {
e3.printStackTrace();
}
return null;
}
public static final String bytesToHexString(byte[] var0) {
if (var0 == null) {
return "";
} else {
StringBuffer var1 = new StringBuffer(var0.length);
for(int var3 = 0; var3 < var0.length; ++var3) {
String var2 = Integer.toHexString(255 & var0[var3]);
if (var2.length() < 2) {
var1.append(0);
}
var1.append(var2.toUpperCase());
}
return var1.toString();
}
}
public static byte[] hexStringToByte(String var0) {
if (var0 != null && !var0.isEmpty()) {
var0 = var0.toUpperCase();
int var1 = var0.length() / 2;
byte[] var2 = new byte[var1];
char[] var3 = var0.toCharArray();
for(int var4 = 0; var4 < var1; ++var4) {
int var5 = var4 * 2;
var2[var4] = (byte)(toByte(var3[var5]) << 4 | toByte(var3[var5 + 1]));
}
return var2;
} else {
return null;
}
}
private static byte toByte(char var0) {
byte var1 = (byte)"0123456789ABCDEF".indexOf(var0);
return var1;
}
}
js
var CryptoJS = require("../crypto/index.js")
var iv = CryptoJS.enc.Hex.parse("db1c29b3a9093117f773295b3f07bf93")
//var iv = CryptoJS.enc.Hex.parse("11223344556677889900")
//var key = CryptoJS.enc.Hex.parse("bc7a3dc9e79563291d7bfa93df67d545")
var key = CryptoJS.enc.Hex.parse("db1c29b3a9093117f773295b3f07bf93")//秘钥(16进制字符串)
//加密设置,根据实际情况修改
var cfg = {
mode: CryptoJS.mode.CBC,
// padding: CryptoJS.pad.Pkcs7 //pkcs7是默认的
padding: CryptoJS.pad.NoPadding, //本套协议采用的nopadding
iv: iv
};
/**
* 设置3des秘钥,在加解密之前必须设置
*
* @param {string} hexKey 秘钥,16进制字符串
*/
function setHexKey(hexKey){
key = CryptoJS.enc.Hex.parse(hexKey)
}
/**
* 设置3des偏移量,在加解密之前必须设置
*
* @param {string} hexKey 秘钥,16进制字符串
*/
function setHexIV(hexIV) {
iv = CryptoJS.enc.Hex.parse(hexIV)
cfg.iv=iv
}
/**
* 加密方法,返回16进制字符串
*
* @param {string} hexWord 16进制字符串,必须是8个字节的倍数
*/
function encrypt(hexWord) {
let srcs = CryptoJS.enc.Hex.parse(hexWord)
let encrypted = CryptoJS.TripleDES.encrypt(srcs, key, cfg)
return encrypted.ciphertext.toString().toUpperCase()
}
/**
* 解密方法,返回16进制字符串
* @param {string} hexWord 16进制字符串
*/
function decrypt(hexWord) {
let encryptedHexStr = CryptoJS.enc.Hex.parse(hexWord)
let srcs = CryptoJS.enc.Base64.stringify(encryptedHexStr)
let decrypt = CryptoJS.TripleDES.decrypt(srcs, key, cfg)
let decryptedStr = decrypt.toString(CryptoJS.enc.Hex)
return decryptedStr.toString()
}
//暴露接口
module.exports = {
setHexKey,
setHexIV,
encrypt,
decrypt
}
crc校验码
-
js-crc - npm
https://www.npmjs.com/package/js-crc -
crc在线计算
http://www.ip33.com/crc.html
工具
- js工具类
/**
* ArrayBuffer转16进制字符串
* @param {ArrayBuffer} buffer
*/
function ab2hex(buffer) {
let hexArr = Array.prototype.map.call(
new Uint8Array(buffer),
function(bit) {
return ('00' + bit.toString(16)).slice(-2)
}
)
return hexArr.join('');
}
/**
* 16进制字符串转ArrayBuffer
* @param {String} hexStr
*/
function hex2ab(hexStr){
var typedArray = new Uint8Array(hexStr.match(/[\da-fA-F]{2}/gi).map(function (h) {
return parseInt(h, 16)
}))
return typedArray.buffer
}
module.exports={
ab2hex,
hex2ab
}
参考
细说CryptoJs使用(微信小程序加密解密)-7855145-51CTO博客
https://blog.51cto.com/7865145/2090041
https://code.google.com/archive/p/crypto-js/downloads
brix/crypto-js: JavaScript library of crypto standards.
https://github.com/brix/crypto-js
JS中ArrayBuffer转字符串,字符串转ArrayBuffer,字符与字节桥转换 - xyzdwf的博客 - CSDN博客
https://blog.csdn.net/xyzdwf/article/details/82220987
标准crc16,通用javascript,java,c语言 - 简书
https://www.jianshu.com/p/0d810302ddff
js-crc - npm
https://www.npmjs.com/package/js-crc
crc在线计算
http://www.ip33.com/crc.html
java 3des加密问题记录 - 无所事事O_o - 博客园
https://www.cnblogs.com/wsss/p/6925090.html