为了让你能快速上手,我将从以下几个方面进行详细说明:

- 核心概念:了解加密的基本术语。
- 对称加密:使用同一个密钥进行加密和解密(AES 是最常用的)。
- 非对称加密:使用公钥和私钥(RSA 是最常用的)。
- 混合加密:结合对称和非对称加密的优点(HTTPS 的原理)。
- 完整示例代码:提供一个可以直接运行的 AES 加密解密示例。
- 最佳实践与注意事项:避免常见的安全陷阱。
核心概念
在深入代码之前,先理解几个关键术语:
- 加密算法:定义了如何将明文转换为密文的数学规则,AES, DES, RSA。
- 密钥:一个用于加密和解密的秘密字符串或数字。密钥管理是加密中最重要也最困难的部分。
- 对称加密:加密和解密使用同一个密钥,优点是速度快,适合加密大量数据;缺点是密钥分发困难。
- 常见算法:AES (Advanced Encryption Standard), DES (已不安全), 3DES。
- 非对称加密:使用一对密钥:公钥 和 私钥。
- 公钥可以自由分发,用它加密的数据只能用对应的私钥解密。
- 私钥必须严格保密,用它签名的数据可以用对应的公钥验证。
- 优点是解决了密钥分发问题;缺点是速度慢,不适合加密大量数据。
- 常见算法:RSA, ECC (Elliptic Curve Cryptography)。
- 填充:现代加密算法要求数据长度是特定块大小的整数倍,填充(如 PKCS5Padding)就是在数据末尾添加一些字节以满足这个要求。
- 初始化向量:在对称加密中,IV 是一个随机数,用于确保即使加密相同的数据,每次生成的密文也不同,从而增加安全性。
- 盐:在哈希或密码加密时,盐是一个随机值,与密码混合后一起进行哈希/加密,目的是防止彩虹表攻击。
对称加密示例 (AES)
AES 是目前最流行的对称加密算法,我们将使用 AES-128-CBC 模式(最常用的组合之一)进行演示。
关键步骤:
- 生成密钥:从一个密码或随机字节生成一个固定长度的密钥。
- 生成 IV:生成一个随机初始化向量。
- 加密:使用密钥和 IV 对数据进行加密。
- 解密:使用相同的密钥和 IV 对密文进行解密。
代码示例:
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.security.SecureRandom;
import.util.Base64;
public class AesEncryptionUtil {
// AES 加密算法
private static final String ALGORITHM = "AES";
// 加密模式与填充方式
private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding";
// 密钥长度 (128, 192, 256)
private static final int KEY_SIZE = 128;
/**
* 生成 AES 密钥
*/
public static SecretKey generateKey() throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);
keyGenerator.init(KEY_SIZE);
return keyGenerator.generateKey();
}
/**
* 从字符串生成密钥 (注意:实际应用中,密码应该更复杂,并使用密钥派生函数如 PBKDF2)
*/
public static SecretKey getKeyFromString(String keyStr) {
byte[] keyBytes = keyStr.getBytes(StandardCharsets.UTF_8);
// 确保密钥长度符合要求 (AES-128 需要 16字节, AES-256 需要 32字节)
byte[] finalKeyBytes = new byte[KEY_SIZE / 8];
System.arraycopy(keyBytes, 0, finalKeyBytes, 0, Math.min(keyBytes.length, finalKeyBytes.length));
return new SecretKeySpec(finalKeyBytes, ALGORITHM);
}
/**
* 生成初始化向量
*/
public static IvParameterSpec generateIv() {
byte[] iv = new byte[16]; // AES block size is 16 bytes
new SecureRandom().nextBytes(iv);
return new IvParameterSpec(iv);
}
/**
* 加密
*/
public static String encrypt(String input, SecretKey key, IvParameterSpec iv) throws Exception {
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
byte[] cipherBytes = cipher.doFinal(input.getBytes(StandardCharsets.UTF_8));
// 使用 Base64 编码,方便存储和传输
return Base64.getEncoder().encodeToString(cipherBytes);
}
/**
* 解密
*/
public static String decrypt(String cipherText, SecretKey key, IvParameterSpec iv) throws Exception {
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.DECRYPT_MODE, key, iv);
byte decipheredBytes = cipher.doFinal(Base64.getDecoder().decode(cipherText));
return new String(decipheredBytes, StandardCharsets.UTF_8);
}
public static void main(String[] args) {
try {
String originalString = "这是一个需要加密的秘密信息!";
// --- 方式一:生成随机密钥 ---
SecretKey secretKey = generateKey();
IvParameterSpec ivParameterSpec = generateIv();
System.out.println("原始字符串: " + originalString);
System.out.println("使用的密钥 (Base64): " + Base64.getEncoder().encodeToString(secretKey.getEncoded()));
System.out.println("使用的 IV (Base64): " + Base64.getEncoder().encodeToString(ivParameterSpec.getIV()));
String encryptedString = encrypt(originalString, secretKey, ivParameterSpec);
System.out.println("加密后 (Base64): " + encryptedString);
String decryptedString = decrypt(encryptedString, secretKey, ivParameterSpec);
System.out.println("解密后: " + decryptedString);
System.out.println("---------------------------------");
// --- 方式二:从字符串密码生成密钥 ---
String password = "mySecretPassword123"; // 实际应用中应更复杂
SecretKey keyFromPassword = getKeyFromString(password);
IvParameterSpec ivForPassword = generateIv();
System.out.println("\n从密码生成密钥");
System.out.println("原始字符串: " + originalString);
System.out.println("使用的密码: " + password);
System.out.println("使用的 IV (Base64): " + Base64.getEncoder().encodeToString(ivForPassword.getIV()));
String encryptedWithPassword = encrypt(originalString, keyFromPassword, ivForPassword);
System.out.println("加密后 (Base64): " + encryptedWithPassword);
String decryptedWithPassword = decrypt(encryptedWithPassword, keyFromPassword, ivForPassword);
System.out.println("解密后: " + decryptedWithPassword);
} catch (Exception e) {
e.printStackTrace();
}
}
}
非对称加密示例 (RSA)
RSA 通常用于加密少量数据,比如对称加密的密钥,或者数字签名。
关键步骤:
- 生成密钥对:生成一个公钥和一个私钥。
- 公钥加密:使用公钥加密数据(或对称密钥)。
- 私钥解密:使用私钥解密数据(或对称密钥)。
代码示例:
import javax.crypto.Cipher;
import java.security.*;
import java.util.Base64;
public class RsaEncryptionUtil {
private static final String ALGORITHM = "RSA";
/**
* 生成 RSA 密钥对
*/
public static KeyPair generateKeyPair() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
keyPairGenerator.initialize(2048); // 密钥长度,至少 2048
return keyPairGenerator.generateKeyPair();
}
/**
* 公钥加密
*/
public static String encrypt(String data, PublicKey publicKey) throws Exception {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] cipherBytes = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(cipherBytes);
}
/**
* 私钥解密
*/
public static String decrypt(String data, PrivateKey privateKey) throws Exception {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decipheredBytes = cipher.doFinal(Base64.getDecoder().decode(data));
return new String(decipheredBytes, StandardCharsets.UTF_8);
}
public static void main(String[] args) {
try {
String originalString = "用 RSA 加密一条短消息";
// 1. 生成密钥对
KeyPair keyPair = generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
System.out.println("原始字符串: " + originalString);
System.out.println("公钥 (Base64):\n" + Base64.getEncoder().encodeToString(publicKey.getEncoded()));
System.out.println("私钥 (Base64):\n" + Base64.getEncoder().encodeToString(privateKey.getEncoded()));
// 2. 使用公钥加密
String encryptedString = encrypt(originalString, publicKey);
System.out.println("\n加密后 (Base64): " + encryptedString);
// 3. 使用私钥解密
String decryptedString = decrypt(encryptedString, privateKey);
System.out.println("解密后: " + decryptedString);
} catch (Exception e) {
e.printStackTrace();
}
}
}
混合加密 (Hybrid Encryption)
这是实际应用中最常见的模式,完美结合了对称和非对称加密的优点。HTTPS/TLS 协议就是使用这个原理。

