Encryption with Web Crypto API

Complete guide to the Web Crypto API for browser-based encryption. Covers key generation, AES-GCM, RSA-OAEP, key import/export, PBKDF2 key derivation, and security best practices.

Practical Examples

Detailed Explanation

Web Crypto API: Browser-Native Encryption

The Web Crypto API (crypto.subtle) provides cryptographic primitives directly in the browser, enabling secure encryption without external libraries. All operations run in native code, offering both security and performance advantages over JavaScript implementations.

Why Use Web Crypto API?

  1. Native implementation — Cryptographic operations run in compiled code, not JavaScript, reducing timing side-channel risks
  2. Non-extractable keys — Keys can be marked as non-extractable, preventing JavaScript from accessing raw key material
  3. No dependencies — Built into all modern browsers, no npm packages needed
  4. Hardware acceleration — Leverages CPU instructions like AES-NI automatically

Supported Algorithms

Category Algorithms
Symmetric Encryption AES-GCM, AES-CBC, AES-CTR, AES-KW
Asymmetric Encryption RSA-OAEP
Signing HMAC, RSASSA-PKCS1-v1_5, RSA-PSS, ECDSA, Ed25519
Key Derivation PBKDF2, HKDF
Hashing SHA-1, SHA-256, SHA-384, SHA-512
Key Agreement ECDH, X25519

Key Management Operations

// Generate a key
const key = await crypto.subtle.generateKey(algorithm, extractable, usages);

// Export a key (JWK, raw, SPKI, PKCS8)
const exported = await crypto.subtle.exportKey("jwk", key);

// Import a key
const imported = await crypto.subtle.importKey(
  "jwk", exported, algorithm, extractable, usages
);

// Derive a key from password
const derived = await crypto.subtle.deriveKey(
  derivationParams, baseKey, derivedAlgorithm, extractable, usages
);

// Wrap/unwrap keys (encrypt a key with another key)
const wrapped = await crypto.subtle.wrapKey("raw", keyToWrap, wrappingKey, wrapAlgo);

Key Export Formats

  • raw — Raw key bytes (symmetric keys only)
  • jwk — JSON Web Key format (all key types, includes metadata)
  • spki — SubjectPublicKeyInfo (public keys, DER-encoded)
  • pkcs8 — PrivateKeyInfo (private keys, DER-encoded)

Security Best Practices

  1. Set extractable: false when you do not need to export the key — this prevents JavaScript from reading the raw key material, protecting against XSS

  2. Specify minimal keyUsages — A key for encryption should only have ["encrypt"], not ["encrypt", "decrypt"], following the principle of least privilege

  3. Handle errors properly — Web Crypto operations throw DOMException on failure. Always use try/catch:

try {
  const plaintext = await crypto.subtle.decrypt(algo, key, ciphertext);
} catch (e) {
  // Authentication failed — ciphertext was tampered with
  console.error("Decryption failed: data may be corrupted");
}
  1. Clear sensitive data — Zero out plaintext buffers after use:
const plaintext = new Uint8Array(decryptedBuffer);
// ... use plaintext ...
plaintext.fill(0); // Clear from memory

Browser Compatibility

Web Crypto API is available in all modern browsers (Chrome 37+, Firefox 34+, Safari 11+, Edge 12+) and in Node.js 15+ via globalThis.crypto.subtle. The API requires a secure context (HTTPS or localhost).

Use Case

The Web Crypto API is the foundation for any browser-based encryption feature. It powers end-to-end encrypted messaging apps (like the web version of Signal), browser-based password managers, client-side file encryption tools, and secure form handling. Developers building privacy-focused applications where data must be encrypted before it leaves the browser rely on Web Crypto API as the trusted, standards-based approach.

Try It — Encryption Playground

Open full tool