Encryption and Decryption Tool

Run text through AES, DES, 3DES, or Rabbit in this tab. Choose whether your key is a passphrase or raw hex bytes, set the cipher mode and padding, and read back exactly what was used so the same ciphertext opens again in OpenSSL, PHP, or Python.

Encryption and decryption console

Algorithm
Cipher mode
Key source
Result
Set a key, paste your text, then press Encrypt.

The same AES key gives two different answers, and here is why

You encrypt a string here, paste the result into a server script with what looks like the identical key, and get garbage back. Nothing is broken. The word "key" is doing two jobs, and most browser encryption tools never tell you which one they picked.

Type hunter2 into a key box and AES has a problem: it needs 16, 24, or 32 bytes of key material, and you gave it seven. Something has to bridge the gap. That something is a key derivation function, and its choice of salt, digest, and round count is part of the ciphertext's identity. Change the derivation and the same passphrase produces a completely different key.

The console above makes the choice visible instead of hiding it. Passphrase mode derives the key the way openssl enc has done for decades. Hex key mode skips derivation entirely and feeds your bytes straight into the cipher.

Passphrase mode against hex key mode

Passphrase

Your text goes through EVP_BytesToKey with a random 8 byte salt, producing both the key and the IV. The salt is written into the output behind the ASCII marker Salted__, which is why every result starts with U2FsdGVkX1 once base64 encoded.

Encrypt the same words twice and the two outputs differ, because the salt is new each run. Both decrypt correctly. This is the format Node's crypto examples, CryptoJS defaults, and old OpenSSL command lines all speak.

Hex key

Your hex string becomes the key with no processing, so a 64 character string is a 256 bit AES key exactly as written. The IV is yours to supply and gets stored alongside the ciphertext rather than inside it.

This is the mode to pick when the other side is openssl enc -K -iv, PHP's openssl_encrypt, Python's cryptography package, or a Java SecretKeySpec. Those all take raw bytes and none of them expect a salt header.

Decryption in the console checks for the header before doing any work. Ciphertext with Salted__ and a hex key selected fails immediately with an explanation rather than returning empty output, which is the failure people spend an afternoon on.

Cipher modes, and why ECB is the wrong default

A block cipher encrypts 16 bytes at a time. The mode decides what happens between blocks, and that decision leaks more than most people expect.

ModeIVPaddingWhat to know
ECBNoneRequiredEach block is encrypted on its own, so identical plaintext blocks give identical ciphertext blocks. Structure in the input survives into the output. Present here for reading legacy data, not for protecting anything.
CBCRequiredRequiredEach block is mixed with the previous one before encryption. The default choice, and the mode almost every existing system uses. Reusing an IV with the same key across messages weakens it.
CFBRequiredApplied hereTurns the block cipher into a stream, so standard implementations produce output the same length as the input. Read the padding note below before matching this against another language.
OFBRequiredApplied hereAlso a stream, with the keystream generated independently of the data. A repeated key and IV pair destroys the security completely, so never reuse the pair.
CTRRequiredApplied hereEncrypts a counter to make the keystream, which allows random access into the ciphertext. Same warning as OFB about reusing the counter start.

Watch what happens with a real example. Set ECB, then encrypt ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP with any key. The 32 character input is two identical 16 byte blocks, and the ciphertext repeats itself in the middle. Switch to CBC and the repetition disappears. That visible pattern is the entire reason ECB stays out of new work.

The padding trap in CFB, OFB, and CTR

Textbooks describe those three modes as producing ciphertext exactly as long as the plaintext, because there is nothing to pad. The CryptoJS library behind this page pads them anyway, applying whatever the padding box says to every block cipher mode without exception.

The result interoperates until you look closely. Encrypt a 17 byte string with AES-256-CFB and PKCS#7 here, and the output is 32 bytes. PHP decrypts it with the right key and hands back 17 bytes of your text followed by fifteen 0x0f bytes, because openssl_decrypt in a stream mode has no padding to strip:

