RSA加密和AES加密混用
先说一下RSA与AES加密的区别
RSA
非对称加密,公钥加密,私钥解密,反之亦然。由于需要大数的乘幂求模等算法,运行速度慢,不易于硬件实现。通常私钥长度有512bit,1024bit,2048bit,4096bit,长度越长,越安全,但是生成密钥越慢,加解密也越耗时。既然是加密,那肯定是不希望别人知道我的消息,所以只有我才能解密,所以可得出公钥负责加密,私钥负责解密;同理,既然是签名,那肯定是不希望有人冒充我发消息,只有我才能发布这个签名,所以可得出私钥负责签名,公钥负责验证。
AES
对称加密,密钥最长只有128/192/256个bit,执行速度快,易于硬件实现。由于是对称加密,密钥需要在传输前通讯双方获知。基于以上特点,通常使用RSA来首先传输AES的密钥给对方,然后再使用AES来进行加密通讯。
重点,也是本人在研究加密时范的错误
刚开始以为生成的随机密钥就是128/192/256位,就拿128为例,本人就直接生成了128位的随机数(前端密钥),这样是错的
正确的做法是生成它们的除以8的结果,对应的是16/24/32位,这才是随机数的真正位数即前端密钥,128/192/256实际上是bit为单位的,要除8,谨记
首先附上测试AES加密的在线工具https://www.toolnb.com/tools/aesEnDe.html
一、安装jsencrypt、crypto-js
cnpm install jsencrypt//RSA加密需要的依赖
cnpm install crypto-js//AES加密需要的依赖
二、单独建一个js文件为crytpto.js(文件名随意起)
import $post from '@H';
import CryptoJS from 'crypto-js';
import JsEncrypt from 'jsencrypt';
// 默认的 KEY 与 iv 如果没有给
let KEY = CryptoJS.enc.Utf8.parse("1234567890123456");
let IV = CryptoJS.enc.Utf8.parse('1234567890123456');
/**
* AES加密 :字符串 key iv 返回base64
*/
export function Encrypt(word, keyStr, ivStr) {
let key = KEY
let iv = IV
if (keyStr) {
key = CryptoJS.enc.Utf8.parse(keyStr);
iv = CryptoJS.enc.Utf8.parse(ivStr);
}
let srcs = CryptoJS.enc.Utf8.parse(JSON.stringify(word));
/**
* mode: 加密模式, 可取值(CBC, CFB, CTR, CTRGladman, OFB, ECB), 都在 CryptoJS.mode 对象下
* padding: 填充方式, 可取值(Pkcs7, AnsiX923, Iso10126, Iso97971, ZeroPadding, NoPadding), 都在 CryptoJS.pad 对象下
* iv: 偏移量, mode === ECB 时, 不需要 iv,设置了也没问题
* 返回的是一个加密对象
*/
let encrypted = CryptoJS.AES.encrypt(srcs, key, {
iv: iv,
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
return CryptoJS.enc.Base64.stringify(encrypted.ciphertext);
}
/**
* AES 解密 :字符串 key iv 返回base64
*
*/
export function Decrypt(word, keyStr, ivStr) {
let key = KEY
let iv = IV
if (keyStr) {
key = CryptoJS.enc.Utf8.parse(keyStr);
iv = CryptoJS.enc.Utf8.parse(ivStr);
}
let base64 = CryptoJS.enc.Base64.parse(word);
let src = CryptoJS.enc.Base64.stringify(base64);
let decrypt = CryptoJS.AES.decrypt(src, key, {
iv: iv,
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
let decryptedStr = decrypt.toString(CryptoJS.enc.Utf8);
return JSON.parse(decryptedStr.toString(decryptedStr));
}
/**
*AES加密有个规则如下:
*加密时用16位为一组,到最后不到16位的时候是自动补充到16位的,而后台在加密补时的充方式上如果是Pkcs5的话,前端就要用Pkcs7去解密(其它方式没试)
*/
/**
* 生成随机数(前端密钥)
*/
export function randomWord(randomFlag, min, max) {
let str = "",
range = min,
arr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
// 随机产生
if (randomFlag) {
range = Math.round(Math.random() * (max - min)) + min;
}
for (let i = 0; i < range; i++) {
let pos = Math.round(Math.random() * (arr.length - 1));
str += arr[pos];
}
return str;
}
/**
*RSA加密(用后台公钥加密前端密钥共AES加解密时使用)
* @param userPublicKey(后台生成密钥时存缓存的依据,方便根据该依据找到所公钥对应的私钥)
* @returns {Promise<any>}
* 获取后台给的公钥
* 将自己生成随机数(前端密钥)加密后传给后台
*
*/
export function getServiceKey(userPublicKey) {
return new Promise((resolve, reject) => {
let url = '';//获取公钥的接口
let params = {
userPublicKey: userPublicKey
};
$post.post(url, params).then(res => {
if (res.data.code == 200) {
let webSecretKey = this.randomWord(false, 16);
this.$cookieStore.setCookie("webSecretKey", webSecretKey, 1);//存储前端密钥,方法很多,用的时候能获取到就行可以用vuex
let jse = new this.$jsEncrypt
jse.setPublicKey(res.data.StrKey)
let encrypted = jse.encrypt(webSecretKey)//给随机数(前端密钥)加密(rsa加密)
resolve({encrypted, userPublicKey})
}
})
}).then((res) => {
let url = '';//将前端密钥传给后台接口
let params = {
aesKey: res.encrypted,
userPublicKey: res.userPublicKey
};
$post.post(url, params).then(res => {
if (res.data.code == 200) {
}
})
})
}
/**
*
* @param url(请求接口)
* @param userPublicKey(后台存缓存的依据,方便根据该依据找到存储的前端密钥后进行AES加解密)
* @param encryptionParams(加密的参数)
*/
export function decryptData(url, userPublicKey, encryptionParams) {
return new Promise((resolve, reject) => {
let webSecretKey = this.$cookieStore.getCookie('webSecretKey');
let par = encryptionParams;
let params = {
userPublicKey: userPublicKey,
params: this.Encrypt(par, webSecretKey, webSecretKey)
}
$post.post(url, params).then(res => {
if (res.data.code == 200) {
if (res.data.encryptData !== undefined) {
let encryptData = this.Decrypt(res.data.encryptData, webSecretKey, webSecretKey);
res.data.encryptData = encryptData
}
resolve(res)
}
})
})
}
三、将上面的方法引入到min.js中
//RSA加密,ESA加密
import JsEncrypt from 'jsencrypt'
Vue.prototype.$jsEncrypt = JsEncrypt
import {Decrypt,Encrypt,getServiceKey,randomWord,decryptData} from '@V/commonFunction/cryptoJS'
Vue.prototype.Decrypt = Decrypt
Vue.prototype.Encrypt = Encrypt
Vue.prototype.getServiceKey = getServiceKey
Vue.prototype.randomWord = randomWord
Vue.prototype.decryptData = decryptData
四、项目中调用
demo() {
let url = '';
let userPublicKey = '';//后台存缓存的key
let encryptionParams = {};//加密的内容
this.decryptData(url,userPublicKey,encryptionParams).then( res=>{
if(res.data.code == 200) {
}
})
},
aes、rsa属于国外的加密方式,咱们国内也有对应的加密,加密程度丝毫不亚于国外的有兴趣的同学可以移步到国产sm2,sm3,sm4加解密.