一、AES加密概述
AES(Advanced Encryption Standard,高级加密标准)是目前全球应用最广泛的对称加密算法,由美国国家标准与技术研究院(NIST)于2001年正式发布,旨在替代老旧的DES加密算法。凭借高效、安全、开源免费的特性,AES已成为金融、互联网、物联网、数据库数据加密等场景的主流加密方案。
对称加密的核心特点:加密和解密使用同一把密钥,加密端通过密钥将明文转为密文,解密端使用相同密钥还原明文,运算速度快、适合大量数据加密,缺点是密钥的传输和存储需要严格保密。
二、AES核心原理与特性
2.1 密钥长度分类
AES支持三种密钥长度,密钥越长,加密安全性越高,暴力破解难度呈指数级增长:
-
AES-128:128位密钥(16字节),10轮加密运算,速度最快、通用性最强,绝大多数业务场景首选
-
AES-192:192位密钥(24字节),12轮加密运算,安全性中等
-
AES-256:256位密钥(32字节),14轮加密运算,最高安全等级,适用于机密数据加密
2.2 核心工作模式
AES本身是分组加密算法,固定对128位(16字节)数据块加密,针对长短不一的业务数据,衍生出多种工作模式,Java中最常用的两种:
-
ECB(电子密码本模式) :最简单的加密工作模式,无需初始化向量IV。加密时会将明文按128位分组,每个分组独立使用同一密钥加密,相同明文+相同密钥的加密结果完全一致 。该模式运算速度快、实现简单,但存在严重安全漏洞,容易被重放、拼接攻击,仅适用于本地简单测试,严禁生产使用。
-
CBC(密码分组链接模式):需要16字节初始化向量IV,属于链式加密模式。首个明文分组通过IV参与加密,后续每个分组的加密结果依赖上一个分组的密文,实现分组关联。相同明文、密钥加密后密文完全不同,有效避免ECB模式的安全缺陷,抗攻击能力强,是生产环境主流选择。
2.3 填充方式
由于AES固定加密16字节数据块,当明文长度不是16字节整数倍时,需要进行数据填充,常用填充方式:PKCS5Padding(Java默认支持,兼容性最好)。
2.4 AES优缺点
优点:加密强度高、运算速度快、资源占用低、开源无版权限制、跨平台兼容性极强;
缺点:对称算法固有缺陷,密钥分发、存储风险高,一旦密钥泄露,密文可被完全破解。
三、Java实现AES加解密(CBC模式,生产可用)
本次采用AES-128-CBC模式(企业最常用方案),搭配PKCS5Padding填充,包含密钥生成、加密、解密完整工具类,代码可直接复制运行,附带详细注释。
核心参数说明:
-
加密算法:AES
-
工作模式:ECB / CBC(本文完整实现两种模式)
-
填充方式:PKCS5Padding
-
密钥长度:128位(16字节)
-
IV初始化向量:仅CBC模式需要(16字节),ECB模式无需IV
3.1 完整工具类代码
java
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Base64;
/**
* AES加密解密工具类
* 完整实现:ECB模式(测试用)、CBC模式(生产可用)
* 算法规格:AES-128、PKCS5Padding、UTF-8编码
*/
public class AESUtil {
// 加密算法名称
private static final String AES_ALGORITHM = "AES";
// CBC模式算法组合(生产使用)
private static final String AES_CBC_MODE = "AES/CBC/PKCS5Padding";
// ECB模式算法组合(仅测试使用)
private static final String AES_ECB_MODE = "AES/ECB/PKCS5Padding";
// 128位密钥固定字节长度
private static final int KEY_LENGTH = 16;
// CBC模式IV初始化向量固定字节长度
private static final int IV_LENGTH = 16;
/**
* 生成随机AES密钥(16字节 AES-128)
* @return 密钥Base64编码字符串
*/
public static String generateAesKey() {
byte[] keyBytes = new byte[KEY_LENGTH];
new SecureRandom().nextBytes(keyBytes);
return Base64.getEncoder().encodeToString(keyBytes);
}
// ====================== ECB模式 加解密(仅测试)======================
/**
* AES ECB模式加密
* @param plainText 明文
* @param aesKey Base64编码的AES密钥
* @return Base64编码密文
* @throws Exception 加密异常
*/
public static String encryptByECB(String plainText, String aesKey) throws Exception {
// 解码密钥
byte[] keyBytes = Base64.getDecoder().decode(aesKey);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, AES_ALGORITHM);
// 初始化加密器(ECB无IV向量)
Cipher cipher = Cipher.getInstance(AES_ECB_MODE);
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
// 执行加密并返回Base64密文
byte[] encryptBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encryptBytes);
}
/**
* AES ECB模式解密
* @param cipherText Base64编码密文
* @param aesKey Base64编码的AES密钥
* @return 解密明文
* @throws Exception 解密异常
*/
public static String decryptByECB(String cipherText, String aesKey) throws Exception {
// 解码密钥和密文
byte[] keyBytes = Base64.getDecoder().decode(aesKey);
byte[] cipherBytes = Base64.getDecoder().decode(cipherText);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, AES_ALGORITHM);
// 初始化解密器
Cipher cipher = Cipher.getInstance(AES_ECB_MODE);
cipher.init(Cipher.DECRYPT_MODE, keySpec);
// 执行解密
byte[] plainBytes = cipher.doFinal(cipherBytes);
return new String(plainBytes, StandardCharsets.UTF_8);
}
// ====================== CBC模式 加解密(生产可用)======================
/**
* AES CBC模式加密
* @param plainText 明文
* @param aesKey Base64编码的AES密钥
* @return 加密后的密文(Base64编码,IV+密文拼接)
* @throws Exception 加密异常
*/
public static String encryptByCBC(String plainText, String aesKey) throws Exception {
// 1. 解码密钥
byte[] keyBytes = Base64.getDecoder().decode(aesKey);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, AES_ALGORITHM);
// 2. 生成随机IV初始化向量
byte[] ivBytes = new byte[IV_LENGTH];
new SecureRandom().nextBytes(ivBytes);
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
// 3. 初始化加密器
Cipher cipher = Cipher.getInstance(AES_CBC_MODE);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
// 4. 执行加密
byte[] encryptBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
// 5. IV和密文拼接后Base64编码(解密需要IV,需一同返回)
byte[] resultBytes = new byte[ivBytes.length + encryptBytes.length];
System.arraycopy(ivBytes, 0, resultBytes, 0, ivBytes.length);
System.arraycopy(encryptBytes, 0, resultBytes, ivBytes.length, encryptBytes.length);
return Base64.getEncoder().encodeToString(resultBytes);
}
/**
* AES CBC模式解密
* @param cipherText Base64编码的密文(IV+密文)
* @param aesKey Base64编码的AES密钥
* @return 解密后的明文
* @throws Exception 解密异常
*/
public static String decryptByCBC(String cipherText, String aesKey) throws Exception {
// 1. 解码密文
byte[] cipherBytes = Base64.getDecoder().decode(cipherText);
// 2. 拆分IV向量和加密数据
byte[] ivBytes = new byte[IV_LENGTH];
byte[] dataBytes = new byte[cipherBytes.length - IV_LENGTH];
System.arraycopy(cipherBytes, 0, ivBytes, 0, IV_LENGTH);
System.arraycopy(cipherBytes, IV_LENGTH, dataBytes, 0, dataBytes.length);
// 3. 初始化密钥和IV
byte[] keyBytes = Base64.getDecoder().decode(aesKey);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, AES_ALGORITHM);
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
// 4. 初始化解密器
Cipher cipher = Cipher.getInstance(AES_CBC_MODE);
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
// 5. 执行解密并返回明文
byte[] plainBytes = cipher.doFinal(dataBytes);
return new String(plainBytes, StandardCharsets.UTF_8);
}
// 测试方法(同时测试ECB和CBC模式)
public static void main(String[] args) {
try {
// 生成统一AES密钥
String aesKey = generateAesKey();
System.out.println("生成的AES密钥:" + aesKey);
String plain = "Hello AES加密!Java实现对称加密测试123";
System.out.println("原始明文:" + plain);
System.out.println("========================================");
// ECB模式测试
String ecbCipher = encryptByECB(plain, aesKey);
System.out.println("ECB模式加密密文:" + ecbCipher);
String ecbPlain = decryptByECB(ecbCipher, aesKey);
System.out.println("ECB模式解密明文:" + ecbPlain);
System.out.println("========================================");
// CBC模式测试
String cbcCipher = encryptByCBC(plain, aesKey);
System.out.println("CBC模式加密密文:" + cbcCipher);
String cbcPlain = decryptByCBC(cbcCipher, aesKey);
System.out.println("CBC模式解密明文:" + cbcPlain);
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.2 代码核心说明
-
双模式适配:工具类同时实现ECB、CBC两种主流模式,代码结构清晰、方法独立,可根据测试/生产场景按需调用;
-
ECB模式特性:无需IV初始化向量,代码简洁、加解密速度快,但无随机因子,相同明文密钥生成密文一致,存在安全漏洞,仅用于功能测试;
-
CBC模式特性:随机生成16位IV向量,IV与密文拼接存储,每次加密结果唯一,规避ECB模式的重放、拼接攻击,适配生产环境;
-
编码统一规范:全局强制UTF-8编码,彻底解决中文、特殊符号加密解密乱码问题;
-
密钥规范处理:统一使用Base64编码存储密钥,规避二进制乱码,方便密钥配置、传输和持久化存储。
3.3 运行结果示例
执行main方法后,控制台输出如下(每次密钥和密文随机不同):
java
生成的AES密钥:5Z+8x9X7sK2pQ3rT4yU6iO9aSdFgHjKlMn
原始明文:Hello AES加密!Java实现对称加密测试123
========================================
ECB模式加密密文:7x9S2dF4gH6jK8lM0nB2vN4cV6bX8zC0aS2dF4gH6jK8
ECB模式解密明文:Hello AES加密!Java实现对称加密测试123
========================================
CBC模式加密密文:8j7s9dX2kL4pQ6rT8yU0iO2aS4dF6gH8jK0lM2nB4vN6cV8bX0zC2
CBC模式解密明文:Hello AES加密!Java实现对称加密测试123
四、生产环境使用注意事项
4.1 密钥安全管理
密钥是AES加密的核心,绝对不能硬编码在代码中!生产环境建议将密钥配置在配置中心、环境变量或密钥管理平台(如阿里云KMS、华为云KMS),避免代码泄露导致数据解密风险。
4.2 模式选择禁忌
严格禁止在生产环境使用ECB模式!ECB模式无随机向量、分组独立加密,相同明文加密结果完全一致,攻击者可通过密文拼接、重放、比对分析数据规律,极易造成数据泄露和篡改。该模式仅用于初学者测试算法功能、学习对比,正式业务系统必须使用CBC模式。
4.3 AES-256 兼容问题
JDK8及以下版本默认限制AES-256加密,需要手动安装无限制权限策略文件;JDK9及以上版本已默认解除限制,可直接使用AES-256。
4.4 异常处理
实际项目中需细化异常捕获,区分密钥错误、密文篡改、格式错误等异常,便于问题排查,同时避免异常信息泄露敏感数据。
五、总结
AES凭借高安全、高性能的优势,成为对称加密的行业标准。本文完整实现了**AES-ECB(测试版)、AES-128-CBC(生产版)**两种主流方案,清晰对比两种模式的优劣,兼顾学习实用性和生产可用性,适用于接口数据加密、用户敏感信息(手机号、身份证)加密、配置文件加密等绝大多数业务场景。
在实际开发中,可结合非对称加密(RSA)使用:通过RSA加密传输AES密钥,AES加密业务数据,完美解决对称密钥分发不安全的问题,实现整套安全加密体系。