Encryption / Hash

AES Encrypt / Decrypt

AES-GCM symmetric encryption and decryption with passphrase-derived keys.

Plaintext
Password

What is AES Encryption?

AES (Advanced Encryption Standard) is a symmetric encryption algorithm widely used to protect sensitive data.

Features

  • Encrypt/Decrypt: Two-way conversion
  • Multiple Modes: CBC, ECB, CTR, GCM
  • Key Sizes: 128, 192, 256 bits

Use Cases

  • Data protection
  • Secure communication
  • File encryption

AES-GCM encryption and decryption that runs in the browser. A 256-bit key is stretched from your passphrase with PBKDF2-SHA256, a fresh salt and IV are drawn for every message, and the result is one Base64URL string carrying salt, IV, ciphertext and authentication tag. Built for protecting configuration values and checking that another service can open the same payload.

The output is four concatenated fields, not bare ciphertext

Encrypting does not return ciphertext alone. The tool generates a random 16-byte salt and a random 12-byte IV, joins them with the GCM output in the order salt, IV, ciphertext, tag, then Base64URL-encodes the whole buffer. Positions are the only delimiter: bytes 0-15 are the salt, 16-27 the IV, and the last 16 bytes are the authentication tag GCM appends to the ciphertext. That fixes a 44-byte floor (16 + 12 + 16) before a single plaintext byte, exactly the length check the decrypt path enforces. The alphabet drops padding, yet the decoder also accepts standard Base64, a missing =, and stray whitespace.

Why the salt and IV differ on every run

The passphrase never becomes the key directly. PBKDF2 with SHA-256 and 100,000 iterations stretches it into a 256-bit AES key, and the per-message salt keeps that derivation unique, so two people choosing the same passphrase still derive unrelated keys. The 12-byte IV is the nonce size GCM is designed around: 96 bits is native to the mode, and longer IVs are hashed down first. Reusing one IV under the same key costs far more than confidentiality, because recovering the authentication key lets an attacker forge tags. Drawing both values from crypto.getRandomValues on every click is why the same sentence encrypts to unrelated strings twice.

A wrong passphrase and an edited payload fail identically

GCM is AEAD: decryption verifies the 128-bit tag before releasing a single byte, and Web Crypto raises the same error whether the key is wrong or the ciphertext was altered. This tool consequently always reports a password problem, even when the real cause is a truncated paste, a character lost in transit, or a payload produced under different parameters. That uniformity is the point — an oracle that separated the two cases would leak information — but it means the message cannot tell you which happened. Two consequences: any edit to the Base64 string, including re-wrapping or case changes, breaks the tag, and the passphrase must match byte for byte, since no Unicode, whitespace or case normalisation is applied.

Worked example

Decrypting a payload this tool produced

Input
ChssPU5fYHGCk6S1xtfo-aGyw9Tl9gcYKTpLXLmfVzH6RV8Xj3QDOCZFugbRjP7fOz9WNv-ah8GgosfSkXzIZqanupn5knI4rpckSTbr
Output
Nonce reuse breaks GCM completely.

The passphrase is devtoolbox-aes. Before Base64 the buffer is 78 bytes: 16 salt, 12 IV, 34 ciphertext, 16 tag. Because 78 divides by 3 the encoding is exactly 104 characters with no = padding, and the 34-byte plaintext is pure ASCII so character count equals byte count.

Limits and pitfalls

  • Only AES-GCM is wired up: there is no CBC, CTR or ECB mode, so ciphertext from systems using those modes cannot be opened here, even with the right key material.
  • The passphrase is the only secret input; there is no field for an existing binary key and no support for additional authenticated data (AAD), so interoperability with an implementation that feeds AAD will fail at the tag check.
  • Inputs shorter than 44 bytes after Base64 decoding are rejected before decryption starts, because 16 salt + 12 IV + 16 tag leave no room for ciphertext.
  • The derived key is never cached, so every encrypt or decrypt run repeats the full 100,000-iteration PBKDF2 stretch rather than reusing the previous one.
  • Nothing is stored: reloading the page drops both text and passphrase, and a forgotten passphrase has no recovery path such as a key hint or escrow.

Related tools

When it goes wrong

Opened over http://192.168.1.20 on a phone, the Encrypt click only shows "Cannot read properties of undefined (reading importKey)".

CauseWeb Crypto exists only in secure contexts, so crypto.subtle is undefined on a plain http origin and the very first importKey call throws. The yellow banner meant to warn about this never appears, because cryptoAvailable starts as true and nothing ever sets it to false.

FixServe the page over https, or reach it through localhost or 127.0.0.1 on the same machine. Check the URL scheme before touching the fields: in decrypt mode the identical failure is reported as a password problem instead.

A value that is just a space, or a single trailing newline, cannot be encrypted at all: the button stays greyed out.

