SpringBoot 实现 RSA+AES 自动接口解密

引言:接口数据安全的重要性

用户注册时,密码明文传输被拦截?支付信息在传输过程中被窃取?或者敏感数据在接口调用时被中间人攻击?再或者App被反编译,接口参数被破解?

这就是接口数据安全的经典难题。传统的明文传输方式已经无法满足现代应用的安全需求。今天我们就来聊聊如何用SpringBoot实现RSA+AES混合加密,让你的接口数据传输更安全。

为什么需要接口加密?

先说说为什么需要接口加密。

想象一下,你是一家金融公司的后端工程师。用户在App上进行转账操作,传输的银行卡号、转账金额、支付密码等敏感信息如果明文传输,一旦被拦截,后果不堪设想。

接口加密的必要性:

  • 数据安全:防止敏感信息泄露
  • 身份认证:防止接口被恶意调用
  • 防篡改:确保数据传输的完整性
  • 合规要求:满足金融、医疗等行业安全规范

技术选型:为什么选择RSA+AES混合加密?

RSA:非对称加密,解决密钥分发问题

RSA是非对称加密算法,有公钥和私钥:

  • 公钥加密,私钥解密:数据加密传输
  • 私钥签名,公钥验签:数据完整性验证
  • 密钥分发安全:公钥可以公开,私钥保密

AES:对称加密,解决性能问题

AES是对称加密算法,加密解密使用同一密钥:

  • 加密速度快:适合大量数据加密
  • 安全性高:256位密钥安全性足够
  • 实现简单:算法成熟稳定

混合加密的优势

  • 安全:RSA解决密钥分发问题
  • 高效:AES解决性能问题
  • 灵活:结合两种算法优势

系统架构设计

我们的加密体系主要包括以下几个模块:

  1. 密钥管理:RSA公私钥、AES密钥管理
  2. 加密流程:数据加密处理
  3. 解密流程:数据解密处理
  4. 自动处理:SpringBoot拦截器自动处理
  5. 安全验证:数据完整性校验

核心实现思路

1. 加密工具类

@Component
public class EncryptionUtil {
    
    /**
     * RSA加密AES密钥
     */
    public static String encryptAesKey(String aesKey, String rsaPublicKey) throws Exception {
        PublicKey publicKey = getPublicKey(rsaPublicKey);
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        
        byte[] encrypted = cipher.doFinal(aesKey.getBytes());
        return Base64.getEncoder().encodeToString(encrypted);
    }
    
    /**
     * AES加密数据
     */
    public static String encryptData(String data, String aesKey) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKeySpec keySpec = new SecretKeySpec(aesKey.getBytes(), "AES");
        IvParameterSpec iv = new IvParameterSpec("0000000000000000".getBytes()); // 实际项目中应使用随机IV
        
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
        byte[] encrypted = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(encrypted);
    }
    
    /**
     * RSA解密AES密钥
     */
    public static String decryptAesKey(String encryptedAesKey, String rsaPrivateKey) throws Exception {
        PrivateKey privateKey = getPrivateKey(rsaPrivateKey);
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        
        byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(encryptedAesKey));
        return new String(decrypted);
    }
    
    /**
     * AES解密数据
     */
    public static String decryptData(String encryptedData, String aesKey) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKeySpec keySpec = new SecretKeySpec(aesKey.getBytes(), "AES");
        IvParameterSpec iv = new IvParameterSpec("0000000000000000".getBytes());
        
        cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
        byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(encryptedData));
        return new String(decrypted, StandardCharsets.UTF_8);
    }
    
    private static PublicKey getPublicKey(String key) throws Exception {
        byte[] keyBytes = Base64.getDecoder().decode(key);
        X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePublic(spec);
    }
    
    private static PrivateKey getPrivateKey(String key) throws Exception {
        byte[] keyBytes = Base64.getDecoder().decode(key);
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePrivate(spec);
    }
}

2. 加密请求包装器

public class EncryptedRequestWrapper extends HttpServletRequestWrapper {
    
    private final String body;
    
