AES Encryption, Explained

After reading this you will know how AES turns a key and a message into ciphertext, why GCM adds authentication that CBC lacks, and how to read the packed output this tool produces so you can decrypt it later.

What AES actually does

AES (Advanced Encryption Standard) is a block cipher standardized by NIST in FIPS 197 in 2001. It maps a 16-byte block of plaintext to a 16-byte block of ciphertext under a secret key of 128, 192, or 256 bits. That is the whole primitive: a keyed, reversible permutation on 128-bit blocks. Everything else (encrypting a long message, protecting against tampering) is built on top by a mode of operation.

Here is the hook. AES by itself only handles 16 bytes. The message attack at dawn xx is 16 bytes, so it fits one block. Under the 256-bit key of all zeros, AES encrypts it to a specific 16-byte ciphertext. Change one bit of the plaintext and roughly half the 128 output bits flip. That avalanche is the property AES is engineered for, and it is why the ciphertext looks like random noise.

Real messages are longer than 16 bytes, so you need a mode that chains blocks together. This tool offers two: AES-GCM and AES-CBC. The choice between them matters more than the key size.

When to use GCM, and when CBC is acceptable

AES-GCM (Galois/Counter Mode, NIST SP 800-38D) does two jobs at once. It encrypts using a counter stream, and it computes an authentication tag over the ciphertext. On decryption, if a single bit of the ciphertext, the IV, or the associated data has changed, verification of the tag fails and you get an error instead of garbage plaintext. This is called authenticated encryption.

AES-CBC (Cipher Block Chaining, SP 800-38A) only encrypts. It gives you confidentiality but no integrity. An attacker who can flip bits in a CBC ciphertext can flip corresponding bits in the decrypted plaintext in predictable ways, and CBC has been the source of padding-oracle attacks (Vaudenay, 2002) that recover plaintext one byte at a time when an application leaks whether padding was valid.

Prefer AES-GCM unless you must interoperate with a system that only speaks CBC. If you use CBC, never let the decryption side reveal whether padding was correct, and add your own authentication (an HMAC over the ciphertext) if the protocol does not.

Both modes are used in practice. TLS 1.3 uses AES-GCM. Older TLS and many file formats use CBC with a separate MAC. Disk encryption uses specialized modes like XTS, which this tool does not implement.

The pieces: key, IV, and for GCM the tag

Three inputs go into a mode, and getting each one right is what separates secure from broken.

Key
The secret. Either 16, 24, or 32 bytes (128, 192, 256 bits). A 256-bit key gives 2^256 possibilities, far beyond brute force. Key size is rarely the weak point.
IV (initialization vector) or nonce
A value that makes each encryption unique even when the key and plaintext repeat. GCM uses a 12-byte nonce. CBC uses a 16-byte IV. It is not secret, but it must be unpredictable (CBC) or unique per key (GCM).
Salt
Used only when you derive the key from a passphrase. It makes the same passphrase produce different keys, defeating precomputed tables.
Tag
GCM only. A 16-byte authenticator appended to the ciphertext. Decryption checks it before returning any plaintext.

Reusing a nonce with the same key in GCM is catastrophic. Two messages encrypted under the same key and nonce leak the XOR of their plaintexts, and worse, they leak the authentication key, letting an attacker forge messages. This tool generates a fresh random nonce every time so you cannot make that mistake by hand.

From passphrase to key with PBKDF2

People remember passphrases, not 32 random bytes. AES needs the 32 bytes. PBKDF2 (Password-Based Key Derivation Function 2, RFC 8018) bridges the gap by hashing the passphrase together with the salt, repeatedly, to produce a key.

DK = \text{PBKDF2}(\text{PRF}, \text{pass}, \text{salt}, c, dkLen)

Here PRF is the pseudorandom function (this tool uses HMAC-SHA256), pass is your passphrase, salt is the random salt, c is the iteration count, and dkLen is the output length in bytes (32 for a 256-bit key). The iteration count is a deliberate cost. Each guess an attacker tries must repeat the PRF c times.

If c = 600{,}000 and one HMAC-SHA256 costs about 1 microsecond, each passphrase guess costs about 0.6 seconds of CPU. An attacker who could test 10 million weak-KDF guesses per second is now down to under 2 guesses per second per core. The salt and iteration count are stored with the ciphertext so decryption reproduces the same key. For the mechanics of PBKDF2 and its sibling HKDF, see Key Derivation: PBKDF2 & HKDF.

Reproducing the demo output

