About AES-GCM Encrypt
AES is a symmetric block cipher: the same key encrypts, and anyone holding the key can reverse the operation. AES itself only transforms one 128-bit block, so the mode of operation - here AES-GCM - is what determines how a message of any length is handled, and it matters far more to the security of the result than the key size does.
AES-GCM is an authenticated mode: a single operation both encrypts the data and produces a 128-bit authentication tag. Decryption verifies that tag first and fails outright if the ciphertext, the nonce or the associated data has been altered, so tampering is detected rather than silently decrypted into garbage.
This is the mode to choose for new work. It needs no separate MAC, no padding, and it is hardware-accelerated on essentially every modern CPU. TLS 1.3 dropped every non-authenticated mode and kept AEAD modes like this one.
Its one sharp edge is nonce reuse. Encrypting two different messages with the same key and the same nonce does not merely leak that the messages relate - it leaks the XOR of the plaintexts and, worse, allows an attacker to recover the authentication subkey and forge tags for that key. Generate a fresh random nonce for every single message and never derive one from a counter you might reset.
Everything on this page happens in your browser through the Web Crypto API. Keys, plaintext and ciphertext are never uploaded, which is what makes it safe to encrypt a real value here while you are debugging.
How to use the AES-GCM Encrypt
- Paste the plaintext you want to encrypt.
- Provide a key of the right length - 16, 24 or 32 bytes for AES-128, AES-192 or AES-256. Decode it from hex or Base64 first if that is how it is stored.
- A random nonce of 12 bytes is generated for you. Never reuse one with the same key - see the warning above.
- Copy the ciphertext together with the nonce and the authentication tag. You cannot decrypt later without all three.
Examples
-
Plaintext
secret message
AES-GCM Encrypt in code
The same operation this tool performs, in the languages you are most likely to need it.
const enc = new TextEncoder();
const key = await crypto.subtle.generateKey(
{ name: "AES-GCM", length: 256 }, true, ["encrypt", "decrypt"]
);
// A fresh 12-byte nonce per message. Never reuse one with the same key.
const iv = crypto.getRandomValues(new Uint8Array(12));
const ct = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv }, key, enc.encode("secret message")
);
// Web Crypto appends the 16-byte auth tag to the ciphertext for you.
const pt = await crypto.subtle.decrypt({ name: "AES-GCM", iv }, key, ct);
console.log(new TextDecoder().decode(pt));
import { randomBytes, createCipheriv, createDecipheriv } from "node:crypto";
const key = randomBytes(32); // AES-256
const iv = randomBytes(12); // 96-bit nonce, fresh every time
const c = createCipheriv("aes-256-gcm", key, iv);
const ct = Buffer.concat([c.update("secret message", "utf8"), c.final()]);
const tag = c.getAuthTag(); // 16 bytes, returned separately
const d = createDecipheriv("aes-256-gcm", key, iv);
d.setAuthTag(tag); // MUST be set before final()
const pt = Buffer.concat([d.update(ct), d.final()]);
console.log(pt.toString("utf8"));
// d.final() throws if the tag doesn't verify - let it throw, don't catch
// and continue with whatever update() returned.
import os
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
key = AESGCM.generate_key(bit_length=256)
aead = AESGCM(key)
nonce = os.urandom(12) # fresh per message
ct = aead.encrypt(nonce, b"secret message", None) # tag is appended
pt = aead.decrypt(nonce, ct, None) # raises InvalidTag if modified
print(pt)
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
byte[] keyBytes = new byte[32], iv = new byte[12];
SecureRandom rng = new SecureRandom();
rng.nextBytes(keyBytes); rng.nextBytes(iv);
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
Cipher c = Cipher.getInstance("AES/GCM/NoPadding");
c.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(128, iv));
byte[] ct = c.doFinal("secret message".getBytes("UTF-8"));
// ct already includes the 128-bit tag.
Cipher d = Cipher.getInstance("AES/GCM/NoPadding");
d.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(128, iv));
System.out.println(new String(d.doFinal(ct), "UTF-8"));
# openssl enc does NOT support GCM properly - it cannot emit or check
# the auth tag. Use a library, or the newer -aead interface. This is a
# common source of "my GCM ciphertext won't decrypt" reports.
openssl list -cipher-algorithms | grep -i gcm
When you need this
- Producing a AES-GCM test vector for an integration that expects it.
- Checking that your application's ciphertext matches another system's for the same key and nonce.
- Seeing how the nonce changes the ciphertext for identical plaintext.
- Working out the exact byte layout to store - ciphertext, nonce and tag.
- Confirming a key you were given is the right length before wiring it in.
Common problems and what causes them
- "Unsupported state or unable to authenticate data" / InvalidTag / BadPaddingException
- One of the key, nonce, tag or associated data is wrong, or the tag was not split off the ciphertext correctly. Web Crypto and Python append the 16-byte tag to the ciphertext; Node returns it separately via getAuthTag(). Mixing those two conventions is the single most common cause.
- Key length is wrong
- AES keys must be exactly 16, 24 or 32 bytes. A 32-character hex string is 16 bytes, not 32 - decode hex before use. Passing a password directly as a key is also wrong: derive a key with PBKDF2, scrypt or Argon2 first.
- Losing the nonce
- The nonce is not secret but it is required to decrypt. Store or transmit it with the ciphertext - the usual convention is to prepend its 12 bytes. Without it the ciphertext is unrecoverable.
- Reusing a nonce with the same key
- This breaks GCM completely: it leaks the XOR of the two plaintexts and lets an attacker recover the authentication subkey and forge valid tags. Use a fresh random 12-byte nonce per message, and if you use a counter, make sure it can never restart after a redeploy or restore.
- Base64 vs hex vs raw bytes
- The same ciphertext looks completely different in each encoding, and Base64 has URL-safe and standard variants that differ in two characters. Confirm which encoding each side uses before concluding the crypto is broken.
- Using "AES" as the algorithm name in Java or .NET
- Cipher.getInstance("AES") means AES/ECB/PKCS5Padding. ECB encrypts identical plaintext blocks to identical ciphertext blocks, which leaks structure badly. Always name the mode explicitly.
FAQ
- What do I need to keep after encrypting with AES-GCM?
- The ciphertext, the 12-byte nonce, and the 16-byte authentication tag. None of those are secret, and all of them are required to decrypt. The conventional layout is to prepend the nonce.
- Do I need a new nonce for every message?
- Yes, and this is the one rule you cannot bend. Reusing a nonce with the same key leaks the XOR of the two plaintexts and lets an attacker recover the authentication subkey and forge tags for that key. Generate 12 fresh random bytes per message.
- Should I use AES-GCM or AES-CBC?
- AES-GCM for anything new: it authenticates as well as encrypts, needs no padding, and is faster on modern hardware. Choose CBC only when an existing format or API requires it, and then always add an HMAC over the IV and ciphertext and verify it before decrypting.
- Is the nonce secret?
- No. The nonce is public and is normally stored or sent alongside the ciphertext. What matters is that it is never repeated for a given key.
- Is AES-128 strong enough, or should I use AES-256?
- AES-128 has no practical attack and is fine for essentially all use. AES-256 gives a larger margin against future advances at a small performance cost. Both are dramatically safer than getting the mode or nonce handling wrong, which is where real failures come from.
- Can I use a password as the key?
- Not directly. Keys must be exactly 16, 24 or 32 random bytes. Run the password through a key derivation function - PBKDF2, scrypt or Argon2, with a random salt and a high iteration or cost parameter - and use its output as the key.
- Why does the same plaintext give different ciphertext each time?
- Because a fresh random nonce is used per operation, and that is exactly what you want. Identical ciphertext for identical plaintext would tell an attacker which messages repeat.
- Is my key or plaintext sent to a server?
- No. All AES operations here run in your browser via the Web Crypto API. Nothing is uploaded or logged, so you can safely test with a real value - check your network tab, or use the page offline.
Related reading
- AES-GCM vs AES-CBC: modes compared
- Base64 converter decode a ciphertext blob
- RSA key pair generator asymmetric alternative