前后端接口AES+RSA混合加解密详解(vue+SpringBoot)
- 前后端接口AES+RSA混合加解密
- 一、AES加密原理和为什么不使用AES加密
- 二、RSA加密原理和为什么不使用rsa加密
- 三、AES和RSA混合加密的原理
- 四、代码样例
- 前端
- 1. 请求增加加密标识
- 2. 前端加密工具类
- 3.前端axios请求统一封装,和返回统一封装
- 后端
- 1.后端加密工具类
- 2. 请求的返回实体封装
- 3. 配置过滤器对请求进行加解密操作
- 4.过滤器注册
- 5. 获取RSA公钥接口
- 总结:
前后端接口AES+RSA混合加解密
为什么需要对前后端接口加解密?主要是甲方要求。
一、AES加密原理和为什么不使用AES加密
AES是最常见的对称加密算法,加密和解密用到的密钥是相同的,这种加密方式加密速度非常快,适合经常发送数据的场合,且加密长文本比较方便。缺点是密钥的传输比较麻烦,只能将一把公钥分别在前后端代码中各存放一份,还有一个遇到的问题下面会讲到,那就是美国对AES加密密钥长度的限制。优点是加密效率高,缺点是安全性低。
二、RSA加密原理和为什么不使用rsa加密
RSA也就是非对称加密算法,是使用不同密钥进行加密和解密的算法,也称为公私钥加密。公钥和私钥是同时生成的,公钥用来加密,私钥用来解密,加解密的密钥是成对出现。但是不推荐用RSA加密请求报文,因为RSA加密后的报文会很长,经常会出现超过请求体长度限制。优点是安全性高,缺点是RSA的加密效率低。
三、AES和RSA混合加密的原理
我们可以结合两者的优点,AES的加密效率高,那我们可以使用aes来加密报文,而RSA的灵活性和安全性来加密aes的密钥。总的思路就是利用RSA来加密传输AES的密钥,用 AES的密钥来加密请求报文。
四、代码样例
前端
1. 请求增加加密标识
首先这个功能我们的出发点是可以灵活配置,所以我们在需要加密的请求头添加一个加密标识代码如下:
import axios from '@common/plugins/Axios'saveStudent: data => {return axios.request({url: `/demo/saveStudent`,method: 'post',headers: {isEncrypt: 1},data})}
2. 前端加密工具类
import JSEncrypt from 'jsencrypt'
import CryptoJS from 'crypto-js'
export function rsaEncrypt(Str, afterPublicKey) {const encryptor = new JSEncrypt()encryptor.setPublicKey(afterPublicKey) return encryptor.encrypt(Str)
}
export function rsaDecrypt(Str, frontPrivateKey) {const encryptor = new JSEncrypt()encryptor.setPrivateKey(frontPrivateKey) return encryptor.decrypt(Str)
}export function aesEncrypt(aeskey, Str) {var key = CryptoJS.enc.Utf8.parse(aeskey)var srcs = CryptoJS.enc.Utf8.parse(Str)var encrypted = CryptoJS.AES.encrypt(srcs, key, {mode: CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7})return encrypted.toString()
}export function aesDecrypt(aeskey, Str) {var key = CryptoJS.enc.Utf8.parse(aeskey)var decrypt = CryptoJS.AES.decrypt(Str, key, {mode: CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7})return CryptoJS.enc.Utf8.stringify(decrypt).toString()
}
export function get16RandomNum() {var chars = [ '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']var nums = ''for (var i = 0; i < 16; i++) {var id = parseInt(Math.random() * 61)nums += chars[id]}return nums
}
export function getRsaKeys() {return new Promise((resolve, reject) => {window.crypto.subtle.generateKey({name: 'RSA-OAEP',modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]),hash: { name: 'SHA-512' } },true, ['encrypt', 'decrypt'] ).then(function(key) {window.crypto.subtle.exportKey('pkcs8', key.privateKey).then(function(keydata1) {window.crypto.subtle.exportKey('spki', key.publicKey).then(function(keydata2) {var privateKey = RSA2text(keydata1, 1)var publicKey = RSA2text(keydata2)resolve({ privateKey, publicKey })}).catch(function(err) {reject(err)})}).catch(function(err) {reject(err)})}).catch(function(err) {reject(err)})})
}
function RSA2text(buffer, isPrivate = 0) {var binary = ''var bytes = new Uint8Array(buffer)var len = bytes.byteLengthfor (var i = 0; i < len; i++) {binary += String.fromCharCode(bytes[i])}var base64 = window.btoa(binary)let text = base64.replace(/[^\x00-\xff]/g, '$&\x01').replace(/.{64}\x01?/g, '$&\n')return text
}
3.前端axios请求统一封装,和返回统一封装
import Axios from 'axios'
import { getRsaKeys, rsaEncrypt, rsaDecrypt, aesDecrypt, aesEncrypt, get32RandomNum } from '@common/util'
const instance = Axios.create({headers: {x_requested_with: 'XMLHttpRequest'}
})
let frontPrivateKey
instance.interceptors.request.use(async config => {if (sessionStorage.getItem('X-Access-Token')) {config.headers['X-Access-Token'] = sessionStorage.getItem('X-Access-Token')}if (config.headers['isEncrypt']) {config.headers['Content-Type'] = 'application/json;charset=utf-8'if (config.method === 'post' || config.method === 'put') {const { privateKey, publicKey } = await getRsaKeys()let afterPublicKey = sessionStorage.getItem('afterPublicKey')frontPrivateKey = privateKeylet aesKey = get16RandomNum()let aesKeyByRsa = rsaEncrypt(aesKey, afterPublicKey)if (config.data) {let data = aesEncrypt(aesKey, JSON.stringify(config.data))config.data = {data: data,aeskey: aesKeyByRsa,frontPublicKey: publicKey}}if (config.params) {let data = aesEncrypt(aesKey, JSON.stringify(config.params))config.params = {params: data,aeskey: aesKeyByRsa,frontPublicKey: publicKey}}}}config.url ="你的后端接口请求地址" + config.urlreturn config},err => {return Promise.reject(err)}
)
instance.interceptors.response.use(response => {let aesKeyByRsa = response.data.aesKeyByRsaif (aesKeyByRsa) {let aesKey = rsaDecrypt(aesKeyByRsa, frontPrivateKey)response.data.data = JSON.parse(JSON.parse(aesDecrypt(aesKey, response.data.data)))return response.data}else{ return response} }},error => {if (error.response.status === 500) {const { data } = erro