Spring Boot + Vue 基于RSA+AES的肴杂加密

打印 上一主题 下一主题

主题 985|帖子 985|积分 2955

目录
一、后端实现
 二、前端实现(Vue2)
三、补充
 1.增强安全措施
 四、最后阐明


步调大致如下:

  • 后端天生RSA密钥对,提供公钥接口。
  • 前端哀求公钥,天生随机AES密钥和IV。
  • 用RSA公钥加密AES密钥,用AES密钥加密数据。
  • 发送包罗加密后的AES密钥和数据的哀求体。
  • 后端用RSA私钥解密AES密钥,再用AES密钥解密数据。
  • 利用注解和拦截器主动处明白密过程。
        须要确保每个步调都精确实现,特殊是加密模式、填充方式以及编码解码的同等性,避免因设置差别导致解密失败。有什么没参加的在批评区艾特我,我进行补充
一、后端实现



  • 新增AES工具类:
  1. public class AesUtils {
  2.         public static String encrypt(String data, String key, String iv) throws Exception {
  3.                 SecretKeySpec keySpec = new SecretKeySpec(
  4.                                 Base64.getDecoder().decode(key), "AES"
  5.                 );
  6.                 IvParameterSpec ivSpec = new IvParameterSpec(
  7.                                 Base64.getDecoder().decode(iv)
  8.                 );
  9.                 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
  10.                 cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
  11.                 byte[] encrypted = cipher.doFinal(data.getBytes());
  12.                 return Base64.getEncoder().encodeToString(encrypted);
  13.         }
  14.         public static String decrypt(String data, String key, String iv) throws Exception {
  15.                 byte[] keyBytes = Base64.getDecoder().decode(key);
  16.                 byte[] ivBytes = Base64.getDecoder().decode(iv);
  17.                 byte[] encryptedBytes = Base64.getDecoder().decode(data);
  18.                 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
  19.                 SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
  20.                 IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
  21.                 cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
  22.                 return new String(cipher.doFinal(encryptedBytes));
  23.         }
  24. }
复制代码


  •  修改哀求处理切面:
  1. /**
  2. * 解密切面
  3. */
  4. @ControllerAdvice
  5. public class DecryptAdvice extends RequestBodyAdviceAdapter {
  6.         private final RsaKeyManager keyManager;
  7.         public DecryptAdvice(RsaKeyManager keyManager) {
  8.                 this.keyManager = keyManager;
  9.         }
  10.         @Override
  11.         public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
  12.                 return methodParameter.hasMethodAnnotation(NeedDecrypt.class);
  13.         }
  14.         @Override
  15.         public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage,
  16.                                                MethodParameter parameter,
  17.                                                Type targetType,
  18.                                                Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
  19.                 try {
  20.                         String encryptedBody = new String(inputMessage.getBody().readAllBytes());
  21.                         JSONObject json = JSONObject.parseObject(encryptedBody);
  22.                         // 解密 AES 密钥
  23.                         String encryptedAesKey = json.getString("encryptedKey");
  24.                         String aesKey = RsaUtils.decryptByPrivateKey(encryptedAesKey, keyManager.getPrivateKey());
  25.                         // 解密数据
  26.                         String decryptedData = AesUtils.decrypt(
  27.                                         json.getString("encryptedData"),
  28.                                         aesKey,
  29.                                         json.getString("iv")
  30.                         );
  31.                         return new DecryptedHttpInputMessage(
  32.                                         new ByteArrayInputStream(decryptedData.getBytes()),
  33.                                         inputMessage.getHeaders()
  34.                         );
  35.                 } catch (Exception e) {
  36.                         throw new RuntimeException("解密失败", e);
  37.                 }
  38.         }
  39. }
复制代码


  • 新增注解
  1. @Target(ElementType.METHOD)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface NeedDecrypt {
  5. }
