前端使用RSA+AES实现请求数据混合加密 2年前

利用 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)
    }
}
image
彩虹气质
人终其一生都是自我改善的过程。
4
发布数
2
关注者
1952
累计阅读

热门教程文档

Golang
23小节
React Native
40小节
Linux
51小节
Javascript
24小节
Gin
17小节
广告