加解密(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