Java对称加密工具类设计与生产实践:从AES到国密SM4
Java对称加密实战:AES/SM4/PBE全场景指南与密钥管理最佳实践
选题说明:对称加密在项目中的常见痛点不是"不会用",而是"用不对"——密钥管理混乱、模式选择不当(ECB 泄露明文模式)、IV 重复使用、大文件加密 OOM。本文基于 pan-common 的对称加密工具实现,覆盖 AES/DES/3DES/SM4/PBE 五种算法的完整 API 和真实业务场景。
一、为什么需要统一的对称加密工具
JDK 的 javax.crypto 提供了基础加密能力,但在实际项目中会遇到这些问题:
- 算法切换成本高:从 AES 换到 SM4(国密合规),需要重写加密逻辑、调整密钥长度、修改 IV 生成
- 密钥/IV 管理碎片化:有的项目密钥硬编码,有的存在配置文件,IV 要么固定要么没存,解密时找不到
- 输出格式不统一:有的存 Hex,有的存 Base64,有的直接存 byte[],跨系统对接时反复转换
- 大文件加密 OOM:
Cipher.doFinal()一次性加载整个文件,GB 级日志或数据库备份直接内存溢出 - 模式选择不当:默认用 ECB 模式,相同明文块产生相同密文块,存在信息泄露风险
pan-common 的对称加密工具围绕这些问题做了系统性封装,统一 API 设计,支持所有算法的便捷方法、流式处理、密钥管理。
环境准备:
<!-- Spring Boot 2.x (javax) -->
<dependency>
<groupId>com.gitee.apanlh</groupId>
<artifactId>pan-common</artifactId>
<version>2.0.6</version>
</dependency>
<!-- Spring Boot 3.x (jakarta) -->
<dependency>
<groupId>com.gitee.apanlh</groupId>
<artifactId>pan-common</artifactId>
<version>3.0.6</version>
</dependency>
<!-- SM4 国密算法需要 Bouncy Castle -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>1.84</version>
</dependency>
二、算法选型对比
| 算法 | 密钥长度 | 块大小 | 安全性 | 适用场景 |
|---|---|---|---|---|
| AES | 128/192/256 位 | 16 字节 | ★★★★★ | 通用场景首选,互联网系统标配 |
| DES | 64 位(有效 56 位) | 8 字节 | ★☆☆☆☆ | 仅用于遗留系统兼容,不推荐新项目 |
| 3DES | 192 位 | 8 字节 | ★★★☆☆ | 兼容旧系统且需要一定安全性 |
| SM4 | 128 位 | 16 字节 | ★★★★☆ | 政务/金融/国企,国密合规要求 |
| PBE | 口令任意 | 取决于底层算法 | ★★★★☆ | 用户口令驱动加密(如文件加密) |
选型建议:
- 新项目统一用 AES-128 + CBC/PKCS5Padding
- 国密合规场景用 SM4 + CBC/PKCS5Padding
- 用户密码驱动加密用 PBE(SHA256 + AES)
- 绝对不要用 ECB 模式加密敏感数据(相同明文产生相同密文)
三、AES 加密实战
3.1 ECB 模式(快速验证,不推荐生产使用)
// 创建 AES-ECB 加密器(随机生成 128 位密钥)
AES aesEcb = AES.createEcb();
// 加密 - 多种输出格式
byte[] cipherBytes = aesEcb.encrypt("Hello World".getBytes(StandardCharsets.UTF_8));
String cipherHex = aesEcb.encryptToHex("Hello World");
String cipherBase64 = aesEcb.encryptToBase64Str("Hello World");
// 解密
byte[] plainBytes = aesEcb.decrypt(cipherBytes);
String plainFromHex = aesEcb.decryptFromHexStr(cipherHex);
String plainFromBase64 = aesEcb.decryptFromBase64Str(cipherBase64);
System.out.println(plainFromHex); // "Hello World"
便捷方法一览:
encrypt(byte[])→byte[]encryptToHex(String)→String(Hex 编码)encryptToBase64Str(String)→String(Base64 编码)decrypt(byte[])→byte[]decryptFromHexStr(String)→StringdecryptFromBase64Str(String)→String
3.2 CBC 模式(生产推荐)
// 生成密钥和 IV
byte[] key = KeyUtils.generate128(); // 16 字节 = 128 位
byte[] iv = KeyUtils.generate128(); // 16 字节 IV
// 创建 AES-CBC 加密器(默认 PKCS5Padding)
AES aesCbc = new AES(key, iv);
// 加密
byte[] encrypted = aesCbc.encrypt("敏感数据".getBytes(StandardCharsets.UTF_8));
String cipherHex = aesCbc.encryptToHex("敏感数据");
// 解密
byte[] decrypted = aesCbc.decrypt(encrypted);
String plainText = aesCbc.decryptFromHexStr(cipherHex);
// 获取密钥和 IV 的编码表示(用于存储和传输)
String keyHex = aesCbc.getKeyToHex();
String keyBase64 = aesCbc.getKeyToBase64Str();
String ivHex = aesCbc.getIvToHex();
String ivBase64 = aesCbc.getIvToBase64Str();
System.out.println("密钥(Hex): " + keyHex);
System.out.println("IV(Base64): " + ivBase64);
3.3 自定义模式与填充
// CFB 流模式(无需填充,适合流式数据)
AES aesCfb = new AES(key, iv, AlgorithmMode.CFB, AlgorithmPadding.NO_PADDING);
// CTR 计数器模式(支持并行加密)
AES aesCtr = new AES(key, iv, AlgorithmMode.CTR, AlgorithmPadding.NO_PADDING);
// OFB 输出反馈模式
AES aesOfb = new AES(key, iv, AlgorithmMode.OFB, AlgorithmPadding.NO_PADDING);
3.4 从存储中重建加密器
// 从数据库/配置文件读取密钥和 IV
String storedKeyHex = "a1b2c3d4e5f6..."; // 从数据库读取
String storedIvBase64 = "dGVzdGl2MTIz..."; // 从数据库读取
// 解码并重建加密器
byte[] restoredKey = HexUtils.decode(storedKeyHex);
byte[] restoredIv = Base64Utils.decode(storedIvBase64);
AES aesRestored = new AES(restoredKey, restoredIv);
// 解密
String plainText = aesRestored.decryptFromHexStr(cipherHex);
3.5 实战:用户手机号加密存储
@Service
public class UserService {
@Value("${encryption.aes.key}")
private String keyHex;
@Value("${encryption.aes.iv}")
private String ivHex;
/**
* 注册时加密手机号
*/
public void registerUser(String phone, String password) {
// 创建加密器
AES aes = new AES(HexUtils.decode(keyHex), HexUtils.decode(ivHex));
// 加密手机号
String encryptedPhone = aes.encryptToBase64Str(phone);
// 存储到数据库
User user = new User();
user.setPhone(encryptedPhone); // 数据库中存密文
user.setPassword(BCrypt.hashpw(password, BCrypt.gensalt()));
userMapper.insert(user);
}
/**
* 查询时解密手机号
*/
public String getPhone(Long userId) {
User user = userMapper.selectById(userId);
if (user == null) {
return null;
}
AES aes = new AES(HexUtils.decode(keyHex), HexUtils.decode(ivHex));
return aes.decryptFromBase64Str(user.getPhone());
}
/**
* 根据手机号查询用户(需要先加密查询条件)
*/
public User findByPhone(String phone) {
AES aes = new AES(HexUtils.decode(keyHex), HexUtils.decode(ivHex));
String encryptedPhone = aes.encryptToBase64Str(phone);
return userMapper.selectByEncryptedPhone(encryptedPhone);
}
}
配置文件(application.yml):
encryption:
aes:
key: "0123456789abcdef0123456789abcdef" # 32 字符 = 16 字节 = 128 位
iv: "abcdef0123456789abcdef0123456789" # 32 字符 = 16 字节
注意:生产环境密钥应使用密钥管理服务(KMS)或环境变量注入,不要硬编码在代码中。
四、SM4 国密加密实战
4.1 基础用法
// ECB 模式(快速测试)
SM4 sm4Ecb = SM4.createEcb();
String cipherHex = sm4Ecb.encryptToHex("国密算法测试");
String plainText = sm4Ecb.decryptFromHexStr(cipherHex);
// CBC 模式(生产推荐)
byte[] key = KeyUtils.generate128(); // 16 字节
byte[] iv = KeyUtils.generate128(); // 16 字节
SM4 sm4Cbc = new SM4(key, iv); // 默认 CBC/PKCS5Padding
byte[] encrypted = sm4Cbc.encrypt("敏感数据".getBytes(StandardCharsets.UTF_8));
byte[] decrypted = sm4Cbc.decrypt(encrypted);
// 自定义模式
SM4 sm4Cfb = new SM4(key, iv, AlgorithmMode.CFB, AlgorithmPadding.NO_PADDING);
4.2 实战:政务系统数据交换
政务系统对接要求使用国密算法,数据格式通常为 JSON,需要对敏感字段加密:
@Service
public class GovDataExchangeService {
@Value("${sm4.key}")
private String sm4KeyHex;
@Value("${sm4.iv}")
private String sm4IvHex;
/**
* 向政务平台提交数据(加密敏感字段)
*/
public String submitToGovPlatform(CitizenInfo info) {
SM4 sm4 = new SM4(HexUtils.decode(sm4KeyHex), HexUtils.decode(sm4IvHex));
// 构造 JSON,加密敏感字段
JSONObject json = new JSONObject();
json.put("name", info.getName()); // 姓名不加密
json.put("idCard", sm4.encryptToBase64Str(info.getIdCard())); // 身份证加密
json.put("phone", sm4.encryptToBase64Str(info.getPhone())); // 手机号加密
json.put("address", sm4.encryptToBase64Str(info.getAddress())); // 地址加密
// 提交到政务平台
String response = httpClient.post("https://gov-api.example.com/submit",
json.toJSONString());
return response;
}
/**
* 从政务平台接收数据(解密敏感字段)
*/
public CitizenInfo receiveFromGovPlatform(String encryptedJson) {
SM4 sm4 = new SM4(HexUtils.decode(sm4KeyHex), HexUtils.decode(sm4IvHex));
JSONObject json = JSON.parseObject(encryptedJson);
CitizenInfo info = new CitizenInfo();
info.setName(json.getString("name"));
info.setIdCard(sm4.decryptFromBase64Str(json.getString("idCard")));
info.setPhone(sm4.decryptFromBase64Str(json.getString("phone")));
info.setAddress(sm4.decryptFromBase64Str(json.getString("address")));
return info;
}
}
4.3 国密合规检查清单
- ✅ 使用 SM4 算法(而非 AES)
- ✅ 密钥长度 128 位
- ✅ CBC 模式 + PKCS5Padding
- ✅ IV 随机生成(每次加密不同)
- ✅ 密钥和 IV 分开存储
- ✅ 密钥定期轮换(建议每年)
- ✅ 密钥通过安全渠道分发(如 U 盘、加密邮件)
五、PBE 口令加密实战
PBE(Password-Based Encryption)适用于"用户输入密码,用这个密码加密文件"的场景,如加密备份、私密文件。
5.1 基础用法
// 最简单用法:自动生成盐值
PBE pbe = PBE.create("userPassword123");
byte[] encrypted = pbe.encrypt("sensitive data".getBytes(StandardCharsets.UTF_8));
byte[] decrypted = pbe.decrypt(encrypted);
// 获取盐值和迭代次数(必须存储,解密时需要)
byte[] salt = pbe.getSalt();
int iteration = pbe.getIterationCount();
System.out.println("盐值(Hex): " + HexUtils.encode(salt));
System.out.println("迭代次数: " + iteration);
5.2 自定义盐值和迭代次数
// 生成 16 字节盐值
byte[] customSalt = KeyUtils.generate128();
// 指定迭代次数 10000 次,使用 SHA256 + AES-CBC
PBE pbe = new PBE("userPassword123", customSalt, 10000,
PBEAlgorithms.SHA256_256_AES_CBC);
byte[] encrypted = pbe.encrypt("sensitive data".getBytes());
5.3 实战:用户文件加密
@Service
public class FileEncryptionService {
/**
* 加密用户文件(基于用户密码)
* @param file 原始文件
* @param userPassword 用户输入的密码
* @return 加密后的文件路径
*/
public String encryptFile(File file, String userPassword) throws IOException {
// 创建 PBE 加密器(自动生成盐值)
PBE pbe = PBE.create(userPassword);
// 加密文件
String encryptedPath = file.getAbsolutePath() + ".enc";
File encryptedFile = new File(encryptedPath);
pbe.encrypt(file, encryptedFile); // 流式加密,支持 GB 级文件
// 保存盐值和迭代次数到元数据文件
String metadataPath = encryptedPath + ".meta";
Properties meta = new Properties();
meta.setProperty("salt", HexUtils.encode(pbe.getSalt()));
meta.setProperty("iteration", String.valueOf(pbe.getIterationCount()));
try (FileOutputStream fos = new FileOutputStream(metadataPath)) {
meta.store(fos, "Encryption metadata");
}
return encryptedPath;
}
/**
* 解密用户文件
* @param encryptedFile 加密文件
* @param userPassword 用户输入的密码
* @return 解密后的文件路径
*/
public String decryptFile(File encryptedFile, String userPassword) throws IOException {
// 读取元数据
String metadataPath = encryptedFile.getAbsolutePath() + ".meta";
Properties meta = new Properties();
try (FileInputStream fis = new FileInputStream(metadataPath)) {
meta.load(fis);
}
byte[] salt = HexUtils.decode(meta.getProperty("salt"));
int iteration = Integer.parseInt(meta.getProperty("iteration"));
// 重建 PBE 加密器
PBE pbe = new PBE(userPassword, salt, iteration,
PBEAlgorithms.SHA256_256_AES_CBC);
// 解密文件
String decryptedPath = encryptedFile.getAbsolutePath().replace(".enc", "");
File decryptedFile = new File(decryptedPath);
pbe.decrypt(encryptedFile, decryptedFile);
return decryptedPath;
}
}
使用示例:
// 加密
File originalFile = new File("/path/to/secret.pdf");
String encryptedPath = fileEncryptionService.encryptFile(originalFile, "MyPassword123");
// 生成: secret.pdf.enc 和 secret.pdf.enc.meta
// 解密
File encryptedFile = new File(encryptedPath);
String decryptedPath = fileEncryptionService.decryptFile(encryptedFile, "MyPassword123");
// 恢复: secret.pdf
5.4 PBE 安全性建议
| 参数 | 推荐值 | 说明 |
|---|---|---|
| 盐值长度 | 16 字节 | 至少 8 字节,防止彩虹表攻击 |
| 迭代次数 | 10000 | 至少 1000,根据性能调整(越高越慢但越安全) |
| 算法选择 | SHA256_256_AES_CBC | SHA256 哈希 + 256 位密钥 + AES-CBC |
| 盐值存储 | 与密文一起存储 | 盐值不需要保密,但必须和解密时使用相同值 |
六、大文件流式加密
6.1 基础用法
// 创建 AES 加密器(随机密钥)
AES aes = new AES();
File srcFile = new File("/path/to/large.dat"); // 10 GB 日志文件
File encFile = new File("/path/to/large.enc"); // 加密后文件
File decFile = new File("/path/to/large.dec"); // 解密后文件
// 加密文件(流式处理,内存占用恒定)
aes.encrypt(srcFile, encFile);
// 解密文件
aes.decrypt(encFile, decFile);
6.2 自定义流控制
// 使用 InputStream/OutputStream 自定义控制
AES aes = new AES(key, iv);
try (InputStream in = new FileInputStream(srcFile);
OutputStream out = new FileOutputStream(encFile)) {
// false 表示不自动关闭流,由 try-with-resources 管理
aes.encrypt(in, out, false);
}
6.3 实战:数据库备份加密
@Service
public class DatabaseBackupService {
@Value("${backup.encryption.key}")
private String backupKeyHex;
/**
* 备份数据库并加密
*/
public void backupAndEncrypt(String dbName, String backupDir) throws Exception {
// 1. 执行数据库备份(mysqldump/pg_dump)
String sqlFile = backupDir + "/" + dbName + "_" +
DateUtils.now("yyyyMMdd_HHmmss") + ".sql";
executeBackupCommand(dbName, sqlFile);
// 2. 加密备份文件
AES aes = new AES(HexUtils.decode(backupKeyHex));
String encryptedFile = sqlFile + ".enc";
aes.encrypt(new File(sqlFile), new File(encryptedFile));
// 3. 删除原始 SQL 文件(只保留加密文件)
new File(sqlFile).delete();
// 4. 保存 IV(密钥固定,IV 随机)
String ivFile = encryptedFile + ".iv";
FileUtils.writeStringToFile(new File(ivFile), aes.getIvToHex(), "UTF-8");
log.info("备份加密完成: {}", encryptedFile);
}
/**
* 恢复数据库(解密 + 导入)
*/
public void decryptAndRestore(String encryptedFile, String dbName) throws Exception {
// 1. 读取 IV
String ivFile = encryptedFile + ".iv";
String ivHex = FileUtils.readFileToString(new File(ivFile), "UTF-8");
// 2. 解密
AES aes = new AES(HexUtils.decode(backupKeyHex), HexUtils.decode(ivHex));
String sqlFile = encryptedFile.replace(".enc", "");
aes.decrypt(new File(encryptedFile), new File(sqlFile));
// 3. 导入数据库
executeRestoreCommand(dbName, sqlFile);
// 4. 删除解密后的 SQL 文件
new File(sqlFile).delete();
log.info("数据库恢复完成: {}", dbName);
}
}
流式加密优势:
- 内存占用恒定(默认 8KB 缓冲区),不受文件大小影响
- 支持 GB 级文件(日志、备份、镜像)
- 所有算法(AES/DES/3DES/SM4/PBE)都支持流式处理
七、密钥与 IV 管理最佳实践
7.1 密钥生成
// 生成不同长度的密钥
byte[] key64 = KeyUtils.generate64(); // 8 字节 = 64 位(DES)
byte[] key128 = KeyUtils.generate128(); // 16 字节 = 128 位(AES/SM4)
byte[] key192 = KeyUtils.generate192(); // 24 字节 = 192 位(3DES)
byte[] key256 = KeyUtils.generate256(); // 32 字节 = 256 位(AES-256)
7.2 密钥存储
方式一:配置文件(适合开发/测试)
# application.yml
encryption:
aes:
key: "0123456789abcdef0123456789abcdef" # Hex 编码
iv: "abcdef0123456789abcdef0123456789"
方式二:环境变量(适合生产)
export AES_KEY="0123456789abcdef0123456789abcdef"
export AES_IV="abcdef0123456789abcdef0123456789"
@Value("${AES_KEY}")
private String keyHex;
方式三:密钥管理服务(KMS)(推荐生产)
阿里云 KMS、AWS KMS、HashiCorp Vault 等,密钥不落地,通过 API 调用。
7.3 IV 管理
原则:IV 不需要保密,但必须随机且唯一
// 方式一:自动生成(推荐)
AES aes = new AES(key); // 不传 IV,自动生成随机 IV
byte[] iv = aes.getIv(); // 获取自动生成的 IV
// 方式二:手动指定
byte[] iv = KeyUtils.generate128();
AES aes = new AES(key, iv);
IV 存储方式:
- 与密文一起存储(前置或后置)
- 存入数据库字段(与密文同行)
- 存入元数据文件(如
.iv文件)
// 示例:密文 + IV 一起存储
AES aes = new AES(key); // 自动生成 IV
byte[] encrypted = aes.encrypt(data);
byte[] iv = aes.getIv();
// 存储格式:IV(16字节) + 密文
byte[] stored = new byte[iv.length + encrypted.length];
System.arraycopy(iv, 0, stored, 0, iv.length);
System.arraycopy(encrypted, 0, stored, iv.length, encrypted.length);
// 存入数据库
mapper.save(stored);
7.4 密钥轮换
/**
* 密钥轮换:用新密钥重新加密数据
*/
public void rotateKey(Long userId, String oldKeyHex, String newKeyHex) {
// 用旧密钥解密
AES oldAes = new AES(HexUtils.decode(oldKeyHex), HexUtils.decode(oldIvHex));
User user = userMapper.selectById(userId);
String phone = oldAes.decryptFromBase64Str(user.getPhone());
// 用新密钥加密
AES newAes = new AES(HexUtils.decode(newKeyHex), HexUtils.decode(newIvHex));
String encryptedPhone = newAes.encryptToBase64Str(phone);
// 更新数据库
user.setPhone(encryptedPhone);
userMapper.update(user);
}
八、模式与填充选型指南
8.1 工作模式对比
| 模式 | 特点 | 适用场景 | 是否需要填充 |
|---|---|---|---|
| ECB | 相同明文 → 相同密文(不安全) | ❌ 不推荐使用 | 是 |
| CBC | 依赖前一个密文块,安全性高 | ✅ 通用场景首选 | 是 |
| CFB | 流模式,无需填充 | 流式数据、实时通信 | 否 |
| OFB | 流模式,错误不传播 | 噪声信道(如卫星通信) | 否 |
| CTR | 计数器模式,支持并行 | 高性能场景、磁盘加密 | 否 |
8.2 填充方式对比
| 填充 | 特点 | 适用场景 |
|---|---|---|
| PKCS5Padding | 标准填充,Java 默认 | ✅ 推荐,通用场景 |
| PKCS7Padding | 与 PKCS5 在 Java 中等价 | ✅ 推荐 |
| NoPadding | 不填充,要求数据对齐 | 流模式(CFB/OFB/CTR) |
| ZeroPadding | 补零,不安全 | ❌ 仅用于遗留系统兼容 |
8.3 推荐组合
// 场景一:通用数据加密(数据库字段、API 传输)
AES aes = new AES(key, iv, AlgorithmMode.CBC, AlgorithmPadding.PKCS5);
// 场景二:流式数据加密(日志、实时通信)
AES aes = new AES(key, iv, AlgorithmMode.CFB, AlgorithmPadding.NO_PADDING);
// 场景三:高性能场景(磁盘加密、大文件)
AES aes = new AES(key, iv, AlgorithmMode.CTR, AlgorithmPadding.NO_PADDING);
九、安全注意事项
9.1 ECB 模式的风险
// ❌ 错误示例:用 ECB 加密图片
AES aesEcb = AES.createEcb();
byte[] encrypted = aesEcb.encrypt(imageBytes);
// 问题:相同颜色的像素块会产生相同密文,加密后的图片仍能看出轮廓
原因:ECB 模式对每个块独立加密,相同明文块产生相同密文块,攻击者可以通过分析密文模式推断明文结构。
解决:使用 CBC/CFB/OFB/CTR 模式,引入 IV 打破模式。
9.2 IV 重复使用的风险
// ❌ 错误示例:固定 IV
byte[] iv = "1234567890123456".getBytes(); // 固定 IV
AES aes = new AES(key, iv);
// 问题:同一密钥 + 相同 IV = 相同密文,失去语义安全性
// ✅ 正确做法:每次加密使用新 IV
AES aes = new AES(key); // 自动生成随机 IV
byte[] iv = aes.getIv(); // 保存 IV 用于解密
9.3 密钥长度选择
| 安全等级 | AES 密钥长度 | 适用场景 |
|---|---|---|
| 标准 | 128 位 | 通用业务数据 |
| 高 | 192 位 | 金融交易、医疗数据 |
| 极高 | 256 位 | 国家机密、军事数据 |
9.4 PBE 迭代次数
// ❌ 迭代次数过低
PBE pbe = new PBE(password, salt, 100, ...); // 100 次,容易被暴力破解
// ✅ 推荐迭代次数
PBE pbe = new PBE(password, salt, 10000, ...); // 10000 次,平衡安全与性能
测试方法:测量 1000 次加密的平均耗时,调整迭代次数使单次加密耗时在 100-500ms。
9.5 密文长度计算
// AES-CBC 密文长度 = ceil(明文长度 / 16) * 16 + 16(最后一个填充块)
// 示例:明文 20 字节 → 密文 32 字节
// 示例:明文 32 字节 → 密文 48 字节(即使对齐也会加一个填充块)
十、DES/3DES 兼容旧系统
10.1 DES(64 位密钥,不推荐新项目)
// ECB 模式
DES desEcb = DES.createEcb();
String encrypted = desEcb.encryptToBase64Str("secret");
byte[] decrypted = desEcb.decryptFromBase64(encrypted);
// CBC 模式
byte[] key = KeyUtils.generate64(); // 8 字节
byte[] iv = KeyUtils.generate64(); // 8 字节
DES desCbc = DES.createCbc(key, iv);
byte[] cipher = desCbc.encrypt("data".getBytes());
10.2 3DES(192 位密钥,兼容旧系统)
// CBC 模式
byte[] key = KeyUtils.generate192(); // 24 字节
byte[] iv = KeyUtils.generate64(); // 8 字节
DESede desEde = DESede.createCbc(key, iv);
byte[] encrypted = desEde.encrypt("sensitive".getBytes());
byte[] decrypted = desEde.decrypt(encrypted);
// ECB 模式
DESede desEcb = DESede.createEcb();
String cipherBase64 = desEcb.encryptToBase64Str("data");
迁移建议:如果旧系统使用 DES/3DES,新项目应逐步迁移到 AES。可以先用 AES 加密新数据,旧数据在访问时解密再用 AES 重新加密(双写策略)。
十一、总结
pan-common 的对称加密工具提供了统一的 API 设计,覆盖 AES/DES/3DES/SM4/PBE 五种算法,支持所有常见模式和填充方式。核心价值在于:
- API 统一:所有算法的加密/解密方法命名一致,切换算法只需改类名
- 便捷方法:
encryptToHex/encryptToBase64Str/decryptFromHexStr等方法减少编码转换 - 密钥管理:
KeyUtils生成安全随机密钥,getKeyToHex/getIvToBase64Str便于存储 - 流式处理:支持 GB 级大文件加密,内存占用恒定
- 国密合规:SM4 算法支持政务/金融场景
生产环境 Checklist:
- ✅ 使用 CBC/CFB/CTR 模式(不要用 ECB)
- ✅ IV 随机生成且唯一
- ✅ 密钥通过 KMS 或环境变量管理
- ✅ 大文件使用流式加密
- ✅ 国密场景使用 SM4
- ✅ PBE 迭代次数 ≥ 10000
项目地址:https://gitee.com/apanlh/pan-common
更多推荐
所有评论(0)