Description
CBC requires an unpredictable IV, while GCM requires that an IV never be reused with the same key. Reusing a key and IV, using a zero-filled IV, or deriving it from a fixed string can reveal repeating ciphertext patterns, particularly in the first CBC block. In GCM, nonce reuse undermines both confidentiality and integrity: an attacker can relate plaintexts through the XOR of ciphertexts and may forge authentication tags.
Potential impact
- Pattern disclosure: with the same CBC key and IV, identical first plaintext blocks produce identical ciphertext blocks.
- Plaintext relationships: GCM ciphertexts produced with the same key and IV can reveal relationships between their plaintexts.
- Forgery: repeated GCM nonces can compromise authentication and allow valid tags to be forged.
- Message correlation: repeated patterns can reveal relationships between requests and responses. Replay prevention requires separate protocol controls.
Remediation
- Generate a fresh IV for each encryption. Use
SecureRandom.nextBytes(iv)for unpredictable random IVs; 12 bytes (96 bits) is the usual recommendation for GCM. - Do not use an array allocated by
new byte[N]while it is still zero-filled, or use constant-derived values such as"constant".getBytes()as IVs. - Store or transmit the IV with the ciphertext and prevent reuse under the same key. Random IVs can collide, so manage per-key usage limits and key rotation as well.
- Where appropriate, consider a nonce-misuse-resistant mode such as AES-GCM-SIV. Unique nonces remain the basic operating principle.
- Use the required IV length: 16 bytes for AES-CBC and normally 12 bytes for GCM.
Examples
Before
java
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class StaticIvBad {
// BAD: 고정(제로) IV 사용으로 GCM 보안 붕괴
public byte[] encrypt(byte[] key, byte[] plaintext) throws Exception {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKeySpec sk = new SecretKeySpec(key, "AES");
byte[] iv = new byte[12]; // 모든 바이트가 0 -> 예측 가능
GCMParameterSpec spec = new GCMParameterSpec(128, iv);
cipher.init(Cipher.ENCRYPT_MODE, sk, spec);
return cipher.doFinal(plaintext);
}
}
After
java
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.ByteBuffer;
import java.security.SecureRandom;
public class RandomIvGood {
// GOOD: 매번 SecureRandom으로 새 IV 생성하고, IV를 암호문과 함께 저장/전달
public byte[] encrypt(byte[] key, byte[] plaintext) throws Exception {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKeySpec sk = new SecretKeySpec(key, "AES");
byte[] iv = new byte[12]; // GCM 권장 12바이트
SecureRandom.getInstanceStrong().nextBytes(iv);
GCMParameterSpec spec = new GCMParameterSpec(128, iv);
cipher.init(Cipher.ENCRYPT_MODE, sk, spec);
byte[] ciphertext = cipher.doFinal(plaintext);
// IV || C 형식으로 결합해 저장/전달
ByteBuffer out = ByteBuffer.allocate(iv.length + ciphertext.length);
out.put(iv).put(ciphertext);
return out.array();
}
}
Explanation:
- Before: Reusing the all-zero IV under the same GCM key repeats the nonce, exposing plaintext relationships and risking tag forgery. Fixed IVs also expose patterns in CBC.
- After:
SecureRandomgenerates a 12-byte IV for each encryption, and the IV is stored or transmitted with the ciphertext. Random generation does not guarantee absolute uniqueness; limit per-key usage and prevent IV reuse.