流程:
- 发送方:
- 生成一个临时的、对称的会话密钥(AES 密钥)。
- 使用接收方的公钥对这个会话密钥进行非对称加密。
- 使用这个会话密钥对要传输的大量数据进行对称加密。
- 将加密后的会话密钥和加密后的数据一起发送给接收方。
- 接收方:
- 使用自己的私钥解密出会话密钥。
- 使用这个会话密钥解密出原始数据。
优点:
- 安全:利用非对称加密安全地传输对称密钥,再利用对称加密高效地传输数据。
- 高效:数据传输使用快速的对对称加密。
最佳实践与注意事项
- 不要自己实现加密算法:使用 Java 标准库或信誉良好的第三方库(如 Bouncy Castle),自己实现极易出错。
- 密钥管理是重中之重:
- 不要硬编码密钥在代码里,密钥应该从安全的地方加载,如环境变量、密钥管理服务 (KMS, 如 AWS KMS, HashiCorp Vault) 或专门的硬件安全模块 (HSM)。
- 对称密钥和非对称密钥的私钥都需要严格保护。
- 选择合适的算法和模式:
- AES 是对称加密的首选。
- RSA 是非对称加密的首选(用于加密少量数据或签名)。
- 使用像
AES/GCM/NoPadding(GCM 模式同时提供加密和认证) 或AES/CBC/PKCS5Padding这样的模式。 - 避免使用 ECB 模式,因为它不能隐藏数据模式,非常不安全。
- 使用安全的随机数生成器:IV 和盐必须使用
java.security.SecureRandom来生成,而不是Math.random()。 - 处理异常:加密操作可能因为各种原因抛出异常(如无效的密钥、错误的填充),必须妥善处理。
- 考虑使用
javax.crypto.CipherInputStream和CipherOutputStream:当你需要加密/解密大文件(如视频、数据库备份)时,使用流可以避免将整个文件加载到内存中,提高性能和稳定性。
希望这份详细的指南能帮助你理解和使用 Java 进行加密!
