AES 加密工具
AesUtil 提供 AES 对称加密功能。默认使用 AES-GCM(认证加密),可同时保证机密性与完整性,防止密文被篡改。
类信息
- 包名:
com.molandev.core.util.encrypt - 类名:
AesUtil - 类型: 静态工具类
使用场景
- ✅ 敏感数据加密存储(如身份证号、手机号)
- ✅ 数据传输加密
- ✅ 配置文件加密
- ✅ Token 加密
- ✅ 需要高性能加密场景
为何默认 GCM
GCM 是认证加密(AEAD):解密时会校验 128 bit Tag,密文被篡改会直接失败。相比 ECB/CBC 仅加密,更适合传输与存储场景。
基础使用
简单加密解密
java
import com.molandev.core.util.encrypt.AesUtil;
public class AesExample {
public static void main(String[] args) {
String password = "mySecretKey";
String content = "敏感数据";
// 加密(默认 AES/GCM/NoPadding)
String encrypted = AesUtil.encrypt(content, password);
System.out.println("加密后: " + encrypted);
// 输出: Base64(IV12 || ciphertext||tag)
// 解密
String decrypted = AesUtil.decrypt(encrypted, password);
System.out.println("解密后: " + decrypted);
// 输出: 敏感数据
}
}核心方法
encrypt
加密字符串内容(默认 GCM)。
java
public static String encrypt(String content, String password)参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| content | String | 是 | 待加密的明文内容 |
| password | String | 是 | 密码/口令(任意非空字符串,内部会派生为 AES-128 密钥) |
返回值:String - Base64 编码的密文(含前置 IV)
异常:
IllegalArgumentException- 密钥为空或加密失败
示例:
java
String encrypted = AesUtil.encrypt("Hello World", "key123");decrypt
解密字符串内容(默认 GCM)。
java
public static String decrypt(String content, String password)参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| content | String | 是 | 待解密的密文(Base64,格式见下文) |
| password | String | 是 | 解密口令(必须与加密时一致) |
返回值:String - 解密后的明文
异常:
IllegalArgumentException- 密钥错误、密文格式错误或 Tag 校验失败
示例:
java
String decrypted = AesUtil.decrypt(encrypted, "key123");encrypt / decrypt(自定义算法)
java
public static String encrypt(String content, String password, String aesCipherAlgorithm)
public static String decrypt(String content, String password, String aesCipherAlgorithm)| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| aesCipherAlgorithm | String | 是 | Cipher 算法名;含 GCM(忽略大小写)走 GCM 路径,否则走无 IV 的旧 Cipher 路径 |
示例(推荐,与默认一致):
java
String encrypted = AesUtil.encrypt("data", "key", "AES/GCM/NoPadding");
String decrypted = AesUtil.decrypt(encrypted, "key", "AES/GCM/NoPadding");示例(非默认,兼容旧路径):
java
// 算法名不含 GCM 时:无随机 IV,直接 Cipher.init(ENCRYPT_MODE, key)
// 一般不推荐新业务使用
String encrypted = AesUtil.encrypt("data", "key", "AES/ECB/PKCS5Padding");完整示例
示例 1:敏感字段加解密
java
import com.molandev.core.util.encrypt.AesUtil;
public class FieldEncryption {
private static final String AES_PASSWORD = "MyApp2024Secret!";
public String encryptField(String raw) {
return AesUtil.encrypt(raw, AES_PASSWORD);
}
public String decryptField(String encrypted) {
return AesUtil.decrypt(encrypted, AES_PASSWORD);
}
}注意
登录密码哈希请使用 PasswordEncoder(BCrypt),不要用 AES 可逆加密存密码。
示例 2:配置文件敏感信息加密
java
import com.molandev.core.util.encrypt.AesUtil;
public class ConfigEncryption {
private static final String KEY = "ConfigKey123456";
public static String encryptConfig(String value) {
return "ENC(" + AesUtil.encrypt(value, KEY) + ")";
}
public static String decryptConfig(String encryptedValue) {
if (encryptedValue.startsWith("ENC(") && encryptedValue.endsWith(")")) {
String encrypted = encryptedValue.substring(4, encryptedValue.length() - 1);
return AesUtil.decrypt(encrypted, KEY);
}
return encryptedValue;
}
}示例 3:数据传输加密
java
import com.molandev.core.util.encrypt.AesUtil;
import com.molandev.core.util.DateUtils;
public class DataTransfer {
private static final String TRANSFER_KEY = "TransferKey@2024";
public static String encryptData(String data) {
String timestamp = DateUtils.now();
String content = data + "|" + timestamp;
return AesUtil.encrypt(content, TRANSFER_KEY);
}
public static String decryptData(String encrypted) {
String decrypted = AesUtil.decrypt(encrypted, TRANSFER_KEY);
String[] parts = decrypted.split("\\|");
if (parts.length == 2) {
return parts[0];
}
throw new IllegalArgumentException("数据格式错误");
}
}技术细节
默认加密算法
| 项 | 说明 |
|---|---|
| 默认算法 | AES/GCM/NoPadding |
| GCM IV | 12 字节随机,前置拼到密文前 |
| GCM Tag | 128 bit(由 Cipher 输出附在 ciphertext 后) |
| 输出格式 | Base64(IV || ciphertext||tag) |
| 明文编码 | UTF-8 |
| AES 密钥长度 | 128 bit(16 字节) |
密钥派生
对 password 做 SHA-256,取摘要的前 16 字节作为 AES-128 密钥:
text
AES_KEY = SHA-256(UTF-8(password))[0..15]- 口令可为任意非空字符串,不再做「不足 16 右侧补
0/ 超过 16 抛异常」 - 空口令仍抛
IllegalArgumentException
java
// 任意长度口令均可(非空)
AesUtil.encrypt("data", "short");
AesUtil.encrypt("data", "thisKeyIsLongerThan16Characters");自定义算法分支
- 算法名包含
GCM(不区分大小写)→ GCM 路径:随机 IV + Tag,输出Base64(IV\|\|cipher+tag) - 否则 → 旧路径:无 IV,
Cipher.init(mode, key),输出Base64(ciphertext)
前端对齐(Web Crypto)
与后端默认 GCM 对接时,密钥派生与密文格式必须一致:
javascript
/**
* 与 AesUtil 对齐:
* - 密钥:SHA-256(password) 前 16 字节 → AES-128
* - 算法:AES-GCM,IV 12 字节前置,Tag 128 bit
* - 输出:Base64(IV || ciphertext||tag)
*/
async function deriveAesKey(password) {
const hash = await crypto.subtle.digest(
'SHA-256',
new TextEncoder().encode(password)
);
const keyBytes = new Uint8Array(hash).slice(0, 16);
return crypto.subtle.importKey(
'raw', keyBytes, { name: 'AES-GCM' }, false, ['encrypt', 'decrypt']
);
}
function bytesToBase64(bytes) {
let s = '';
bytes.forEach((b) => { s += String.fromCharCode(b); });
return btoa(s);
}
function base64ToBytes(b64) {
const bin = atob(b64);
const out = new Uint8Array(bin.length);
for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
return out;
}
async function aesGcmEncrypt(plaintext, password) {
const key = await deriveAesKey(password);
const iv = crypto.getRandomValues(new Uint8Array(12));
const cipherBuf = await crypto.subtle.encrypt(
{ name: 'AES-GCM', iv, tagLength: 128 },
key,
new TextEncoder().encode(plaintext)
);
const combined = new Uint8Array(12 + cipherBuf.byteLength);
combined.set(iv, 0);
combined.set(new Uint8Array(cipherBuf), 12);
return bytesToBase64(combined);
}
async function aesGcmDecrypt(base64Cipher, password) {
const key = await deriveAesKey(password);
const data = base64ToBytes(base64Cipher);
const iv = data.slice(0, 12);
const cipher = data.slice(12);
const plainBuf = await crypto.subtle.decrypt(
{ name: 'AES-GCM', iv, tagLength: 128 },
key,
cipher
);
return new TextDecoder().decode(plainBuf);
}注意事项
⚠️ 密钥管理
java
// ❌ 不要在代码中硬编码密钥
public static final String KEY = "mykey123";
// ✅ 推荐:从配置文件或环境变量读取
String key = System.getenv("AES_KEY");⚠️ 加解密一致性
java
// ❌ 口令不一致会解密失败(GCM 下 Tag 校验失败)
String encrypted = AesUtil.encrypt("data", "key1");
AesUtil.decrypt(encrypted, "key2");
// ✅ 使用相同口令
String decrypted = AesUtil.decrypt(encrypted, "key1");⚠️ 异常处理
java
try {
String encrypted = AesUtil.encrypt("data", "key");
} catch (IllegalArgumentException e) {
logger.error("加密失败", e);
}性能说明
- 加密速度: 约 100MB/s(取决于硬件)
- 内存占用: 低,无状态设计
- 线程安全: 是(每次调用独立 Cipher / IV)
安全建议
- 定期更换密钥: 建议定期更换加密口令
- 密钥存储: 口令/密钥应存储在安全处,不要提交到版本控制
- 使用强口令: 口令应足够长且不可预测(派生后仍为 AES-128)
- 优先默认 GCM: 新业务不要再选用 ECB
常见问题
Q: AES 和 RSA 如何选择?
A:
- AES: 对称加密,性能高,适合大量数据加密
- RSA: 非对称加密,适合密钥交换和小数据加密(如登录密码参数)
Q: 口令长度有限制吗?
A: 没有 16 字符上限。任意非空口令都会经 SHA-256 派生为 16 字节 AES-128 密钥。
Q: 为什么每次加密同一明文结果不同?
A: 默认 GCM 每次使用随机 12 字节 IV,密文会变化;解密时从密文前 12 字节取出 IV,结果仍正确。这是正常且期望的行为。
Q: 加密后的数据可以存储到数据库吗?
A: 可以。结果是 Base64 字符串;GCM 相对 ECB 会多约 12 字节 IV + 16 字节 Tag,字段长度请预留余量。