开发者

SpringBoot实现RSA+AES自动接口解密的实战指南

目录
  • 一、项目依赖与环境准备
    • 1.1 Maven依赖配置
    • 1.2 密钥生成与配置
  • 二、加密工具类实现
    • 2.1 RSA工具类
    • 2.2 AES工具类
  • 三、自定义注解与AOP切面
    • 3.1 自定义解密注解 @RequestRSA
    • 3.2 AOP切面实现自动解密
  • 四、客户端加密逻辑实现
    • 4.1 客户端加密流程
  • 五、服务器端接口示例
    • 5.1 带解密注解的接口
  • 六、安全优化与最佳实践
    • 6.1 密钥管理策略
    • 6.2 时间戳防重放攻击
    • 6.3 性能优化建议
  • 七、完整调用流程图解
    • 八、 构建高安全接口的核心要点
      • 九、 工具类与依赖说明

        在当今数据泄露频发的网络环境中,接口安全已成为开发者不可忽视的核心议题。RSA+AES混合加密方案因其安全性高、性能优越而被广泛采用:

        • RSA(非对称加密):解决密钥分发难题,适合加密小数据(如AES密钥)。
        • AES(对称加密):高效加密大量数据,适合传输业务参数。

        本文将深入SpringBoot框架,手把手演示如何通过自定义注解+拦截器实现接口的自动化加解密,并提供完整工具类、AOP切片代码及安全优化策略

        一、项目依赖与环境准备

        1.1 Maven依赖配置

        pom.XML中添加必要的依赖:

        <dependencies>
            <!-- SpringBoot Web支持 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
        
            <!-- jsON处理工具 -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.83</version>
            </dependency>
        
            <!-- 加密算法扩展(支持BCrypt等) -->
            <dependency>
                <groupId>org.bouncycastle</groupId>
                <artifactId>bcprov-jdk15on</artifactId>
                <version>1.70</version>
            </dependency>
        
            <!-- Lombok简化代码 -->
            <depenwww.devze.comdency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
        </dependencies>js
        

        1.2 密钥生成与配置

        RSA密钥对生成

        // 工具类:生成RSA密钥对
        public class KeyGenerator {
            public static void main(String[] args) throws Exception {
                KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
                keyGen.initialize(2048); // 2048位密钥长度
                KeyPair keyPair = keyGen.generateKeyPair();
        
                // 公钥(客户端使用)
                String publicKeyStr = Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded());
                System.out.println("Public Key:\n" + publicKeyStr);
        编程
                // 私钥(服务端使用)
                String privateKeyStr = Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded());
                System.out.println("Private Key:\n" + privateKeyStr);
            }
        }
        

        配置文件中存储密钥

        # application.yml
        encrypt:
          rsa:
            public-key: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..."
            private-key: "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC..."
        

        二、加密工具类实现

        2.1 RSA工具类

        import org.bouncycastle.jce.provider.BouncyCastleProvider;
        import Javax.crypto.Cipher;
        import java.security.*;
        import java.security.spec.PKCS8EncodedKeySpec;
        import java.security.spec.X509EncodedKeySpec;
        import java.util.Base64;
        
        public class RSAUtil {
            static {
                Security.addProvider(new BouncyCastleProvider()); // 添加BC支持
            }
        
            // 使用公钥加密
            public static String encrypt(String data, String publicKeyStr) throws Exception {
                PublicKey publicKey = getPublicKeyFromBase64(publicKeyStr);
                Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
                cipher.init(Cipher.ENCRYPT_MODE, publicKey);
                return Base64.getEncoder().encodeToString(cipher.doFinal(data.getBytes()));
            }
        
            // 使用私钥解密
            public static String decrypt(String encryptedData, String privateKeyStr) throws Exception {
                PrivateKey privateKey = getPrivateKeyFromBase64(privateKeyStr);
                Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
                cipher.init(Cipher.DECRYPT_MODE, privateKey);
                return new String(cipher.doFinal(Base64.getDecoder().decode(encryptedData)));
            }
        
            // 从Base64字符串生成公钥
            private static PublicKey getPublicKeyFromBase64(String keyStr) throws Exception {
                byte[] keyBytes = Base64.getDecoder().decode(keyStr);
                X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
                KeyFactory keyFactory = KeyFactory.getInstance("RSA");
                return keyFactory.generatePublic(spec);
            }
        
            // 从Base64字符串生成私钥
            private static PrivateKey getPrivateKeyFromBase64(String keyStr) throws Exception {
                byte[] keyBytes = Base64.getDecoder().decode(keyStr);
                PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
                KeyFactory keyFactory = KeyFactory.getInstance("RSA");
                return keyFactory.generatePrivate(spec);
            }
        }
        

        2.2 AES工具类

        import javax.crypto.Cipher;
        import javax.crypto.spec.IvParameterSpec;
        import javax.crypto.spec.SecretKeySpec;
        import java.nio.charset.StandardCharsets;
        import java.security.SecureRandom;
        import java.util.Base64;
        
        public class AESUtil {
            // AES加密模式:CBC + PKCS7Padding
            private static final String AES_MODE = "AES/CBC/PKCS7Padding";
            private static final int IV_LENGTH = 16; // 初始化向量长度
        
            // 生成随机AES密钥
            public static String generateAESKey() throws Exception {
                byte[] key = new byte[16]; // 128位密钥
                new SecureRandom().nextBytes(key);
                return Base64.getEncoder().encodeToString(ke编程y);
            }
        
            // AES加密
            public static String encrypt(String data, String aesKey) throws Exception {
                byte[] keyBytes = Base64.getDecoder().decode(aesKey);
                SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
                byte[] iv = new byte[IV_LENGTH];
                new SecureRandom().nextBytes(iv);
                IvParameterSpec ivSpec = new IvParameterSpec(iv);
        
                Cipher cipher = Cipher.getInstance(AES_MODE);
                cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
                byte[] encryptedBytes = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
        
                // 将IV和密文合并返回
                byte[] result = new byte[iv.length + encryptedBytes.length];
                System.arraycopy(iv, 0, result, 0, iv.length);
                System.arraycopy(encryptedBytes, 0, result, iv.length, encryptedBytes.length);
                return Base64.getEncoder().encodeToString(result);
            }
        
            // AES解密
            public static String decrypt(String encryptedData, String aesKey) throws Exception {
                byte[] data = Base64.getDecoder().decode(encryptedData);
                byte[] iv = new byte[IV_LENGTH];
                byte[] encryptedBytes = new byte[data.length - IV_LENGTH];
                System.arraycopy(data, 0, iv, 0, IV_LENGTH);
                System.arraycopy(data, IV_LENGTH, encryptedBytes, 0, encryptedBytes.length);
        
                SecretKeySpec keySpec = new SecretKeySpec(Base64.getDecoder().decode(aesKey), "AES");
                IvParameterSpec ivSpec = new IvParameterSpec(iv);
        
                Cipher cipher = Cipher.getInstjavascriptance(AES_MODE);
                cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
                return new String(cipher.doFinal(encryptedBytes), StandardCharsets.UTF_8);
            }
        }
        

        三、自定义注解与AOP切面

        3.1 自定义解密注解 @RequestRSA

        import java.lang.annotation.*;
        
        @Target({ElementType.TYPE, ElementType.METHOD})
        @Retention(RetentionPolicy.RUNTIME)
        @Documented
        public @interface RequestRSA {
            // 标记需要解密的接口
        }
        

        3.2 AOP切面实现自动解密

        import com.alibaba.fastjson.JSONObject;
        import org.ASPectj.lang.ProceedingJoinPoint;
        import org.aspectj.lang.annotation.Around;
        import org.aspectj.lang.annotation.Aspect;
        import org.aspectj.lang.reflect.MethodSignature;
        import org.springframework.beans.factory.annotation.Value;
        import org.springframework.core.annotation.Order;
        import org.springframework.stereotype.Component;
        import org.springframework.web.bind.annotation.RequestBody;
        
        import javax.servlet.http.HttpServletRequest;
        import java.io.BufferedReader;
        import java.io.InputStreamReader;
        import java.lang.reflect.Method;
        import java.lang.reflect.Parameter;
        
        /**
         * RSA+AES接口解密切面
         */
        @Aspect
        @Component
        @Order(1)
        public class RequestRSAAspect {
        
            @Value("${encrypt.rsa.private-key}")
            private String privateKey;
        
            /**
             * 拦截带有@RequestRSA注解的方法
             */
            @Around("@annotation(requestRSA) || @within(requestRSA)")
            public Object decryptRequest(ProceedingJoinPoint joinPoint, RequestRSA requestRSA) throws Throwable {
                // 获取请求体
                HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
                String body = getRequestBody(request);
        
                // 解析加密参数
                JSONObject encryptedJson = JSONObject.parseobject(body);
                String sym = encryptedJson.getString("sym"); // RSA加密的AES密钥
                String asy = encryptedJson.getString("asy"); // AES加密的业务数据
        
                // RSA解密获取AES密钥
                String aesKey = RSAUtil.decrypt(sym, privateKey);
        
                // AES解密业务数据
                String decryptedData = AESUtil.decrypt(asy, aesKey);
        
                // 将解密后的数据注入方法参数
                Object[] args = injectDecryptedData(joinPoint, decryptedData);
                return joinPoint.proceed(args);
            }
        
            /**
             * 读取请求体内容
             */
            private String getRequestBody(HttpServletRequest request) throws Exception {
                StringBuilder sb = new StringBuilder();
                BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()));
                String line;
                while ((line = reader.readLine()) != null) {
                    sb.append(line);
                }
                return sb.toString();
            }
        
            /**
             * 将解密后的数据注入方法参数
             */
            private Object[] injectDecryptedData(ProceedingJoinPoint joinPoint, String decryptedData) throws Exception {
                MethodSignature signature = (MethodSignature) joinPoint.getSignature();
                Method method = signature.getMethod();
                Parameter[] parameters = method.getParameters();
                Object[] args = joinPoint.getArgs();
        
                for (int i = 0; i < parameters.length; i++) {
                    if (parameters[i].isAnnotationPresent(RequestBody.class)) {
                        // 将解密后的JSON字符串反序列化为目标对象
                        args[i] = JSONObject.parseObject(decryptedData, parameters[i].getParameterizedType());
                    }
                }
                return args;
            }
        }
        

        四、客户端加密逻辑实现

        4.1 客户端加密流程

        public class ClientEncryption {
            public static void main(String[] args) throws Exception {
                // 1. 随机生成AES密钥和IV
                String aesKey = AESUtil.generateAESKey();
                String aesIV = AESUtil.generateAESKey(); // 实际应使用SecureRandom生成IV
        
                // 2. 构造请求参数
                JSONObject requestData = new JSONObject();
                requestData.put("username", "user123");
                requestData.put("password", "securePass");
        
                // 3. AES加密业务数据
                String asy = AESUtil.encrypt(requestData.toJSONString(), aesKey);
        
                // 4. 构造AES密钥信息
                JSONObject aesInfo = new JSONObject();
                aesInfo.put("key", aesKey);
                aesInfo.put("keyVI", aesIV);
                aesInfo.put("time", System.currentTimeMillis());
        
                // 5. RSA加密AES密钥信息
                String sym = RSAUtil.encrypt(aesInfo.toJSONString(), "服务器公钥");
        
                // 6. 构造最终请求体
                JSONObject finalRequest = new JSONObject();
                finalRequest.put("sym", sym);
                finalRequest.put("asy", asy);
        
                System.out.println("加密后请求体:\n" + finalRequest.toJSONString());
            }
        }
        

        五、服务器端接口示例

        5.1 带解密注解的接口

        @RestController
        @RequestMapping("/api")
        public class SecureController {
        
            @PostMapping("/login")
            @RequestRSA // 标记需要解密
            public Response login(@RequestBody LoginRequest request) {
                // 直接使用解密后的参数
                return new Response("登录成功", request.getUsername());
            }
        }
        
        // 请求参数类
        @Data
        public class LoginRequest {
            private String username;
            private String password;
        }
        

        六、安全优化与最佳实践

        6.1 密钥管理策略

        • 私钥存储:使用配置中心(如Nacos)或密钥管理服务(如AWS KMS)。
        • 公钥分发:通过HTTPS协议分发,避免中间人攻击。
        • 定期轮换:每30天更新一次RSA密钥对。

        6.2 时间戳防重放攻击

        在AOP切面中添加时间戳校验:

        private void validateTimestamp(JSONObject aesInfo) {
            long requestTime = aesInfo.getLong("time");
            long currentTime = System.currentTimeMillis();
            if (Math.abs(currentTime - requestTime) > 60000) { // 允许1分钟内
                throw new RuntimeException("请求超时,请重试");
            }
        }
        

        6.3 性能优化建议

        • 缓存公钥/私钥对象:避免重复解析Base64字符串。
        • 异步解密:对高并发接口采用线程池异步处理。
        • 压缩数据:对加密后的数据进行GZIP压缩减少传输体积。

        七、完整调用流程图解

        客户端流程:
        [生成AES密钥] → [AES加密数据] → [构造AES信息] → [RSA加密AES信息] → [发送请求]
        
        服务端流程:
        [接收请求] → [AOP切面拦截] → [RSA解密AES密钥] → [AES解密业务数据] → [注入参数执行接口]
        

        八、 构建高安全接口的核心要点

        模块关键点注意事项
        密钥管理RSA公私钥分离存储,AES密钥动态生成私钥需加密存储,定期轮换
        加密流程RSA加密AES密钥,AES加密业务数据确保IV随机性,避免重复使用
        AOP切面设计自动拦截、解密、参数注入处理异常情况(如密钥错误、时间戳过期)
        安全防护时间戳防重放、HTTPS传输、数据完整性校验避免硬编码密钥,使用配置中心

        九、 工具类与依赖说明

        • Bouncy Castle:提供扩展的加密算法支持(如PKCS7Padding)。
        • FastJSON:高效JSON序列化/反序列化。
        • Lombok:简化POJO类的编写(如@Data注解)。

        以上就是SpringBoot实现RSA+AES自动接口解密的实战指南的详细内容,更多关于SpringBoot RSA AES接口解密的资料请关注编程客栈(www.devze.com)其它相关文章!

        0

        上一篇:

        下一篇:

        精彩评论

        暂无评论...
        验证码 换一张
        取 消

        最新开发

        开发排行榜