AES(Advanced Encryption Standard)是目前最廣泛採用的對稱加密算法,以其高安全性、高性能、硬件加速友好的特點,成為數據加密的事實標準。Java 通過 JCA(Java Cryptography Architecture)原生支持 AES,無需引入第三方庫。本文將從核心 API 用法、關鍵參數選擇、常見坑點到生產級封裝進行全方位講解。
// 生成 256 位 AES 密鑰(需 JCE 無限制強度策略文件)
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256); // 可選 128/192/256
SecretKey secretKey = keyGen.generateKey();
// 若需從字符串派生密鑰(如用戶輸入密碼),請使用 PBKDF2
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 256);
SecretKey derivedKey = factory.generateSecret(spec);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); // 推薦 GCM 模式
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] iv = cipher.getIV(); // GCM 必須獲取隨機生成的 IV
byte[] ciphertext = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
// 存儲格式:IV + 密文(或 IV + 密文 + AuthTag)
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec spec = new GCMParameterSpec(128, iv); // 128-bit 認證標籤
cipher.init(Cipher.DECRYPT_MODE, secretKey, spec);
byte[] plaintext = cipher.doFinal(ciphertext);
String result = new String(plaintext, StandardCharsets.UTF_8);
| 參數 | 推薦值 | 說明 |
|---|---|---|
| 密鑰長度 | 256 bits | 長期機密性需求選 256;性能敏感可用 128 |
| 工作模式 | GCM (Galois/Counter Mode) | 提供機密性+完整性認證(AEAD),防篡改;避免使用 ECB、CBC(需額外 MAC) |
| 填充方式 | NoPadding (GCM 內建) | GCM/CFB/OFB/CTR 為流模式,無需填充 |
| IV/Nonce | 96-bit 隨機數 | GCM 推薦 12 字節,**同一密鑰下絕不重複**;可用 SecureRandom 生成 |
| 認證標籤長度 | 128 bits | GCMParameterSpec 指定,越長越安全 |
Cipher.getInstance("AES") 默認為 ECB/PKCS5Padding,**極不安全**(相同明文塊產生相同密文塊),圖像加密時輪廓清晰可見。local_policy.jar/US_export_policy.jar 或升級至 JDK 8u161+(默認解除限制)。public final class AesGcmUtil {
private static final String ALGORITHM = "AES/GCM/NoPadding";
private static final int IV_LENGTH = 12; // 96-bit
private static final int TAG_LENGTH = 128; // 128-bit
private static final SecureRandom RANDOM = new SecureRandom();
private final SecretKey key;
public AesGcmUtil(SecretKey key) { this.key = key; }
/** 加密,返回 Base64(IV + 密文) */
public String encrypt(String plaintext) {
try {
byte[] iv = new byte[IV_LENGTH];
RANDOM.nextBytes(iv);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(TAG_LENGTH, iv));
byte[] ciphertext = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
// 合併 IV + 密文
byte[] combined = new byte[iv.length + ciphertext.length];
System.arraycopy(iv, 0, combined, 0, iv.length);
System.arraycopy(ciphertext, 0, combined, iv.length, ciphertext.length);
return Base64.getEncoder().encodeToString(combined);
} catch (GeneralSecurityException e) {
throw new IllegalStateException("加密失敗", e);
}
}
/** 解密,輸入 Base64(IV + 密文) */
public String decrypt(String combinedBase64) {
try {
byte[] combined = Base64.getDecoder().decode(combinedBase64);
if (combined.length < IV_LENGTH) throw new IllegalArgumentException("數據長度非法");
byte[] iv = Arrays.copyOfRange(combined, 0, IV_LENGTH);
byte[] ciphertext = Arrays.copyOfRange(combined, IV_LENGTH, combined.length);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(TAG_LENGTH, iv));
byte[] plaintext = cipher.doFinal(ciphertext);
return new String(plaintext, StandardCharsets.UTF_8);
} catch (GeneralSecurityException e) {
throw new IllegalStateException("解密失敗/數據被篡改", e);
}
}
}
// 初始化(實際從 KMS 獲取)
SecretKey key = ...;
AesGcmUtil util = new AesGcmUtil(key);
// 業務調用
String encrypted = util.encrypt("敏感數據:身份證號 1234567890");
String decrypted = util.decrypt(encrypted);
assert "敏感數據:身份證號 1234567890".equals(decrypted);
Java 實現 AES 加密雖然 API 簡單,但模式選擇、IV 管理、密鑰托管、認證驗證等工程細節決定了系統的真實安全性。牢記:優先選 GCM、IV 唯一隨機、密鑰上 KMS、標籤必驗證。將上述工具類納入項目公共庫,統一加密規範,才能在合規審計與實戰攻防中立於不敗之地。