Spring Boot + Vue 基于RSA+AES的混合加密

devtools/2025/3/17 16:08:41/

目录

一、后端实现

 二、前端实现(Vue2)

三、补充

 1.增强安全措施

 四、最后说明


步骤大致如下:

  1. 后端生成RSA密钥对,提供公钥接口。
  2. 前端请求公钥,生成随机AES密钥和IV。
  3. 用RSA公钥加密AES密钥,用AES密钥加密数据。
  4. 发送包含加密后的AES密钥和数据的请求体。
  5. 后端用RSA私钥解密AES密钥,再用AES密钥解密数据。
  6. 使用注解和拦截器自动处理解密过程。

        需要确保每个步骤都正确实现,特别是加密模式、填充方式以及编码解码的一致性,避免因配置不同导致解密失败。有什么没加入的在评论区艾特我,我进行补充

一、后端实现

  • 新增AES工具类:
public class AesUtils {public static String encrypt(String data, String key, String iv) throws Exception {SecretKeySpec keySpec = new SecretKeySpec(Base64.getDecoder().decode(key), "AES");IvParameterSpec ivSpec = new IvParameterSpec(Base64.getDecoder().decode(iv));Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);byte[] encrypted = cipher.doFinal(data.getBytes());return Base64.getEncoder().encodeToString(encrypted);}public static String decrypt(String data, String key, String iv) throws Exception {byte[] keyBytes = Base64.getDecoder().decode(key);byte[] ivBytes = Base64.getDecoder().decode(iv);byte[] encryptedBytes = Base64.getDecoder().decode(data);Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);return new String(cipher.doFinal(encryptedBytes));}
}
  •  修改请求处理切面:
/*** 解密切面*/
@ControllerAdvice
public class DecryptAdvice extends RequestBodyAdviceAdapter {private final RsaKeyManager keyManager;public DecryptAdvice(RsaKeyManager keyManager) {this.keyManager = keyManager;}@Overridepublic boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {return methodParameter.hasMethodAnnotation(NeedDecrypt.class);}@Overridepublic HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage,MethodParameter parameter,Type targetType,Class<? extends HttpMessageConverter<?>> converterType) throws IOException {try {String encryptedBody = new String(inputMessage.getBody().readAllBytes());JSONObject json = JSONObject.parseObject(encryptedBody);// 解密 AES 密钥String encryptedAesKey = json.getString("encryptedKey");String aesKey = RsaUtils.decryptByPrivateKey(encryptedAesKey, keyManager.getPrivateKey());// 解密数据String decryptedData = AesUtils.decrypt(json.getString("encryptedData"),aesKey,json.getString("iv"));return new DecryptedHttpInputMessage(new ByteArrayInputStream(decryptedData.getBytes()),inputMessage.getHeaders());} catch (Exception e) {throw new RuntimeException("解密失败", e);}}
}
  • 新增注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NeedDecrypt {
}
  • 新增公私钥管理类
/*** 生成RSA*/
public class RsaKeyManager {Logger log = LoggerFactory.getLogger(RsaKeyManager.class);private final RedisService redisService;// Redis 键名private static final String PUBLIC_KEY = "rsa:public";private static final String PRIVATE_KEY = "rsa:private";public RsaKeyManager(RedisService redisService) {this.redisService = redisService;}/*** 初始化密钥(全局唯一)*/@PostConstructpublic void initKeyPair() throws Exception {// 使用 SETNX 原子操作确保只有一个服务生成密钥Boolean isAbsent = redisService.setIfAbsent(PUBLIC_KEY, "");if (Boolean.TRUE.equals(isAbsent)) {KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");generator.initialize(2048);KeyPair keyPair = generator.generateKeyPair();// 存储密钥redisService.set(PUBLIC_KEY,Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded()));redisService.set(PRIVATE_KEY,Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded()));log.info("---------------------------初始化RSA秘钥---------------------------");}}/*** 获取公钥*/public String getPublicKey() {Object publicKey = redisService.get(PUBLIC_KEY);return Objects.isNull(publicKey)?null:publicKey.toString();}/*** 获取私钥*/public String getPrivateKey() {Object privateKey = redisService.get(PRIVATE_KEY);return Objects.isNull(privateKey)?null:privateKey.toString();}
}
  • 新增DecryptedHttpInputMessage
public class DecryptedHttpInputMessage implements HttpInputMessage {private final InputStream body;private final HttpHeaders headers;public DecryptedHttpInputMessage(InputStream body, HttpHeaders headers) {this.body = body;this.headers = headers;}@Overridepublic InputStream getBody() throws IOException {return this.body;}@Overridepublic HttpHeaders getHeaders() {return this.headers;}
}
  • 新增获取公钥接口 
@RestController
@RequestMapping("/rsa")
public class RSAController {@Autowiredprivate RsaKeyManager rsaKeyManager;/*** 获取公钥* @return 结果*/@GetMapping("/publicKey")public R<String> getPublicKey() {String publicKey = rsaKeyManager.getPublicKey();return R.ok(publicKey);}}

