在微服务架构盛行的今天,API接口安全已成为开发者必须直面的挑战。传统JWT方案虽然解决了无状态认证问题,但在敏感数据传输环节仍存在明显短板——一旦Token被截获,攻击者就能轻易获取原始数据。这正是为什么金融、医疗等领域普遍采用混合加密方案:用RSA保护密钥交换过程,用AES保障大数据量传输效率。
当我们评估一个加密方案时,需要从三个维度进行权衡:安全性、性能和实现复杂度。单纯使用RSA加密时,虽然非对称特性保证了传输安全,但加密过程需要执行模幂运算,实测加密1KB数据需要50ms以上。而AES-256加密同样数据仅需0.3ms,但密钥分发成为安全隐患。
典型场景对比表:
| 场景 | 纯RSA方案 | 纯AES方案 | 混合方案 |
|---|---|---|---|
| 密钥交换安全性 | ★★★★★ | ★☆☆☆☆ | ★★★★★ |
| 大数据加密性能 | ★☆☆☆☆ | ★★★★★ | ★★★★★ |
| 防重放攻击 | ★★☆☆☆ | ★☆☆☆☆ | ★★★★☆ |
| 实现复杂度 | ★★☆☆☆ | ★☆☆☆☆ | ★★★☆☆ |
实际测试数据显示,对1MB数据加密时:
提示:选择密钥长度时要注意平衡安全与性能,RSA-2048和AES-256目前被认为是安全与性能的最佳平衡点
在pom.xml中添加必要的依赖:
xml复制<dependencies>
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 加密工具包 -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.70</version>
</dependency>
<!-- 编解码支持 -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
</dependency>
</dependencies>
推荐采用分层密钥管理架构:
生成RSA密钥对的实用方法:
java复制public static KeyPair generateRSAKeyPair() throws NoSuchAlgorithmException {
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(2048, new SecureRandom());
return generator.generateKeyPair();
}
java复制public class CryptoUtils {
private static final String AES_ALGORITHM = "AES/CBC/PKCS5Padding";
private static final String RSA_ALGORITHM = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding";
// AES加密
public static String encryptAES(String data, String key) throws Exception {
byte[] keyBytes = Base64.decodeBase64(key);
SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] iv = cipher.getIV();
byte[] encrypted = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
return Base64.encodeBase64String(iv) + ":" + Base64.encodeBase64String(encrypted);
}
// RSA加密AES密钥
public static String encryptRSA(String data, PublicKey publicKey) throws Exception {
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return Base64.encodeBase64String(cipher.doFinal(data.getBytes(StandardCharsets.UTF_8)));
}
}
java复制public class CryptoUtils {
// ...其他方法
// AES解密
public static String decryptAES(String encryptedData, String key) throws Exception {
String[] parts = encryptedData.split(":");
byte[] iv = Base64.decodeBase64(parts[0]);
byte[] encrypted = Base64.decodeBase64(parts[1]);
byte[] keyBytes = Base64.decodeBase64(key);
SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
return new String(cipher.doFinal(encrypted), StandardCharsets.UTF_8);
}
// RSA解密AES密钥
public static String decryptRSA(String encryptedData, PrivateKey privateKey) throws Exception {
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return new String(cipher.doFinal(Base64.decodeBase64(encryptedData)), StandardCharsets.UTF_8);
}
}
java复制@RestController
@RequestMapping("/api/secure")
public class CryptoController {
@PostMapping("/encrypt")
public ResponseEntity<EncryptedData> encryptData(@RequestBody PlainData data) throws Exception {
// 1. 生成随机AES会话密钥
String sessionKey = generateAESKey();
// 2. 使用AES加密业务数据
String encryptedData = CryptoUtils.encryptAES(data.getContent(), sessionKey);
// 3. 使用RSA公钥加密会话密钥
String encryptedKey = CryptoUtils.encryptRSA(sessionKey, getRSAPublicKey());
return ResponseEntity.ok(new EncryptedData(encryptedData, encryptedKey));
}
private String generateAESKey() throws NoSuchAlgorithmException {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256);
return Base64.encodeBase64String(keyGen.generateKey().getEncoded());
}
}
java复制@RestController
@RequestMapping("/api/secure")
public class CryptoController {
@PostMapping("/decrypt")
public ResponseEntity<PlainData> decryptData(@RequestBody EncryptedData data) throws Exception {
// 1. 使用RSA私钥解密会话密钥
String sessionKey = CryptoUtils.decryptRSA(data.getEncryptedKey(), getRSAPrivateKey());
// 2. 使用AES密钥解密业务数据
String decryptedData = CryptoUtils.decryptAES(data.getEncryptedData(), sessionKey);
return ResponseEntity.ok(new PlainData(decryptedData));
}
}
在实际项目落地时,我们总结出几个关键优化点:
典型性能优化配置示例:
properties复制# application.properties
crypto.aes.key-size=256
crypto.rsa.key-size=2048
crypto.session.timeout=300000 # 5分钟会话有效期
在网关层实现的一个典型拦截逻辑:
java复制@Component
public class CryptoInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String encryptedKey = request.getHeader("X-Encrypted-Key");
String signature = request.getHeader("X-Signature");
// 1. 验证签名
if(!verifySignature(encryptedKey, signature)) {
response.sendError(401, "Invalid signature");
return false;
}
// 2. 解密会话密钥
String sessionKey = decryptSessionKey(encryptedKey);
// 3. 将会话密钥存入请求上下文
request.setAttribute("SESSION_KEY", sessionKey);
return true;
}
}
经过多个生产项目验证,这套方案在保证安全性的同时,将加密性能损耗控制在3%以内。特别是在医疗影像传输等大数据量场景下,相比纯RSA方案性能提升达300倍以上