复制代码


  • 新增公私钥管理类
  1. /**
  2. * 生成RSA
  3. */
  4. public class RsaKeyManager {
  5.         Logger log = LoggerFactory.getLogger(RsaKeyManager.class);
  6.         private final RedisService redisService;
  7.         // Redis 键名
  8.         private static final String PUBLIC_KEY = "rsa:public";
  9.         private static final String PRIVATE_KEY = "rsa:private";
  10.         public RsaKeyManager(RedisService redisService) {
  11.                 this.redisService = redisService;
  12.         }
  13.         /**
  14.          * 初始化密钥(全局唯一)
  15.          */
  16.         @PostConstruct
  17.         public void initKeyPair() throws Exception {
  18.                 // 使用 SETNX 原子操作确保只有一个服务生成密钥
  19.                 Boolean isAbsent = redisService.setIfAbsent(PUBLIC_KEY, "");
  20.                 if (Boolean.TRUE.equals(isAbsent)) {
  21.                         KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
  22.                         generator.initialize(2048);
  23.                         KeyPair keyPair = generator.generateKeyPair();
  24.                         // 存储密钥
  25.                         redisService.set(PUBLIC_KEY,
  26.                                         Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded())
  27.                         );
  28.                         redisService.set(PRIVATE_KEY,
  29.                                         Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded())
  30.                         );
  31.                         log.info("---------------------------初始化RSA秘钥---------------------------");
  32.                 }
  33.         }
  34.         /**
  35.          * 获取公钥
  36.          */
  37.         public String getPublicKey() {
  38.                 Object publicKey = redisService.get(PUBLIC_KEY);
  39.                 return Objects.isNull(publicKey)?null:publicKey.toString();
  40.         }
  41.         /**
  42.          * 获取私钥
  43.          */
  44.         public String getPrivateKey() {
  45.                 Object privateKey = redisService.get(PRIVATE_KEY);
  46.                 return Objects.isNull(privateKey)?null:privateKey.toString();
  47.         }
  48. }
复制代码


  • 新增DecryptedHttpInputMessage
  1. public class DecryptedHttpInputMessage implements HttpInputMessage {
  2.         private final InputStream body;
  3.         private final HttpHeaders headers;
  4.         public DecryptedHttpInputMessage(InputStream body, HttpHeaders headers) {
  5.                 this.body = body;
  6.                 this.headers = headers;
  7.         }
  8.         @Override
  9.         public InputStream getBody() throws IOException {
  10.                 return this.body;
  11.         }
  12.         @Override
  13.         public HttpHeaders getHeaders() {
  14.                 return this.headers;
  15.         }
  16. }
复制代码


  • 新增获取公钥接口 
  1. @RestController
  2. @RequestMapping("/rsa")
  3. public class RSAController {
  4.         @Autowired
  5.         private RsaKeyManager rsaKeyManager;
  6.         /**
  7.          * 获取公钥
  8.          * @return 结果
  9.          */
  10.         @GetMapping("/publicKey")
  11.         public R<String> getPublicKey() {
  12.                 String publicKey = rsaKeyManager.getPublicKey();
  13.                 return R.ok(publicKey);
  14.         }
  15. }
复制代码
 二、前端实现(Vue2)



  • 安装新依赖:
  1. npm install crypto-js
