Java对称加密实战:AES/SM4/PBE全场景指南与密钥管理最佳实践

选题说明:对称加密在项目中的常见痛点不是"不会用",而是"用不对"——密钥管理混乱、模式选择不当(ECB 泄露明文模式)、IV 重复使用、大文件加密 OOM。本文基于 pan-common 的对称加密工具实现,覆盖 AES/DES/3DES/SM4/PBE 五种算法的完整 API 和真实业务场景。


一、为什么需要统一的对称加密工具

JDK 的 javax.crypto 提供了基础加密能力,但在实际项目中会遇到这些问题:

  1. 算法切换成本高:从 AES 换到 SM4(国密合规),需要重写加密逻辑、调整密钥长度、修改 IV 生成
  2. 密钥/IV 管理碎片化:有的项目密钥硬编码,有的存在配置文件,IV 要么固定要么没存,解密时找不到
  3. 输出格式不统一:有的存 Hex,有的存 Base64,有的直接存 byte[],跨系统对接时反复转换
  4. 大文件加密 OOMCipher.doFinal() 一次性加载整个文件,GB 级日志或数据库备份直接内存溢出
  5. 模式选择不当:默认用 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)String
  • decryptFromBase64Str(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 五种算法,支持所有常见模式和填充方式。核心价值在于:

  1. API 统一:所有算法的加密/解密方法命名一致,切换算法只需改类名
  2. 便捷方法encryptToHex/encryptToBase64Str/decryptFromHexStr 等方法减少编码转换
  3. 密钥管理KeyUtils 生成安全随机密钥,getKeyToHex/getIvToBase64Str 便于存储
  4. 流式处理:支持 GB 级大文件加密,内存占用恒定
  5. 国密合规:SM4 算法支持政务/金融场景

生产环境 Checklist:

  • ✅ 使用 CBC/CFB/CTR 模式(不要用 ECB)
  • ✅ IV 随机生成且唯一
  • ✅ 密钥通过 KMS 或环境变量管理
  • ✅ 大文件使用流式加密
  • ✅ 国密场景使用 SM4
  • ✅ PBE 迭代次数 ≥ 10000

项目地址:https://gitee.com/apanlh/pan-common

更多推荐