Encrypt Text with AES Step by Step

Step-by-step tutorial on encrypting text with AES-256-GCM in the browser. Covers key generation, encoding, encryption, Base64 output, and decryption. All processing runs client-side.

Practical Examples

Detailed Explanation

Encrypting Text with AES: Complete Walkthrough

This guide walks through every step of encrypting a text string using AES-256-GCM in the browser, from raw text to a portable encrypted output that can be safely stored or transmitted.

Step 1: Prepare the Plaintext

Text must be converted to bytes before encryption. JavaScript strings are UTF-16, but encryption operates on byte arrays:

const plaintext = "Confidential: Q4 revenue was $12.5M";
const plaintextBytes = new TextEncoder().encode(plaintext);
// Result: Uint8Array of UTF-8 bytes

Step 2: Generate the Encryption Key

const key = await crypto.subtle.generateKey(
  { name: "AES-GCM", length: 256 },
  true, // extractable — set to false in production
  ["encrypt", "decrypt"]
);

For password-based encryption, derive the key from a password using PBKDF2 instead of generating a random key.

Step 3: Generate a Random IV

const iv = crypto.getRandomValues(new Uint8Array(12));
// 12 bytes = 96 bits, recommended for AES-GCM

A fresh IV is required for every encryption operation with the same key.

Step 4: Encrypt

const ciphertextBuffer = await crypto.subtle.encrypt(
  { name: "AES-GCM", iv },
  key,
  plaintextBytes
);
// ciphertextBuffer includes the 16-byte authentication tag appended at the end

The Web Crypto API automatically appends the 128-bit authentication tag to the ciphertext.

Step 5: Encode for Storage

Binary ciphertext is not safe for text-based storage or transmission. Encode to Base64:

function arrayBufferToBase64(buffer) {
  const bytes = new Uint8Array(buffer);
  let binary = '';
  bytes.forEach(b => binary += String.fromCharCode(b));
  return btoa(binary);
}

const result = {
  iv: arrayBufferToBase64(iv),
  ciphertext: arrayBufferToBase64(ciphertextBuffer),
  algorithm: "AES-256-GCM"
};

console.log(JSON.stringify(result));
// {"iv":"rK3jF...","ciphertext":"x8mP2...","algorithm":"AES-256-GCM"}

Step 6: Decrypt

// Parse the stored data
const ivBytes = base64ToArrayBuffer(result.iv);
const ciphertextBytes = base64ToArrayBuffer(result.ciphertext);

const decryptedBuffer = await crypto.subtle.decrypt(
  { name: "AES-GCM", iv: ivBytes },
  key,
  ciphertextBytes
);

const decryptedText = new TextDecoder().decode(decryptedBuffer);
// "Confidential: Q4 revenue was $12.5M"

If the ciphertext or IV has been tampered with, decrypt() throws an OperationError — this is the authentication tag verification failing, protecting you from accepting corrupted data.

Common Mistakes

  1. Reusing the IV — Each encrypt call must use a unique IV
  2. Losing the IV — The IV must be stored alongside the ciphertext
  3. Not handling decryption errors — Always wrap decrypt in try/catch
  4. Using extractable keys in production — Set extractable: false when the raw key bytes are not needed

Use Case

Text encryption with AES is used in browser-based note-taking applications with client-side encryption, password managers that encrypt vault entries before sync, secure form submissions where sensitive data is encrypted before reaching the server, and local storage encryption to protect cached data. This step-by-step approach is the foundation for any browser-based encryption feature.

Try It — Encryption Playground

Open full tool