利用 RSA 来加密传输 AES的密钥,用 AES的密钥 来加密数据 RSA的公钥,私钥由后端提供
创建crpto.js文件
// 引入crypto-js,封装 AES加密,解密方法
import CryptoJS from 'crypto-js'
/**
word :要加密的数据,
key:秘钥(随机生成)
*/
// AES加密
encryptAES (word, key) {
let keyAES = CryptoJS.enc.Utf8.parse(key)
let srcs = CryptoJS.enc.Utf8.parse(word)
let enc = CryptoJS.AES.encrypt(srcs, key, {mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7}) // 加密模式为ECB,补码方式为PKCS7
return enc.toString()
},
// AES解密
decryptAES (word, key) {
let key = CryptoJS.enc.Utf8.parse(key)
let dec = CryptoJS.AES.decrypt(word, key, {mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7})
return CryptoJS.enc.Utf8.stringify(dec).toString()
}
后端每次接收到请求,都会将该请求的nonce存入缓存并保持60秒(根据需要设置),时间过后该值将被移除,建议后端采用Redis存储nonce,这样可省去检测和移除nonce的代码。如果后端发现当前请求的nonce存在于已存储的nonce之中,则此请求发生重复 如果只使用nonce我们只能保证该请求60秒内不会重复,但60秒后依然任人宰割,这不是要的结果。所以timestamp将用来限制时间,后端时间戳减去前端发送请求的时间戳,得到的差值为N秒,如果N秒大于60秒则此请求过期,那么则可以保证,60秒内因为nonce相同而被判为请求重放,60秒后因为时间差超过而被判为请求已过期,因此确保了请求不会被重放。
创建RSA.js文件
import CryptoJS from 'crypto-js'
import {JSEncrypt} from 'jsencrypt'
// data:需要加密的请求数据,这里暂时使用假数据
rsaEncrypt(data){
let data = { name:"张三",telPhone:"15789087655" }
// 获取时间戳
let timestamp = new Date().getTime();
// 获取随机码
let nonce = randomCode();
data.timestamp = timestamp;
data.nonce = nonce;
// 字段排序(按照首字母)
const keys = Object.keys(data);
keys.sort();
// 拼接字符串
let signStr = "";
for(let i=0;i<keys.length;i++){
const element = keys[i];
singStr+=`${element}=${data[element]}&`;
}
// 拼接key(登录时后端返回的key)
let key = "SnksifhLAteurhf" // 这里用的假的
signStr = signStr + 'key='+key;
// 计算MD5哈希值
// 拼接的字符串很容易被攻击者伪造签名并篡改数据,所以需要加上登录时返回的唯一的key
// MD5的目的并不是为了隐藏明文数据,而是让后端进行数据校验
// 后端取得请求数据后,将除了sign之外的参数名进行字典排序,
// sign用一个临时变量存下,然后排好序的参数和前端一样拼接得到字符串。
// 接下来进行MD5计算即可得到后端获得的签名,此时与请求中
// 携带的sign比较是否一致则可确定签名是否有效,如果不一致返回签名错误
const sign = CryptoJS.MD5(signStr).toString();
data.sign = sign;
// RSA公钥(登录时后端返回的)(这里用的假的,真实的公钥很长------)
let publicKey = "Misfsdfsfffsfsdfsfss"
// 随机生成AES秘钥(长度自定义)
let aesRandomStr = randomCode();
// 使用AES秘钥加密数据
let datas = crpto.encryptAES(JSON.Stringfy(data),aesRandomStr)
// rsa加密AES秘钥
const encrypts = new JSEncrypt()
// 设置公钥
encrypts.setPublicKey(publicKey)
// 加密AES秘钥
let encryptedKey = encrypts.encrypt(aesRandomStr)
let json={
"encryptedData":data,
"encryptedKey":encryptedKey
}
return json
}
// 生成随机码
randomCode(){
let possible="QWERTYUIOPLKMJNHBGVFCDXSZAqwertyuiolpkmjnhbgvfcdxsza1234567890"
let randomCode = '';
for(let i=0;i<16;i++){
randomCode+=possible.charAt(Math.floor(Math.random()*possible.length))
}
// 解密
// 解密是后端进行的,这里的解密方法只是为了前端自测,确保前端加密的数据-自己能解开
rsaDecryptData(result){
const decrypted = new JSEncrypt()
let privateKey = "hasahdaiasuivuivb";
decrypted.setPrivateKey(privateKey)
let aesKey = decrypted.decrypt(result.encryptedKey)
let dataRes = crpto.decryptAES(result.encryptedData,aesKey)
return JSON.Parse(dataRes)
}
}