复制代码


  • 加密工具(src/utils/crypto.js): 
        getPublicKey 为哀求公钥的接口,须要按照自己哀求方式去获取 
  1. import JSEncrypt from 'jsencrypt'
  2. import CryptoJS from 'crypto-js'
  3. import { getPublicKey } from '../request/api/auth'
  4. // 初始化公钥
  5. export async function initPublicKey() {
  6.     try {
  7.         const res = await getPublicKey()
  8.         return formatPublicKey(res.data)
  9.     } catch (error) {
  10.         console.error('公钥获取失败:', error)
  11.         throw new Error('安全模块初始化失败')
  12.     }
  13. }
  14. // 生成AES密钥
  15. export function generateAesKey() {
  16.     const key = CryptoJS.lib.WordArray.random(32)
  17.     const iv = CryptoJS.lib.WordArray.random(16)
  18.     return {
  19.         key: CryptoJS.enc.Base64.stringify(key),
  20.         iv: CryptoJS.enc.Base64.stringify(iv)
  21.     }
  22. }
  23. // AES加密
  24. export function aesEncrypt(data, key, iv) {
  25.     const encrypted = CryptoJS.AES.encrypt(
  26.         JSON.stringify(data),
  27.         CryptoJS.enc.Base64.parse(key),
  28.         {
  29.             iv: CryptoJS.enc.Base64.parse(iv),
  30.             mode: CryptoJS.mode.CBC,
  31.             padding: CryptoJS.pad.Pkcs7
  32.         }
  33.     )
  34.     return encrypted.toString()
  35. }
  36. // 格式化公钥
  37. function formatPublicKey(rawKey) {
  38.     return `-----BEGIN PUBLIC KEY-----\n${wrapKey(rawKey)}\n-----END PUBLIC KEY-----`
  39. }
  40. // 每64字符换行
  41. function wrapKey(key) {
  42.     return key.match(/.{1,64}/g).join('\n')
  43. }
复制代码


  • 修改哀求拦截器: 
  1. service.interceptors.request.use(async config => {
  2.     if (config.needEncrypt) {
  3.         await initPublicKey(service)
  4.         
  5.         // 生成AES密钥
  6.         const aes = generateAesKey()
  7.         
  8.         // 加密数据
  9.         const encryptedData = aesEncrypt(config.data, aes.key, aes.iv)
  10.         
  11.         // 加密AES密钥
  12.         const encryptor = new JSEncrypt()
  13.         encryptor.setPublicKey(publicKey)
  14.         const encryptedKey = encryptor.encrypt(aes.key)
  15.         
  16.         // 构造请求体
  17.         config.data = {
  18.             encryptedKey: encryptedKey,
  19.             encryptedData: encryptedData,
  20.             iv: aes.iv
  21.         }
  22.     }
  23.     return config
  24. })
复制代码
三、补充



  • 后端须要加密的接口示例 
  1. @PostMapping("/secure-data")
  2. @NeedDecrypt
  3. public String handleSecureData(@RequestBody Map<String, Object> decryptedData) {
  4.     return "Decrypted data: " + decryptedData.toString();
  5. }
复制代码


  • 哀求布局体 
  1. {
  2.     "encryptedKey": "RSA加密后的AES密钥",
  3.     "encryptedData": "AES加密后的数据",
  4.     "iv": "Base64编码的IV"
  5. }
复制代码
 1.增强安全措施



  • 密钥时效性
  1. // 前端每次请求生成新密钥
  2. const aes = generateAesKey()
复制代码


  • 完整性校验
  1. // 后端解密后可添加HMAC校验
  2. String hmac = json.getString("hmac");
  3. if(!verifyHMAC(decryptedData, hmac, aesKey)) {
  4.     throw new SecurityException("Data tampered");
  5. }
复制代码


  • 防御重放攻击
  1. // 前端添加时间戳和随机数
  2. config.data.timestamp = Date.now()
  3. config.data.nonce = Math.random().toString(36).substr(2)
复制代码
 四、最后阐明

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

  • 性能提升:AES加密大数据效率比RSA高1000倍以上
  • 前向安全性:每次哀求利用差别AES密钥
  • 安全性增强:CBC模式+随机IV避免模式分析攻击
实际部署时需注意:

  • 利用HTTPS传输加密后的数据
  • 定期轮换RSA密钥对
  • 对敏感接口添加频率限制
  • 在网关层实现解密拦截器(而非应用层)

 
 
 
 
 
 
 
   
 
 
 
 
 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

愛在花開的季節

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表