AES-256-GCM Decrypt / Encrypt

Mirror of the Node.js AES-256-GCM middleware pattern with a SHA-256 key-derivation step (no PBKDF2, no salt). Format: 24-hex IV + 32-hex auth tag + base64 ciphertext. Runs entirely in your browser via the Web Crypto API — keys never leave your machine.

payload = [ 24 hex chars / 12 bytes IV ] + [ 32 hex chars / 16 bytes auth tag ] + [ base64 ciphertext ] key = SHA-256(secretKey) // 32-byte derived key, used directly with AES-GCM
Decrypted output will appear here.
Encrypted payload (IV + tag + ciphertext) will appear here.

Frequently Asked Questions

What payload format does this tool expect?

The exact byte-for-byte output of the Node crypto middleware below — 12-byte IV hex + 16-byte GCM auth tag hex + base64 ciphertext, all concatenated as one string.

// Server-side encryption (Node crypto)
const derivedKey = crypto.createHash('sha256').update(secretKey).digest()
const iv = crypto.randomBytes(12)
const cipher = crypto.createCipheriv('aes-256-gcm', derivedKey, iv)
let enc = cipher.update(JSON.stringify(data), 'utf8', 'base64')
enc += cipher.final('base64')
const tag = cipher.getAuthTag()
return iv.toString('hex') + tag.toString('hex') + enc

Why SHA-256 of the secret instead of PBKDF2 or scrypt?

SHA-256 turns an arbitrary-length secret into exactly 32 bytes — the size AES-256 needs. It's fast and deterministic. It's not a slow KDF, so it provides zero brute-force resistance — if your secret is weak, an attacker who steals a single ciphertext can crack it offline. For server-to-server middleware where the secret is a long random env var, that's acceptable. For user passwords, prefer PBKDF2 / scrypt / Argon2 with a salt.

Why does GCM use a separate auth tag?

AES-GCM is authenticated encryption — every ciphertext comes with a 16-byte tag that proves the ciphertext (and IV) haven't been tampered with. Decryption recomputes the tag and rejects the payload if it doesn't match. That's why the wrong key, a flipped bit, or a truncated payload all fail loudly instead of producing garbage plaintext. Web Crypto's decrypt() expects ciphertext + tag concatenated as one buffer — this tool reassembles them automatically.

Why does decryption fail with "OperationError"?

Almost always one of:

  • The secret key is wrong (most common)
  • The payload was tampered with — AES-GCM fails on a single flipped bit, that's by design
  • You pasted only the base64 part without the IV/tag prefix (or vice-versa)
  • The server used a different KDF (PBKDF2, scrypt) instead of plain SHA-256

Difference vs. the CryptoJS AES (IV-prefix) tool?

Completely different stack: that tool is AES-256-CBC with OpenSSL KDF (salt-derived key+iv from a passphrase). This tool is AES-256-GCM with SHA-256(secret) as the literal key — no salt, no KDF iterations. GCM also adds authentication; CBC does not. Use whichever matches your middleware. If you're not sure, check the imports: import CryptoJS means the other tool, import crypto from 'node:crypto' means this one.