前端RSA+AES混合加密

3,661 阅读2分钟

前言

​ 最近在工作中使用到了RSA和AES加密,网上也有很多教程,但是没有什么时效性了,所以想把内容记录下来和同学们一起学习交流

一、为什么要使用RSA和AES?

  1. RSA和AES是什么?

    • RSA:非对称加密,使用公钥私钥加密和解密,加密速度慢

    • AES:对称加密,加密和解密使用同一个密钥,加密速度快

  2. 为什么要混淆?只用一个不行吗?

    因为AES加密速度快,自然是我们的第一选择,但是缺点也明显。因为使用同一个密钥,如果有一方密钥泄露,那么数据也就不安全了。所以我们可以结合RSA互补二者的缺点,使用RSA来加密传递AES密钥,用AES来加密数据。

二、思路

  • 服务端生成RSA密钥对(privateKey-1publicKey-1),把公钥 publicKey-1发送给客户端
  • 客户端生成RSA密钥对(privateKey-2publicKey-2),使用服务端公钥加密自己的 公钥publicKey-2 ,将加密后的数据发送给服务端
  • 服务端使用自己的私钥解密获取到客户端的公钥,将AESKey使用客户端公钥 publicKey-2加密发送给客户端
  • 客户端使用自己的私钥解密获取AESkey
  • 双方使用AESkey来进行数据传输

流程图

qY5VzD.png

如果流程从客户端开始,原理也是一样的,只需要把双方角色互换即可

三、代码

前端需要用到两个npm包

生成RSA密钥

import CryptoJS from 'crypto-js/crypto-js'
import JSEncrypt from 'jsencrypt'

// 初始化
const keyPair = new JSEncrypt()

const genKeyPair = () => {
    const genKeyPair = {}   
    genKeyPair.privateKey = rsaUtil.keyPair.getPrivateKey() // 生成RSA密钥
    genKeyPair.publicKey = rsaUtil.keyPair.getPublicKey() // 生成RSA公钥
    return genKeyPair
}

RSA公钥加密 - 收到服务端公钥后加密公钥

/**
 * rsa公钥加密
 * @param {*} cipherContent 需要加密内容
 * @param {*} publicKey 服务端的公钥
 * @returns
 */
const encryptByRsa = (cipherContent, publicKey) => {
    const newValue = typeof cipherContent === 'string' ? cipherContent : cipherContent.toString()
    keyPair.setPublicKey(publicKey)
    return keyPair.encryptLong(newValue) // 注意:加密类型为string
},

RSA解密 - 解密后取到AES key

/**
 * rsa私钥解密
 * @param {*} plainContent 解密的内容
 * @param {*} privateKey 解密私钥
 * @returns
 */
const decryptByRsa = (plainContent, privateKey) => {
    keyPair.setPrivateKey(privateKey)
    return keyPair.decryptLong(plainContent)
},

AES 加密

/**
 * AES 加密
 * @param {*} cipherContent 加密的内容
 * @param {*} key AES key
 * @returns
 */
const encrypt = (cipherContent, key) => {
    const aesKey = CryptoJS.enc.Utf8.parse(key)
	const newValue = typeof cipherContent === 'string' ? cipherContent : cipherContent.toString()    
    const encrypted = CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(newValue), aesKey, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 })
    return encrypted.toString()
}

AES 解密

/**
 * AES解密
 * @param {*} plainContent 解密的内容
 * @param {*} key AES key
 * @returns
 */
decrypt: (plainContent, key) => {
    const aesKey = CryptoJS.enc.Utf8.parse(key)
    const decrypt = CryptoJS.AES.decrypt(plainContent, aesKey, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 })
    const decString = CryptoJS.enc.Utf8.stringify(decrypt).toString()
    return decString instanceof Object ? decString : JSON.parse(decString)
},

四、Q&A

剩下几个问题想和大家一起探讨下

  1. 前端加密有意义吗?

    我的回答是防君子不防小人,真正的"安全"还是得靠后端的同学

  2. 前端怎么安全的存储密钥?

    我现在的操作还是二次加密后存储session,不知道同学们是否有更好的方案