一、前言:
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
参考文章: