一、前言
之前我们试了md5和des加密,第一个是直接转义,第二个是密钥长度太短,这两种都可能被暴力破解,通过穷举法算出明文,所以今天我们来试试新的加密方式aes和sm2,这两种的安全性更高,更有保障。
二、Aes
1、简介
aes全称(Advanced Encryption Standard),又叫Rijndael加密法,是高级加密标准。
它也是对称加密的方法,但这里用的密钥长度为128bit、192bit、256bit其中之一(字节就是16字节、24字节、32字节),都远远大于des加密的的56bit密钥,安全性有了极大的提升。
2、加解密过程
加密: 明文 + 密钥+ 偏移量(IV)+密码模式(算法/模式/填充)
解密: 密文 + 密钥+ 偏移量(IV)+密码模式(算法/模式/填充)
3、优势和应用
对称加密的算法,加密速度都很快,适合应用在大量数据传输加密的场景,也可以用来加密一些非敏感数据。
4、风险
第一个是暴力破解,这个风险性比较小,aes的密钥长度可以劝退大部分情况了。
第二个是密钥泄露,由于是对称加密,即加解密共用一个密钥,所以前端是存有一份的,这份密钥明文存储在前端项目中,懂前端的人用心找找,还是能找到该密钥的。有密钥之后,解密将变得简单起来。(所以如果要更安全,可以选用非对称方法加密,也可以对aes的密钥进行加密)
5、使用方法
5.1 安装
安装crypto-js,已经集成了,就不额外安装了。
import CryptoJS from 'crypto-js'
5.2 科普
由于aes是分组加密算法,区块长度固定128位,所以我们选用pkcs7填充方式,这里科普一下pkcs7和pkcs5。
pkcs7填充方式: 假设数据长度需要填充n(n>0)个字节才对齐,那么填充n个字节,每个字节都是n;如果数据本身就已经对齐了,则填充一块长度为块大小的数据,每个字节都是块大小;PKCS5只填充到8字节,而PKCS7可以在1-255之间任意填充。
pkcs5填充方式: PKCS#5填充是将数据填充到8的倍数,填充后数据长度的计算公式是 定于[元数据]长度为x, 填充后的长度是 x + (8 - (x % 8)), 填充的数据是 8 - (x % 8),块大小固定为8字节。
pkcs5的固定8字节填充,已经劝退了aes了(aes最小字节数为16字节)。
5.3 具体函数封装
下面是加解密代码函数的写法,和des基本相同,只需要传入明文或者密文,调用之后对应函数后,就能返回加密后的密文或者解密后的明文字符串。
const CBCIV = 'sajajg' // 密钥串
// 加密
encrypt(data) {
const key = CryptoJS.enc.Utf8.parse(CBCIV)
const secretData = CryptoJS.enc.Utf8.parse(data)
const encrypted = CryptoJS.AES.encrypt(
secretData,
key,
{ mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7
}
)
return encrypted.toString()
},
// 解密
decrypt(data) {
const key = CryptoJS.enc.Utf8.parse(CBCIV)
const decrypt = CryptoJS.AES.decrypt(
data,
key,
{
mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7
})
return CryptoJS.enc.Utf8.stringify(decrypt).toString()
}
三、 sm2非对称加密
1、简介
SM2是[国家密码管理局]于2010年12月17日发布的椭圆曲线公钥密码算法。
目前RSA加密算法已经不再安全,RSA是基于‘大数分解’这一数学难题设计的、确定性的一种加密算法。主要是靠指数难分解,运算量大来确保安全性。
如果‘大数分解’这一数学难题被攻克(这个比较难说),就是运算量变简单了,那也会导致RSA加密变得不再具备安全性。
另一个的运算量大,对目前的计算机性能来说,已经不再是难事了,1024位RSA已经可以在短时间内被破译出来明文,2048位RSA暂时还没被攻克,但估计也不远了。
所以目前是用密码复杂度高、处理速度快、机器性能消耗更小的SM2加密算法来替代RSA算法。
2、加解密过程
加密
每次加密都会经过三种加密算法,c1、c2、c3分别计算得出结果,最后将这三个结果依次异或处理,得到加密的密文。
c1: 随机数K与G(x,y)的多倍点运算结果,结果也是一个点,记录为(kx,ky)。
c2: 实际密文值。
c3:使用SM3对于 kx||data||ky的hash值,在解密时校验解密结果是否正确。
整体流程图如下:
解密
首先是密钥流的计算
1. 从密文中分离出 c1(x,y)
2. 使用私钥d与c1进行多倍点运算,得到计算结果 (cx,cy),d·c1(cx,cy)
3. 根据c2长度计算密钥流,KDF(cipherlen,cx,cy)
其次是计算出原文
1. 原文 = 密钥流 (异或) 密文
最后是校验准确性
1. 基于以上的运算结果的cx,cy与得到的原文
2. SM3(cx,计算得到的明文,cy)
3. 对比计算结果与c1,一致解密成功,不一致则解密失败
整体流程图如下:
3、优势和应用
安全系数更高
sm2采用椭圆曲线密码理论基础设计得出,加密强度比2048位RSA还高,同时可扩展性强于RSA,破解难度也比RSA要大得多。
性能表现更好
sm2的密钥长度仅256位,计算过程对计算机各方面的需求不高,占用低,效率高。
下面有个对比图,可以看看。
4、风险
除了密钥泄露,目前暂时没有其他风险。
5、使用方法
5.1 安装
这次不在crypto-js里了,我们需要额外安装
npm install --save sm-crypto
5.2 具体函数封装
一般我们都是新加一个js文件(路径/utils/sm2.js),将加密方法封装起来,在模块里按需引用
const sm2 = require('sm-crypto').sm2 // 获取sm2对象
定义加密策略和公钥a,私钥b,这是两对密钥对,剩下的后端持有私钥a和公钥b,所以就算前端这边泄露了公钥a,私钥b,两个密钥也不匹配,篡改不了信息。
同时这里的密钥对,必须是符合规范的,不能是以下我提供的样例,否则会报错
// 加密策略为0时在加密的串前加上04,后端才能解密成功,
// 同样后端加密后也会返回带04的加密串给前端,
// cipherMode为1的话必须去掉04才能解密成功
const cipherMode = 0 // 选择加密策略,1 - C1C3C2,0 - C1C2C3,默认为1
const sysPublicKey = 'asfaa' // 系统后台公钥
const uiPrivateKey = 'sjsjfb' // 前端UI私钥
封装具体函数,主要是否要+04
export default {
// 加密
sm2DoEncrypt(data) {
if (cipherMode === 0) {
// 加'04'
return '04' + sm2.doEncrypt(data, sysPublicKey, cipherMode)
} else {
return sm2.doEncrypt(data, sysPublicKey, cipherMode)
}
},
// 解密
sm2DoDecrypt(data) {
let dataHex
if (cipherMode === 0) {
data += '04'
dataHex = data.substring(2).toLocaleLowerCase()
}
return sm2.doDecrypt(dataHex, uiPrivateKey, cipherMode)
}
}
最后在模块里使用
import sm2 from '@/utils/sm2'
// 加密
const str = sm2.sm2DoEncrypt('042ssaf1231')
加密成功差不多就是这样一串字符串
四、总结
今天试了两种加密算法aes和国密sm2,如果是安全性要求比较高的系统,建议还是选用这两种加密方式,安全性更有保障。
ps: 我是地霊殿__三無