AES(Advanced Encryption Standard)是目前廣泛採用的對稱加密算法。本文提供一個生產級工具類,涵蓋 AES 加解密、Base64 編解碼、MD5 摘要,並重點解決「跨平台密鑰派生不一致」的經典坑,代碼開箱即用。
public class AesUtils {
// Base64 編解碼(JDK 8+ 建議用 java.util.Base64,此處兼容舊版本用 sun.misc)
public static String base64Encode(byte[] bytes) { return new BASE64Encoder().encode(bytes); }
public static byte[] base64Decode(String str) throws Exception {
return str == null ? null : new BASE64Decoder().decodeBuffer(str);
}
// MD5 摘要
public static byte[] md5(byte[] data) throws Exception {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(data);
return md.digest();
}
public static String md5Encrypt(String msg) throws Exception {
return msg == null ? null : base64Encode(md5(msg.getBytes()));
}
// AES 加密核心:關鍵在於 SecureRandom 實例化方式
public static byte[] aesEncryptToBytes(String content, String encryptKey) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
// 必須顯式指定 SHA1PRNG 並 setSeed,否則不同 JDK 實現(Windows/Linux)派生密鑰不同
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
random.setSeed(encryptKey.getBytes());
kgen.init(128, random); // 128 位密鑰
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(kgen.generateKey().getEncoded(), "AES"));
return cipher.doFinal(content.getBytes(StandardCharsets.UTF_8));
}
public static String aesEncrypt(String content, String key) throws Exception {
return base64Encode(aesEncryptToBytes(content, key));
}
public static String aesDecrypt(String encryptStr, String decryptKey) throws Exception {
if (encryptStr == null) return null;
byte[] decryptBytes = aesDecryptByBytes(base64Decode(encryptStr), decryptKey);
return new String(decryptBytes, StandardCharsets.UTF_8);
}
private static String aesDecryptByBytes(byte[] encryptBytes, String decryptKey) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
random.setSeed(decryptKey.getBytes());
kgen.init(128, random);
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(kgen.generateKey().getEncoded(), "AES"));
byte[] decrypted = cipher.doFinal(encryptBytes);
return new String(decrypted, StandardCharsets.UTF_8);
}
}
SecureRandom.getInstance("SHA1PRNG") + setSeed()?早期寫法 new SecureRandom(key.getBytes()) 在 Windows(SunJCE)與 Linux(OpenJDK)上行為不同:Windows 使用種子作為唯一熵源,Linux 則混合系統熵池。導致相同密鑰字符串派生出不同的 AES Key,造成跨平台解密失敗。顯式指定 SHA1PRNG 並 setSeed 可強制統一行為,確保跨平台一致性。
AES 輸出為二進制字節數組,可能包含不可見控制字符,直接存儲傳輸易損壞。Base64 編碼為可打印 ASCII 字符串,安全用於 JSON、URL、數據庫文本字段。
public static void main(String[] args) throws Exception {
String content = "敏感業務數據:訂單號 20240101001,金額 ¥999.99";
String key = "MySecretKey123"; // 生產環境建議從密管系統獲取,長度≥16 字符
String encrypted = AesUtils.aesEncrypt(content, key);
System.out.println("密文: " + encrypted);
String decrypted = AesUtils.aesDecrypt(encrypted, key);
System.out.println("解密: " + decrypted);
// 輸出: 解密: 敏感業務數據:訂單號 20240101001,金額 ¥999.99
}
AES/GCM/NoPadding(需 12 字節 IV),提供認證加密(AEAD),防止密文篡改。ECB 模式不安全,CBC 模式需防填充預言機攻擊。BadPaddingException、IllegalBlockSizeException 區分密鑰錯誤與數據損壞,避免信息洩露。sun.misc.BASE64Encoder,遷移至 java.util.Base64。無第三方依賴,僅使用 JDK 標準庫(javax.crypto、java.security、java.math.BigInteger)。兼容 JDK 1.7+,JDK 9+ 需替換 Base64 實現。
掌握 AES 正確姿勢,不僅能保護數據機密性,更能避開跨平台兼容性坑,構建可信的安全基礎設施。