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.
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?
- Native implementation — Cryptographic operations run in compiled code, not JavaScript, reducing timing side-channel risks
- Non-extractable keys — Keys can be marked as non-extractable, preventing JavaScript from accessing raw key material
- No dependencies — Built into all modern browsers, no npm packages needed
- 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
Set
extractable: falsewhen you do not need to export the key — this prevents JavaScript from reading the raw key material, protecting against XSSSpecify minimal
keyUsages— A key for encryption should only have["encrypt"], not["encrypt", "decrypt"], following the principle of least privilegeHandle errors properly — Web Crypto operations throw
DOMExceptionon 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");
}
- 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.