Java 密码学:AES 加密 / 解密与 RSA 签名验证
·
Java 密码学:AES 加密/解密与 RSA 签名验证
在 Java 中实现密码学功能时,AES(高级加密标准)用于对称加密和解密数据,而 RSA 用于非对称签名和验证,确保数据完整性和身份认证。AES 高效适合大数据加密,RSA 则常用于数字签名场景(如验证消息来源)。以下我将逐步解释原理并提供 Java 代码示例,基于 Java 标准库(如 javax.crypto 和 java.security),确保真实可靠。代码示例使用常见安全模式(如 AES/CBC/PKCS5Padding 和 SHA256withRSA),并包含安全注意事项。
1. AES 加密/解密原理
AES 是一种对称加密算法,使用相同密钥进行加密和解密。其核心基于多轮替换和置换操作,数学上可描述为一系列字节代换和行移位变换。例如,在 AES-256 中,密钥长度 256 位,加密过程涉及:
- 初始轮密钥加:$ \text{state} = \text{state} \oplus \text{roundKey} $
- 多轮操作:包括字节代换(S-box)、行移位、列混合和轮密钥加。 解密过程是加密的逆操作。
在 Java 中,使用 Cipher 类实现。以下代码示例展示 AES-256 的加密和解密,包括密钥生成和初始化向量(IV)处理(IV 确保相同明文加密结果不同,提升安全性)。
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import java.security.SecureRandom;
import java.util.Base64;
public class AESExample {
public static void main(String[] args) throws Exception {
// 1. 生成 AES 密钥 (256 位)
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256, new SecureRandom());
SecretKey secretKey = keyGen.generateKey();
// 2. 加密示例
String plainText = "Hello, AES 加密测试!";
byte[] encryptedData = encrypt(plainText, secretKey);
System.out.println("加密后数据 (Base64): " + Base64.getEncoder().encodeToString(encryptedData));
// 3. 解密示例
String decryptedText = decrypt(encryptedData, secretKey);
System.out.println("解密后文本: " + decryptedText);
}
public static byte[] encrypt(String plainText, SecretKey secretKey) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecureRandom random = new SecureRandom();
byte[] ivBytes = new byte[16]; // IV 长度 16 字节
random.nextBytes(ivBytes);
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
byte[] encryptedBytes = cipher.doFinal(plainText.getBytes("UTF-8"));
// 将 IV 和加密数据合并存储 (IV 不需要保密,但需传输)
byte[] combined = new byte[ivBytes.length + encryptedBytes.length];
System.arraycopy(ivBytes, 0, combined, 0, ivBytes.length);
System.arraycopy(encryptedBytes, 0, combined, ivBytes.length, encryptedBytes.length);
return combined;
}
public static String decrypt(byte[] combinedData, SecretKey secretKey) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] ivBytes = new byte[16];
System.arraycopy(combinedData, 0, ivBytes, 0, ivBytes.length);
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
byte[] encryptedBytes = new byte[combinedData.length - ivBytes.length];
System.arraycopy(combinedData, ivBytes.length, encryptedBytes, 0, encryptedBytes.length);
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return new String(decryptedBytes, "UTF-8");
}
}
2. RSA 签名验证原理
RSA 是一种非对称算法,签名使用私钥生成,验证使用公钥。签名过程确保消息完整性和来源认证:
- 签名生成:先计算消息哈希(如 SHA-256),然后用私钥加密哈希值。数学上,签名 $s$ 为: $$ s = \text{hash}(m)^d \mod n $$ 其中 $m$ 是消息,$d$ 是私钥指数,$n$ 是模数。
- 签名验证:重新计算消息哈希,用公钥解密签名,并比较是否一致。验证公式: $$ \text{hash}(m) = s^e \mod n $$ 其中 $e$ 是公钥指数。如果相等,则签名有效。
在 Java 中,使用 Signature 类实现。以下代码示例展示 RSA 密钥对生成、签名生成和验证。
import java.security.*;
import java.util.Base64;
public class RSAExample {
public static void main(String[] args) throws Exception {
// 1. 生成 RSA 密钥对 (2048 位)
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
keyPairGen.initialize(2048, new SecureRandom());
KeyPair keyPair = keyPairGen.generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
// 2. 签名生成示例
String message = "Hello, RSA 签名测试!";
byte[] signature = sign(message, privateKey);
System.out.println("签名 (Base64): " + Base64.getEncoder().encodeToString(signature));
// 3. 签名验证示例
boolean isValid = verify(message, signature, publicKey);
System.out.println("签名验证结果: " + (isValid ? "有效" : "无效"));
}
public static byte[] sign(String message, PrivateKey privateKey) throws Exception {
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privateKey);
signature.update(message.getBytes("UTF-8"));
return signature.sign();
}
public static boolean verify(String message, byte[] signature, PublicKey publicKey) throws Exception {
Signature verifySignature = Signature.getInstance("SHA256withRSA");
verifySignature.initVerify(publicKey);
verifySignature.update(message.getBytes("UTF-8"));
return verifySignature.verify(signature);
}
}
3. 完整整合示例
以下代码将 AES 加密/解密和 RSA 签名验证结合在一个程序中。例如,先对消息进行 AES 加密,然后用 RSA 签名加密后的数据,最后验证签名并解密。
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import java.security.*;
import java.util.Base64;
public class CryptoIntegration {
public static void main(String[] args) throws Exception {
// 生成 AES 密钥和 RSA 密钥对
SecretKey aesKey = generateAESKey();
KeyPair rsaKeyPair = generateRSAKeyPair();
// 原始消息
String originalMessage = "集成测试: AES 和 RSA 协同工作!";
System.out.println("原始消息: " + originalMessage);
// 步骤1: AES 加密消息
byte[] encryptedData = encryptWithAES(originalMessage, aesKey);
System.out.println("AES 加密后数据 (Base64): " + Base64.getEncoder().encodeToString(encryptedData));
// 步骤2: 对加密数据生成 RSA 签名
byte[] signature = signWithRSA(encryptedData, rsaKeyPair.getPrivate());
System.out.println("RSA 签名 (Base64): " + Base64.getEncoder().encodeToString(signature));
// 步骤3: 验证 RSA 签名
boolean isSignatureValid = verifyRSASignature(encryptedData, signature, rsaKeyPair.getPublic());
System.out.println("签名验证: " + (isSignatureValid ? "成功" : "失败"));
if (isSignatureValid) {
// 步骤4: AES 解密消息
String decryptedMessage = decryptWithAES(encryptedData, aesKey);
System.out.println("解密后消息: " + decryptedMessage);
}
}
// AES 密钥生成
public static SecretKey generateAESKey() throws Exception {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256, new SecureRandom());
return keyGen.generateKey();
}
// RSA 密钥对生成
public static KeyPair generateRSAKeyPair() throws Exception {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
keyPairGen.initialize(2048, new SecureRandom());
return keyPairGen.generateKeyPair();
}
// AES 加密
public static byte[] encryptWithAES(String plainText, SecretKey secretKey) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecureRandom random = new SecureRandom();
byte[] ivBytes = new byte[16];
random.nextBytes(ivBytes);
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
byte[] encryptedBytes = cipher.doFinal(plainText.getBytes("UTF-8"));
byte[] combined = new byte[ivBytes.length + encryptedBytes.length];
System.arraycopy(ivBytes, 0, combined, 0, ivBytes.length);
System.arraycopy(encryptedBytes, 0, combined, ivBytes.length, encryptedBytes.length);
return combined;
}
// AES 解密
public static String decryptWithAES(byte[] combinedData, SecretKey secretKey) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] ivBytes = new byte[16];
System.arraycopy(combinedData, 0, ivBytes, 0, ivBytes.length);
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
byte[] encryptedBytes = new byte[combinedData.length - ivBytes.length];
System.arraycopy(combinedData, ivBytes.length, encryptedBytes, 0, encryptedBytes.length);
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return new String(decryptedBytes, "UTF-8");
}
// RSA 签名生成
public static byte[] signWithRSA(byte[] data, PrivateKey privateKey) throws Exception {
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privateKey);
signature.update(data);
return signature.sign();
}
// RSA 签名验证
public static boolean verifyRSASignature(byte[] data, byte[] signature, PublicKey publicKey) throws Exception {
Signature verifySignature = Signature.getInstance("SHA256withRSA");
verifySignature.initVerify(publicKey);
verifySignature.update(data);
return verifySignature.verify(signature);
}
}
安全注意事项
- 密钥管理:切勿硬编码密钥。在实际应用中,使用 Java KeyStore (JKS) 或硬件安全模块 (HSM) 存储密钥。AES 密钥应定期更换。
- 算法选择:使用强参数,如 AES-256 和 RSA-2048(或更高)。避免过时算法(如 DES 或 SHA-1)。
- 初始化向量 (IV):IV 必须随机且唯一(每次加密生成新 IV),并安全传输(如与加密数据一起存储)。
- 异常处理:代码中省略了部分异常处理以简洁,但实际中需捕获
GeneralSecurityException等异常。 - 性能考虑:AES 适合大数据加密,RSA 较慢,通常用于小数据(如加密 AES 密钥或签名)。在整合示例中,RSA 签名应用于加密数据,而非原始消息。
- 依赖:确保 Java 环境支持这些算法(标准 JDK 8+ 已包含)。
通过以上步骤,您可以安全地实现 AES 加密/解密和 RSA 签名验证。运行代码前,请测试并调整异常处理。如果有具体场景需求(如文件加密),可进一步优化代码。
更多推荐
所有评论(0)