    public EncryptedRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        body = getBody(request);
    }
    
    private String getBody(HttpServletRequest request) throws IOException {
        StringBuilder stringBuilder = new StringBuilder();
        try (BufferedReader reader = request.getReader()) {
            String line;
            while ((line = reader.readLine()) != null) {
                stringBuilder.append(line);
            }
        }
        return stringBuilder.toString();
    }
    
    @Override
    public ServletInputStream getInputStream() throws IOException {
        // 解密请求体
        String decryptedBody = decryptRequestBody(body);
        ByteArrayInputStream byteArrayInputStream = 
            new ByteArrayInputStream(decryptedBody.getBytes(StandardCharsets.UTF_8));
        
        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return byteArrayInputStream.available() == 0;
            }
            
            @Override
            public boolean isReady() {
                return true;
            }
            
            @Override
            public void setReadListener(ReadListener readListener) {
                // 不需要实现
            }
            
            @Override
            public int read() throws IOException {
                return byteArrayInputStream.read();
            }
        };
    }
    
    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }
    
    private String decryptRequestBody(String encryptedBody) {
        try {
            // 假设请求体格式为: {"encryptedData": "xxx", "encryptedKey": "xxx"}
            JSONObject jsonObject = JSON.parseObject(encryptedBody);
            String encryptedData = jsonObject.getString("encryptedData");
            String encryptedKey = jsonObject.getString("encryptedKey");
            
            // 解密AES密钥
            String aesKey = EncryptionUtil.decryptAesKey(encryptedKey, getPrivateKey());
            
            // 解密数据
            return EncryptionUtil.decryptData(encryptedData, aesKey);
        } catch (Exception e) {
            throw new RuntimeException("解密失败", e);
        }
    }
    
    private String getPrivateKey() {
        // 从配置或数据库获取私钥
        return System.getProperty("rsa.private.key");
    }
}

3. 解密拦截器

@Component
public class DecryptionInterceptor implements HandlerInterceptor {
    
    @Override
    public boolean preHandle(HttpServletRequest request, 
                           HttpServletResponse response, 
                           Object handler) throws Exception {
        
        // 检查是否需要解密
        if (isEncryptedRequest(request)) {
            // 包装请求,实现自动解密
            EncryptedRequestWrapper wrappedRequest = new EncryptedRequestWrapper(request);
            // 将包装后的请求传递给后续处理
            RequestContextHolder.getRequestAttributes()
                .setAttribute("wrappedRequest", wrappedRequest, RequestAttributes.SCOPE_REQUEST);
        }
        
        return true;
    }
    
    private boolean isEncryptedRequest(HttpServletRequest request) {
        // 检查请求头或参数,判断是否为加密请求
        String encryptedHeader = request.getHeader("X-Encrypted");
        return "true".equalsIgnoreCase(encryptedHeader);
    }
}

4. 自定义参数解析器

@Component
public class EncryptedParameterResolver implements HandlerMethodArgumentResolver {
    
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(EncryptedParam.class);
    }
    
    @Override
    public Object resolveArgument(MethodParameter parameter, 
                                ModelAndViewContainer mavContainer,
                                NativeWebRequest webRequest, 
                                WebDataBinderFactory binderFactory) throws Exception {
        
        String encryptedValue = webRequest.getParameter(parameter.getParameterName());
        if (encryptedValue != null) {
            // 解密参数
            String decryptedValue = decryptParameter(encryptedValue);
            return convertValue(decryptedValue, parameter.getParameterType());
        }
        
        return null;
    }
    
    private String decryptParameter(String encryptedValue) {
        try {
            // 解密逻辑
            return EncryptionUtil.decryptData(encryptedValue, getAesKey());
        } catch (Exception e) {
            throw new RuntimeException("参数解密失败", e);
        }
    }
    
    private String getAesKey() {
        // 获取AES密钥
        return System.getProperty("aes.key");
    }
    
    private Object convertValue(String value, Class<?> targetType) {
        // 类型转换逻辑
        return value;
    }
}

5. 注解定义

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptedParam {
    String value() default "";
}

6. 控制器使用示例

@RestController
public class UserController {
    
    @PostMapping("/user/register")
    public ResponseEntity<String> register(
            @RequestBody @EncryptedBody UserRegisterRequest request) {
        // 业务逻辑
        return ResponseEntity.ok("注册成功");
    }
    
    @PostMapping("/user/login")
    public ResponseEntity<String> login(
            @EncryptedParam("username") String username,
            @EncryptedParam("password") String password) {
        // 业务逻辑
        return ResponseEntity.ok("登录成功");
    }
}

高级特性实现

1. 密钥动态管理