// PKCS#7 selected here, decrypted in PHP 496e7465726f7020636865636b203132330f0f0f0f0f0f0f0f0f0f0f0f0f0f0f

Set padding to None before encrypting in CFB, OFB, or CTR and the output drops to 17 bytes, matching openssl enc byte for byte. The console warns you about this under the padding box whenever one of those modes is selected. ECB and CBC are unaffected, since both genuinely need padding and PKCS#7 is the shared default everywhere.

Matching this page with OpenSSL and PHP

Pick hex key mode, AES with a 32 byte key, CBC, PKCS#7, and Base64 output. The result decrypts on a shell with:

echo 'PASTE_BASE64' | openssl enc -d -aes-256-cbc -a -K <hex-key> -iv <hex-iv>

In PHP the matching call takes the key as raw bytes, so convert the hex first:

$plain = openssl_decrypt($base64,'aes-256-cbc',hex2bin($hexKey),0,hex2bin($hexIv));

The 0 flag matters. Leaving it out is fine, but passing OPENSSL_RAW_DATA tells PHP the input is raw bytes rather than base64, and mixing that up returns false with no error message worth reading.

For passphrase mode the shell equivalent needs the legacy derivation turned back on, since OpenSSL 3 defaults to PBKDF2:

echo 'PASTE_BASE64' | openssl enc -d -aes-256-cbc -a -md md5 -pass pass:YOUR_PASSPHRASE

OpenSSL prints a warning about the deprecated derivation. That warning is correct and worth taking seriously, which brings us to the limits of this page.

Do not protect real secrets with this

EVP_BytesToKey runs a single pass of MD5 over the passphrase and salt. Modern derivations run tens of thousands of rounds of a slow hash for exactly this reason, and a weak passphrase falls to a guessing attack in seconds against a single MD5 round. Passphrase mode exists to read and write a legacy format, not to secure a password.

None of the modes here authenticate the ciphertext either. AES-CBC hides content but does not detect tampering, and an attacker who flips bits in the ciphertext produces predictable changes in the decrypted output. Real systems pair encryption with AES-GCM or an HMAC over the ciphertext. GCM is not implemented in the library behind this page, so it is not offered.

Treat this as a workbench for understanding, debugging, and interoperating. For storing customer data, use the crypto library your platform ships, keys from a key management service, and AES-GCM.

Choosing between the four algorithms

  • AES at 128 or 256 bits is the answer for anything new. Hardware acceleration lives in every current CPU, and no practical break exists against the cipher itself.
  • DES has a 56 bit key. It was broken by brute force in 1998 and falls to rented cloud hardware today. Keep it here only for decrypting archives from systems built before 2005.
  • 3DES chains DES three times for an effective 112 bits, and its 64 bit block size is the real problem. NIST withdrew approval for new applications after 2023, and the Sweet32 attack becomes practical once a single key protects more than a few gigabytes. Payment and banking systems still hold plenty of it.
  • Rabbit is a stream cipher from the eSTREAM portfolio with a 128 bit key and a 64 bit IV. It has no mode and no padding, so the toggles above hide themselves when you select it. Support outside JavaScript libraries is thin, which makes it a poor pick for anything that has to interoperate.

Where this tool stops

  • Text onlyFiles are not accepted. Encrypting a 40 MB archive in a browser tab means holding it twice in memory and base64 growing it by a third. Use openssl enc or gpg on the file directly.
  • No GCMAuthenticated encryption is missing because CryptoJS never implemented it. If you need a tag alongside your ciphertext, the Web Crypto API in the same browser does AES-GCM natively.
  • Symmetric onlyOne key both encrypts and decrypts. RSA, ECC, and key exchange are outside what this page does, and generating a real key pair in a browser is not something to trust for production keys.
  • UTF-8 outputDecryption returns text. Feed it ciphertext that decrypts to binary and the result is empty rather than mangled bytes, which reads as a wrong key even when the key was right.
  • No key storageNothing persists. Reload the page and the key field is empty, by design. Losing a key means losing the ciphertext, so write it down before you close the tab.

