一、前言:
1. 最近又被領導叫去談話,公司最近有個二維碼模塊項目要開發,要求使用微信小程序,說是方面和快捷,不用安裝手機APP。o(╥﹏╥)o真是無語,老子在公司的職位是Windwos 開發,現在他們竟然爲了省錢,叫我去做微信小程序,礙於今年疫情嚴重,沒有辦法,只能重新拾起微信小程序。
2. 因公司做的產品爲門禁讀卡設備,所以一般數據安全性有要求,並且與13.56MhHZ ISO14443A CPU卡通訊需要用到3DES 對稱加密算法。
二、相關概念
1. 3DES算法:3DES(或稱爲Triple DES)是三重數據加密算法(TDEA,Triple Data Encryption Algorithm)塊密碼的通稱。它相當於是對每個數據塊應用三次DES加密算法 。數據加密標準(DES)是美國的一種由來已久的加密標準,它使用對稱密鑰加密法。
設Ek()和Dk()代表DES算法的加密和解密過程,K代表DES算法使用的密鑰,M代表明文,C代表密文,這樣:
3DES加密過程爲:C=Ek3(Dk2(Ek1(M)))
3DES解密過程爲:M=Dk1(EK2(Dk3(C)))
2. 對稱加密和非對稱加密:
對稱加密:對稱加密採用了對稱密碼編碼技術,它的特點是文件加密和解密使用相同的密鑰加密。
非對稱加密:對稱加密算法不同,非對稱加密算法需要兩個密鑰:公開密鑰(publickey)和私有密鑰(privatekey)
所以,3DES 屬於對稱加密算法。
3. 3DES 算法中,加密模式有多種,常用分別ECB CBC ,其中
設Ek()和Dk()分別代表DES算法的加密和解密過程,k代表DES算法使用的密鑰,M代表明文,C代表密文,
則3DES算法的過程可表示爲:
加密:C=Ek3(Dk2(Ek1(M))) 即對明文數據進行,加密 --> 解密 --> 加密的過程,最後得到密文數據。
解密:M=Dk1(Ek2(Dk3(C))) 即對密文數據進行,解密 --> 加密 --> 解密的過程,最後得到明文數據。
這裏可以K1=K3,但不能K1=K2=K3(如果相等的話就成了DES算法了)
3DES(K1=K3),可以是3DES-CBC,也可以是3DES-ECB ,此時,密鑰可以爲16個字節,第三部分的8個字節,使用前8個字節補充。
3DES-CBC整個算法的流程和DES-CBC一樣,但是在原來的加密或者解密處增加了異或運算的步驟,使用的**密鑰是16字節長度的密鑰**,將密鑰分成左8字節和右8字節的兩部分,即k1=左8字節,k2=右8字節,然後進行加密運算和解密運算。
3DES(K1≠K2≠K3),和3DES-CBC的流程完全一樣,只是使用的密鑰是24字節的,但在每個加密解密加密時候用的密鑰不一樣,將密鑰分爲3段8字節的密鑰分別爲密鑰1、密鑰2、密鑰3,在3DES加密時對加密解密加密依次使用密鑰1、密鑰2、密鑰3,在3DES解密時對解密加密解密依次使用密鑰3、密鑰2、密鑰1。
由於DES加解密算法是每8個字節作爲一個加解密數據塊,因此在實現該算法時,需要對數據進行分塊和補位(即最後不足8字節時,要補足8字節)
ECB模式:將待處理的數據分成若干塊,每塊的長度都爲8字節。然後對每塊進行加密或解密,最後將他們連接在一起便是最終的結果。每一塊的數據互不干擾。
CBC模式:也需要將待處理的數據分塊,但是每一塊數據在加密或者解密之前都要與前一塊的結果做一次異或操作,因此該模式需要定義一個特殊的8字節Key,用於和第一塊數據做異或操作。
這個特殊的Key就是通常說的初始化向量。在代碼中書寫時需要配置iv參數,注意IV參數是對應CBC模式的。
這樣一來,每一塊數據都是有聯繫的,這是與ECB模式不同的一點。
三、JS 平臺3DES算法
1. 由於在平時工作中,使用的語言是C/C++ 和 Java 所以,對於這兩個平臺使用的3DES早已用爛,所以沒有什麼關注,但是
這次項目要求是微信小程序,而小程序使用的JS,所以有必要學會怎麼使用,因此寫下這篇文章記錄,在微信小程序上如何使用3DES.
2. 對於我項目使用的3DES,可以確定是ECB模式的,而且對於24個字節長度密鑰,其中,K1 = K3 ,即,第0 - 7 字節密鑰和
17 - 23 字節密鑰相等。對於,cpu卡片使用的3DES 算法也是如此,K1 = K3, 。
3. Java Script 版本的3DES 算法加密庫:
在網上查找,發現有很多開源的JS版本的3DES 算法加密庫,其中,比較好用的谷歌的開源算法庫,Google的Cryptojs庫,gitbub 地址: https://github.com/sytelus/CryptoJS。
打開可以發現存在衆多的常用的加密算法,並且這個都是的源碼,可以選擇一種加密算法的源代碼的文件參與,非常適合對解密文件大小有限制的項目,例如微信小程序這個對源代碼有限制的工程項目。
4. Cryptojs 庫3DES 算法庫的使用
4.1 下載源代碼,解壓,找到rollups中的tripledes.js 文件和components 目錄下 mode-ecb.js文件
4.2 將兩個文件放到微信小程序同一目錄下,
在微信小程序使用其他類庫文件,需要將對象 以module.export的形式,或其他小程序支持的形式輸出,才能正常使用.
因此, 在tripledes.js 文件底部將CryptoJS對象聲明出去供外部其他對象使用,加上下面代碼:
module.exports = {
CryptoJS: CryptoJS
};
對於,加密模式文件,mode-ecb.js文件,由於裏面CryptoJS.mode.ECB 類使用到了,CryptoJS對象,因此需要在文件頭部引入CryptoJS對象,然後將CryptoJS.mode.ECB export .
module.exports = {
CryptoJS_mode_ECB:CryptoJS.mode.ECB
}
接着編寫,tripledestils.js 工具類, 選用 3DES對稱加密加密方式爲ECB,java 和 android 填充爲Pkcs7,對應js填充爲Pkcs5. 對於網上的資料,大部分都是使用與前端用的,所以,大部分的對於算法的使用都是UTF-8 的字符集的,
使用的加密和解密都是下面的方法和字符集,而我常用的是16進制字符串加密的。
//3DES字符串加密
export const getEncodeStrData = (str,key) => {
var keyHex = CryptoJS.enc.Utf8.parse(key);
var encrypted = CryptoJS.TripleDES.encrypt(str, keyHex, {
mode: CryptoJS_mode_ECB,
padding: CryptoJS.pad.Pkcs7 //後端pkcs5填充,前端對應pkcs7
});
return encrypted.toString()
};
//3DES字符串解密
export const getDecodeStrData = (str,key) => {
var keyHex = CryptoJS.enc.Utf8.parse(key);
var decrypted = CryptoJS.TripleDES.decrypt(str, keyHex, {
mode:CryptoJS_mode_ECB,
padding: CryptoJS.pad.Pkcs7 //後端pkcs5填充,前端對應pkcs7
});
return decrypted.toString(CryptoJS.enc.Utf8)
};
於是,加密測試,然後與我常用的3DES 算法加密工具比對,發現,算出來結果,一點都對不上,這個其實對於一般使用做網頁的人,應該可以使用了,但是我一般都是跟硬件打交道的,使用字符串加密並不適合我。
仔細觀察,發現輸出的結果是是MD5字符串,於是,我查一下資料,發現加密返回的結果可以16進制字符串返回。修改,
再次比對加密的結果,想哭的心都有。
//3DES hex 16進制字符串加密
const encodeString = (data, key) =>
{
return encrypted.toString();*/
var keyHex = CryptoJS.enc.Hex.parse(key);
var dataHex = CryptoJS.enc.Hex.parse(key);
var encrypted = CryptoJS.TripleDES.encrypt(dataHex, keyHex, {
mode: CryptoJS_mode_ECB,
padding: CryptoJS.pad.Pkcs7 //後端pkcs5填充,前端對應pkcs7
});
//return encrypted.toString()
return encrypted.ciphertext.toString();
};
//3DES hex 16進制字符串解密
const decodeString = (data, key) => {
var keyHex = CryptoJS.enc.Hex.parse(key);
var dataHex = CryptoJS.enc.Hex.parse(data);
var decrypted = CryptoJS.TripleDES.decrypt(dataHex, keyHex, {
mode:CryptoJS_mode_ECB,
padding: CryptoJS.pad.Pkcs7 //後端pkcs5填充,前端對應pkcs7
});
return decrypted.toString(CryptoJS.enc.Utf8)
};
修改Ut8 爲 Hex ,key 和 data 都進行解析,再次比對加密結果不對,發現解密的結果爲空,
真的無語,無論怎麼嘗試,結果都是如此,無奈查看tripledes.js 算法的源代碼,發現一個問題, 這裏使用的.Base64
調整爲Hex ,並且由於使用的對比工具使用的是3DES 模式爲 K1 = K3 , 所以,修改代碼如下:
//var CryptoJS = require("./tripledes.js").CryptoJs;
//var CryptoJS_mode_ECB = require("./mode-ecb.js").CryptoJS_mode_ECB;
import {CryptoJS} from './../des/tripledes'
import {CryptoJS_mode_ECB} from './../des/mode-ecb.js'
//3DES hex 16進制字符串加密
const encodeString = (data, key) =>
{
//key不足24位自動以0(最小位數是0)補齊,如果多餘24位,則截取前24位,後面多餘則捨棄掉
if(key.length != 32 && key.length != 48)
return null;
if(key.length == 32)
{
key = key + key.substr(0, 16);
}
let keyHex = CryptoJS.enc.Hex.parse(key);
let dataHex = CryptoJS.enc.Hex.parse(data);
let encrypted = CryptoJS.TripleDES.encrypt(dataHex, keyHex, {
mode: CryptoJS_mode_ECB,
padding: CryptoJS.pad.Pkcs7 //java 和 android pkcs5填充,js對應pkcs7
});
return encrypted.toString().toUpperCase();
};
//3DES hex 16進制字符串解密
const decodeString = (data, key) => {
if(key.length != 32 && key.length != 48)
return null;
if(key.length == 32)
{
key = key + key.substr(0, 16);
}
let keyHex = CryptoJS.enc.Hex.parse(key);
let decrypted = CryptoJS.TripleDES.decrypt(data, keyHex, {
mode:CryptoJS_mode_ECB,
padding: CryptoJS.pad.Pkcs7 //java 和 android pkcs5填充,js對應pkcs7
});
return decrypted.toString();
};
module.exports = {
encodeString:encodeString,
decodeString:decodeString
}
對比工具,發現,加解密結果一樣,哈哈,此時非常開心。
此時,發現還是有一個問題,有CryptoJs 算出的結果 總是16個字節,經過多次,比對發現,無論怎麼修改明文數據,加密結果後面的8個字節總是固定的,解密這個8個字節,發現算法是在明文8個字節後面增加8字節的0808080808080808查看一些資料,發現這個做是爲了加快算法的效率。
因此,爲了方便我的使用,並且和Android JAVA C/C++ 一樣方便使用,最終,代碼如下:
//var CryptoJS = require("./tripledes.js").CryptoJs;
//var CryptoJS_mode_ECB = require("./mode-ecb.js").CryptoJS_mode_ECB;
import { CryptoJS } from './../des/tripledes'
import { CryptoJS_mode_ECB } from './../des/mode-ecb.js'
//3DES hex 16進制字符串加密
const encodeHexString = (data, key) => {
let keyHex = CryptoJS.enc.Hex.parse(key);
let dataHex = CryptoJS.enc.Hex.parse(data);
let encrypted = CryptoJS.TripleDES.encrypt(dataHex, keyHex, {
mode: CryptoJS_mode_ECB,
padding: CryptoJS.pad.Pkcs7 //java 和 android pkcs5填充,js對應pkcs7
});
return encrypted.toString().toUpperCase();
};
//3DES hex 16進制字符串解密
const decodeHexString = (data, key) => {
let keyHex = CryptoJS.enc.Hex.parse(key);
let decrypted = CryptoJS.TripleDES.decrypt(data, keyHex, {
mode: CryptoJS_mode_ECB,
padding: CryptoJS.pad.Pkcs7 //java 和 android pkcs5填充,js對應pkcs7
});
return decrypted.toString();
};
const encodeHex = (data, key) =>
{
//key 不是16個字節,或者 24 字節返回空,
if (key.length != 32 && key.length != 48)
return null;
if (key.length == 32) {
//16字節密鑰,則K1 = K3, 使用前8字節填充
key = key + key.substr(0, 16);
}
let encData = encodeHexString(data, key);
return encData.substr(0, 16);
};
const decodeHex = (data, key) =>
{
//key 不是16個字節,或者 24 字節返回空,
if (key.length != 32 && key.length != 48)
return null;
if (key.length == 32) {
//16字節密鑰,則K1 = K3, 使用前8字節填充
key = key + key.substr(0, 16);
}
//由於加密結果的後面8字節固定使用0808080808080808的密文 ,9F4AB5416A6D9DE5 填充,因此固定在需要解密的密文,後面增加9F4AB5416A6D9DE5
if (data.length == 16) {
data = data + "9F4AB5416A6D9DE5";
}
return decodeHexString(data, key);
};
module.exports = {
encodeHex: encodeHex,
decodeHex: decodeHex
}
對比加密結果,O(∩_∩)O哈哈~, 開心,有點小成就感。。。。。。。。。。。。
/**
* ┏┓ ┏┓+ +
* ┏┛┻━━━┛┻┓ + +
* ┃ ┃
* ┃ ━ ┃ ++ + + +
* ████━████ ┃+
* ┃ ┃ +
* ┃ ┻ ┃
* ┃ ┃ + +
* ┗━┓ ┏━┛
* ┃ ┃
* ┃ ┃ + + + +
* ┃ ┃ Code is far away from bug with the animal protecting
* ┃ ┃ + 神獸保佑,代碼無bug
* ┃ ┃
* ┃ ┃ +
* ┃ ┗━━━┓ + +
* ┃ ┣┓
* ┃ ┏┛
* ┗┓┓┏━┳┓┏┛ + + + +
* ┃┫┫ ┃┫┫
* ┗┻┛ ┗┻┛+ + + +
*
* @author chenxi
* @date 2020年4月25日21:13:18
*/
————————————————
版權聲明:本文爲CSDN博主「gd6321374」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/gd6321374/article/details/102687903
參考文章: