CryptoJS AES Decrypt / Encrypt

Mirror of the CryptoJS AES-256-CBC + IV-hex-prefix pattern used by Adonis/Express middlewares (and similar). Paste the encrypted payload + secret key to decrypt, or paste plaintext to produce a payload your server can decrypt with the same library. 100% in your browser — keys never leave your machine.

Decrypted output will appear here.
Encrypted payload (32-char IV hex + base64) will appear here.

Frequently Asked Questions

What payload format does this tool expect?

Exactly what the middleware below produces — 32-char hex IV concatenated with the CryptoJS AES base64 ciphertext (which starts with U2FsdGVkX1 because CryptoJS uses OpenSSL's salted format when the key is a string passphrase).

// Server-side encryption (Adonis middleware)
const iv = CryptoJS.lib.WordArray.random(16)
const enc = CryptoJS.AES.encrypt(JSON.stringify(data), secretKey, {
    iv, mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.Pkcs7, keySize: 256/32
})
return iv.toString(CryptoJS.enc.Hex) + enc.toString()

Does the explicit IV in the middleware actually do anything?

No — when CryptoJS receives a string as the key, it runs OpenSSL's KDF (EVP_BytesToKey) which derives BOTH the actual key and IV from the passphrase + an embedded 8-byte salt. The iv you pass in the config is overwritten by the derived value. The 32-char IV prefix the middleware emits is therefore cosmetic — but kept here for byte-for-byte compatibility, and stripped automatically before decryption.

Are keys / payloads uploaded anywhere?

No. Decryption and encryption happen entirely in your browser via the CryptoJS library loaded from a CDN. Neither the secret key nor the payload ever leaves your machine — the page makes no XHR/fetch calls during the operation.

Why does decryption fail with "Malformed UTF-8 data"?

Almost always one of:

  • The secret key is wrong (most common)
  • The payload was tampered with or truncated in transit
  • "Auto-strip first 32 chars" is on for a payload that has NO IV prefix (or vice-versa)
  • The server actually used CryptoJS.enc.Utf8.parse(secretKey) instead of a string — that bypasses OpenSSL KDF and you'd need explicit IV + raw key bytes

Equivalent decryption code on the server

// Mirrors the middleware
const decryptData = (payload, secretKey) => {
    const cipherText = payload.substring(32)  // skip cosmetic IV
    const bytes = CryptoJS.AES.decrypt(cipherText, secretKey)
    return JSON.parse(bytes.toString(CryptoJS.enc.Utf8))
}