CauseBoth the disabled binding and the guard at the top of encrypt() test inputText.trim(), so whitespace-only input is treated as empty. The passphrase field has the opposite quirk: one space passes, because only its truthiness is checked and it is then encoded untrimmed.

FixPut at least one visible character in the plaintext box and keep the whitespace inside a longer value. Do not pad a passphrase with a trailing space to satisfy the field, because that byte changes the key PBKDF2 derives.

Decryption reports success but the plaintext comes back as a run of replacement diamonds instead of the original Chinese or accented text.

CauseThe 128-bit tag already verified, so key and layout were correct. TextDecoder runs without fatal: true, so bytes that are not valid UTF-8 are silently swapped for U+FFFD. The source was almost certainly GBK, Shift-JIS or Latin-1, or raw file bytes rather than UTF-8.

FixTranscode the source to UTF-8 before encrypting it here. If the payload is genuinely binary, stop using this tool for it: the result box is text and the round trip is lossy by design.

Switching the mode radio to Decrypt wipes the ciphertext that was just generated, and the Example button then fills in nothing.

CauseA watcher on mode clears outputText on every switch, and loadExample only populates the plaintext and passphrase when mode is encrypt; in decrypt mode it assigns empty strings to both.

FixCopy the output before changing mode, since it cannot be recovered afterwards. To study the bundled sample, run Example in encrypt mode first, keep the resulting string, then switch to Decrypt and paste it back.

A payload lifted out of a query string, or pasted with the quotes still around it, is rejected as invalid ciphertext before anything is decrypted.

Causebase64ToBytes maps only - and _ back to + and /, drops whitespace and restores = padding. Percent-escapes such as %2B, and quote characters, survive into the string, so atob throws and the input is classified as unreadable Base64.

FixURL-decode the value and strip the surrounding quotes first, then paste. Wrapped line breaks need no attention, because every whitespace character is already removed before decoding.

When a passphrase-derived AES-GCM string is the wrong route

DecisionThis AES-GCM toolAlternative
The recipient cannot be handed a secret out of bandNot workable: both sides must already hold the same passphrase, since the 256-bit key is re-derived from it with PBKDF2-SHA256 on every run.RSA tool (or any hybrid scheme): encrypt a throwaway AES key to the recipient public key, so no shared secret ever travels between you.
You already hold a 32-byte random key rather than a passphraseThe only secret input is a passphrase, and whatever you type is fed to PBKDF2 for 100000 iterations, so a hex key string is stretched as if it were ordinary text.A short Python script using AESGCM from the cryptography library: the key bytes are used as-is, and you set the IV and any AAD yourself.
The value has to survive as one line in an .env file or a JSON fieldOutput is one Base64URL string holding 16-byte salt, 12-byte IV, ciphertext and the 16-byte tag, with no padding and no whitespace, so it copies cleanly into a single line.gpg --symmetric or age: both emit binary or ASCII-armored blocks with header lines, which have to be escaped or re-wrapped before a JSON field accepts them.
A backend service has to open the same payloadInteroperates only if that service repeats the layout byte for byte: salt 16, IV 12, ciphertext, tag 16, key from PBKDF2-SHA256 at 100000 iterations, and no AAD.If the service already speaks AES-CBC with PKCS#7, or keeps its salt in a separate column, stay on its library; this tool can neither read nor emit that format.
You want to detect edits but keep the value human-readableWrong fit: the output is unreadable without the passphrase, so nobody can diff two config revisions or review a value in a log without decrypting first.HMAC tool: the same shared-secret model, but you ship the plaintext beside a tag, so a changed byte is still visible while the value stays reviewable.
You only want the text to stop looking like plaintextOverkill: a passphrase to invent and keep, plus a deliberate speed bump of 100000 PBKDF2 rounds on each click, and a lost passphrase loses the value for good.Base64 tool: no key at all and reversible by anyone, which is the correct trade when the goal is avoiding accidental reading rather than access control.

Frequently Asked Questions

The AES ciphertext differs every run — is something wrong?
No. Modern modes (CBC, CTR, GCM) generate a random IV per encryption, so the same plaintext never produces the same ciphertext — that randomness is a security feature. Decryption reads the IV carried with the ciphertext and recovers the exact plaintext.
128-bit or 256-bit key — how do I choose?
Neither has a known practical attack today: 128 is sufficient and slightly faster for most uses; 256 is headroom for "secret for decades" or compliance mandates. The real weak point is never key length — it is key management, which matters far more than the number.
AES-GCM versus AES-CBC?
GCM is authenticated encryption (AEAD): it produces a tag that detects any tampering, making it the default for new systems. CBC provides confidentiality only, and its history of padding-oracle vulnerabilities makes it a liability unless handled expertly. The Web Crypto API supports both; this tool offers each.

Content reviewed: