Java AES 加密/解密实现
1. 项目背景与介绍
在数据传输和存储过程中,保护敏感信息的安全性非常关键。AES(Advanced Encryption Standard,高级加密标准)是一种广泛使用的对称加密算法,具有高效、安全、易于实现等特点。由于 AES 使用同一个密钥进行加密和解密,因此密钥的管理非常重要。
本项目将通过 Java 内置的加密 API(主要在 javax.crypto
包中提供)实现 AES 加密与解密。示例中我们采用 AES/CBC/PKCS5Padding 模式,并使用一个 128 位(16 字节)的密钥和固定的初始向量(IV)。为了便于演示,本示例使用了固定的 IV,但在实际应用中,建议使用随机 IV,并将 IV 与密文一起存储或传输。
通过本项目,你将掌握如何使用 Java 进行 AES 加密和解密操作,并了解如何对密钥和初始向量进行设置、Base64 编码结果输出等知识点。
2. 相关知识
2.1 AES 加密算法
- 对称加密:AES 算法属于对称加密算法,即加密和解密使用相同的密钥。其优点是速度快,适合大数据量加密;缺点是密钥传输和管理存在一定风险。
- 加密模式与填充方式:常用的模式有 ECB、CBC、CFB 等,本示例采用 CBC 模式。由于 AES 的块大小为 16 字节,当明文长度不是 16 字节的整数倍时,需要采用填充方式,本示例采用 PKCS5Padding。
- 初始向量(IV):在 CBC 模式下,初始向量用于增加加密随机性,通常需要随机生成并与密文一起存储。但本示例为了简化演示,使用了固定的 IV(仅供学习参考)。
2.2 Java 加密 API
Java 提供了一整套加密相关的类:
- Cipher:核心加密解密类,用于执行加密和解密操作。
- SecretKeySpec:将原始密钥数据封装为密钥对象。
- IvParameterSpec:封装初始向量数据。
- Base64:用于将二进制数据转换为可读的字符串格式,便于传输和存储。
3. 项目实现思路
本项目主要分为以下几个步骤:
-
准备密钥和初始向量
定义 AES 加密所需的 128 位密钥和 16 字节的初始向量(IV)。在实际应用中,这些应由安全随机数生成器产生,并妥善管理。 -
创建 Cipher 对象
使用Cipher.getInstance("AES/CBC/PKCS5Padding")
获取 Cipher 对象,并分别用密钥和 IV 初始化加密和解密模式。 -
执行加密和解密操作
- 加密:将明文字符串转换为字节数组,调用
doFinal()
方法获得密文字节数组,再进行 Base64 编码。 - 解密:将 Base64 解码后的密文字节数组通过
doFinal()
方法还原为明文字节数组,并转换为字符串。
- 加密:将明文字符串转换为字节数组,调用
-
整合代码并测试
将上述流程封装到一个 Java 类中,在主函数中调用加密和解密方法,验证加解密是否正确。
4. 完整代码实现
下面是一份完整的 Java 代码示例,实现了 AES 加密与解密功能。代码中附有详细中文注释,便于理解每个步骤的实现细节。
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.util.Base64;
/**
* AESEncryptionDemo 类演示了如何使用 Java 实现 AES 加密和解密。
* 本示例使用 AES/CBC/PKCS5Padding 模式,密钥和初始向量均为 16 字节(128 位)。
* 注意:示例中使用固定 IV 仅供学习,实际应用中应使用随机 IV 并与密文一起传输。
*/
public class AESEncryptionDemo {
// AES 密钥(16 字节):请确保密钥足够随机和安全
private static final String KEY = "0123456789abcdef";
// 初始向量(IV,16 字节):实际应用中应随机生成
private static final String IV = "abcdefghijklmnop";
// 指定加密算法和模式
private static final String ALGORITHM = "AES/CBC/PKCS5Padding";
/**
* 对给定的明文进行 AES 加密,并返回 Base64 编码后的密文字符串。
*
* @param plainText 明文字符串
* @return Base64 编码后的密文字符串,如果加密过程中发生异常则返回 null
*/
public static String encrypt(String plainText) {
try {
// 获取 Cipher 实例,指定使用 AES/CBC/PKCS5Padding 模式
Cipher cipher = Cipher.getInstance(ALGORITHM);
// 构造密钥规范
SecretKeySpec keySpec = new SecretKeySpec(KEY.getBytes("UTF-8"), "AES");
// 构造初始向量规范
IvParameterSpec ivSpec = new IvParameterSpec(IV.getBytes("UTF-8"));
// 初始化 Cipher 为加密模式
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
// 对明文进行加密
byte[] encryptedBytes = cipher.doFinal(plainText.getBytes("UTF-8"));
// 使用 Base64 对密文字节数组进行编码,返回字符串
return Base64.getEncoder().encodeToString(encryptedBytes);
} catch (Exception e) {
// 捕获异常并打印错误信息
e.printStackTrace();
return null;
}
}
/**
* 对给定的 Base64 编码密文进行 AES 解密,还原出原始明文字符串。
*
* @param cipherText Base64 编码后的密文字符串
* @return 解密后的明文字符串,如果解密过程中发生异常则返回 null
*/
public static String decrypt(String cipherText) {
try {
// 获取 Cipher 实例,指定使用 AES/CBC/PKCS5Padding 模式
Cipher cipher = Cipher.getInstance(ALGORITHM);
// 构造密钥规范
SecretKeySpec keySpec = new SecretKeySpec(KEY.getBytes("UTF-8"), "AES");
// 构造初始向量规范
IvParameterSpec ivSpec = new IvParameterSpec(IV.getBytes("UTF-8"));
// 初始化 Cipher 为解密模式
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
// 将 Base64 编码的密文转换为字节数组
byte[] decodedBytes = Base64.getDecoder().decode(cipherText);
// 对密文进行解密
byte[] decryptedBytes = cipher.doFinal(decodedBytes);
// 将解密后的字节数组转换为字符串
return new String(decryptedBytes, "UTF-8");
} catch (Exception e) {
// 捕获异常并打印错误信息
e.printStackTrace();
return null;
}
}
/**
* 主函数,演示 AES 加密和解密的完整流程。
*
* @param args 命令行参数(未使用)
*/
public static void main(String[] args) {
// 定义待加密的明文
String plainText = "Hello, AES Encryption!";
System.out.println("原始明文: " + plainText);
// 对明文进行加密
String encryptedText = encrypt(plainText);
System.out.println("加密后的密文 (Base64 编码): " + encryptedText);
// 对密文进行解密
String decryptedText = decrypt(encryptedText);
System.out.println("解密后的明文: " + decryptedText);
}
}
5. 代码解读
5.1 密钥和初始向量设置
- KEY 和 IV
- 本示例中密钥和初始向量均为固定字符串,长度均为 16 字节,适用于 AES-128。
- 实际应用中,应使用安全随机数生成密钥和 IV,并对 IV 进行传输或存储(例如,将 IV 附加在密文前)。
5.2 加密过程
- Cipher 获取与初始化
使用Cipher.getInstance("AES/CBC/PKCS5Padding")
获取 Cipher 对象,并利用SecretKeySpec
和IvParameterSpec
初始化 Cipher 为加密模式。 - 数据加密
将明文转换为字节数组后调用doFinal()
方法进行加密,生成密文字节数组;之后使用 Base64 编码便于输出和存储。
5.3 解密过程
- Cipher 初始化为解密模式
同样使用SecretKeySpec
和IvParameterSpec
初始化 Cipher 为解密模式。 - 数据解密
将 Base64 解码后的密文字节数组传入doFinal()
方法进行解密,还原出原始明文字节数组,再转换为字符串。
5.4 异常处理
- 在加密和解密过程中均使用 try-catch 捕获可能出现的异常,并输出堆栈信息,确保程序调试和排查问题时能获得足够的信息。
6. 项目总结与展望
本项目通过 Java 实现了 AES 加密和解密的基本流程,主要收获和体会包括:
-
掌握对称加密基本原理
学习了如何使用同一密钥对数据进行加密和解密,并理解了 AES 加密中密钥与初始向量的重要性。 -
熟悉 Java 加密 API
通过 Cipher、SecretKeySpec、IvParameterSpec 等类的使用,掌握了 Java 内置加密工具的基本操作。 -
安全性与实际应用
虽然本示例使用固定的 IV 仅供学习,但在实际应用中应采用随机 IV 并妥善管理密钥,确保数据加密安全性。 -
扩展与优化方向
- 可改进为生成随机 IV,并将 IV 与密文组合(例如将 IV 前置于密文中一起传输)。
- 可以使用更高位数的 AES 加密(如 AES-256),前提是环境支持。
- 将加密/解密过程封装为工具类,便于在实际项目中复用和集成。
总之,本项目为开发者提供了一个简单、直观的 AES 加密/解密示例,既有助于理解对称加密的基本流程,也为构建实际安全通信系统提供了实践基础。希望这篇博客文章能为你在 Java 安全编程领域提供有价值的参考和启发。