@Service
public class KeyManagementService {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    /**
     * 生成新的AES密钥
     */
    public String generateAesKey() {
        byte[] key = new byte[32]; // 256位
        new SecureRandom().nextBytes(key);
        return Base64.getEncoder().encodeToString(key);
    }
    
    /**
     * 获取或创建AES密钥
     */
    public String getOrCreateAesKey(String userId) {
        String cacheKey = "aes_key:" + userId;
        String cachedKey = redisTemplate.opsForValue().get(cacheKey);
        
        if (cachedKey == null) {
            cachedKey = generateAesKey();
            // 设置过期时间,定期更新密钥
            redisTemplate.opsForValue().set(cacheKey, cachedKey, Duration.ofHours(24));
        }
        
        return cachedKey;
    }
    
    /**
     * 轮换密钥
     */
    public void rotateKey(String userId) {
        String newKey = generateAesKey();
        String cacheKey = "aes_key:" + userId;
        redisTemplate.opsForValue().set(cacheKey, newKey, Duration.ofHours(24));
    }
}

2. 请求签名验证

@Component
public class SignatureValidator {
    
    public boolean validateSignature(String data, String signature, String publicKey) {
        try {
            Signature sign = Signature.getInstance("SHA256withRSA");
            sign.initVerify(EncryptionUtil.getPublicKey(publicKey));
            sign.update(data.getBytes(StandardCharsets.UTF_8));
            return sign.verify(Base64.getDecoder().decode(signature));
        } catch (Exception e) {
            return false;
        }
    }
}

3. 批量数据处理

public class BatchEncryptionProcessor {
    
    public List<String> encryptBatchData(List<String> dataList, String aesKey) {
        return dataList.parallelStream()
            .map(data -> {
                try {
                    return EncryptionUtil.encryptData(data, aesKey);
                } catch (Exception e) {
                    throw new RuntimeException("批量加密失败", e);
                }
            })
            .collect(Collectors.toList());
    }
}

性能优化建议

1. 缓存优化

@Component
public class EncryptedCacheManager {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    public void cacheEncryptedData(String key, String encryptedData) {
        // 缓存加密后的数据
        redisTemplate.opsForValue().set("encrypted:" + key, encryptedData, Duration.ofMinutes(30));
    }
    
    public String getCachedEncryptedData(String key) {
        return (String) redisTemplate.opsForValue().get("encrypted:" + key);
    }
}

2. 线程池优化

@Configuration
public class EncryptionThreadPoolConfig {
    
    @Bean("encryptionExecutor")
    public Executor encryptionExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(20);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("encryption-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

安全考虑

1. 密钥安全存储

@Component
public class SecureKeyStore {
    
    /**
     * 从环境变量或配置中心获取密钥
     */
    public String getPrivateKey() {
        String key = System.getenv("RSA_PRIVATE_KEY");
        if (key == null) {
            // 从配置中心获取
            key = configService.getRsaPrivateKey();
        }
        return key;
    }
}

2. 传输安全

  • 使用HTTPS协议
  • 定期轮换密钥
  • 限制密钥有效期
  • 监控异常访问

最佳实践

1. 选择性加密

public class EncryptionPolicy {
    
    public static boolean shouldEncrypt(String fieldName) {
        // 只对敏感字段进行加密
        Set<String> sensitiveFields = Set.of("password", "idCard", "phone", "email");
        return sensitiveFields.contains(fieldName.toLowerCase());
    }
}

2. 错误处理

@ControllerAdvice
public class EncryptionExceptionHandler {
    
    @ExceptionHandler(EncryptionException.class)
    public ResponseEntity<String> handleEncryptionError(EncryptionException e) {
        log.error("加密解密异常", e);
        return ResponseEntity.status(400).body("数据格式错误");
    }
}

总结

通过SpringBoot实现RSA+AES混合加密,我们可以构建一个安全的接口数据传输体系。关键在于:

  1. 合理架构:加密工具、拦截器、参数解析器的合理设计
  2. 性能考虑:AES加密大数据,RSA加密密钥
  3. 安全实践:密钥安全存储、定期轮换
  4. 用户体验:自动解密,业务代码无感知

记住,安全不是一成不变的,需要根据业务特点和安全威胁持续优化。掌握了这些技巧,你就能让接口数据传输更加安全可靠。


标题:SpringBoot 实现 RSA+AES 自动接口解密
作者:jiangyi
地址:http://jiangyi.space/articles/2026/01/02/1767336646634.html

    0 评论
avatar