What AES actually is
AES is a block cipher: exactly sixteen bytes of input, a key of sixteen, twenty-four or thirty-two bytes, and exactly sixteen bytes of output. Everything else — a message longer than one block, a message shorter, two messages under the same key — is the job of a mode of operation wrapped around it.
hello → five bytes of ciphertext and a sixteen-byte tag — AES-GCM output is exactly as long as its input, and the tag is what proves nobody altered it on the way
The cipher has been standardised since 2001 and remains unbroken; there is no practical attack on AES-128, let alone AES-256. Essentially every failure of a system that uses AES is a failure of the wrapping — a reused initialisation vector, a key derived from a password with no stretching, a ciphertext nobody checked before decrypting — which is why the mode, the key size, the salt, the IV and the iteration count are all on screen here and all editable. Everything runs in this tab through Web Crypto, and no URL on this page carries the payload, the key, the salt or the IV.
The three modes, and what each one promises
The mode decides how blocks are chained, how long the initialisation vector is, and — the part that matters most — whether anybody would notice a ciphertext being altered on the way.
| Mode | IV width | Detects tampering | Reach for it when |
|---|---|---|---|
| CBC | 16 bytes | No. An altered ciphertext decrypts into altered plaintext, and an attacker who can flip bits in one block can flip chosen bits in the next. | Matching a system that already uses it. It is the most widely implemented mode and the one most existing ciphertext is written in. |
| GCM | 12 bytes | Yes. An authentication tag is computed over the ciphertext and checked before anything is returned. It is 128 bits by default and can be truncated to any of the widths the specification lists. | Anything new. It is the default here, and the only mode on this page that will tell you when something is wrong. |
| CTR | 16 bytes | No, and worse than CBC in one respect: flipping a bit of ciphertext flips exactly that bit of plaintext, with nothing else disturbed. | Matching a system that uses it, or encrypting a stream whose length must not change — CTR output is exactly as long as its input. |
That column is the whole argument for GCM. Under CBC and CTR, decryption always succeeds in the sense that bytes come out; whether they are the bytes anybody wrote is a question the cipher does not answer. The only reason this page ever reports a problem under those modes is that the UTF-8 reader downstream refuses the result — switch the output to Hex and even that goes quiet.
Never use the same initialisation vector twice with the same key. Under CTR and GCM this is catastrophic rather than merely weak: two messages encrypted with the same keystream can be combined to remove the keystream entirely, and under GCM it also leaks the value used to compute the authentication tags. The IV is not secret and does not need to be — it needs to be different every time, which is what the regenerate button is for.
ECB is deliberately absent. No browser implements it, so offering it would mean writing a block cipher by hand on a page whose entire promise is that it does not — and ECB encrypts identical plaintext blocks to identical ciphertext blocks, which leaves the structure of the data legible in the ciphertext. If you need to reproduce an ECB ciphertext, a command-line tool is the honest answer.
Where the key comes from
AES is symmetric: the same key encrypts and decrypts. It is the wrong tool when the two ends cannot already share a secret — that is what public-key cryptography is for — and the right one for encrypting something you will read back yourself, or that you can hand a key to out of band.
AES wants a key of exactly sixteen, twenty-four or thirty-two bytes of full-entropy randomness. A passphrase is neither of those things: it is text, it is the wrong length, and its entropy is far below its byte count. Handing one to AES directly — padding it with zeros, or hashing it once — is the single most common way a system that uses AES correctly still ends up broken.
A key derivation function fixes both problems at once. PBKDF2 takes the passphrase and a salt, runs an HMAC over them a great many times, and returns exactly as many bytes as you asked for. The iteration count is the point: it makes each guess expensive, so an attacker working through a password list pays that cost for every attempt rather than once.
The salt is what stops one precomputed table working against everybody. It is not secret and does not need to be — it is stored in plain sight beside the ciphertext, exactly as it is shown here. What it must be is different for every key derived. Regenerating it is what makes the same passphrase produce a different key.
Choose Hex key or Base64 key when you already hold key bytes — from a key manager, from a configuration file, from another tool. Those are taken as they are: nothing is derived, nothing is padded, and a key of the wrong width is refused rather than quietly stretched to fit. Under a raw key the salt and the iteration count do nothing at all, and are disabled to say so.
Every setting, and when it matters
Which settings apply depends on the mode and on where the key comes from. A setting that cannot apply is disabled with a note saying why, rather than being quietly ignored.
| Setting | What it does | Reach for it when |
|---|---|---|
| Direction | Switches between producing a ciphertext and reading one back. | Always. It also swaps which box the two encoding pickers apply to. |
| Cipher mode | Picks how blocks are chained, how wide the IV is, and whether tampering is detected. | Always. Leave it on GCM unless you are matching a system that uses something else. |
| Key size | Sets how many bytes of key AES is given. | Matching an existing system. All three are unbroken, and 256 is the default here because it costs nothing to choose. |
| Key source | Decides whether the box below holds a passphrase to stretch or the key bytes themselves. | You already hold a key rather than a password. Switching to a raw key disables the salt and the iteration count entirely. |
| Key | The passphrase or the raw key. Masked by default, and revealable. | Always. This is the only value on the page that is actually secret. |
| Generate a key | Draws a random secret in whichever form the key field is reading, and reveals it. | You need a key rather than one you already hold. It is drawn from the browser's cryptographic random source, never from Math.random. |
| File | Replaces the typed box with an opened file. | Encrypting something that is not text, or decrypting a ciphertext you saved to disk. Encrypting takes the file bytes as the plaintext; decrypting reads the file as text. |
| Plaintext encoding | Says how the plaintext side is written — as text, as hex, or as base64. | The plaintext is not text. Encrypting raw bytes, or reading back a decryption that is not valid UTF-8, both need Hex. |
| Ciphertext encoding | Says how the ciphertext side is written. Never as text, because ciphertext is not text. | Matching whatever produced or expects the ciphertext. Base64 is more compact; hex is easier to compare by eye. |
| Salt | Random bytes mixed into the key derivation, so the same passphrase yields a different key. | Decrypting something encrypted elsewhere — paste that system's salt. Draw a new one for every fresh encryption. |
| Initialisation vector | Random bytes that make identical plaintexts encrypt to different ciphertexts. | Decrypting something encrypted elsewhere — paste their IV, and under GCM its width is kept. Otherwise draw a new one every time, and never reuse one with the same key. |
| Authentication tag | Sets how many bits of the GCM tag are kept and appended to the ciphertext. | Matching a system that uses a shortened tag — 96 bits is common where every byte on the wire counts. Leave it at 128 otherwise. |
| PBKDF2 iterations | Sets how much work deriving the key takes. | Matching an existing system's setting, or working out what your servers can carry. Higher is better right up to the point it is annoying. |
| Use the result as the input | Moves the result into the input box and flips the direction. | Checking that a ciphertext reads back — encrypt, press it, and the same parameters decrypt what you just produced. |
| Reset | Empties both boxes and the key, restores every setting to how the page opened, and draws a fresh salt and IV. | Starting a different job. It also drops the derived key held in memory, so nothing from the last one is carried into the next. |
| Download | Saves the current result as a timestamped text file. | You want the ciphertext on disk. It deliberately saves only the payload, so the file cannot be mistaken for a complete record. |
| Bytes | Saves the decrypted result as the bytes it actually is, rather than as the hex or base64 shown in the box. | You decrypted a file and want the file back. It appears only while decrypting, because a ciphertext is already text. |
The salt and the iteration count belong to the key derivation, so under a raw key they are disabled rather than ignored. This matters more than it looks: a tool that left them enabled and quietly did nothing with them would let you spend an afternoon adjusting a number that changes no byte of the output.
The generate button follows the key source. Under Hex key or Base64 key it draws exactly the bytes the chosen key size wants. Under Passphrase it draws one long enough to be worth as much as the key it will derive — forty-three characters at 256 bits — because stretching cannot create entropy it was not given. Iterations make each guess expensive; they do not make a short secret long. The alphabet is RFC 3986's unreserved set, so the result survives a shell, a URL and a config file without escaping.
The two directions treat a file differently, and the asymmetry is what makes the round trip close. Encrypting takes the file's bytes as the plaintext, so the plaintext encoding is switched off — those bytes are the message. Decrypting reads the file as text, because a ciphertext saved from this page is hex or base64 in a .txt. So: open a picture, encrypt, download the text; later open that text, decrypt, and press Bytes to get the picture back. One thing to expect on the way — a decrypted picture is not valid UTF-8, so the output box refuses to render it until you switch the plaintext encoding to Hex. That refusal is the same one that tells you a key was wrong, and it keeps its own name rather than being softened into an empty box.
Changing the mode redraws the initialisation vector, because the modes disagree about it. CBC and CTR need exactly one sixteen-byte block — there the IV is a block, so the width is arithmetic. GCM is different: its nonce may be any width, twelve bytes used directly and anything else run through GHASH first, which is a different construction producing a different keystream. Twelve is the recommendation and what the button draws, but plenty of systems use sixteen, so paste theirs and the redraw button will keep that width from then on. Runtimes disagree below twelve bytes, and one that will not take a width says so rather than failing as a bad tag.
The tag width is not recorded in the ciphertext, so it has to be agreed out of band exactly like the key and the IV. Decrypting with the wrong width reads part of the tag as ciphertext and the check fails — which is the authenticated mode working, not a fault. Truncation is genuine truncation: a 96-bit tag is the first twelve bytes of the 128-bit one, and buys back four bytes per message in return for thirty-two bits of forgery resistance. That is a trade worth making only when something else is dictating it.
The defaults are AES-256 in GCM mode, a passphrase stretched through PBKDF2-HMAC-SHA256 at the current OWASP iteration count, a sixteen-byte salt, and base64 output. Those are the settings to keep unless something else is dictating them.
Reproducing this somewhere else
Everything needed to get the same bytes from another language is on screen. There is no hidden step and no envelope format of our own.
- Derive the key the same way.
PBKDF2withHMAC-SHA256, the salt exactly as shown, the iteration count exactly as shown, and an output length matching the key size. In Node that iscrypto.pbkdf2Sync; in Python,hashlib.pbkdf2_hmac; in Java,PBKDF2WithHmacSHA256. A raw key skips this step entirely. - Use the same mode with the same initialisation vector, and under GCM the same tag width. CBC applies
PKCS#7padding, which every standard library does by default. GCM appends its authentication tag to the ciphertext, which is whatWeb Cryptoreturns as one value and what most other libraries hand back separately — if the other side wants the tag on its own, it is the last bytes, as many as the tag width says, and Node takes that width asauthTagLength. CTR here counts in the low sixty-four bits of the counter block, which is the common default. - Read the ciphertext in the same encoding. Hex and base64 describe the same bytes; the picker changes only how they are written down.
- If the bytes still differ, the difference is almost always the key derivation rather than the cipher. Set the iteration count to something small, print the derived key on both sides, and compare those first — the cipher itself is the part least likely to be wrong.
There is deliberately no combined output format here — no header packing the salt, the IV and the ciphertext into one blob. Several exist, they disagree with each other, and every one of them hides a parameter that this page is trying to show you. Storing three visible values beside a ciphertext is less convenient and considerably easier to get right.
What people use this for
Checking an implementation. You have written encryption into an application and want to know whether it produces what it should. Paste the same key material, salt and IV here and compare the ciphertext byte for byte.
Reading something back. A configuration value, a stored field, a message from a colleague — you have the ciphertext and the parameters and simply need the plaintext, without writing a script to get it.
Understanding what a mode actually promises. Encrypt something under CTR, change one character of the ciphertext, decrypt it, and watch exactly one character of the plaintext change. Then do the same under GCM and watch it refuse. That demonstration is worth more than any amount of prose about authenticated encryption.
Frequently asked questions
Web Crypto implementation, and the page makes no network request while encrypting or decrypting. There is also deliberately no URL parameter carrying the payload, the key, the salt or the IV: a value in a URL ends up in browser history, in proxy and server logs, and in the referrer header of every outbound link. That said, treat any web page as untrusted with a secret you actually use.AES-192: Chrome's Web Crypto implementation does not include it, while Firefox does. That is a browser limitation rather than anything about the algorithm, and the tool says so rather than silently offering a size that cannot run.PBKDF2; choose Hex key or Base64 key when you genuinely hold key bytes already.PBKDF2-HMAC-SHA256, and there is little reason to go below it. Higher is better until the wait becomes annoying — and here it is paid once per passphrase rather than once per keystroke, because the derived key is kept for as long as nothing it depends on changes. If you are reproducing another system's output, use whatever that system uses.