IT评测·应用市场-qidao123.com

标题: Spring Boot + Vue 基于RSA+AES的肴杂加密 [打印本页]

作者: 愛在花開的季節    时间: 2025-3-14 19:07
标题: Spring Boot + Vue 基于RSA+AES的肴杂加密
目录
一、后端实现
 二、前端实现(Vue2)
三、补充
 1.增强安全措施
 四、最后阐明


步调大致如下:
        须要确保每个步调都精确实现,特殊是加密模式、填充方式以及编码解码的同等性,避免因设置差别导致解密失败。有什么没参加的在批评区艾特我,我进行补充
一、后端实现


  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. }
复制代码

  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
复制代码

        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加密有以下优势:
实际部署时需注意:

 
 
 
 
 
 
 
   
 
 
 
 
 

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




欢迎光临 IT评测·应用市场-qidao123.com (https://dis.qidao123.com/) Powered by Discuz! X3.4