说明:使用aes对数据进行加密解密,对aes的秘钥进行rsa加密解密。前端获取后台公钥(调用获取公钥接口,后台会自动生成)并保存;前端加密:前台发起请求如果要加密的话在headers中加上isEncrypt属性,经过axios请求拦截器生成rsa公钥私钥队,并将公钥传给后端(后端使用前端公钥进行加密),然后再生成aes秘钥,使用aes秘钥对参数进行加密传给后端,使用之前获取的后端公钥对aes秘钥进行加密并传给后端;前端解密:使用前端私钥对后端传来解密aes秘钥解密,在使用aes秘钥对数据进行解密。
一、vue 前端aes和rsa生成工具类
import JSEncrypt from 'jsencrypt'
import CryptoJS from 'crypto-js'
import get from 'core-js/library/fn/reflect/get'
import { resolve } from 'core-js/library/es6/promise'
import { reject } from 'core-js/fn/promise'
// 加密
export function rsaEncrypt(txt,afterPublicKey) {
const encryptor = new JSEncrypt()
encryptor.setPublicKey(afterPublicKey) // 设置公钥
return encryptor.encrypt(txt) // 对数据进行加密
}
// 解密
export function rsaDecrypt(txt,frontPrivateKey) {
const encryptor = new JSEncrypt()
encryptor.setPrivateKey(frontPrivateKey) // 设置私钥
return encryptor.decrypt(txt) // 对数据进行解密
}
export function aesEncrypt(keyStr, word) {
// 设置一个默认值,如果第二个参数为空采用默认值,不为空则采用新设置的密钥
var key = CryptoJS.enc.Utf8.parse(keyStr);
var srcs = CryptoJS.enc.Utf8.parse(word);
var encrypted = CryptoJS.AES.encrypt(srcs, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
return encrypted.toString();
}
export function aesDecrypt(keyStr, word) {
var key = CryptoJS.enc.Utf8.parse(keyStr);
var decrypt = CryptoJS.AES.decrypt(word, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
return CryptoJS.enc.Utf8.stringify(decrypt).toString();
}
/**
* 获取32位随机码AES
* @returns {string}
*/
export function get32RandomNum() {
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 < 32; 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, //can be 1024, 2048, or 4096
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
hash: {name: "SHA-512"}, //can be "SHA-1", "SHA-256", "SHA-384", or "SHA-512"
},
true, //whether the key is extractable (i.e. can be used in exportKey)
["encrypt", "decrypt"] //must be ["encrypt", "decrypt"] or ["wrapKey", "unwrapKey"]
).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.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;
}
二、 axios 配置拦截
import axios from 'axios'
import {
getRsaKeys,
rsaEncrypt,
rsaDecrypt,
aesDecrypt,
aesEncrypt,
get32RandomNum
} from '../utils/jsEncryptUtils'
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
let frontPrivateKey
const request = axios.create({
baseURL: 'http://localhost:8090'
})
request.interceptors.request.use(async config => {
if (config.method !== 'post' && config.method !== 'put') {
return config
}
if (!config.headers["isEncrypt"]) {
return config
}
const {
privateKey,
publicKey
} = await getRsaKeys()
frontPrivateKey = privateKey
let afterPublicKey = localStorage.getItem("afterPublicKey")
let aesKey = get32RandomNum()
let aesKeyByRsa = rsaEncrypt(aesKey, afterPublicKey)
let data = aesEncrypt(aesKey, JSON.stringify(config.data))
config.data = {
data: data,
aeskey: aesKeyByRsa,
frontPublicKey: publicKey
}
return config
})
request.interceptors.response.use(res => {
let aesKeyByRsa = res.data.data.aesKeyByRsa
if (!aesKeyByRsa) {
return res
}
let aesKey = rsaDecrypt(aesKeyByRsa, frontPrivateKey)
res.data.data = JSON.parse(aesDecrypt(aesKey,res.data.data.data));
return res
})
export default request
三、后台公钥获取接口
@GetMapping("/getPublicKey")
public R<?> getPublicKey() throws Exception {
String publicKey = RSAUtils.getPublicKey();
if (publicKey == null) {
return R.ok(RSAUtils.genKeyPair().get("publicKey"));
}
return R.ok(publicKey);
}
四、aesutils
package com.qi.demo1.utils;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Random;
/**
* @author qyb
* @version 1.0
* @date 2022/8/16-16:14
*/
public class AESUtils {
/**
* 加密算法AES
*/
private static final String KEY_ALGORITHM = "AES";
/**
* key的长度,Wrong key size: must be equal to 128, 192 or 256
* 传入时需要16、24、36
*/
private static final int KEY_LENGTH = 32 * 8;
/**
* 算法名称/加密模式/数据填充方式
* 默认:AES/ECB/PKCS5Padding
*/
private static final String ALGORITHMS = "AES/ECB/PKCS5Padding";
/**
* 后端AES的key,由静态代码块赋值
*/
public static String key;
static {
key = getKey();
}
/**
* 获取key
*/
public static String getKey() {
int length = KEY_LENGTH / 8;
StringBuilder uid = new StringBuilder(length);
//产生32位的强随机数
Random rd = new SecureRandom();
for (int i = 0; i < length; i++) {
//产生0-2的3位随机数
switch (rd.nextInt(3)) {
case 0:
//0-9的随机数
uid.append(rd.nextInt(10));
break;
case 1:
//ASCII在65-90之间为大写,获取大写随机
uid.append((char) (rd.nextInt(26) + 65));
break;
case 2:
//ASCII在97-122之间为小写,获取小写随机
uid.append((char) (rd.nextInt(26) + 97));
break;
default:
break;
}
}
return uid.toString();
}
/**
* AES 加密
*
* @param content 加密的字符串
* @param encryptKey key值
*/
public static String encrypt(String content, String encryptKey) throws Exception {
//设置Cipher对象
Cipher cipher = Cipher.getInstance(ALGORITHMS);
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey.getBytes(), KEY_ALGORITHM));
//调用doFinal
// 转base64
return Base64.encodeBase64String(cipher.doFinal(content.getBytes(StandardCharsets.UTF_8)));
}
/**
* AES 解密
*
* @param encryptStr 解密的字符串
* @param decryptKey 解密的key值
*/
public static String decrypt(String encryptStr, String decryptKey) throws Exception {
//base64格式的key字符串转byte
byte[] decodeBase64 = Base64.decodeBase64(encryptStr);
//设置Cipher对象
Cipher cipher = Cipher.getInstance(ALGORITHMS);
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(decryptKey.getBytes(), KEY_ALGORITHM));
//调用doFinal解密
return new String(cipher.doFinal(decodeBase64));
}
}
五、rsautils
package com.qi.demo1.utils;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author qyb
* @version 1.0
* @date 2022/8/16-17:13
*/
public class RSAUtils {
/**
* 加密算法RSA
*/
private static final String KEY_ALGORITHM = "RSA";
/**
* 算法名称/加密模式/数据填充方式
* 默认:RSA/ECB/PKCS1Padding
*/
private static final String ALGORITHMS = "RSA/ECB/PKCS1Padding";
/**
* RSA最大加密明文大小
*/
private static final int MAX_ENCRYPT_BLOCK = 245;
/**
* RSA最大解密密文大小
*/
private static final int MAX_DECRYPT_BLOCK = 256;
/**
* RSA 位数 如果采用2048 上面最大加密和最大解密则须填写: 245 256
*/
private static final int INITIALIZE_LENGTH = 2048;
/**
* 后端RSA的密钥对(公钥和私钥)Map,由静态代码块赋值
*/
private static final Map<String, String> map = new LinkedHashMap<>(2);
/**
* 生成密钥对(公钥和私钥)
*/
public static Map<String,String> genKeyPair() throws Exception {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
keyPairGen.initialize(INITIALIZE_LENGTH);
KeyPair keyPair = keyPairGen.generateKeyPair();
// 获取公钥
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
// 获取私钥
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
// 得到公钥字符串
String publicKeyString = Base64.encodeBase64String(publicKey.getEncoded());
// 得到私钥字符串
String privateKeyString = Base64.encodeBase64String((privateKey.getEncoded()));
map.put("publicKey",publicKeyString);
map.put("privateKey",privateKeyString);
return map;
}
public static String getPrivateKey(){
return map.get("privateKey");
}
public static String getPublicKey(){
return map.get("publicKey");
}
/**
* RSA私钥解密
* @param data BASE64编码过的密文
* @param privateKey 私钥(BASE64编码)
* @return utf-8编码的明文
*/
public static byte[] decryptByPrivateKey(byte[] data, String privateKey) throws Exception {
//base64格式的key字符串转Key对象
Key privateK = KeyFactory.getInstance(KEY_ALGORITHM).generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey)));
Cipher cipher = Cipher.getInstance(ALGORITHMS);
cipher.init(Cipher.DECRYPT_MODE, privateK);
//分段进行解密操作
return encryptAndDecryptOfSubsection(data, cipher, MAX_DECRYPT_BLOCK);
}
/**
* RSA公钥加密
* @param data BASE64编码过的密文
* @param publicKey 公钥(BASE64编码)
* @return utf-8编码的明文
*/
public static byte[] encryptByPublicKey(byte[] data, String publicKey) throws Exception {
//base64格式的key字符串转Key对象
Key publicK = KeyFactory.getInstance(KEY_ALGORITHM).generatePublic(new X509EncodedKeySpec(Base64.decodeBase64(publicKey)));
Cipher cipher = Cipher.getInstance(ALGORITHMS);
cipher.init(Cipher.ENCRYPT_MODE, publicK);
//分段进行加密操作
return encryptAndDecryptOfSubsection(data, cipher, MAX_ENCRYPT_BLOCK);
}
/**
* RSA公钥解密
* @param data BASE64编码过的密文
* @param publicKey RSA公钥
* @return utf-8编码的明文
*/
public static byte[] pubKeyDec(byte[] data, String publicKey) throws Exception {
//base64格式的key字符串转Key对象
Key privateK = KeyFactory.getInstance(KEY_ALGORITHM).generatePublic(new X509EncodedKeySpec(Base64.decodeBase64(publicKey)));
Cipher cipher = Cipher.getInstance(ALGORITHMS);
cipher.init(Cipher.DECRYPT_MODE, privateK);
//分段进行解密操作
return encryptAndDecryptOfSubsection(data, cipher, MAX_DECRYPT_BLOCK);
}
/**
* RSA私钥加密
* @param data 待加密的明文
* @param privateKey RSA私钥
* @return 经BASE64编码后的密文
*/
public static byte[] privKeyEnc(byte[] data, String privateKey) throws Exception {
//base64格式的key字符串转Key对象
Key publicK = KeyFactory.getInstance(KEY_ALGORITHM).generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey)));
Cipher cipher = Cipher.getInstance(ALGORITHMS);
cipher.init(Cipher.ENCRYPT_MODE, publicK);
//分段进行加密操作
return encryptAndDecryptOfSubsection(data, cipher, MAX_ENCRYPT_BLOCK);
}
/**
* 分段进行加密、解密操作
*/
private static byte[] encryptAndDecryptOfSubsection(byte[] data, Cipher cipher, int encryptBlock) throws Exception {
int inputLen = data.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
// 对数据分段加密
while (inputLen - offSet > 0) {
if (inputLen - offSet > encryptBlock) {
cache = cipher.doFinal(data, offSet, encryptBlock);
} else {
cache = cipher.doFinal(data, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * encryptBlock;
}
out.close();
return out.toByteArray();
}
}
六、自定义加密解密注解
package com.qi.demo1.annotation;
import org.springframework.data.elasticsearch.annotations.Document;
import java.lang.annotation.*;
/**
* @author qyb
* @version 1.0
* @date 2022/8/17-9:51
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EncryptAndDecrypt {
/**
* 是否加密 默认为加密
*
*/
boolean isEncrypt() default true;
/**
*
* 是否解密 默认为解密
*/
boolean isDecrypt() default true;
}
七、aop 拦截处理加密解密(前后端都是使用application/json)
package com.qi.demo1.aspect;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import com.qi.demo1.annotation.EncryptAndDecrypt;
import com.qi.demo1.common.ApiEncryptRes;
import com.qi.demo1.common.R;
import com.qi.demo1.utils.ApiSecurityUtils;
import com.qi.demo1.utils.ServletUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
/**
* @author qyb
* @version 1.0
* @date 2022/8/17-9:54
*/
@Slf4j
@Component
@Aspect
public class EncryptAndDecryptAspect {
@Around("@annotation(encryptAndDecrypt)")
public Object doAround(ProceedingJoinPoint point, EncryptAndDecrypt encryptAndDecrypt) throws Throwable {
HttpServletRequest request = ServletUtils.getRequest();
String method = request.getMethod().toUpperCase();
Object[] args = point.getArgs();
String frontPublicKey="";
if ("POST".equals(method) || "PUT".equals(method)) {
// 解密
if (encryptAndDecrypt.isDecrypt()) {
if (args.length > 0) {
// TODO: 2022/8/17 解密
String bodyString = getBodyString(request);
JSONObject jsonObject = JSONObject.parseObject(bodyString);
String aesKey = (String) jsonObject.get("aeskey");
frontPublicKey = (String) jsonObject.get("frontPublicKey");
String data = (String) jsonObject.get("data");
if (StringUtils.isNotEmpty(aesKey)) {
String decrypt = ApiSecurityUtils.decrypt(aesKey, data);
args[0] = JSON.parseObject(decrypt, args[0].getClass());
log.info("解密成功");
} else {
args[0] = JSON.parseObject(data, args[0].getClass());
}
}
}
}
// 执行方法
Object object = point.proceed(args);
if (encryptAndDecrypt.isEncrypt()) {
R<?> r = (R<?>) object;
Object data = r.getData();
if (StringUtils.isEmpty(frontPublicKey) || data == null) {
return object;
}
ApiEncryptRes apiEncryptRes = ApiSecurityUtils.encrypt(JSON.toJSONString(data), frontPublicKey);
log.info("加密成功");
return R.ok(apiEncryptRes);
}
return object;
}
private String getBodyString(HttpServletRequest request) {
BufferedReader bufferedReader;
StringBuilder sb = new StringBuilder();
try (InputStream inputStream = request.getInputStream()) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
String line = "";
while ((line = bufferedReader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString();
}
}
八、apiSecurityUtils
package com.qi.demo1.utils;
import com.qi.demo1.common.ApiEncryptRes;
import org.apache.commons.codec.binary.Base64;
/**
* @author qyb
* @version 1.0
* @date 2022/8/17-15:40
*/
public class ApiSecurityUtils {
/**
*
* @param aesKeyByRsa 经过rsa加密的aeskey
* @param decryptStr 经过aes加密的数据
* @return 解密后的数据
*/
public static String decrypt(String aesKeyByRsa,String decryptStr) throws Exception {
byte[] bytes = RSAUtils.decryptByPrivateKey(Base64.decodeBase64(aesKeyByRsa), RSAUtils.getPrivateKey());
String aesKey = new String(bytes);
return AESUtils.decrypt(decryptStr, aesKey);
}
/**
*
* @param encryptStr 要加密的数据
* @param frontPublicKey 前端公钥
* @return 加密后的数据
*/
public static ApiEncryptRes encrypt(String encryptStr, String frontPublicKey) throws Exception {
String aesKey = AESUtils.getKey();
String data = AESUtils.encrypt(encryptStr, aesKey);
ApiEncryptRes apiEncryptRes = new ApiEncryptRes();
String aesKeyByRsa = Base64.encodeBase64String(RSAUtils.encryptByPublicKey(aesKey.getBytes(), frontPublicKey));
apiEncryptRes.setAesKeyByRsa(aesKeyByRsa);
apiEncryptRes.setData(data);
return apiEncryptRes;
}
}
九、相关请求api
import request from '../utils/request'
export function saveGoods(data) {
return request({
url: '/goods/saveGoods',
method: 'post',
data,
headers: {
isEncrypt: 1
}
})
}
export function getPublicKey() {
return request({
url: '/goods/getPublicKey',
method: 'get',
headers: {
isEncrypt: 0
}
})
}