利用SpringBoot實(shí)現(xiàn)RSA+AES混合加密
引言:接口數(shù)據(jù)安全的重要性
用戶(hù)注冊(cè)時(shí),密碼明文傳輸被攔截?支付信息在傳輸過(guò)程中被竊???或者敏感數(shù)據(jù)在接口調(diào)用時(shí)被中間人攻擊?再或者App被反編譯,接口參數(shù)被破解?
這就是接口數(shù)據(jù)安全的經(jīng)典難題。傳統(tǒng)的明文傳輸方式已經(jīng)無(wú)法滿(mǎn)足現(xiàn)代應(yīng)用的安全需求。今天我們就來(lái)聊聊如何用SpringBoot實(shí)現(xiàn)RSA+AES混合加密,讓你的接口數(shù)據(jù)傳輸更安全。
為什么需要接口加密?
先說(shuō)說(shuō)為什么需要接口加密。
想象一下,你是一家金融公司的后端工程師。用戶(hù)在App上進(jìn)行轉(zhuǎn)賬操作,傳輸?shù)你y行卡號(hào)、轉(zhuǎn)賬金額、支付密碼等敏感信息如果明文傳輸,一旦被攔截,后果不堪設(shè)想。
接口加密的必要性:
- 數(shù)據(jù)安全:防止敏感信息泄露
- 身份認(rèn)證:防止接口被惡意調(diào)用
- 防篡改:確保數(shù)據(jù)傳輸?shù)耐暾?/li>
- 合規(guī)要求:滿(mǎn)足金融、醫(yī)療等行業(yè)安全規(guī)范
技術(shù)選型:為什么選擇RSA+AES混合加密?
RSA:非對(duì)稱(chēng)加密,解決密鑰分發(fā)問(wèn)題
RSA是非對(duì)稱(chēng)加密算法,有公鑰和私鑰:
- 公鑰加密,私鑰解密:數(shù)據(jù)加密傳輸
- 私鑰簽名,公鑰驗(yàn)簽:數(shù)據(jù)完整性驗(yàn)證
- 密鑰分發(fā)安全:公鑰可以公開(kāi),私鑰保密
AES:對(duì)稱(chēng)加密,解決性能問(wèn)題
AES是對(duì)稱(chēng)加密算法,加密解密使用同一密鑰:
- 加密速度快:適合大量數(shù)據(jù)加密
- 安全性高:256位密鑰安全性足夠
- 實(shí)現(xiàn)簡(jiǎn)單:算法成熟穩(wěn)定
混合加密的優(yōu)勢(shì)
- 安全:RSA解決密鑰分發(fā)問(wèn)題
- 高效:AES解決性能問(wèn)題
- 靈活:結(jié)合兩種算法優(yōu)勢(shì)
系統(tǒng)架構(gòu)設(shè)計(jì)
我們的加密體系主要包括以下幾個(gè)模塊:
- 密鑰管理:RSA公私鑰、AES密鑰管理
- 加密流程:數(shù)據(jù)加密處理
- 解密流程:數(shù)據(jù)解密處理
- 自動(dòng)處理:SpringBoot攔截器自動(dòng)處理
- 安全驗(yàn)證:數(shù)據(jù)完整性校驗(yàn)
核心實(shí)現(xiàn)思路
1. 加密工具類(lèi)
@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加密數(shù)據(jù)
*/
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()); // 實(shí)際項(xiàng)目中應(yīng)使用隨機(jī)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解密數(shù)據(jù)
*/
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. 加密請(qǐng)求包裝器
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 {
// 解密請(qǐng)求體
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) {
// 不需要實(shí)現(xiàn)
}
@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 {
// 假設(shè)請(qǐng)求體格式為: {"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());
// 解密數(shù)據(jù)
return EncryptionUtil.decryptData(encryptedData, aesKey);
} catch (Exception e) {
throw new RuntimeException("解密失敗", e);
}
}
private String getPrivateKey() {
// 從配置或數(shù)據(jù)庫(kù)獲取私鑰
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)) {
// 包裝請(qǐng)求,實(shí)現(xiàn)自動(dòng)解密
EncryptedRequestWrapper wrappedRequest = new EncryptedRequestWrapper(request);
// 將包裝后的請(qǐng)求傳遞給后續(xù)處理
RequestContextHolder.getRequestAttributes()
.setAttribute("wrappedRequest", wrappedRequest, RequestAttributes.SCOPE_REQUEST);
}
return true;
}
private boolean isEncryptedRequest(HttpServletRequest request) {
// 檢查請(qǐng)求頭或參數(shù),判斷是否為加密請(qǐng)求
String encryptedHeader = request.getHeader("X-Encrypted");
return "true".equalsIgnoreCase(encryptedHeader);
}
}
4. 自定義參數(shù)解析器
@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) {
// 解密參數(shù)
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("參數(shù)解密失敗", e);
}
}
private String getAesKey() {
// 獲取AES密鑰
return System.getProperty("aes.key");
}
private Object convertValue(String value, Class<?> targetType) {
// 類(lèi)型轉(zhuǎn)換邏輯
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) {
// 業(yè)務(wù)邏輯
return ResponseEntity.ok("注冊(cè)成功");
}
@PostMapping("/user/login")
public ResponseEntity<String> login(
@EncryptedParam("username") String username,
@EncryptedParam("password") String password) {
// 業(yè)務(wù)邏輯
return ResponseEntity.ok("登錄成功");
}
}
高級(jí)特性實(shí)現(xiàn)
1. 密鑰動(dòng)態(tài)管理
@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);
}
/**
* 獲取或創(chuàng)建AES密鑰
*/
public String getOrCreateAesKey(String userId) {
String cacheKey = "aes_key:" + userId;
String cachedKey = redisTemplate.opsForValue().get(cacheKey);
if (cachedKey == null) {
cachedKey = generateAesKey();
// 設(shè)置過(guò)期時(shí)間,定期更新密鑰
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. 請(qǐng)求簽名驗(yàn)證
@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. 批量數(shù)據(jù)處理
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());
}
}
性能優(yōu)化建議
1. 緩存優(yōu)化
@Component
public class EncryptedCacheManager {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public void cacheEncryptedData(String key, String encryptedData) {
// 緩存加密后的數(shù)據(jù)
redisTemplate.opsForValue().set("encrypted:" + key, encryptedData, Duration.ofMinutes(30));
}
public String getCachedEncryptedData(String key) {
return (String) redisTemplate.opsForValue().get("encrypted:" + key);
}
}
2. 線(xiàn)程池優(yōu)化
@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. 密鑰安全存儲(chǔ)
@Component
public class SecureKeyStore {
/**
* 從環(huán)境變量或配置中心獲取密鑰
*/
public String getPrivateKey() {
String key = System.getenv("RSA_PRIVATE_KEY");
if (key == null) {
// 從配置中心獲取
key = configService.getRsaPrivateKey();
}
return key;
}
}
2. 傳輸安全
- 使用HTTPS協(xié)議
- 定期輪換密鑰
- 限制密鑰有效期
- 監(jiān)控異常訪(fǎng)問(wèn)
最佳實(shí)踐
1. 選擇性加密
public class EncryptionPolicy {
public static boolean shouldEncrypt(String fieldName) {
// 只對(duì)敏感字段進(jìn)行加密
Set<String> sensitiveFields = Set.of("password", "idCard", "phone", "email");
return sensitiveFields.contains(fieldName.toLowerCase());
}
}
2. 錯(cuò)誤處理
@ControllerAdvice
public class EncryptionExceptionHandler {
@ExceptionHandler(EncryptionException.class)
public ResponseEntity<String> handleEncryptionError(EncryptionException e) {
log.error("加密解密異常", e);
return ResponseEntity.status(400).body("數(shù)據(jù)格式錯(cuò)誤");
}
}
總結(jié)
通過(guò)SpringBoot實(shí)現(xiàn)RSA+AES混合加密,我們可以構(gòu)建一個(gè)安全的接口數(shù)據(jù)傳輸體系。關(guān)鍵在于:
- 合理架構(gòu):加密工具、攔截器、參數(shù)解析器的合理設(shè)計(jì)
- 性能考慮:AES加密大數(shù)據(jù),RSA加密密鑰
- 安全實(shí)踐:密鑰安全存儲(chǔ)、定期輪換
- 用戶(hù)體驗(yàn):自動(dòng)解密,業(yè)務(wù)代碼無(wú)感知
記住,安全不是一成不變的,需要根據(jù)業(yè)務(wù)特點(diǎn)和安全威脅持續(xù)優(yōu)化。掌握了這些技巧,你就能讓接口數(shù)據(jù)傳輸更加安全可靠。
以上就是利用SpringBoot實(shí)現(xiàn)RSA+AES混合加密的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot RSA+AES混合加密的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Spring?Boot集成validation實(shí)現(xiàn)參數(shù)校驗(yàn)功能
Bean?Validation?是一個(gè)運(yùn)行時(shí)的數(shù)據(jù)驗(yàn)證框架,在驗(yàn)證之后驗(yàn)證的錯(cuò)誤信息會(huì)被馬上返回,這篇文章主要介紹了Spring?Boot集成validation實(shí)現(xiàn)參數(shù)校驗(yàn)功能,需要的朋友可以參考下2024-05-05
基于JavaSwing設(shè)計(jì)和實(shí)現(xiàn)的酒店管理系統(tǒng)
這篇文章主要介紹了基于JavaSwing+mysql的酒店管理系統(tǒng)設(shè)計(jì)和實(shí)現(xiàn),它可以實(shí)現(xiàn)酒店日常的管理功能包括開(kāi)房、退房、房間信息、顧客信息管理等2021-08-08
基于Spark實(shí)現(xiàn)隨機(jī)森林代碼
這篇文章主要為大家詳細(xì)介紹了基于Spark實(shí)現(xiàn)隨機(jī)森林代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-08-08
Spring Boot Admin監(jiān)控服務(wù)如何使用
這篇文章主要介紹了Spring Boot Admin監(jiān)控服務(wù)如何使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04
SpringSecurity request過(guò)濾問(wèn)題示例小結(jié)
這篇文章主要介紹了SpringSecurity request過(guò)濾問(wèn)題示例小結(jié),本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2024-02-02
五種SpringBoot實(shí)現(xiàn)數(shù)據(jù)加密存儲(chǔ)的方式總結(jié)
這篇文章主要為大家詳細(xì)介紹了五種常見(jiàn)數(shù)據(jù)加密存儲(chǔ)的方法(結(jié)合SpringBoot和MyBatisPlus框架進(jìn)行實(shí)現(xiàn)),文中的示例代碼講解詳細(xì),需要的可以參考下2023-11-11
Java HashMap源碼及并發(fā)環(huán)境常見(jiàn)問(wèn)題解決
這篇文章主要介紹了Java HashMap源碼及并發(fā)環(huán)境常見(jiàn)問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09