Everything runs in your browser through the CryptoJS library loaded with the page. No text, key, or result is sent to a server, which you can confirm by opening the network panel and pressing Encrypt. That said, a secret typed into any browser tab has touched your clipboard and possibly a form autofill store, so rotate anything sensitive you paste here.

Questions from people whose ciphertext will not open

The mismatches that turn a two minute task into an afternoon.

Why does my ciphertext always start with U2FsdGVkX1?

That is the base64 of the ASCII text Salted__ followed by the first salt byte. OpenSSL writes the marker plus 8 salt bytes ahead of the ciphertext whenever a key is derived from a passphrase, and this page follows the same layout in passphrase mode. Anything reading it back has to strip those 16 bytes and rerun the derivation with that salt. Switch to hex key mode if you want ciphertext with no header.

Decryption returns nothing at all instead of an error. What went wrong?

The cipher produced bytes, then the UTF-8 step found they were not valid text and gave back an empty string. That happens when the key, IV, mode, or padding differs by any amount from what encrypted the data. Work through them in order: key length first, then the mode, then the IV, then padding. A wrong IV in CBC corrupts only the first block, so if the tail of your text looks right and the opening is noise, the IV is your problem.

Do I need to keep the IV secret?

No. The IV is stored or transmitted in the clear, usually prepended to the ciphertext. What it needs is to be unpredictable and never reused with the same key. Reusing an IV in CBC leaks whether two messages share an opening block, and in CTR or OFB it destroys the encryption entirely by producing the same keystream twice. Press Generate for every new message rather than keeping one around.

What key length should I pick for AES?

AES-256 for anything long lived, AES-128 when throughput matters and the data has a short life. Both are far beyond brute force reach. The practical difference is that 256 bit keys are the usual requirement in compliance frameworks, while 128 bit runs about 40 percent faster on the same hardware. Neither choice saves you from a weak passphrase, which is the failure that shows up in real breaches.

My PHP openssl_encrypt output does not match this page. Why?

Almost always the OPENSSL_RAW_DATA flag or the key format. Without the flag PHP base64 encodes the result, which matches this page in Base64 format and not in Hex. On the key side PHP wants raw bytes, so pass hex2bin($hexKey) rather than the hex string. Passing the hex text directly gives PHP a 64 byte key, which it silently truncates to 32 bytes of the wrong material.

Can I decrypt something without the key?

No, and no tool anywhere does. AES with a properly random key has no shortcut, and a brute force run against 128 bits outlasts the sun. What does recover data is guessing a weak passphrase, which is an attack on your password choice rather than on AES. If you have lost a key, the ciphertext is gone. Restore from backup.

Which padding option should I use?

PKCS#7 unless something else forces your hand. It is the default in OpenSSL, PHP, Java, and .NET, so it is what the other side expects. Zero padding is the one to choose when working with older embedded or mainframe systems that fill the last block with null bytes, and it has a catch: trailing zeros in the original data are indistinguishable from padding and get stripped on the way out. NoPadding requires your input to be an exact multiple of the block size and throws otherwise.

Is ECB ever the right choice?

For encrypting messages, no. It is kept here for two narrow cases: decrypting data from a legacy system that used it, and encrypting a single block of exactly one block length where there is nothing to repeat. Anything longer leaks its structure. The well known penguin image encrypted under ECB stays recognisable, and the same effect applies to database columns where repeated values are visible as repeated ciphertext.

Does the text I encrypt reach your server?

No. The cipher work happens in the page script through CryptoJS, with no request in the encryption path. Open your browser network panel and press Encrypt to check for yourself. The library file itself loads from a CDN when the page loads, so the page needs a connection to start, then works offline afterwards.