前端开发
292
利用 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) } }
广告