PropertyDetail
Block sizeAlways 128 bits (16 bytes)
Key sizes128, 192, or 256 bits
Recommended key size256-bit (strongest; 128-bit is also considered secure)
IV (Initialisation Vector)Required for CBC (16 bytes) and GCM (12 bytes recommended)
Recommended modeGCM — provides confidentiality + integrity + authenticity
AvoidECB mode — deterministic, leaks patterns; CBC without authentication

Use KeyGenerator to create a random AES key, or reconstruct a key from raw bytes using SecretKeySpec.

import javax.crypto.*; import javax.crypto.spec.*; import java.security.*; // Generate a new random 256-bit AES key KeyGenerator keyGen = KeyGenerator.getInstance("AES"); keyGen.init(256, new SecureRandom()); SecretKey key = keyGen.generateKey(); // Serialise to bytes (for storage/transmission) byte[] keyBytes = key.getEncoded(); // Reconstruct from bytes SecretKey reconstructed = new SecretKeySpec(keyBytes, "AES"); Store secret keys in a KeyStore or a dedicated secrets manager (AWS Secrets Manager, HashiCorp Vault). Never hardcode key bytes in source code or commit them to version control.

GCM (Galois/Counter Mode) provides authenticated encryption — it simultaneously encrypts and computes an authentication tag. Decryption verifies the tag; any tampering causes an AEADBadTagException.

import javax.crypto.*; import javax.crypto.spec.*; import java.security.*; import java.util.Base64; public class AesGcm { private static final int GCM_IV_LENGTH = 12; // 96 bits private static final int GCM_TAG_LENGTH = 128; // bits /** Encrypt plaintext with AES-GCM. Returns IV + ciphertext (base64). */ public static String encrypt(String plaintext, SecretKey key) throws Exception { // 1. Generate a random 12-byte IV (never reuse an IV with the same key!) byte[] iv = new byte[GCM_IV_LENGTH]; new SecureRandom().nextBytes(iv); // 2. Initialise cipher Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH, iv); cipher.init(Cipher.ENCRYPT_MODE, key, spec); // 3. Optionally add AAD (Additional Authenticated Data — not encrypted, but authenticated) // cipher.updateAAD("context-info".getBytes()); // 4. Encrypt byte[] ciphertext = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8)); // 5. Prepend IV to ciphertext for transmission 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); } /** Decrypt AES-GCM ciphertext. Throws AEADBadTagException if tampered. */ public static String decrypt(String encoded, SecretKey key) throws Exception { byte[] combined = Base64.getDecoder().decode(encoded); // Extract IV and ciphertext byte[] iv = new byte[GCM_IV_LENGTH]; byte[] ciphertext = new byte[combined.length - GCM_IV_LENGTH]; System.arraycopy(combined, 0, iv, 0, iv.length); System.arraycopy(combined, iv.length, ciphertext, 0, ciphertext.length); Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); cipher.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(GCM_TAG_LENGTH, iv)); return new String(cipher.doFinal(ciphertext), StandardCharsets.UTF_8); } public static void main(String[] args) throws Exception { KeyGenerator kg = KeyGenerator.getInstance("AES"); kg.init(256); SecretKey key = kg.generateKey(); String encrypted = encrypt("Secret message!", key); System.out.println("Encrypted: " + encrypted); String decrypted = decrypt(encrypted, key); System.out.println("Decrypted: " + decrypted); // Secret message! } } The IV (nonce) must be unique for every encryption with the same key. Reusing an IV in GCM completely breaks confidentiality and allows an attacker to recover the key. Always generate a fresh random IV per encryption operation.

CBC (Cipher Block Chaining) is older and widely supported but provides only confidentiality — it has no built-in integrity check. If you use CBC, always add a separate HMAC for authentication (Encrypt-then-MAC).

import javax.crypto.*; import javax.crypto.spec.*; import java.security.*; import java.util.Base64; public class AesCbc { public static byte[] encrypt(byte[] plaintext, SecretKey key) throws Exception { // 1. Random 16-byte IV byte[] iv = new byte[16]; new SecureRandom().nextBytes(iv); // 2. Encrypt with PKCS5Padding (auto-pads to 16-byte block boundary) Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv)); byte[] ciphertext = cipher.doFinal(plaintext); // 3. Prepend IV byte[] result = new byte[16 + ciphertext.length]; System.arraycopy(iv, 0, result, 0, 16); System.arraycopy(ciphertext, 0, result, 16, ciphertext.length); return result; } public static byte[] decrypt(byte[] ivPlusCiphertext, SecretKey key) throws Exception { byte[] iv = new byte[16]; byte[] ciphertext = new byte[ivPlusCiphertext.length - 16]; System.arraycopy(ivPlusCiphertext, 0, iv, 0, 16); System.arraycopy(ivPlusCiphertext, 16, ciphertext, 0, ciphertext.length); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv)); return cipher.doFinal(ciphertext); } } AES-CBC is vulnerable to padding oracle attacks (BEAST, POODLE) when error messages reveal padding validity. Prefer AES-GCM for all new code.

When the encryption key must come from a user password, use PBKDF2 to derive a key — never use a raw password as a key. See also the SecureRandom & PBKDF2 article for full details.

import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; public static SecretKey deriveKey(char[] password, byte[] salt) throws Exception { PBEKeySpec spec = new PBEKeySpec( password, salt, 310_000, // iterations (OWASP 2023 recommendation for PBKDF2-SHA256) 256 // key length in bits ); SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); byte[] keyBytes = skf.generateSecret(spec).getEncoded(); spec.clearPassword(); // wipe password from memory return new SecretKeySpec(keyBytes, "AES"); }
TopicRecommendation
ModeAlways use AES-GCM; avoid ECB and raw CBC
Key size256-bit; 128-bit is acceptable
IVRandom per-encryption; never reuse with the same key
IV size (GCM)12 bytes (96 bits) — optimal for GCM counter
Key storageKeyStore or external secrets manager
Password-derived keyUse PBKDF2 / bcrypt / Argon2 — never hash the password directly
EncodingBase64 for text transport; raw bytes for binary protocols
AADBind ciphertext to context (user ID, file name) using GCM's AAD