The Bundle Your AES Key Should Have Shipped In: Why AES-256-GCM Is Not a Magic Word
Ending (written first)
AES-256-GCM is the part of the encryption that almost every tutorial gets right and almost every homegrown script gets wrong. The cipher itself is unbreakable in any practical sense. The bundle format around it — the IV, the auth tag, the key derivation, the output encoding — is where 90% of the real-world failures live. If you remember nothing else: do not roll your own wire format, do not reuse a nonce, do not call it encrypt(password) in your code review and move on. A good tool is the one that hands you a self-describing bundle with the IV, the ciphertext, the auth tag, and the algorithm all in one JSON object — so the same input decrypts cleanly six months later on a different machine, with a different library, after the original author has left the team. That is the difference between encryption that exists and encryption that works.
Hook
A senior engineer once told me his team had been “using AES” for three years and then realized, in a tabletop exercise, that none of their three services could decrypt each other’s output. The same algorithm. The same key. Three different bundle shapes. They had been passing around ciphertexts that looked identical to a casual eye and were actually unrecognizable to the receiving service. Nobody caught it in code review because the code review was looking for the word AES, and the code had the word AES in it. The whole thing collapsed the first time someone tried to recover data in a real incident. That is the failure mode this tool was built to prevent.
The encryption is the easy part
AES-256-GCM is one of three block ciphers the U.S. National Institute of Standards and Technology recommends for new applications, alongside ChaCha20-Poly1305. It is the same cipher inside TLS 1.3, inside Apple’s FileVault, inside the encrypted disks on most cloud VMs, and inside every credit card chip produced in the last decade. The math is settled. The implementation is in hardware on every modern CPU under the name AES-NI, which means a single block encryption takes less than a nanosecond. The algorithm is not the problem. The problem is everything that touches it.
When you call aes-256-gcm in a library — crypto.createCipheriv('aes-256-gcm', key, iv) in Node, Cipher.getInstance("AES/GCM/NoPadding") in Java, AES.GCM.new(...) in WebCrypto — the library will hand you a stateful cipher object. You feed it plaintext, you call final(), and out comes a buffer. That buffer is the concatenation of the ciphertext and a 16-byte authentication tag. If anyone in the next service round-trips the key but mangles the tag — truncates it, drops it, or stores it in a separate column that gets lost in a schema migration — the ciphertext becomes undecryptable. The decryption will fail with a generic error and no hint about which piece is missing.
The bundle format is the contract. Get the contract wrong and the algorithm does not save you.
Where the bundle goes wrong in real systems

There is a recurring family of bugs I have seen in code that “uses AES.” The first is IV reuse. AES-GCM is catastrophically broken when you encrypt two different messages under the same key and the same nonce — an attacker who recovers the two ciphertexts can recover the XOR of the two plaintexts and, in many cases, recover the plaintexts themselves. NIST SP 800-38D is unambiguous about this. The fix is mechanical: generate a fresh 12-byte random IV for every encryption, store it alongside the ciphertext, never derive it from a counter unless you have a system that guarantees uniqueness across all time and all replicas. The second bug is authentication-tag handling. A surprising number of implementations either store only the first 8 bytes of the 16-byte tag (cutting security in half) or skip the tag verification entirely on decryption and call it a “best-effort” check. The third bug is key derivation. Calling AES.encrypt(password) where the password is a 6-character user-typed string is not AES-256 — it is AES-256 with a key that has roughly 36 bits of effective entropy, and a modern GPU cluster can crack it in hours. PBKDF2, scrypt, or Argon2 is the floor.
A tool that does this right does three things. It uses a 96-bit random IV per encryption. It stores the IV, the ciphertext, the auth tag, and the algorithm identifier together — usually as a single JSON object so the bundle is self-describing. And it accepts a passphrase and runs it through a key derivation function before passing it to the cipher, with parameters that are honest about the cost. The AES Encryption Tool follows exactly that pattern. You hand it plaintext and a passphrase; it hands you back a JSON bundle with iv, ciphertext, authTag, and algorithm fields. You paste that JSON back into the decrypt box a year later, on a different machine, and the same plaintext comes out. That round-trip property is the actual feature, and it is the one homegrown scripts almost never get right.
Why the JSON bundle is the contract

