SecurityBy Sam Holloway··Updated ·11 min read

AES Encryption Explained: How It Works and When to Use It

AES is the encryption standard used by HTTPS, WhatsApp, and FileVault. A plain-English guide covering how it works, cipher modes, a Node.js code example, and AES vs ChaCha20.

AES (Advanced Encryption Standard) is the symmetric encryption algorithm used by virtually everything that needs to keep data secret: TLS (the HTTPS protocol), full-disk encryption, password managers, VPNs, file archiving tools, and most encrypted messaging systems. Understanding how AES works helps you use it correctly in your own applications and recognise when it is being used incorrectly by others.

What makes AES different from hashing

Hashing is a one-way transformation: you cannot reverse a hash to get the original data. Encryption is two-way: you can encrypt data and later decrypt it with the correct key. AES takes plaintext and a key and produces ciphertext that is meaningless without the key. Given the key, decryption recovers the original plaintext exactly.

This reversibility is what distinguishes encryption from hashing and makes it appropriate for different use cases. Use hashing when you need to verify something (a password, a file's integrity) without ever recovering the original. Use encryption when you need to store or transmit data that must later be read by an authorised recipient.

Key sizes: AES-128, AES-192, AES-256

AES supports three key lengths. AES-128 uses a 128-bit key (16 bytes). AES-192 uses a 192-bit key (24 bytes). AES-256 uses a 256-bit key (32 bytes). All three are considered secure for all practical purposes: no known attack against properly implemented AES makes any of them feasible to break with current or near-future computing power.

AES-256 provides a higher security margin and is the standard choice for new implementations, government use, and anything dealing with long-term sensitive data. AES-128 is still appropriate for general-purpose encryption and has a slight performance advantage on hardware without dedicated AES acceleration. AES-192 is rarely used: it offers no practical advantage over either 128 or 256.

Modes of operation: ECB, CBC, GCM, CTR

AES encrypts exactly one 128-bit block at a time. For real data (which is longer than 16 bytes), you need a mode of operation that specifies how to chain multiple block encryptions together. The mode you choose matters as much as the key size.

  • ECB (Electronic Codebook): encrypts each block independently. Identical plaintext blocks produce identical ciphertext blocks, so patterns in the original data remain visible in the ciphertext. Never use ECB for anything other than single-block operations.
  • CBC (Cipher Block Chaining): each block is XORed with the previous ciphertext block before encryption. Requires an initialisation vector (IV). Patterns are hidden, but CBC does not authenticate the ciphertext, so tampering can go undetected.
  • GCM (Galois/Counter Mode): authenticated encryption that provides both confidentiality and integrity verification. Produces an authentication tag alongside the ciphertext. If the ciphertext is modified, decryption fails. The recommended mode for new implementations.
  • CTR (Counter Mode): turns AES into a stream cipher by encrypting a counter value and XORing the result with plaintext. Efficient and parallelisable, but provides no authentication. Use GCM instead unless you have a specific reason for CTR.

Why GCM is the recommended mode

AES-GCM provides authenticated encryption with associated data (AEAD). In addition to encrypting data, it produces a 128-bit authentication tag that verifies the ciphertext was not tampered with. If anyone modifies even a single bit of the ciphertext in transit or storage, decryption fails with an authentication error rather than silently producing corrupted plaintext.

This protection against tampering is what separates GCM from older modes like CBC. A CBC-encrypted message can be modified in ways that alter the decrypted content without being detected. GCM prevents this by binding the ciphertext to its authentication tag mathematically. TLS 1.3 removed CBC cipher suites entirely and uses only AEAD modes, which is why modern HTTPS is more secure than older TLS 1.2 connections.

The IV (initialisation vector): what it is and why it matters

Most AES modes require an IV: a random value included alongside the ciphertext that ensures the same plaintext encrypted twice produces different ciphertext. The IV does not need to be secret, but it must be random and unique for each encryption operation. In GCM mode, the IV is typically 12 bytes (96 bits), which gives a 96-bit nonce space.

Reusing an IV with the same key is a critical security vulnerability in GCM mode. If two messages are encrypted with the same key and same IV, an attacker who observes both ciphertexts can XOR them together and recover information about the plaintexts, and in some cases recover the authentication key entirely. Always generate a fresh cryptographically random IV for every encryption operation.

AES in Node.js: a working encrypt/decrypt example

Node.js includes AES-256-GCM support in its built-in crypto module. No third-party packages are required. The following example shows the correct pattern: random IV per operation, authentication tag stored with the ciphertext, and key derived separately from a secure source.

javascript
const crypto = require('crypto');

function encrypt(plaintext, key) {
  const iv = crypto.randomBytes(12); // 96-bit nonce for GCM
  const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
  const encrypted = Buffer.concat([
    cipher.update(plaintext, 'utf8'),
    cipher.final()
  ]);
  const authTag = cipher.getAuthTag(); // 128-bit authentication tag
  // Pack iv + authTag + ciphertext into one base64 string
  return Buffer.concat([iv, authTag, encrypted]).toString('base64');
}

function decrypt(data, key) {
  const buf = Buffer.from(data, 'base64');
  const iv = buf.subarray(0, 12);
  const authTag = buf.subarray(12, 28);
  const encrypted = buf.subarray(28);
  const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
  decipher.setAuthTag(authTag);
  // Throws if ciphertext was tampered with
  return decipher.update(encrypted) + decipher.final('utf8');
}

// In production: load key from environment variable or key management service
const key = crypto.randomBytes(32); // 256-bit key
const message = 'Sensitive data here';

const ciphertext = encrypt(message, key);
console.log('Encrypted:', ciphertext);

const decrypted = decrypt(ciphertext, key);
console.log('Decrypted:', decrypted); // Sensitive data here

A few things to note about this implementation. The IV is prepended to the ciphertext so it can be extracted at decryption time without storing it separately. The authentication tag immediately follows the IV, so the format is always: 12 bytes IV, 16 bytes auth tag, then the ciphertext. The decrypt function will throw an error if the auth tag does not match, which means any tampering is detected before the decrypted data is used. In production, the 32-byte key should be loaded from an environment variable or a key management service, never hardcoded.

AES vs ChaCha20-Poly1305: which should you use?

ChaCha20-Poly1305 is the main alternative to AES-GCM in modern cryptography. Both are AEAD ciphers used in TLS 1.3. The choice between them depends primarily on the hardware your code runs on.

FeatureAES-256-GCMChaCha20-Poly1305
Cipher typeBlock cipherStream cipher
Key size256 bits256 bits
Speed with hardware AES (AES-NI)Very fastSlower
Speed without hardware AESSlowerFaster
AuthenticationGHASH (GCM tag)Poly1305 MAC
IV reuse riskCritical (key recovery possible)Nonce misuse resistant variants exist
TLS 1.3 supportYes (TLS_AES_256_GCM_SHA384)Yes (TLS_CHACHA20_POLY1305_SHA256)
Used byHTTPS, FileVault, BitLocker, S3WireGuard, older Android, low-power IoT
FIPS 140 compliantYesNo (as of 2025)

For server-side code running on modern x86 hardware (AWS, GCP, Azure, most VPS providers), AES-256-GCM is the better choice because Intel and AMD CPUs include AES-NI hardware acceleration that makes AES roughly 10x faster than a software implementation. For mobile apps targeting older Android devices without AES-NI, or for IoT devices running on ARM without hardware AES, ChaCha20-Poly1305 is typically faster and equally secure. TLS 1.3 negotiates between the two automatically based on what both sides support and prefer.

Where AES appears in everyday software

AES is not just an algorithm developers implement directly: it is the foundation of the encryption systems most people use every day without thinking about it.

HTTPS and TLS

Every HTTPS connection uses AES to encrypt the data flowing between your browser and a web server. TLS 1.3 (the current version) negotiates either AES-256-GCM or ChaCha20-Poly1305 as the symmetric cipher after an initial asymmetric handshake (using ECDH key exchange). The green padlock in your browser address bar means AES is actively encrypting your traffic.

WhatsApp and Signal

WhatsApp uses the Signal Protocol for end-to-end encryption. The Signal Protocol uses AES-256-CBC for message encryption and AES-256-CTR in some contexts, combined with HMAC-SHA256 for authentication. The protocol also uses a Double Ratchet algorithm that generates new AES keys for each message, so even if one message key is compromised, past and future messages remain protected.

Apple FileVault and Windows BitLocker

Apple's FileVault 2 encrypts the entire macOS startup disk using AES-128-XTS (XTS mode is designed specifically for disk encryption and protects against patterns that could emerge from encrypting data at fixed disk sector positions). Windows BitLocker uses AES-128 or AES-256 in XTS mode by default on systems that support it. Both systems derive the encryption key from a combination of user credentials and hardware security features, so the disk cannot be read even if physically removed from the device.

Practical checklist for using AES correctly

  • Use AES-256-GCM for new server-side implementations. Use ChaCha20-Poly1305 for mobile or IoT targets without AES hardware acceleration.
  • Generate a cryptographically random IV (12 bytes for GCM) for every single encryption operation. Never reuse an IV with the same key.
  • Store the IV alongside the ciphertext: it is needed for decryption and is not secret.
  • Store and verify the authentication tag. GCM decryption must fail if the tag is missing or wrong.
  • Derive encryption keys from passwords using a proper KDF such as PBKDF2 or Argon2, not by using the password directly as a key.
  • Never hardcode encryption keys. Load them from environment variables or a dedicated key management service.

AlteredIdea's AES Encrypt / Decrypt tool uses AES-256-GCM via the Web Cryptography API directly in your browser. Encrypt text or files locally: the key and data never leave your device, and no account is required.

S

Sam Holloway

Sam is a technical writer and developer who has spent over a decade building web tools and writing about software, security, and the web platform. He focuses on making complex topics genuinely useful for working developers and non-technical users alike.

Try it yourself

AES Encrypt / Decrypt

Free, browser-based: your files never leave your device.

Open tool