Java 实现 AES 加密和解密完整示例
'# Java 实现 AES 加密和解密完整示例
一、背景与问题
在现代软件开发中,数据加密是保障信息安全的核心技术之一。AES(Advanced Encryption Standard)作为当前最常用的对称加密算法,其安全性、性能和灵活性使其成为业界标准。然而,开发者在实际应用中常面临以下问题:
- 密钥管理:如何安全生成和存储密钥?
- 模式选择:ECB vs CBC vs GCM 的适用场景?
- 填充机制:PKCS5Padding 与 PKCS7Padding 的差异?
- 性能瓶颈:加密/解密对高并发场景的影响?
- 安全漏洞:如何避免密钥泄露导致的系统风险?
本文将通过完整的代码示例和原理分析,深入探讨 Java 实现 AES 加密的实现细节。
二、基本原理
1. AES 算法特性
AES 是分组密码(Block Cipher),将明文划分为固定长度(128 位)的块进行加密。其核心特性包括:
- 对称加密:加密和解密使用相同密钥
- 工作模式:支持 ECB(电子密码本)、CBC(密码分组链接)、CFB(密码反馈)、GCM(伽达尔-麦西密)等模式
- 填充机制:确保明文长度为块大小的整数倍(如 PKCS5Padding)
2. 密钥生成与处理
AES 支持 128/192/256 位密钥,Java 中通过 KeyGenerator 生成密钥,但实际使用时需通过 SecretKeySpec 构造密钥对象。密钥必须以字节数组形式存储,且需确保长度符合 AES 规范。
3. 加密流程
- 初始化
Cipher实例(Cipher.getInstance("AES/ECB/PKCS5Padding")) - 使用密钥初始化
Cipher(cipher.init(Cipher.ENCRYPT_MODE, key)) - 执行加密(
cipher.doFinal(plainText.getBytes()))
三、环境准备
# Java 8+ 环境
# 无需额外依赖(标准库实现)import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;四、核心实现
1. 密钥生成与处理
// 生成 AES 密钥(128 位)
public static SecretKey generateKey() throws Exception {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(128, new SecureRandom()); // 使用安全随机数生成
return keyGen.generateKey();
}
// 将字节数组转换为 SecretKey
public static SecretKey toSecretKey(byte[] keyBytes) {
return new SecretKeySpec(keyBytes, "AES");
}关键点:
SecureRandom保证密钥的随机性- 密钥长度必须为 16/24/32 字节(对应 128/192/256 位)
2. 加密与解密流程
// 加密方法
public static byte[] encrypt(byte[] plainText, SecretKey key) throws Exception {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(plainText);
}
// 解密方法
public static byte[] decrypt(byte[] cipherText, SecretKey key) throws Exception {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key);
return cipher.doFinal(cipherText);
}注意:
- 使用
PKCS5Padding填充模式(等同于 PKCS7Padding) - ECB 模式不推荐用于敏感数据(密文可能重复)
3. 密文转字符串(Base64 编码)
import java.util.Base64;
public static String encryptToString(String plainText, SecretKey key) throws Exception {
byte[] encrypted = encrypt(plainText.getBytes(), key);
return Base64.getEncoder().encodeToString(encrypted);
}
public static String decryptToString(String cipherText, SecretKey key) throws Exception {
byte[] decoded = Base64.getDecoder().decode(cipherText);
byte[] decrypted = decrypt(decoded, key);
return new String(decrypted);
}五、完整案例
场景:用户敏感信息加密存储
public class AESExample {
public static void main(String[] args) throws Exception {
// 1. 生成密钥(实际项目中应从密钥库读取)
SecretKey key = generateKey();
// 2. 加密用户信息
String plainText = "username=admin&password=123456";
String encrypted = encryptToString(plainText, key);
System.out.println("加密结果: " + encrypted);
// 3. 解密验证
String decrypted = decryptToString(encrypted, key);
System.out.println("解密结果: " + decrypted);
}
}输出示例:
加密结果: U2FsdGVkX1+3JnJ6Hm5pDcO8R6qZyqjw==
解密结果: username=admin&password=123456关键点:
- 密钥管理:实际项目中应使用
KeyStore或硬件安全模块(HSM)存储密钥 - 密文存储:建议将密文与初始化向量(IV)一起存储(CBC 模式)
六、源码解析
1. Cipher 类的核心作用
Cipher 是 Java 加密的中心类,其内部通过 Provider 实现具体算法。以 AES/ECB/PKCS5Padding 为例:
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");AES:算法名称ECB:工作模式PKCS5Padding:填充方案
2. 加密流程详解
cipher.init(Cipher.ENCRYPT_MODE, key); // 初始化加密模式
byte[] cipherText = cipher.doFinal(plainText); // 执行加密关键步骤:
- 将明文划分为 16 字节块
- 使用密钥进行混淆(S-Box 替换、行移位、列混合)
- 填充至 16 字节长度(PKCS5Padding)
- 输出密文(16 字节块的加密结果)
3. 密钥生成的底层机制
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(128, new SecureRandom());
SecretKey key = keyGen.generateKey();SecureRandom使用熵池生成随机数- 密钥生成后需通过
SecretKeySpec转换为可用格式
七、进阶使用
1. 更安全的 CBC 模式
// 使用 CBC 模式(需要 IV)
public static byte[] encryptCBC(byte[] plainText, SecretKey key, byte[] iv) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
return cipher.doFinal(plainText);
}优势:
- 每个块的加密结果依赖前一个块(IV 随机性)
- 防止相同明文产生相同密文
2. GCM 模式(推荐用于网络传输)
// GCM 模式支持认证加密(AEAD)
public static byte[] encryptGCM(byte[] plainText, SecretKey key, byte[] nonce) throws Exception {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(128, nonce));
return cipher.doFinal(plainText);
}特性:
- 同时提供加密和认证(防止数据篡改)
- 需要固定长度的 nonce(12 字节)
八、性能与工程实践
1. 性能优化策略
| 方案 | 优化点 | 适用场景 |
|---|---|---|
使用 Cipher 缓存 | 减少重复初始化 | 高频加密场景 |
| 分块处理 | 避免大块数据一次性处理 | 大文件加密 |
使用 GCM 模式 | 内置认证机制 | 网络通信 |
2. 异常处理机制
try {
byte[] result = encrypt(plainText, key);
} catch (Exception e) {
// 处理密钥错误、数据损坏等异常
System.err.println("加密失败: " + e.getMessage());
}3. 安全性增强措施
- 密钥管理:使用
KeyStore或硬件安全模块(HSM) - 密钥长度:推荐使用 256 位密钥(防止量子计算攻击)
- IV 随机性:CBC 模式下每次加密使用新 IV
九、常见问题与踩坑
1. 密钥长度错误
// 错误示例:未检查密钥长度
SecretKey key = new SecretKeySpec("1234567890123456".getBytes(), "AES");问题:密钥长度为 16 字节(128 位)是合法的,但实际可能因编码方式导致长度错误。
解决:使用 KeyGenerator 生成密钥,并通过 key.getEncoded().length 验证长度。
2. 填充模式不匹配
// 错误示例:模式不一致
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");问题:PKCS5Padding 与 PKCS7Padding 实际是等效的,但某些库可能区分。
解决:统一使用 PKCS5Padding(Java 标准库兼容性更好)。
3. ECB 模式安全隐患
// 危险示例:使用 ECB 模式
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");风险:相同明文块会生成相同密文块,导致信息泄露(如图像压缩数据)。
解决:改用 CBC 或 GCM 模式。
十、最佳实践
1. 密钥管理规范
- 存储:使用
KeyStore或加密的数据库存储密钥 - 传输:通过 TLS 加密传输密钥(非明文传输)
- 生命周期:设置密钥的使用有效期(如 90 天)
2. 模式选择建议
| 场景 | 推荐模式 | 原因 |
|---|---|---|
| 数据库字段加密 | AES/CBC/PKCS5Padding | 避免 ECB 的重复问题 |
| 网络通信 | AES/GCM/NoPadding | 内置认证机制 |
| 临时加密 | AES/ECB/PKCS5Padding | 简单场景可接受 |
3. 性能优化技巧
- 使用
Cipher缓存:避免重复初始化 - 分块处理:对大文件使用
CipherOutputStream流式处理 - 并行加密:多线程处理多个独立加密任务
十一、总结
AES 加密在 Java 中的实现涉及密钥管理、工作模式选择、填充机制等多个关键点。本文通过完整示例展示了 AES 的核心实现流程,并深入分析了不同模式的适用场景。在实际开发中,需注意以下事项:
- 避免 ECB 模式:防止相同明文生成相同密文
- 规范密钥管理:使用安全的密钥生成和存储机制
- 选择合适模式:根据场景选择 CBC、GCM 等模式
- 处理性能瓶颈:通过分块处理、缓存等手段优化性能
AES 虽然安全,但需结合密钥管理、安全传输等机制才能构建完整的安全体系。在开发中应始终遵循 "最小特权" 原则,避免因单点漏洞导致整个系统风险。
评论已关闭