A bundle is more than a serialization format — it is a commitment to a particular ciphertext identity. The same plaintext encrypted twice with the same key will produce two different ciphertexts, because the IV is random. The bundle is what tells the decryptor which ciphertext this is, what IV to use, what algorithm, what tag to verify. If any of those four fields is missing, the decryption either fails silently with a wrong-but-plausible plaintext (in CBC mode without a MAC, the worst case) or fails loudly with an auth-tag mismatch (in GCM, the best case — GCM was designed to make silent failure impossible).
This is why so many production systems end up using a single-field hex string as the “encrypted blob.” It looks simple, but it throws away the version field that tells you which algorithm was used. The day you migrate from AES-256-CBC to AES-256-GCM, every old blob becomes ambiguous. The day you rotate the key derivation function from a single SHA-256 to PBKDF2, every old blob becomes ambiguous. The day you realize you need to add a key-id field so the decryptor knows which of five rotating keys to use, every old blob is broken. The JSON bundle makes those migrations possible because it is structured. There is room to add fields without breaking the old format.
Files are not text

The other half of the AES picture is file encryption, which is what people actually need most of the time. The same tool, given a 20 MB file, will encrypt it and hand you back a base64 bundle — but a 20 MB file base64-encoded is roughly 27 MB of text, which is unwieldy to paste, unwieldy to email, and easy to mangle in transit. The right answer for files is binary output: write the bundle as a binary file with a small header (magic bytes + version + algorithm + IV length + tag length) followed by the raw ciphertext. The AES Encryption Tool supports both modes — paste-friendly JSON for short secrets, file upload for everything else. The distinction is not cosmetic. It is the difference between a tool you can use to encrypt an API key you want to share with one teammate, and a tool you can use to encrypt a backup you want to keep for ten years.
For most file-encryption use cases the practical threat model is not “an attacker brute-forces AES” but “the disk fails and the backup is corrupted.” That is why the authentication tag matters even when the attacker never appears. GCM will detect any bit-flip in the ciphertext and refuse to decrypt, which is exactly the behavior you want from a long-term archive. CBC without a MAC will happily decrypt corrupted data and hand you back a plausible-looking file with garbage in the middle, which is how you end up with backups that “restore successfully” but contain a corrupted database that crashes on first query.
How to use it well
Three rules. First, generate a passphrase with at least 80 bits of entropy — a random English phrase from a 2048-word list, or a 12-character random alphanumeric string, or anything a password manager produced. Anything less and the key derivation function is the only thing standing between your data and a GPU farm. Second, treat the output bundle as opaque. Do not parse the IV out of it and try to store the fields separately in different database columns. Do not edit the JSON by hand. Do not try to “optimize” it by stripping the auth tag. The whole point of the format is that it is a single atomic unit. Third, test the round-trip. Encrypt something, immediately decrypt it, and confirm the output matches the input byte-for-byte. If you cannot, your bundle format is broken in a way that will not be apparent until the day you actually need it.
If you want a deeper look at how the key derivation parameters interact with the cipher mode, the AES Encryption Tool samples page walks through a few worked examples — short text round-trips, file uploads, and the difference between base64 and hex output encodings for the same underlying bytes. The samples exist because the difference between “this worked in my test” and “this worked at 2 AM during the incident” is almost always a parameter you did not test.
The closing thought
AES-256-GCM is one of those rare pieces of cryptography that does exactly what it says on the tin. The trap is that the tin is a small part of the system. The IV, the auth tag, the key derivation, the output format, the version field, the round-trip property — those are the parts you have to get right, and they are the parts a good tool handles for you so you do not have to be a cryptographer to encrypt a file correctly. Use the algorithm. Respect the bundle. Test the round-trip. That is the whole job.
Explore more tools at elysiatools.com.

