package com.xiaopeng.common.core.utils;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Base64;
/**
* AES 加密解密工具类
*
* 支持功能:
* 1. AES 加密、解密(支持多种模式和填充方式)
* 2. 密钥生成(支持 128/192/256 位)
* 3. 初始化向量(IV)生成
* 4. Base64 编码/解码
*
* 支持的模式:
* - AES/ECB/PKCS5Padding:简单模式,不需要IV
* - AES/CBC/PKCS5Padding:需要IV,更安全
* - AES/GCM/NoPadding:需要IV和Tag,最适合现代应用
*
* 注意事项:
* - ECB 模式不安全,不建议使用
* - 密钥长度必须是 16/24/32 字节(对应 128/192/256 位)
* - IV 长度:AES 块大小为 16 字节
* - GCM 模式 Tag 长度为 16 字节
* - 使用 ThreadLocal 缓存 Cipher 实例,提升高并发性能
*/
public class AESUtil {
private static final String AES_ALGORITHM = "AES";
private static final String TRANSFORMATION_ECB = "AES/ECB/PKCS5Padding";
private static final String TRANSFORMATION_CBC = "AES/CBC/PKCS5Padding";
private static final String TRANSFORMATION_GCM = "AES/GCM/NoPadding";
private static final int GCM_TAG_LENGTH = 128;
private static final int IV_LENGTH = 16;
public static final int KEY_LENGTH_128 = 128;
public static final int KEY_LENGTH_192 = 192;
public static final int KEY_LENGTH_256 = 256;
/**
* 缓存 Cipher 实例,提高高并发性能
* Cipher 不是线程安全的,每个线程使用自己的实例
*/
private static final ThreadLocal ECB_ENCRYPT_CIPHER = ThreadLocal.withInitial(() -> {
try {
return Cipher.getInstance(TRANSFORMATION_ECB);
} catch (Exception e) {
throw new RuntimeException("初始化 ECB 加密 Cipher 失败", e);
}
});
private static final ThreadLocal ECB_DECRYPT_CIPHER = ThreadLocal.withInitial(() -> {
try {
return Cipher.getInstance(TRANSFORMATION_ECB);
} catch (Exception e) {
throw new RuntimeException("初始化 ECB 解密 Cipher 失败", e);
}
});
private static final ThreadLocal CBC_ENCRYPT_CIPHER = ThreadLocal.withInitial(() -> {
try {
return Cipher.getInstance(TRANSFORMATION_CBC);
} catch (Exception e) {
throw new RuntimeException("初始化 CBC 加密 Cipher 失败", e);
}
});
private static final ThreadLocal CBC_DECRYPT_CIPHER = ThreadLocal.withInitial(() -> {
try {
return Cipher.getInstance(TRANSFORMATION_CBC);
} catch (Exception e) {
throw new RuntimeException("初始化 CBC 解密 Cipher 失败", e);
}
});
private static final ThreadLocal GCM_ENCRYPT_CIPHER = ThreadLocal.withInitial(() -> {
try {
return Cipher.getInstance(TRANSFORMATION_GCM);
} catch (Exception e) {
throw new RuntimeException("初始化 GCM 加密 Cipher 失败", e);
}
});
private static final ThreadLocal GCM_DECRYPT_CIPHER = ThreadLocal.withInitial(() -> {
try {
return Cipher.getInstance(TRANSFORMATION_GCM);
} catch (Exception e) {
throw new RuntimeException("初始化 GCM 解密 Cipher 失败", e);
}
});
/**
* 生成 AES 密钥(默认 128 位)
*
* @return Base64 编码的密钥
*/
public static String generateKey() {
return generateKey(KEY_LENGTH_128);
}
/**
* 生成指定长度的 AES 密钥
*
* @param keySize 密钥长度(128/192/256)
* @return Base64 编码的密钥
*/
public static String generateKey(int keySize) {
try {
KeyGenerator keyGenerator = KeyGenerator.getInstance(AES_ALGORITHM);
keyGenerator.init(keySize, new SecureRandom());
SecretKey secretKey = keyGenerator.generateKey();
return Base64.getEncoder().encodeToString(secretKey.getEncoded());
} catch (Exception e) {
throw new RuntimeException("生成密钥失败", e);
}
}
/**
* 从字符串生成 AES 密钥
*
* @param password 密码字符串
* @param keySize 密钥长度(128/192/256)
* @return Base64 编码的密钥
*/
public static String generateKeyFromPassword(String password, int keySize) {
try {
byte[] passwordBytes = password.getBytes(StandardCharsets.UTF_8);
byte[] keyBytes = new byte[keySize / 8];
System.arraycopy(passwordBytes, 0, keyBytes, 0, Math.min(passwordBytes.length, keyBytes.length));
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, AES_ALGORITHM);
return Base64.getEncoder().encodeToString(keySpec.getEncoded());
} catch (Exception e) {
throw new RuntimeException("从密码生成密钥失败", e);
}
}
/**
* 生成初始化向量(IV)
*
* @return Base64 编码的 IV
*/
public static String generateIV() {
try {
byte[] iv = new byte[IV_LENGTH];
new SecureRandom().nextBytes(iv);
return Base64.getEncoder().encodeToString(iv);
} catch (Exception e) {
throw new RuntimeException("生成 IV 失败", e);
}
}
/**
* ECB 模式加密
*
* @param data 待加密数据
* @param key Base64 编码的密钥
* @return Base64 编码的密文
*/
public static String encryptByECB(String data, String key) {
try {
byte[] keyBytes = Base64.getDecoder().decode(key);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, AES_ALGORITHM);
Cipher cipher = ECB_ENCRYPT_CIPHER.get();
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
byte[] encrypted = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encrypted);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("ECB 加密失败", e);
}
}
/**
* ECB 模式解密
*
* @param data Base64 编码的密文
* @param key Base64 编码的密钥
* @return 明文
*/
public static String decryptByECB(String data, String key) {
try {
byte[] keyBytes = Base64.getDecoder().decode(key);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, AES_ALGORITHM);
Cipher cipher = ECB_DECRYPT_CIPHER.get();
cipher.init(Cipher.DECRYPT_MODE, keySpec);
byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(data));
return new String(decrypted, StandardCharsets.UTF_8);
} catch (Exception e) {
throw new RuntimeException("ECB 解密失败", e);
}
}
/**
* CBC 模式加密
*
* @param data 待加密数据
* @param key Base64 编码的密钥
* @param iv Base64 编码的 IV
* @return Base64 编码的密文
*/
public static String encryptByCBC(String data, String key, String iv) {
try {
byte[] keyBytes = Base64.getDecoder().decode(key);
byte[] ivBytes = Base64.getDecoder().decode(iv);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, AES_ALGORITHM);
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
Cipher cipher = CBC_ENCRYPT_CIPHER.get();
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
byte[] encrypted = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encrypted);
} catch (Exception e) {
throw new RuntimeException("CBC 加密失败", e);
}
}
/**
* CBC 模式解密
*
* @param data Base64 编码的密文
* @param key Base64 编码的密钥
* @param iv Base64 编码的 IV
* @return 明文
*/
public static String decryptByCBC(String data, String key, String iv) {
try {
byte[] keyBytes = Base64.getDecoder().decode(key);
byte[] ivBytes = Base64.getDecoder().decode(iv);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, AES_ALGORITHM);
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
Cipher cipher = CBC_DECRYPT_CIPHER.get();
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(data));
return new String(decrypted, StandardCharsets.UTF_8);
} catch (Exception e) {
throw new RuntimeException("CBC 解密失败", e);
}
}
/**
* GCM 模式加密(推荐)
*
* @param data 待加密数据
* @param key Base64 编码的密钥
* @param iv Base64 编码的 IV
* @return Base64 编码的密文
*/
public static String encryptByGCM(String data, String key, String iv) {
try {
byte[] keyBytes = Base64.getDecoder().decode(key);
byte[] ivBytes = Base64.getDecoder().decode(iv);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, AES_ALGORITHM);
GCMParameterSpec gcmSpec = new GCMParameterSpec(GCM_TAG_LENGTH, ivBytes);
Cipher cipher = GCM_ENCRYPT_CIPHER.get();
cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmSpec);
byte[] encrypted = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encrypted);
} catch (Exception e) {
throw new RuntimeException("GCM 加密失败", e);
}
}
/**
* GCM 模式解密(推荐)
*
* @param data Base64 编码的密文
* @param key Base64 编码的密钥
* @param iv Base64 编码的 IV
* @return 明文
*/
public static String decryptByGCM(String data, String key, String iv) {
try {
byte[] keyBytes = Base64.getDecoder().decode(key);
byte[] ivBytes = Base64.getDecoder().decode(iv);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, AES_ALGORITHM);
GCMParameterSpec gcmSpec = new GCMParameterSpec(GCM_TAG_LENGTH, ivBytes);
Cipher cipher = GCM_DECRYPT_CIPHER.get();
cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmSpec);
byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(data));
return new String(decrypted, StandardCharsets.UTF_8);
} catch (Exception e) {
throw new RuntimeException("GCM 解密失败", e);
}
}
/**
* 默认加密(使用 CBC 模式)
*
* @param data 待加密数据
* @param key Base64 编码的密钥
* @return Base64 编码的密文(包含 IV)
*/
public static String encrypt(String data, String key) {
String iv = generateIV();
String encrypted = encryptByCBC(data, key, iv);
return iv + ":" + encrypted;
}
/**
* 默认解密(使用 CBC 模式)
*
* @param data Base64 编码的密文(格式:iv:encrypted)
* @param key Base64 编码的密钥
* @return 明文
*/
public static String decrypt(String data, String key) {
try {
String[] parts = data.split(":");
if (parts.length != 2) {
throw new IllegalArgumentException("密文格式错误,应为 iv:encrypted");
}
return decryptByCBC(parts[1], key, parts[0]);
} catch (Exception e) {
throw new RuntimeException("解密失败", e);
}
}
/**
* 获取密钥字节数组
*
* @param key Base64 编码的密钥
* @return 密钥字节数组
*/
public static byte[] getKeyBytes(String key) {
return Base64.getDecoder().decode(key);
}
/**
* 获取 IV 字节数组
*
* @param iv Base64 编码的 IV
* @return IV 字节数组
*/
public static byte[] getIVBytes(String iv) {
return Base64.getDecoder().decode(iv);
}
/**
* 测试示例
*/
public static void main(String[] args) {
// 1. 生成密钥
String key = generateKey(KEY_LENGTH_256);
System.out.println("密钥: " + key);
System.out.println();
// 2. 测试 ECB 模式
String originalData = "Hello AES 加密测试!123456789";
System.out.println("原始数据: " + originalData);
String ecbEncrypted = encryptByECB(originalData, key);
System.out.println("ECB 加密: " + ecbEncrypted);
String ecbDecrypted = decryptByECB(ecbEncrypted, key);
System.out.println("ECB 解密: " + ecbDecrypted);
System.out.println();
// 3. 测试 CBC 模式
String iv = generateIV();
System.out.println("IV: " + iv);
String cbcEncrypted = encryptByCBC(originalData, key, iv);
System.out.println("CBC 加密: " + cbcEncrypted);
String cbcDecrypted = decryptByCBC(cbcEncrypted, key, iv);
System.out.println("CBC 解密: " + cbcDecrypted);
System.out.println();
// 4. 测试 GCM 模式(推荐)
String gcmEncrypted = encryptByGCM(originalData, key, iv);
System.out.println("GCM 加密: " + gcmEncrypted);
String gcmDecrypted = decryptByGCM(gcmEncrypted, key, iv);
System.out.println("GCM 解密: " + gcmDecrypted);
System.out.println();
// 5. 测试默认加密(包含 IV)
String encrypted = encrypt(originalData, key);
System.out.println("默认加密(含IV): " + encrypted);
String decrypted = decrypt(encrypted, key);
System.out.println("默认解密: " + decrypted);
System.out.println();
// 6. 从密码生成密钥
String passwordKey = generateKeyFromPassword("MySecretPassword123", KEY_LENGTH_256);
System.out.println("从密码生成的密钥: " + passwordKey);
String passEncrypted = encrypt(originalData, passwordKey);
String passDecrypted = decrypt(passEncrypted, passwordKey);
System.out.println("使用密码密钥解密: " + passDecrypted);
}
}