前后端接口AES +RSA 混合加解密详解(vue +SpringBoot)
前后端接口AES +RSA 混合加解密
一、AES 加密原理和为什么不使用AES 加密
二、RSA 加密原理和为什么不使用rsa加密
三、AES 和RSA 混合加密的原理
四、代码样例
前端
1. 请求增加加密标识
2. 前端加密工具类
3.前端axios请求统一封装,和返回统一封装
后端
1.后端加密工具类
2. 请求的返回实体封装
3. 配置过滤器对请求进行加解密操作
4.过滤器注册
5. 获取RSA 公钥接口
总结:
为什么需要对前后端接口加解密 ?主要是甲方要求。
AES 是最常见的对称加密算法,加密和解密用到的密钥是相同的,这种加密方式加密速度非常快,适合经常发送数据的场合,且加密长文本比较方便。缺点是密钥的传输比较麻烦,只能将一把公钥分别在前后端代码中各存放一份,还有一个遇到的问题下面会讲到,那就是美国对AES 加密密钥长度的限制。优点是加密效率高,缺点是安全性低。
RSA rsa_10">二、RSA 加密原理和为什么不使用rsa加密
RSA 也就是非对称加密算法,是使用不同密钥进行加密和解密的算法,也称为公私钥加密。公钥和私钥是同时生成的,公钥用来加密,私钥用来解密,加解密的密钥是成对出现。但是不推荐用RSA 加密请求报文,因为RSA 加密后的报文会很长,经常会出现超过请求体长度限制。优点是安全性高,缺点是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 = RSA 2text( keydata1, 1 )
var publicKey = RSA 2text( keydata2)
resolve ( {
privateKey, publicKey } )
} )
. catch ( function ( err ) {
reject ( err)
} )
} )
. catch ( function ( err ) {
reject ( err)
} )
} )
. catch ( function ( err ) {
reject ( err)
} )
} )
}
function RSA 2text( buffer, isPrivate = 0 ) {
var binary = ''
var bytes = new Uint8Array ( buffer)
var len = bytes. byteLength
for ( 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 = privateKey
let 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. url
return config
} ,
err => {
return Promise. reject ( err)
}
)
instance. interceptors. response. use (
response => {
let aesKeyByRsa = response. data. aesKeyByRsa
if ( 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