About the AES Encrypt
Do AES encryption online without choosing a mode, a key size or an IV: type your text, click Encrypt, and copy one Base64 string encrypted with AES-256-GCM under a passphrase. The salt, IV and authentication tag travel inside that string, so the passphrase is all anyone needs to read it with AES Decrypt.
Encryption runs in your browser through the Web Crypto API, and neither the text nor the passphrase is sent anywhere. If you don't have a passphrase in mind, the page makes a random 20-character one for you.
- Type or paste the message into Text to encrypt. Any length and any language works; it's encrypted as UTF-8.
- Leave Use my own passphrase unticked to get a random passphrase, or tick it and type yours into Passphrase. The eye button shows what you typed.
- Click Encrypt. The button reads Encrypting... for a moment while 600,000 rounds of PBKDF2 turn the passphrase into a key.
- Copy the Encrypted text (Base64). If the passphrase was generated, copy it from Generated passphrase as well, because the text can't be decrypted without it.
- Send the two by different routes, for example the ciphertext by email and the passphrase in a phone call. Editing the text or passphrase clears the old result.
InputText to encrypt: Hello, world
Passphrase: correct horse battery stapleOutputVEwCjLZX28BsQqMpvdnir+KgEDOORtLmVH2/Jd6RNXLZCWM4HO0vXMtM44pxNACJy94KCwC6gNCG7u4=Paste this into AES Decrypt with the same passphrase and you get Hello, world back.
InputText to encrypt: Hello, world
Passphrase: correct horse battery stapleOutputVEwCF/i91ty2dmUUoZM2q/+ebGcMYBMtS3q+5A4JhyaOyxL+XwR4mUDYsAUogytwXzsfC7+W5QjQ5zA=A completely different result, because every click uses a fresh random salt and IV. Both strings decrypt to the same message, and nobody who sees them can tell that they match.
InputText to encrypt: Hello, world
Use my own passphrase: untickedOutputEncrypted text (Base64):
VEwC6sSodNOxFLMF54tA29UVnCkaako6IzLwYZRXT5ezUodh9nOhLJKxRgItDjLtirUC3jA3BnJOKkU=
Generated passphrase:
4QrmKKr3UhQW1uB9iCLX20 random characters from A to Z, a to z and 0 to 9 give about 119 bits of entropy. There are no symbols, so it survives being read aloud or pasted into any chat app.
What happens when you click Encrypt
The settings are fixed and public, so you can decrypt the result in your own code:
- A random 16-byte salt and a random 12-byte IV (also called a nonce) are drawn with
crypto.getRandomValues. - PBKDF2-HMAC-SHA256 with 600,000 iterations turns the passphrase and salt into a 256-bit key.
- The text, as UTF-8, is encrypted with AES-256 in GCM mode, which appends a 16-byte authentication tag.
- The output is the Base64 of the bytes
54 4C 02("TL" and format version 2), the salt, the IV, then the ciphertext with its tag. That header is why every result starts withVEwC. - The encrypted bytes are always 47 longer than your text, and Base64 adds about a third.
Hello, world(12 bytes) becomes 59 bytes, or 80 Base64 characters.
AES modes compared: ECB, CBC, CTR and GCM
AES itself only encrypts one 16-byte block. The mode of operation decides how longer messages are handled, and that choice matters more than the key size:
- ECB encrypts each block on its own, so identical blocks give identical ciphertext and patterns in the data stay visible. Don't use it for anything.
- CBC chains each block to the one before, starting from a random 16-byte IV, and needs PKCS#7 padding. It has no integrity check, and servers that reveal padding errors can be attacked with padding oracle attacks.
- CTR turns AES into a stream cipher with no padding. Like CBC it can't detect changes, and reusing a nonce with the same key exposes the plaintext.
- GCM (NIST SP 800-38D) is CTR plus an authentication tag. It detects any change to the ciphertext and any wrong key, and it's the AES mode most TLS 1.3 connections use. This page uses GCM.
Passphrase, key, salt and IV
AES keys are exactly 16, 24 or 32 bytes (AES-128, AES-192 or AES-256), usually written as 32, 48 or 64 hex characters. A passphrase isn't a key, so PBKDF2 stretches it into one. The 600,000 iterations follow the OWASP recommendation for PBKDF2 with SHA-256 and make every guess slow.
The random salt gives a different key every time, even for the same passphrase, so precomputed tables don't help an attacker. The random IV matters even more: with GCM, an IV used twice under one key leaks information about both messages and allows forgery.
AES, RSA and DES
AES is symmetric: one key encrypts and decrypts, and it's fast enough for gigabytes. RSA is asymmetric (a public key encrypts, a private key decrypts) and is slow and limited to small inputs, so real systems like TLS use public-key cryptography to agree on a key and AES to encrypt the data. DES, the standard before AES, had a 56-bit key and was brute-forced in 1998. AES (FIPS 197, based on Rijndael) replaced it in 2001.
When to use something else
The page is for short secrets like an API key for a colleague or a private note. Know where it stops:
- The passphrase is the weak point. PBKDF2 can't save a short or common one, so use the generated passphrase or one from the Password Generator.
- The output format is toolo's own.
openssl enccan't read it (it has no GCM support), and CryptoJS uses a different layout. Use AES Decrypt or the code on the In code tab. - Don't encrypt user passwords for storage. Hash them with Argon2id, scrypt or bcrypt, so nobody can decrypt them, you included.
- For files, backups and long-term storage, use audited tools like age or GPG, or your cloud provider's secret manager.
- Base64 alone hides nothing. Text run through Base64 Encode can be read by anyone; here Base64 is only the text form of the encrypted bytes.
Encrypt in the same format in code
Both functions produce output that AES Decrypt can read, using only built-in APIs. The Web Crypto version runs in browsers, Deno and Node 19 or later.
const crypto = require("node:crypto");
function encryptToolo(text, password) {
const salt = crypto.randomBytes(16);
const iv = crypto.randomBytes(12);
const key = crypto.pbkdf2Sync(password, salt, 600000, 32, "sha256");
const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
const ciphertext = Buffer.concat([cipher.update(text, "utf8"), cipher.final()]);
return Buffer.concat([
Buffer.from([0x54, 0x4c, 0x02]), // "TL" + version 2
salt,
iv,
ciphertext,
cipher.getAuthTag(), // 16 bytes
]).toString("base64");
}async function encryptToolo(text, password) {
const salt = crypto.getRandomValues(new Uint8Array(16));
const iv = crypto.getRandomValues(new Uint8Array(12));
const material = await crypto.subtle.importKey(
"raw", new TextEncoder().encode(password), "PBKDF2", false, ["deriveKey"]
);
const key = await crypto.subtle.deriveKey(
{ name: "PBKDF2", salt, iterations: 600000, hash: "SHA-256" },
material,
{ name: "AES-GCM", length: 256 },
false,
["encrypt"]
);
// Web Crypto appends the 16-byte tag to the ciphertext.
const ciphertext = new Uint8Array(
await crypto.subtle.encrypt({ name: "AES-GCM", iv }, key, new TextEncoder().encode(text))
);
const out = new Uint8Array([0x54, 0x4c, 0x02, ...salt, ...iv, ...ciphertext]);
return btoa(String.fromCharCode(...out)); // fine for short text
}Can AES-256 be cracked?
Not by attacking AES itself: no practical attack is known, and trying every 256-bit key is physically out of reach. Real breaks come from weak passphrases, reused IVs and missing authentication, which is why this page uses a slow key derivation, random IVs and GCM.
What is the difference between AES-128 and AES-256?
The key length, 128 or 256 bits, and the number of rounds, 10 or 14. Both are secure today. AES-256 has a larger safety margin, including against future quantum computers, and costs only a little more time.
Which AES mode should I use?
GCM, or another authenticated mode, with a new random IV for every message. Avoid ECB completely, and avoid plain CBC or CTR unless you also add an HMAC over the ciphertext.
Why is the encrypted text different every time?
Each click draws a new random salt and IV and stores both in the output. Decryption still works, and two encryptions of the same message can't be linked to each other.
Can I decrypt the result with OpenSSL or CryptoJS?
Not directly. The output has its own header, salt, IV and GCM tag layout, and openssl enc doesn't support GCM. Use AES Decrypt, or the Node.js and Web Crypto decrypt functions on that page.
What happens if I lose the passphrase?
The text is gone for good. There's no reset and no recovery key, and toolo never sees or stores your passphrase.
Is it safe to encrypt a real secret on this page?
The plaintext, the passphrase and the generated passphrase exist only in your browser tab, and encrypting makes no network request. The bigger risk is how you share the result: if the passphrase travels in the same email as the ciphertext, the encryption protects nothing.