RSA + AES 混合加密通信
引言
在现代信息安全中,我们经常需要在不安全的网络环境中传输敏感数据。单独使用 AES(对称加密)或 RSA(非对称加密)都存在一定的安全隐患,因此,结合 RSA 和 AES 的混合加密方式成为一种常见的安全解决方案。
在这篇博客中,我们将介绍如何使用 RSA + AES 实现混合加密。
一、对称加密:AES的简洁与高效
1. 核心思想:
对称加密,顾名思义,就是加密和解密使用同一把“钥匙”。这把“钥匙”被称为密钥。发送方使用密钥对信息进行加密,接收方使用相同的密钥进行解密。
2. 代表算法:AES (Advanced Encryption Standard)
AES是目前应用最广泛的对称加密算法之一。它采用分组加密的方式,将数据分成固定长度的块,然后使用密钥对每个块进行加密。AES具有以下优点:
- 高效快速: 加解密速度快,适合处理大量数据。
- 安全性高: 密钥长度可选(128位、192位、256位),安全性极高。
- 实现简单: 算法结构清晰,易于硬件和软件实现。
3. 应用场景:
- 文件加密:保护本地存储的文件安全。
- 网络通信:保障数据传输过程中的机密性,例如HTTPS协议。
- 数据库加密:保护敏感数据不被泄露。
二、非对称加密:RSA的巧妙与安全
1. 核心思想:
非对称加密使用一对密钥:公钥和私钥。公钥可以公开,用于加密信息;私钥必须严格保密,用于解密信息。发送方使用接收方的公钥加密信息,只有拥有对应私钥的接收方才能解密。
2. 代表算法:RSA (Rivest-Shamir-Adleman)
RSA是最早的非对称加密算法之一,也是目前应用最广泛的。它的安全性基于大整数分解的数学难题。RSA具有以下特点:
- 安全性高: 基于数学难题,破解难度极大。
- 密钥管理方便: 公钥可以公开,私钥只需自己保管。
- 数字签名: 除了加密,RSA还可以用于数字签名,验证信息真实性和完整性。
3. 应用场景:
- 安全通信:例如SSH、SSL/TLS等协议,用于建立安全通信通道。
- 数字签名:用于验证软件、文档的真实性和完整性。
- 密钥交换:例如Diffie-Hellman密钥交换协议,用于安全地交换对称加密的密钥。
三、RSA与AES的对比
特性 | RSA | AES |
---|---|---|
加密类型 | 非对称加密 | 对称加密 |
密钥数量 | 一对密钥(公钥、私钥) | 一个密钥 |
加解密速度 | 慢 | 快 |
安全性 | 基于数学难题 | 基于密钥长度 |
应用场景 | 安全通信、数字签名、密钥交换 | 文件加密、网络通信、数据库加密 |
为什么选择 RSA + AES ?
AES(对称加密): 速度快,适用于大数据量的加密,但密钥传输存在安全问题。
RSA(非对称加密): 适用于密钥加密,解决密钥分发问题,但直接加密大数据时性能较低。
在加密领域,RSA 和 AES 是两种最常用的加密算法,但它们各有优缺点。为了兼顾安全性、效率和实际应用需求,RSA + AES 的组合方案 成为了现代加密系统的首选。以下是选择这种方案的核心原因:
1. 解决密钥分发问题
- AES 的局限性:
- AES 是对称加密算法,加密和解密使用相同的密钥。
- 虽然 AES 加密速度快,但密钥需要在发送方和接收方之间安全传输,而直接传输密钥存在被窃取的风险。
- RSA 的优势:
- RSA 是非对称加密算法,使用公钥加密、私钥解密。
- 公钥可以公开,私钥由接收方保管,因此 RSA 非常适合用于加密 AES 密钥,解决密钥分发的安全问题。
2. 兼顾加密效率
- RSA 的局限性:
- RSA 直接加密大数据时性能较低,速度慢,不适合处理大量数据。
- AES 的优势:
- AES 是专为高效加密大量数据而设计的对称加密算法,速度极快。
- RSA + AES 的组合:
- RSA 只用于加密 AES 密钥(数据量小),解决了 RSA 加密效率低的问题。
- AES 用于加密实际数据,充分发挥其高效性。
3. 增强安全性
- RSA 的安全性:
- RSA 基于大整数分解的数学难题,安全性极高。
- 使用 RSA 加密 AES 密钥,可以确保密钥在传输过程中不被窃取。
- AES 的安全性:
- AES 支持 128 位、192 位和 256 位密钥长度,安全性极高。
- 每次通信都可以生成一个新的随机 AES 密钥,进一步降低密钥泄露的风险。
4. 为什么不用单一的 RSA 或 AES?
- 如果只用 RSA:
- 加密大量数据时速度极慢,效率低下。
- 不适合实时通信或大数据加密。
- 如果只用 AES:
- 密钥分发困难,需要安全的通道传输密钥。
- 无法解决密钥交换的安全性问题。
5. 综合方案(RSA + AES):
- 随机生成 AES 密钥,加密数据。
- 用 RSA 公钥加密 AES 密钥。
- 传输加密数据和使用RSA公钥加密过的随机 AES 密钥。
- 接收方使用 RSA 私钥解密随机 AES 密钥,再用随机 AES 密钥解密数据。
RSA + AES 的组合方案是一种经典的“混合加密”方案,它结合了非对称加密的安全性和对称加密的高效性,解决了密钥分发和大量数据加密的难题。这种方案在现代加密通信、数据存储和安全传输中得到了广泛应用,是保障信息安全的基石之一。
通过这种组合,我们既能享受 AES 的高效加密能力,又能利用 RSA 解决密钥分发的安全问题,真正实现了 安全与效率的完美平衡。
代码实现
1. 发送加密请求
我们模拟向服务器发送加密数据和加密后的 AES 密钥:
public static void sendMessage(String encryptedData, String encryptedAESKey) throws Exception {
URL url = new URL("http://localhost:8080/test");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type", "application/json");
//connection.setRequestProperty("token", "wJ6pnt/bk32jIzFM...");
String jsonBody = String.format("{\"encryptedData\":\"%s\", \"encryptedAESKey\":\"%s\"}", encryptedData, encryptedAESKey);
try (OutputStream os = connection.getOutputStream()) {
os.write(jsonBody.getBytes(StandardCharsets.UTF_8));
}
int responseCode = connection.getResponseCode();
System.out.println("Response Code: " + responseCode);
}
2. 数据加密示例
使用 AES 加密消息:
String data = "曾经的天才萧炎已沦落为被人看不起的废物,昔日的辉煌仿佛一夜之间化为泡影。曾经的他,天赋异禀,修炼速度远超同龄人,被誉为家族百年难得一见的天才,备受瞩目和期待。然而,不知从何时起,他的修为停滞不前,甚至开始倒退,曾经的荣耀与光环逐渐消散,取而代之的是无尽的嘲讽与冷眼。\n" +
"\n" +
"家族中的长辈对他失望透顶,昔日的朋友也渐渐疏远,甚至连曾经仰慕他的人也开始对他嗤之以鼻。萧炎从云端跌落,成为了众人茶余饭后的笑柄。然而,没有人知道,这一切的背后隐藏着怎样的秘密与阴谋。\n" +
"\n" +
"就在他几乎绝望之际,命运的齿轮悄然转动。一次偶然的机遇,萧炎遇到了改变他一生的贵人——药老。这位神秘强者的出现,不仅揭开了他修为倒退的真相,更为他打开了一扇通往全新世界的大门。在药老的指导下,萧炎重新踏上修炼之路,凭借坚韧的意志和不屈的精神,他一步步从谷底爬起,誓要夺回属于自己的一切。\n" +
"\n" +
"曾经的废物,终将再次崛起,成为让世人仰望的巅峰强者。";
Map<String, Object> orderData = new HashMap<>();
orderData.put("orderId", 123456);
orderData.put("username", "萧炎");
orderData.put("status", "pending");
orderData.put("description", data);
// 生成 AES 密钥
SecretKey aesKey = AESUtils.generateAESKey();
// 加密数据
SecureMessageUtil.EncryptedMessage encryptedMessage = SecureMessageUtil.encryptMessage(orderData, publicKeyBase64);
// 发送加密数据
sendMessage(encryptedMessage.getEncryptedData(), encryptedMessage.getEncryptedKey());
3. AES 加解密工具类
// 加密
public static String encryptAESKey(String data, SecretKey aesKey) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// 生成随机 IV
byte[] ivBytes = new byte[16];
new SecureRandom().nextBytes(ivBytes);
IvParameterSpec iv = new IvParameterSpec(ivBytes);
cipher.init(Cipher.ENCRYPT_MODE, aesKey, iv);
byte[] encryptedData = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
// 拼接 IV + 密文,一起 Base64 编码
byte[] combined = new byte[ivBytes.length + encryptedData.length];
System.arraycopy(ivBytes, 0, combined, 0, ivBytes.length);
System.arraycopy(encryptedData, 0, combined, ivBytes.length, encryptedData.length);
return Base64.getEncoder().encodeToString(combined);
}
//解密
public static String decryptAESKey(String encryptedData, SecretKey aesKey) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] combined = Base64.getDecoder().decode(encryptedData);
// 提取 IV
byte[] ivBytes = new byte[16];
System.arraycopy(combined, 0, ivBytes, 0, ivBytes.length);
IvParameterSpec iv = new IvParameterSpec(ivBytes);
// 提取密文
byte[] encryptedBytes = new byte[combined.length - ivBytes.length];
System.arraycopy(combined, ivBytes.length, encryptedBytes, 0, encryptedBytes.length);
cipher.init(Cipher.DECRYPT_MODE, aesKey, iv);
byte[] decryptedData = cipher.doFinal(encryptedBytes);
return new String(decryptedData, StandardCharsets.UTF_8);
}
//生成随机AES密钥
public static SecretKey generateAESKey() throws Exception {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256);
return keyGen.generateKey();
}
4. RSA 加解密工具类
// 使用 RSA 公钥加密 AES 密钥
public static String encryptAESKey(SecretKey aesKey, PublicKey publicKey) {
try {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedKey = cipher.doFinal(aesKey.getEncoded());
return Base64.getEncoder().encodeToString(encryptedKey);
} catch (Exception e) {
throw new RuntimeException("AES 密钥加密失败", e);
}
}
// 使用 RSA 私钥解密 AES 密钥
public static SecretKey decryptAESKey(String encryptedKey, PrivateKey privateKey) {
try {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decryptedKey = cipher.doFinal(Base64.getDecoder().decode(encryptedKey));
return new SecretKeySpec(decryptedKey, "AES");
} catch (Exception e) {
throw new RuntimeException("AES 密钥解密失败", e);
}
}
5. Map类型转换工具
public class SecureMessageUtil {
private static final ObjectMapper objectMapper = new ObjectMapper();
/**
* 使用 AES + RSA 进行加密
* @param dataMap 需要加密的 JSON 数据 (Map 结构)
* @param publicKeyBase64 公钥 (Base64 编码)
* @return 加密后的数据,包含加密内容和加密密钥
*/
public static EncryptedMessage encryptMessage(Map<String, Object> dataMap, String publicKeyBase64) throws Exception {
// 获取公钥
PublicKey publicKey = RSAUtil.getPublicKeyFromBase64(publicKeyBase64);
// 生成 AES 密钥
SecretKey aesKey = AESUtils.generateAESKey();
// 将 Map 转换为 JSON 字符串
String jsonData = objectMapper.writeValueAsString(dataMap);
// 使用 AES 加密 JSON 数据
String encryptedData = AESUtils.encryptAESKey(jsonData, aesKey);
// 使用 RSA 加密 AES 密钥
String encryptedAESKey = RSAUtil.encryptAESKey(aesKey, publicKey);
// 返回封装的加密对象
return new EncryptedMessage(encryptedData, encryptedAESKey);
}
/**
* 使用 AES + RSA 进行解密
* @param encryptedData 加密后的数据
* @param encryptedKey 加密后的密钥
* @param privateKeyBase64 私钥 (Base64 编码)
* @return 解密后的数据 (Map 结构)
*/
public static Map<String, Object> decryptMessage(String encryptedData,String encryptedKey, String privateKeyBase64) throws Exception {
// 1. 获取私钥
PrivateKey privateKey = RSAUtil.getPrivateKeyFromBase64(privateKeyBase64);
// 2. 用 RSA 私钥解密 AES 密钥
SecretKey aesKey = RSAUtil.decryptAESKey(encryptedKey, privateKey);
// 3. 用 AES 密钥解密数据
String decryptedJson = AESUtils.decryptAESKey(encryptedData, aesKey);
// 4. 确保 JSON 数据有效
if (decryptedJson.trim().isEmpty()) {
throw new IllegalArgumentException("解密后的 JSON 为空");
}
System.out.println("解密后的 JSON: " + decryptedJson);
// 5. 确保 objectMapper 已初始化
ObjectMapper objectMapper = new ObjectMapper();
// 6. 转换 JSON -> Map
try {
return objectMapper.readValue(decryptedJson, new TypeReference<Map<String, Object>>() {});
} catch (Exception e) {
throw new RuntimeException("JSON 解析失败,数据格式可能有误: " + decryptedJson, e);
}
}
/**
* 内部静态类:封装加密后的数据
*/
@Getter
public static class EncryptedMessage {
private final String encryptedData;
private final String encryptedKey;
public EncryptedMessage(String encryptedData, String encryptedKey) {
this.encryptedData = encryptedData;
this.encryptedKey = encryptedKey;
}
}
}
6. 接收消息
@PostMapping("/test")
public String handleOrderPush(@RequestBody OrderPushRequest request) {
try {
System.out.println("获取的加密内容: " + request.getEncryptedData());
System.out.println("获取的加密AES密钥: " + request.getEncryptedAESKey());
String shopUsername = userService.getCurrentShopUsername();
System.out.println("shopUsername: " + shopUsername);
// 从数据库中获取私钥的 Base64 编码字符串 这里也可以直接读取私钥文件
String privateKeyBase64 = outShopDao.getPrivateKeyByShopUsername(shopUsername);
if (privateKeyBase64 == null) {
throw new RuntimeException("Private key not found for shopUsername: " + shopUsername);
}
// 将 Base64 编码字符串转换为 PrivateKey 对象
PrivateKey privateKey = RSAUtil.getPrivateKeyFromBase64(privateKeyBase64);
if (privateKey == null) {
throw new RuntimeException("Failed to load private key for shopUsername: " + shopUsername);
}
System.out.println("________________________________________");
// 解密
Map<String, Object> decryptedData = SecureMessageUtil.decryptMessage(request.getEncryptedData(), request.getEncryptedAESKey(), privateKeyBase64);
// 打印解密结果
System.out.println("________________________________________");
System.out.println("解密后的 Map 数据: " + decryptedData);
return "{\"status\":\"success\"}";
} catch (Exception e) {
e.printStackTrace();
return "{\"status\":\"error\", \"message\": \"" + e.getMessage() + "\"}";
}
}
6. 消息结果
获取的加密内容: tm7JXhnLqvGOFP9MPtmRN0JitP4uAPaHv+y0Eb4fQmRZtLHFnC+TGKcJEMSUwrOGbKS+uxgkdHVvBYzv1R5klJ2HT8hfhvJj0sfeVSgIKzF5pclaVTYP+9RHnIhwRHXMfqszZT6pMKjxiS95CcUqK4D17xSUiOgCqOTjTn0LruIdZTqY4d1tOa/dSKOuQ/G7TQEZlk20D2aq3SuhW3f0cxkIzXe6vfxAVcG1jHPs+mS5lFUFSiKc41dKySfefaGHrg75U+8u0YMHczIsRyZCczuLeefw1kR4fz9A+1Bhu+wg5KVJtuZpndZZYuKYihEgn22nTx7o8Rf1EljYYwj9gydmcP2FxXPz5zOeiLIwYG4S7Qrzs09/DdX9tQLfw0CDRCBJuKliRWN3TjQw4bO+ZzX4xe2PJ/yB+/E6ZuKEHHQS1MJp15RZ4vAHGGXqQWJw2voG5Go9jOmUvJydTm8r0bqvNCj1OhivcJf9iNfIc7XhSWVQZnLUizcc1hKX23mbCqBhr9/Eq5+BjJDgehUe/f4rykUVAx2VOFExK2JzqBehfxUhi8L86CEPELjXgF6xVLxUEhIAxE7NFWnMlhcHzK2Y6C29HP3l/R23gpVTL1wcd7ltaV3TUOIRxG7GLms9okEqMYvr8o9hVeCaOwyAg9yxpfC93B+oalDaJhUyrKElhWbVhdO+tnGsFAW7yEAuIXne4eRpsj/bL61LHRB7oWq/hjlW0GdyY7VOx2AKBBXpMKmoKrT6OEYjtj7ImJesNZl93F9zhXv8kRGwTQ+6AeEraCofpWs1Av+mIHPxfMkHlk+5S96jCpzh1hXgVx03hKqM/0o9RmwdjJRNs7OOZryahxSQyRpuCXM+7Zr5mI15+S5NrxdqXWnZAAVuw1DnvxJHME2rlqq7U8hkKQ8j2YcOJ/sXYN6okW8e+CjoU3ik7jOBKk41r9BLI5FzbLoJDll2wquN0uuD1z+Fs6XxClOyXN50x0pk771Ng5Tli5m5Fhf9B0s0Z/QiHeXjCkOplC8dKWHHY0+emgVVYRRwnUiyHVrV6710p8o6luQT+McVBek/5UfF09kV3Ps+Xj0wCqdv9FLF/ydXo+T9tkxoHsk/CP5bTbsdVkJRH19fyCovFBoKSMYMfbjP8M9C9RIItPW+gG3rCB2JN0eMgl6Ut2RLhA2CIpazSfopH2BLW0EPv9ijnhh4s6/vWbppcTHq0ybWtTUoP9fO8nM8CuGLDr9ipJnNCa4eR9q3l+fs5RqjjZbpgBoPaZBW14qjW44l7rSj3TQQ23tEpIXQc9s2hSXDOsiQuZsGsKxoo52UedX499zCuywYtuaRq5hCWsn9yUT+pQ3atblmm2FfRvPJFzMTs60plhoKHI4lPLm1EbHniC1jT71V6G5bQt5E593htKKjqUemi9Jb/lvpF07oCLurftvdDMHSpTd738X9ExyBL0zWZUeXpw9aNH5uOtcvWw6j0LHI5x5GJytQf0lkTH8A1Z0wLusOKPrXAZoDws9biaa3ryvsXU8Fvj8EdwZ/mtgCYOhaQHraBQzlnF1SJ9WlyDBvhUNpgfYVWoTkhFRJQcXLIH6Bjr/PVWa7v3CyfLj2e3QpfuRnZ8vk48ntjhJA1hRzU0LPjVdCRj6eD9a6LvSCvHQNTnpJaQHfLPslHyhm9jQsvfIFyEe1NFLzxI5FXH+g11eJ2nVOkpmxiju+9TkDGFifNYHNyO+IaLZ3
获取的加密AES密钥: ItjFO7GvNrBF3aHoQmex4KeocPf4qea74yEj3PBuyvWBoS9XAkiOyDGflCnOlub8n1KASd7C2GCUyBgMJt18nHj9eM04ULxHl+wr6SibWUeQ+bs6ktUerD4ivk63LeRy1KJ3FXIqZqya6MsoKhjGQJXQ1a5074yZ+XSYRSp+GdYUJQuRaaKue4s30ZdN0INxyDSNslJDoDn1gm4SJroVA/3FgYoYrowp+0PRoaj0eI/k9oTpVUk5uNgePfteP/dvPzv5uTBWwkP+mhWehjfWI9DVaImG6ER8uGd5FK7kKkT1CV9uAuJ8M2oTklLbSI3Qna+K00BNBqzR0muAfhcn+A==
shopUsername: cqxh_1741837332119
________________________________________
解密后的 JSON: {"orderId":123456,"description":"曾经的天才萧炎已沦落为被人看不起的废物,昔日的辉煌仿佛一夜之间化为泡影。曾经的他,天赋异禀,修炼速度远超同龄人,被誉为家族百年难得一见的天才,备受瞩目和期待。然而,不知从何时起,他的修为停滞不前,甚至开始倒退,曾经的荣耀与光环逐渐消散,取而代之的是无尽的嘲讽与冷眼。\n\n家族中的长辈对他失望透顶,昔日的朋友也渐渐疏远,甚至连曾经仰慕他的人也开始对他嗤之以鼻。萧炎从云端跌落,成为了众人茶余饭后的笑柄。然而,没有人知道,这一切的背后隐藏着怎样的秘密与阴谋。\n\n就在他几乎绝望之际,命运的齿轮悄然转动。一次偶然的机遇,萧炎遇到了改变他一生的贵人——药老。这位神秘强者的出现,不仅揭开了他修为倒退的真相,更为他打开了一扇通往全新世界的大门。在药老的指导下,萧炎重新踏上修炼之路,凭借坚韧的意志和不屈的精神,他一步步从谷底爬起,誓要夺回属于自己的一切。\n\n曾经的废物,终将再次崛起,成为让世人仰望的巅峰强者。","username":"萧炎","status":"pending"}
________________________________________
解密后的 Map 数据: {orderId=123456, description=曾经的天才萧炎已沦落为被人看不起的废物,昔日的辉煌仿佛一夜之间化为泡影。曾经的他,天赋异禀,修炼速度远超同龄人,被誉为家族百年难得一见的天才,备受瞩目和期待。然而,不知从何时起,他的修为停滞不前,甚至开始倒退,曾经的荣耀与光环逐渐消散,取而代之的是无尽的嘲讽与冷眼。
家族中的长辈对他失望透顶,昔日的朋友也渐渐疏远,甚至连曾经仰慕他的人也开始对他嗤之以鼻。萧炎从云端跌落,成为了众人茶余饭后的笑柄。然而,没有人知道,这一切的背后隐藏着怎样的秘密与阴谋。
就在他几乎绝望之际,命运的齿轮悄然转动。一次偶然的机遇,萧炎遇到了改变他一生的贵人——药老。这位神秘强者的出现,不仅揭开了他修为倒退的真相,更为他打开了一扇通往全新世界的大门。在药老的指导下,萧炎重新踏上修炼之路,凭借坚韧的意志和不屈的精神,他一步步从谷底爬起,誓要夺回属于自己的一切。
曾经的废物,终将再次崛起,成为让世人仰望的巅峰强者。, username=萧炎, status=pending}
结论
使用 RSA + AES 结合的方式可以有效保证数据传输的安全性。AES 负责加密大数据,RSA 负责安全地传输 AES 密钥。这种方式兼顾了安全性和性能,适用于大多数的安全通信场景。
希望这篇文章对你有所帮助!
标签:AES,加密,String,RSA,密钥,new From: https://www.cnblogs.com/chl-cys/p/18770230