Upload a file
  1. base64hub
  2. Keys & hashes
  3. AES encrypt

AES Encrypt to Base64

Type a message and a password to get a Base64 string that only someone with the password can read. The format is AES-256-GCM with PBKDF2-SHA256, documented below, so it can be decrypted in Python or Node.js as well as here.

Free no sign-up, no limits Local nothing uploaded AES-256-GCM PBKDF2, 310,000 iterations Updated Oct 27, 2026
Format – decrypt it anywhere

Output = Base64( salt 16 bytes ‖ IV 12 bytes ‖ ciphertext ‖ GCM tag 16 bytes ). Key = PBKDF2-HMAC-SHA256(password as UTF-8, salt, 310,000 iterations, 32 bytes). Cipher: AES-256-GCM, no associated data. Code that decrypts it:

Python (pip install cryptography)

import base64
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives import hashes

def decrypt(b64, password):
    data = base64.b64decode(b64)
    salt, iv, ct = data[:16], data[16:28], data[28:]
    key = PBKDF2HMAC(algorithm=hashes.SHA256(), length=32, salt=salt,
                     iterations=310000).derive(password.encode("utf-8"))
    return AESGCM(key).decrypt(iv, ct, None).decode("utf-8")
Node.js

const crypto = require("crypto");
function decrypt(b64, password) {
  const data = Buffer.from(b64, "base64");
  const salt = data.subarray(0, 16), iv = data.subarray(16, 28);
  const tag = data.subarray(data.length - 16), ct = data.subarray(28, data.length - 16);
  const key = crypto.pbkdf2Sync(password, salt, 310000, 32, "sha256");
  const d = crypto.createDecipheriv("aes-256-gcm", key, iv);
  d.setAuthTag(tag);
  return Buffer.concat([d.update(ct), d.final()]).toString("utf8");
}

Processed in your browser. This page cannot send your data anywhere – how we know

How to encrypt text with a password

  1. Keep Encrypt and the Password key type selected.
  2. Enter a Password. Show password reveals it while you type. Leave PBKDF2 iterations at 310,000 unless you have a reason; whoever decrypts must enter the same number.
  3. Type the message in Plain text and press Encrypt, or Enter in the password field, or Ctrl+Enter in the text.
  4. Copy the Encrypted Base64 or download it. Tick URL-safe output (- _ , no =) if the string goes into a URL.

Below the output the tool shows a breakdown: salt, IV, ciphertext and tag sizes, the total, and the key derivation used. To send the result, put the Base64 in one message and the password in another channel, such as a phone call or a different chat app. The recipient opens AES decrypt and reverses the process.

What this tool is for

This tool is for sharing a piece of text securely when no better channel exists: a Wi-Fi password for a guest, a recovery code sent to a colleague, a note pasted into a ticket that too many people can read. It replaces “I’ll just send it over chat” with something an eavesdropper cannot read.

It is not a secrets manager. It does not store anything, control who has access, log who decrypted what, rotate keys, or revoke a secret once sent. Application credentials, production keys and anything shared across a team belong in a proper secrets manager or a password manager with sharing. Also keep in mind that the encrypted text stays decryptable forever by anyone who later learns the password.

The exact format

In password mode the output is:

Base64( salt[16] ‖ iv[12] ‖ ciphertext ‖ tag[16] )
  • Key: PBKDF2-HMAC-SHA256 over the password as UTF-8, with the 16-byte salt and 310,000 iterations, producing 32 bytes.
  • Cipher: AES-256-GCM with the 12-byte IV, a 128-bit tag and no associated data.
  • Randomness: a new salt and IV for every encryption, from crypto.getRandomValues.

The ciphertext is exactly as long as the UTF-8 message, so the blob has 44 bytes of overhead. A 33-byte message becomes 77 bytes, which is 104 Base64 characters. There is no version byte, and the iteration count is not stored: if you change it, the recipient must type the same value or decryption fails with the same error as a wrong password.

In Raw key mode the PBKDF2 step disappears and the output is Base64( iv[12] ‖ ciphertext ‖ tag[16] ), encrypted with your 16-, 24- or 32-byte key given as Base64 or hex. Generate random key creates one. Chrome and Edge do not support 192-bit AES keys, and the tool says so instead of failing silently; use 128 or 256 bits.

Decrypting it in Python or Node.js

The recipient does not need this website. With Python and the cryptography package (pip install cryptography):

import base64, os
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives import hashes

def _key(password, salt):
    return PBKDF2HMAC(algorithm=hashes.SHA256(), length=32, salt=salt,
                      iterations=310000).derive(password.encode("utf-8"))

def decrypt(b64, password):
    data = base64.b64decode(b64)
    salt, iv, ct = data[:16], data[16:28], data[28:]
    return AESGCM(_key(password, salt)).decrypt(iv, ct, None).decode("utf-8")

def encrypt(text, password):
    salt, iv = os.urandom(16), os.urandom(12)
    ct = AESGCM(_key(password, salt)).encrypt(iv, text.encode("utf-8"), None)
    return base64.b64encode(salt + iv + ct).decode("ascii")

AESGCM.decrypt expects the tag at the end of the ciphertext, which is where this format puts it. In Node.js the tag has to be split off and passed separately:

const crypto = require("crypto");

function decrypt(b64, password) {
  const data = Buffer.from(b64, "base64");
  const salt = data.subarray(0, 16), iv = data.subarray(16, 28);
  const tag = data.subarray(data.length - 16), ct = data.subarray(28, data.length - 16);
  const key = crypto.pbkdf2Sync(password, salt, 310000, 32, "sha256");
  const d = crypto.createDecipheriv("aes-256-gcm", key, iv);
  d.setAuthTag(tag);
  return Buffer.concat([d.update(ct), d.final()]).toString("utf8");
}

Blobs from this page decrypt with both functions, and blobs from encrypt() decrypt on this page. If you used URL-safe output, Node’s Buffer.from(b64, "base64") reads it as is; in Python use base64.urlsafe_b64decode(b64 + "=" * (-len(b64) % 4)). Note that openssl enc cannot help here, as it does not support GCM. The Python and Node.js pages cover the Base64 side in more depth.

FAQ

Is Base64 encoding enough to protect the text?

No. Base64 only changes how bytes are written, and anyone can decode it without a key. This tool encrypts with AES first and uses Base64 just to turn the ciphertext into text you can paste. More in Base64 is not encryption.

Why is the output different every time I encrypt the same text?

Each run uses a fresh random 16-byte salt and 12-byte IV. That is intended: identical messages must not produce identical ciphertexts, and AES-GCM must never reuse an IV with the same key.

How strong does the password need to be?

PBKDF2 with 310,000 iterations slows down guessing, but it cannot save a short or common password. Use a long random passphrase, such as five or more random words, and send it through a different channel than the ciphertext.

Can I encrypt files with this tool?

No, it encrypts text only. For files, use a dedicated tool such as age or gpg.

Which AES mode and key size does the tool use?

AES-256 in GCM mode with a 128-bit authentication tag in password mode. In raw-key mode the key size follows the key you supply: 128, 192 or 256 bits.