 二、前端实现(Vue2)

  • 安装新依赖:
npm install crypto-js
  • 加密工具(src/utils/crypto.js): 

        getPublicKey 为请求公钥的接口,需要按照自己请求方式去获取 

import JSEncrypt from 'jsencrypt'
import CryptoJS from 'crypto-js'
import { getPublicKey } from '../request/api/auth'// 初始化公钥
export async function initPublicKey() { try {const res = await getPublicKey()return formatPublicKey(res.data)} catch (error) {console.error('公钥获取失败:', error)throw new Error('安全模块初始化失败')}
}// 生成AES密钥
export function generateAesKey() {const key = CryptoJS.lib.WordArray.random(32)const iv = CryptoJS.lib.WordArray.random(16)return {key: CryptoJS.enc.Base64.stringify(key),iv: CryptoJS.enc.Base64.stringify(iv)}
}// AES加密
export function aesEncrypt(data, key, iv) {const encrypted = CryptoJS.AES.encrypt(JSON.stringify(data),CryptoJS.enc.Base64.parse(key), { iv: CryptoJS.enc.Base64.parse(iv),mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.Pkcs7})return encrypted.toString()
}// 格式化公钥
function formatPublicKey(rawKey) {return `-----BEGIN PUBLIC KEY-----\n${wrapKey(rawKey)}\n-----END PUBLIC KEY-----`
}// 每64字符换行
function wrapKey(key) {return key.match(/.{1,64}/g).join('\n')
}
  • 修改请求拦截器: 
service.interceptors.request.use(async config => {if (config.needEncrypt) {await initPublicKey(service)// 生成AES密钥const aes = generateAesKey()// 加密数据const encryptedData = aesEncrypt(config.data, aes.key, aes.iv)// 加密AES密钥const encryptor = new JSEncrypt()encryptor.setPublicKey(publicKey)const encryptedKey = encryptor.encrypt(aes.key)// 构造请求体config.data = {encryptedKey: encryptedKey,encryptedData: encryptedData,iv: aes.iv}}return config
})

三、补充

  • 后端需要加密的接口示例 
@PostMapping("/secure-data")
@NeedDecrypt
public String handleSecureData(@RequestBody Map<String, Object> decryptedData) {return "Decrypted data: " + decryptedData.toString();
}
  • 请求结构体 
{"encryptedKey": "RSA加密后的AES密钥","encryptedData": "AES加密后的数据","iv": "Base64编码的IV"
}

 1.增强安全措施

  • 密钥时效性

// 前端每次请求生成新密钥
const aes = generateAesKey()
  • 完整性校验

// 后端解密后可添加HMAC校验
String hmac = json.getString("hmac");
if(!verifyHMAC(decryptedData, hmac, aesKey)) {throw new SecurityException("Data tampered");
}
  • 防御重放攻击

// 前端添加时间戳和随机数
config.data.timestamp = Date.now()
config.data.nonce = Math.random().toString(36).substr(2)

 四、最后说明

该方案相比纯RSA加密有以下优势:

  1. 性能提升:AES加密大数据效率比RSA高1000倍以上

  2. 前向安全性:每次请求使用不同AES密钥

  3. 安全性增强:CBC模式+随机IV避免模式分析攻击

实际部署时需注意:

  1. 使用HTTPS传输加密后的数据

  2. 定期轮换RSA密钥对

  3. 对敏感接口添加频率限制

  4. 在网关层实现解密拦截器(而非应用层)

 

 

 

 

 

 

 

     

     

     

     

     

     


      http://www.ppmy.cn/devtools/167864.html

      相关文章

      【ES6】03-Set + Map

      本文介绍两种集合 set map 的操作和方法。 目录 1. Set 1.1 set基本使用 1.2 add 1.3 delete 1.4 has 1.5 size 1.6 set转换为数组 1.7 拓展运算符 1.8 for...of 1.9 forEach 1.10 set给数组去重 2. Map 2.1 创建map集合 2.2 set添加元素 2.3 delete删除元素 …

      奇安信二面

      《网安面试指南》https://mp.weixin.qq.com/s/RIVYDmxI9g_TgGrpbdDKtA?token1860256701&langzh_CN 5000篇网安资料库https://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247486065&idx2&snb30ade8200e842743339d428f414475e&chksmc0e4732df793fa3bf39…

      文生图技术的演进、挑战与未来:一场重构人类创造力的革命

      摘要 文生图&#xff08;Text-to-Image Generation&#xff09;技术作为生成式人工智能&#xff08;Generative AI&#xff09;的核心分支&#xff0c;正在以颠覆性力量重塑内容生产范式。本文系统梳理文生图技术从早期实验到多模态大模型的演进路径&#xff0c;分析其在设计、…

      读 Gemma 3 二分

      读 Gemma 3 二分 flyfish 1. 引言 Gemma 3 是 Google DeepMind 开发的轻量级开源多模态模型&#xff0c;参数规模从 10 亿到 270 亿不等。作为 Gemini 家族的新成员&#xff0c;它在 Gemma 2 的基础上引入了视觉理解能力、多语言支持和超长上下文窗口&#xff08;最高 128K …

      Linux驱动开发实战(四):设备树点RGB灯

      Linux驱动开发实战&#xff08;四&#xff09;&#xff1a;设备树点RGB灯 文章目录 Linux驱动开发实战&#xff08;四&#xff09;&#xff1a;设备树点RGB灯前言一、驱动实现1.1 驱动设计思路1.2 关键数据结构1.3 字符设备操作函数1.4 平台驱动探测函数1.5 匹配表和平台驱动结…

      贪心算法--

      1.柠檬水找零 link:860. 柠檬水找零 - 力扣&#xff08;LeetCode&#xff09; code class Solution { public:bool lemonadeChange(vector<int>& bills) {// 贪心算法&#xff0c; 优先花出大面额bill&#xff0c; 尽可能保护小面额billint five 0, ten 0;// 不…

      路由器和网关支持边缘计算

      路由器和网关可以支持边缘计算&#xff0c;但它们的功能和性能可能有所不同&#xff0c;具体取决于设备的设计和用途。以下是路由器和网关在边缘计算中的作用及其支持方式&#xff1a; 路由器在边缘计算中的作用 网络连接与数据传输 路由器主要负责在网络中传输数据包&#xff…

      Cisdem Video Converter for Mac v8.4.1 视频格式转换 支持M、Intel芯片

      应用介绍 Cisdem Video Converter 将您的视频和音频文件转换为任何格式&#xff0c;以便在一系列设备上即时播放&#xff0c;支持所有编码格式&#xff0c;包括 H.265/HEVC、H.264、Xvid、VP8、VP9 等&#xff0c;并导出视频在最新的 4K UHD 中。它在不牺牲质量的情况下提供了…