SpringBoot 实现 RSA+AES 自动接口解密
引言:接口数据安全的重要性
用户注册时,密码明文传输被拦截?支付信息在传输过程中被窃取?或者敏感数据在接口调用时被中间人攻击?再或者App被反编译,接口参数被破解?
这就是接口数据安全的经典难题。传统的明文传输方式已经无法满足现代应用的安全需求。今天我们就来聊聊如何用SpringBoot实现RSA+AES混合加密,让你的接口数据传输更安全。
为什么需要接口加密?
先说说为什么需要接口加密。
想象一下,你是一家金融公司的后端工程师。用户在App上进行转账操作,传输的银行卡号、转账金额、支付密码等敏感信息如果明文传输,一旦被拦截,后果不堪设想。
接口加密的必要性:
- 数据安全:防止敏感信息泄露
- 身份认证:防止接口被恶意调用
- 防篡改:确保数据传输的完整性
- 合规要求:满足金融、医疗等行业安全规范
技术选型:为什么选择RSA+AES混合加密?
RSA:非对称加密,解决密钥分发问题
RSA是非对称加密算法,有公钥和私钥:
- 公钥加密,私钥解密:数据加密传输
- 私钥签名,公钥验签:数据完整性验证
- 密钥分发安全:公钥可以公开,私钥保密
AES:对称加密,解决性能问题
AES是对称加密算法,加密解密使用同一密钥:
- 加密速度快:适合大量数据加密
- 安全性高:256位密钥安全性足够
- 实现简单:算法成熟稳定
混合加密的优势
- 安全:RSA解决密钥分发问题
- 高效:AES解决性能问题
- 灵活:结合两种算法优势
系统架构设计
我们的加密体系主要包括以下几个模块:
- 密钥管理:RSA公私钥、AES密钥管理
- 加密流程:数据加密处理
- 解密流程:数据解密处理
- 自动处理:SpringBoot拦截器自动处理
- 安全验证:数据完整性校验
核心实现思路
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混合加密,我们可以构建一个安全的接口数据传输体系。关键在于:
- 合理架构:加密工具、拦截器、参数解析器的合理设计
- 性能考虑:AES加密大数据,RSA加密密钥
- 安全实践:密钥安全存储、定期轮换
- 用户体验:自动解密,业务代码无感知
记住,安全不是一成不变的,需要根据业务特点和安全威胁持续优化。掌握了这些技巧,你就能让接口数据传输更加安全可靠。
标题:SpringBoot 实现 RSA+AES 自动接口解密
作者:jiangyi
地址:http://jiangyi.space/articles/2026/01/02/1767336646634.html
0 评论