AES(Advanced Encryption Standard)是對稱加密標準,廣泛用於敏感數據傳輸與存儲。本文提供生產級工具類,解決「Linux/Windows 结果不一致」「中文亂碼」「密鑰派生不穩定」等常見坑點,採用 AES/ECB/PKCS5Padding 模式,輸出 Base64 字串便於 JSON/URL 傳輸。
SHA1PRNG 派生,確保跨平台一致(需注意:JDK 8+ SHA1PRNG 實現可能差異,生產建議改用 PBKDF2)。AES/ECB/PKCS5Padding(ECB 模式無 IV,簡單但安全性較低;高安全場景請用 CBC/GCM + 隨機 IV)。UTF-8,解決中文亂碼。import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;
import java.util.Base64; // JDK 8+ 內建,舊版用 Apache Commons Codec
public class AesUtil {
private static final String ALGO = "AES";
private static final String TRANSFORMATION = "AES/ECB/PKCS5Padding";
private static final int KEY_SIZE = 128;
private static final String CHARSET = "UTF-8";
/** 從密碼派生 128 位 AES 密鑰 */
private static SecretKeySpec deriveKey(String password) throws NoSuchAlgorithmException, InvalidKeyException {
// 注意:SecureRandom SHA1PRNG 在不同 JDK 實現可能不同,生產建議用 PBKDF2WithHmacSHA256
KeyGenerator kgen = KeyGenerator.getInstance(ALGO);
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(password.getBytes(StandardCharsets.UTF_8));
kgen.init(KEY_SIZE, sr);
SecretKey secretKey = kgen.generateKey();
return new SecretKeySpec(secretKey.getEncoded(), ALGO);
}
/** 加密 → Base64 字串 */
public static String encrypt(String content, String password) {
try {
SecretKeySpec key = deriveKey(password);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encrypted = cipher.doFinal(content.getBytes(CHARSET));
return Base64.getEncoder().encodeToString(encrypted);
} catch (Exception e) {
throw new RuntimeException("AES 加密失敗", e);
}
}
/** Base64 字串 → 解密明文 */
public static String decrypt(String base64Content, String password) {
try {
SecretKeySpec key = deriveKey(password);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decoded = Base64.getDecoder().decode(base64Content);
byte[] decrypted = cipher.doFinal(decoded);
return new String(decrypted, CHARSET);
} catch (Exception e) {
throw new RuntimeException("AES 解密失敗", e);
}
}
// 測試
public static void main(String[] args) {
String plain = "t太陽est地";
String pwd = "12345678";
String cipher = encrypt(plain, pwd);
System.out.println("加密: " + cipher); // 例: Bpf0jyJDj/pVHaRf66+OMA==
System.out.println("解密: " + decrypt(cipher, pwd)); // t太陽est地
}
}
| 問題 | 原因 | 解決方案 |
|---|---|---|
| Linux/Windows 加密結果不同 | SHA1PRNG 實現差異 | 改用 PBKDF2WithHmacSHA256 固定迭代次數、鹽值派生密鑰 |
| 中文加密後解密亂碼 | 字符集不一致 | 全鏈路強制 UTF-8(getBytes(CHARSET)/new String(bytes, CHARSET)) |
| 密文長度非 16 倍數報錯 | ECB/PKCS5Padding 自動填充,解密時需完整密文 | 確保 Base64 完整傳輸,無截斷/換行 |
| 金鑰長度不足/非法 | 密碼過短導致派生弱 | 建議密碼 ≥ 16 字符,或改用 PBKDF2 + 固定鹽值 |
SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"),迭代 ≥ 100,000,鹽值 16 字節隨機生成。本文提供的工具類可直接用於測試、內部非核心數據加密。對於高安全要求場景,請務必採用 AES-GCM + PBKDF2 + 安全金鑰管理 方案。