Run the tool with its defaults: AES-GCM, a 256-bit key derived from a passphrase with PBKDF2-SHA256. The tool does these steps in order.

  1. Generate a random 16-byte salt from the browser CSPRNG, for example 3f9a...c2.
  2. Run PBKDF2-SHA256 on your passphrase and that salt with the configured iteration count (say 600,000) to get a 32-byte key.
  3. Generate a fresh random 12-byte nonce.
  4. Encrypt the plaintext with AES-256-GCM under the key and nonce, producing ciphertext plus a 16-byte tag.
  5. Pack salt || iterations || nonce || ciphertext || tag and encode the whole thing (Base64) as the output.

To decrypt, the tool unpacks the salt and iteration count, reruns PBKDF2 to rebuild the key, reads the nonce, and calls GCM decryption. If the tag does not verify, it stops and reports failure. Nothing partial is returned.

Reading the packed output

The output is one blob because decryption needs every non-secret parameter. Only the passphrase (or raw key) stays in your head. A typical layout for the GCM default looks like the table below. Byte counts are exact; the ciphertext length equals the plaintext length because GCM is a stream mode with no padding.

Fields packed into the output, in order
FieldBytesSecret?Purpose
salt16NoSeeds PBKDF2
iterations4NoPBKDF2 cost
nonce12NoMakes GCM unique
ciphertext= plaintextEncryptedThe message
tag16NoIntegrity check

So encrypting a 20-byte message produces 16 + 4 + 12 + 20 + 16 = 68 bytes before Base64. Base64 expands by 4/3, giving about 91 characters. If you paste that string back in, you get the original 20 bytes only when the passphrase and every field are intact.

Why the iteration count reshapes an attacker's cost

The KDF cost is the one dial most people set wrong. Too low, and a stolen ciphertext plus a weak passphrase falls quickly. The chart shows attacker guesses per second against iteration count, assuming one HMAC-SHA256 costs 1 microsecond on the attacker's hardware.

At 10,000 iterations an attacker tests 100 guesses per second per core. At 1,000,000 iterations that drops to 1. Cost scales inversely with the count.

A stronger passphrase and a higher iteration count both raise the time to crack. At 600,000 iterations, a 6-word random passphrase (about 77 bits of entropy) is effectively uncrackable, while a single dictionary word (about 13 bits) still falls in under an hour on one core.

Common mistakes

Most AES failures are not attacks on the cipher. AES-256 has no practical break. The failures are in how the pieces are used.

  • Choosing CBC and skipping integrity. Without a MAC or a switch to GCM, a tampered CBC ciphertext can decrypt to attacker-influenced plaintext. Use GCM.
  • Reusing a nonce. Fatal in GCM. Always draw a fresh random nonce, which this tool does.
  • Treating a passphrase as a raw key. A passphrase is not 32 uniform bytes. Feed it through PBKDF2, or generate a real key with the Cryptographic Key Generator.
  • Low iteration counts. A count of 1,000 might pass tests but leaves weak passphrases exposed. OWASP recommends at least 600,000 for PBKDF2-SHA256 as of 2023.
  • Assuming encryption hides length. GCM ciphertext is exactly as long as the plaintext. If message length is sensitive, pad before encrypting.

Frequently asked questions

Is AES-256 meaningfully stronger than AES-128?

Both are considered secure against brute force. AES-128 has 2^128 keys, already unreachable. AES-256 gives a margin against future attacks and quantum search (Grover's algorithm halves the effective bits, so 256 becomes 128). Use 256 when you can; the speed cost is small.

Why does decryption fail with the right passphrase?

In GCM, decryption verifies the tag first. If the ciphertext blob was truncated, re-encoded, or edited by even one character, the tag will not match and you get an error. Copy the whole output exactly, including trailing Base64 characters.

Can I decrypt output made by another tool?

Only if it uses the same packing layout, KDF, and mode. AES itself is standard, but there is no universal format for how salt, IV, and tag are stored. Match the field order shown above.

Is a raw key safer than a passphrase?

A 32-byte key from a CSPRNG has full 256-bit entropy, so no KDF stretching is needed and there is nothing to guess. A passphrase is only as strong as its entropy. Use a raw key for machine-to-machine secrets and a strong passphrase with high PBKDF2 iterations for human-memorable ones.

Does the IV need to be secret?

No. The IV or nonce is stored in the clear with the ciphertext. It must be unique per key in GCM and unpredictable in CBC, but it carries no secret information.