強(qiáng)大加密!SpringBoot 實(shí)現(xiàn) RSA+AES 自動(dòng)解密,保障接口安全
在當(dāng)今的應(yīng)用開發(fā)中,保障接口的安全性變得尤為關(guān)鍵。尤其是當(dāng)敏感數(shù)據(jù)通過網(wǎng)絡(luò)傳輸時(shí),如何避免數(shù)據(jù)泄露或篡改,成為了開發(fā)者必須考慮的問題。本篇文章將詳細(xì)探討如何在 SpringBoot 3.4 框架下,利用 RSA 和 AES 混合加密方案,確保接口通信的安全性。
為什么需要接口加密?
在沒有加密的情況下,通過網(wǎng)絡(luò)傳輸?shù)臄?shù)據(jù)很容易被中間人或抓包工具截獲。尤其是當(dāng)數(shù)據(jù)中包含用戶隱私信息或支付數(shù)據(jù)時(shí),缺乏加密的傳輸方式會(huì)帶來巨大的安全隱患。通過對(duì)接口數(shù)據(jù)進(jìn)行加密,即使數(shù)據(jù)在傳輸過程中被截獲,黑客也無法解密和理解數(shù)據(jù)內(nèi)容,從而有效避免了數(shù)據(jù)泄漏的風(fēng)險(xiǎn)。
RSA+AES 混合加密方案的優(yōu)勢(shì)
選擇 RSA 和 AES 混合加密方案,主要是因?yàn)檫@兩種加密算法的結(jié)合,能夠平衡加密的安全性和性能:
- RSA 是一種非對(duì)稱加密算法,雖然加密安全性高,但加密速度較慢,適合用來加密較小的數(shù)據(jù),比如加密 AES 密鑰。
- AES 是一種對(duì)稱加密算法,速度較快,適用于大量數(shù)據(jù)的加密,但密鑰的分發(fā)和管理是一個(gè)挑戰(zhàn)。
通過結(jié)合這兩種算法,我們利用 RSA 加密 AES 的密鑰,再使用 AES 加密實(shí)際的數(shù)據(jù),從而實(shí)現(xiàn)了高安全性和高性能的平衡。
實(shí)現(xiàn)原理
- 客戶端和服務(wù)端預(yù)先約定好 RSA 公鑰和私鑰。
- 客戶端生成一個(gè)隨機(jī)的 AES 密鑰,并使用 RSA 公鑰加密這個(gè) AES 密鑰。
- 客戶端使用 AES 密鑰加密實(shí)際的數(shù)據(jù)。
- 客戶端將加密后的 AES 密鑰和加密的數(shù)據(jù)一并發(fā)送給服務(wù)端。
- 服務(wù)端使用 RSA 私鑰解密得到 AES 密鑰,然后用 AES 密鑰解密數(shù)據(jù)。
項(xiàng)目依賴
在 pom.xml
中,我們需要添加以下依賴,來支持加密解密功能:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.78</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.68</version>
</dependency>
</dependencies>
加密工具類
接下來,我們將實(shí)現(xiàn)一個(gè)加密工具類,用于處理 RSA 和 AES 加密解密邏輯。以下是該類的實(shí)現(xiàn)代碼:
package com.icoderoad.secureapi.utils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class EncryptionUtils {
static {
Security.addProvider(new BouncyCastleProvider());
}
private static final String AES_ALGORITHM = "AES/CBC/PKCS7Padding";
private static final int AES_KEY_SIZE = 256;
private static final String RSA_ALGORITHM = "RSA/ECB/PKCS1Padding";
private static final int RSA_KEY_SIZE = 2048;
public static KeyPair generateRSAKeyPair() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(RSA_KEY_SIZE);
return keyPairGenerator.generateKeyPair();
}
public static String keyToString(Key key) {
return Base64.getEncoder().encodeToString(key.getEncoded());
}
public static PublicKey stringToRSAPublicKey(String keyStr) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(keyStr);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(keySpec);
}
public static PrivateKey stringToRSAPrivateKey(String keyStr) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(keyStr);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePrivate(keySpec);
}
public static SecretKey generateAESKey() throws Exception {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(AES_KEY_SIZE);
return keyGen.generateKey();
}
public static SecretKey stringToAESKey(String keyStr) {
byte[] keyBytes = Base64.getDecoder().decode(keyStr);
return new SecretKeySpec(keyBytes, "AES");
}
public static String encryptWithRSA(String data, PublicKey publicKey) throws Exception {
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedBytes = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encryptedBytes);
}
public static String decryptWithRSA(String encryptedData, PrivateKey privateKey) throws Exception {
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData);
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return new String(decryptedBytes, StandardCharsets.UTF_8);
}
public static String encryptWithAES(String data, SecretKey secretKey, byte[] iv) throws Exception {
Cipher cipher = Cipher.getInstance(AES_ALGORITHM, "BC");
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
byte[] encryptedBytes = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encryptedBytes);
}
public static String decryptWithAES(String encryptedData, SecretKey secretKey, byte[] iv) throws Exception {
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData);
Cipher cipher = Cipher.getInstance(AES_ALGORITHM, "BC");
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return new String(decryptedBytes, StandardCharsets.UTF_8);
}
public static byte[] generateIV() {
SecureRandom random = new SecureRandom();
byte[] iv = new byte[16];
random.nextBytes(iv);
return iv;
}
}
請(qǐng)求包裝類與解密攔截器
為了便于加密請(qǐng)求的自動(dòng)解密,我們將創(chuàng)建一個(gè) EncryptedRequest
請(qǐng)求包裝類:
package com.icoderoad.secureapi.model;
import lombok.Data;
@Data
public class EncryptedRequest {
private String encryptedKey;
private String iv;
private String encryptedData;
private Long timestamp;
private String signature;
}
接著,創(chuàng)建一個(gè)解密攔截器,自動(dòng)在控制器處理請(qǐng)求前進(jìn)行解密:
package com.icoderoad.secureapi.utils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class EncryptionUtils {
static {
Security.addProvider(new BouncyCastleProvider());
}
private static final String AES_ALGORITHM = "AES/CBC/PKCS7Padding";
private static final int AES_KEY_SIZE = 256;
private static final String RSA_ALGORITHM = "RSA/ECB/PKCS1Padding";
private static final int RSA_KEY_SIZE = 2048;
public static KeyPair generateRSAKeyPair() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(RSA_KEY_SIZE);
return keyPairGenerator.generateKeyPair();
}
public static String keyToString(Key key) {
return Base64.getEncoder().encodeToString(key.getEncoded());
}
public static PublicKey stringToRSAPublicKey(String keyStr) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(keyStr);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(keySpec);
}
public static PrivateKey stringToRSAPrivateKey(String keyStr) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(keyStr);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePrivate(keySpec);
}
public static SecretKey generateAESKey() throws Exception {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(AES_KEY_SIZE);
return keyGen.generateKey();
}
public static SecretKey stringToAESKey(String keyStr) {
byte[] keyBytes = Base64.getDecoder().decode(keyStr);
return new SecretKeySpec(keyBytes, "AES");
}
public static String encryptWithRSA(String data, PublicKey publicKey) throws Exception {
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedBytes = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encryptedBytes);
}
public static String decryptWithRSA(String encryptedData, PrivateKey privateKey) throws Exception {
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData);
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return new String(decryptedBytes, StandardCharsets.UTF_8);
}
public static String encryptWithAES(String data, SecretKey secretKey, byte[] iv) throws Exception {
Cipher cipher = Cipher.getInstance(AES_ALGORITHM, "BC");
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
byte[] encryptedBytes = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encryptedBytes);
}
public static String decryptWithAES(String encryptedData, SecretKey secretKey, byte[] iv) throws Exception {
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData);
Cipher cipher = Cipher.getInstance(AES_ALGORITHM, "BC");
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return new String(decryptedBytes, StandardCharsets.UTF_8);
}
public static byte[] generateIV() {
SecureRandom random = new SecureRandom();
byte[] iv = new byte[16];
random.nextBytes(iv);
return iv;
}
}
完整的實(shí)現(xiàn)
- RSA 密鑰生成在應(yīng)用啟動(dòng)時(shí),生成并存儲(chǔ)公鑰和私鑰。
- AES 密鑰生成與加密客戶端生成一個(gè)隨機(jī) AES 密鑰,并通過 RSA 公鑰加密。
- 數(shù)據(jù)加密與解密使用 AES 加密數(shù)據(jù),服務(wù)端使用 RSA 解密 AES 密鑰后,再用 AES 解密數(shù)據(jù)。
總結(jié)
本文展示了如何在 Spring Boot 應(yīng)用中實(shí)現(xiàn) RSA + AES 混合加密方案,從而保障接口數(shù)據(jù)傳輸?shù)陌踩浴Mㄟ^結(jié)合這兩種加密算法,能夠在確保安全的同時(shí),不影響系統(tǒng)性能。