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. 加密工具类

java 复制代码
@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. 加密请求包装器

java 复制代码
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. 解密拦截器

java 复制代码
@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. 自定义参数解析器

java 复制代码
@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. 注解定义

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

6. 控制器使用示例

java 复制代码
@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. 密钥动态管理

java 复制代码
@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. 请求签名验证

java 复制代码
@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. 批量数据处理

java 复制代码
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. 缓存优化

java 复制代码
@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. 线程池优化

java 复制代码
@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. 密钥安全存储

java 复制代码
@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. 选择性加密

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

2. 错误处理

java 复制代码
@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. 用户体验:自动解密,业务代码无感知

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

相关推荐
瑞雪兆丰年兮1 小时前
[从0开始学Java|第一天]Java入门
java·开发语言
崎岖Qiu1 小时前
SpringBoot:基于注解 @PostConstruct 和 ApplicationRunner 进行初始化的区别
java·spring boot·后端·spring·javaee
沈雅馨1 小时前
SQL语言的云计算
开发语言·后端·golang
东东最爱敲键盘2 小时前
第7天 进程间通信
java·服务器·前端
九皇叔叔2 小时前
【04】SpringBoot3 MybatisPlus 查询(Mapper)
java·mybatis·mybatis plus
人道领域2 小时前
javaWeb从入门到进阶(SpringBoot基础案例)
java·开发语言·spring
u0104058362 小时前
利用Java CompletableFuture优化企业微信批量消息发送的异步编排
java·开发语言·企业微信
yangminlei2 小时前
SpringSecurity核心源码剖析+jwt+OAuth(一):SpringSecurity的初次邂逅(概念、认证、授权)
java·开发语言·python
小张快跑。2 小时前
【SpringBoot进阶指南(一)】SpringBoot整合MyBatis实战、Bean管理、自动配置原理、自定义starter
java·开发语言·spring boot