The essentials

Quick reference

One focused task per row. Jump to the related section for complete, working examples.

UseSyntaxExamples
Generate random bytescrypto.getRandomValues(new Uint8Array(32))View examples
Generate a random UUIDconst id = crypto.randomUUID()View examples
Hash bytesconst digest = await crypto.subtle.digest('SHA-256', data)View examples
Generate an HMAC keyawait crypto.subtle.generateKey({ name: 'HMAC', hash: 'SHA-256' }, false, ['sign', 'verify'])View examples
Verify an HMACawait crypto.subtle.verify('HMAC', key, tag, message)View examples
Generate an AES-GCM keyawait crypto.subtle.generateKey({ name: 'AES-GCM', length: 256 }, false, ['encrypt', 'decrypt'])View examples
Encrypt with AES-GCMawait crypto.subtle.encrypt({ name: 'AES-GCM', iv, additionalData, tagLength: 128 }, key, plaintext)View examples
Decrypt and authenticateawait crypto.subtle.decrypt({ name: 'AES-GCM', iv, additionalData, tagLength: 128 }, key, ciphertext)View examples
Derive a password keyawait crypto.subtle.deriveKey({ name: 'PBKDF2', salt, iterations, hash: 'SHA-256' }, baseKey, aes, false, usages)View examples
Expand input key materialawait crypto.subtle.deriveBits({ name: 'HKDF', hash: 'SHA-256', salt, info }, baseKey, 256)View examples
Generate an ECDSA pairawait crypto.subtle.generateKey({ name: 'ECDSA', namedCurve: 'P-256' }, false, ['sign', 'verify'])View examples
Verify an ECDSA signatureawait crypto.subtle.verify({ name: 'ECDSA', hash: 'SHA-256' }, publicKey, signature, data)View examples
Import a JWKawait crypto.subtle.importKey('jwk', jwk, algorithm, false, ['verify'])View examples
Export a public keyconst jwk = await crypto.subtle.exportKey('jwk', publicKey)View examples

Web Crypto provides low-level, asynchronous cryptographic primitives—not a secure application protocol. Algorithm parameters, serialization, key lifecycle, identity, replay protection, and error handling remain application responsibilities. Prefer reviewed protocols and libraries, keep TLS, use authenticated encryption, generate unique nonces, and never invent a format from isolated snippets.

Step by step

Detailed examples

01

Use the Crypto source for secrets, not Math.random

getRandomValues() fills integer typed arrays and rejects requests larger than 65,536 bytes per call. randomUUID() creates random identifiers but UUID text is public identity, not a secret token by itself. Generate session tokens, nonces, salts, and keys from crypto, give authorization tokens enough entropy, and encode bytes without reducing their range through modulo bias. Web Crypto is broadly limited to secure contexts, with getRandomValues historically available more widely.

Create an unpadded base64url token
function randomToken(bytes = 32) {
  const value = crypto.getRandomValues(new Uint8Array(bytes));
  let binary = '';
  for (const byte of value) binary += String.fromCharCode(byte);
  return btoa(binary).replaceAll('+', '-').replaceAll('/', '_').replace(/=+$/u, '');
}
Back to quick reference ↑
02

Distinguish digests from message authentication

SHA-256 and stronger digests detect accidental changes only when the expected digest is trusted; anyone who can change the message can compute a new digest. HMAC authenticates with a shared secret and verify() avoids application-level comparison mistakes. Neither fast digest nor a single HMAC is a password-storage scheme. Use a server-side password hashing function designed to be slow and memory-hard. Encode strings with TextEncoder and define canonical bytes before signing or hashing structured data.

Authenticate exact bytes with HMAC
const key = await crypto.subtle.generateKey(
  { name: 'HMAC', hash: 'SHA-256' }, false, ['sign', 'verify']
);
const message = new TextEncoder().encode('version=1&action=approve');
const tag = await crypto.subtle.sign('HMAC', key, message);
const valid = await crypto.subtle.verify('HMAC', key, tag, message);
Back to quick reference ↑
03

Use AES-GCM with a unique IV and authenticated context

AES-GCM provides confidentiality and integrity. Reusing an IV with the same key can catastrophically break both, so generate a fresh 96-bit IV for every encryption and store it alongside the ciphertext. additionalData is authenticated but not encrypted and can bind a version, record ID, or content type. Keep tagLength consistent, reject OperationError without revealing a detailed oracle, and define a versioned envelope. Encryption does not replace access control, TLS, backups, or key rotation.

Return a versioned AES-GCM envelope
async function seal(key, plaintext, recordId) {
  const iv = crypto.getRandomValues(new Uint8Array(12));
  const additionalData = new TextEncoder().encode(`v1:${recordId}`);
  const ciphertext = await crypto.subtle.encrypt(
    { name: 'AES-GCM', iv, additionalData, tagLength: 128 }, key, plaintext
  );
  return { version: 1, iv, ciphertext: new Uint8Array(ciphertext) };
}
Back to quick reference ↑
04

Use derivation algorithms for their intended input

PBKDF2 can turn a password into key material, but its iteration count must be benchmarked and versioned, every record needs a random salt, and modern server storage usually favors a memory-hard password hashing system outside Web Crypto. HKDF extracts and expands already-strong key material; it does not slow password guessing. Bind derived keys to protocol purpose through distinct info values. Salt and parameters are public metadata, while the password and derived key remain secret.

Derive a nonextractable encryption key from a password
async function derivePasswordKey(password, salt, iterations) {
  const base = await crypto.subtle.importKey('raw', new TextEncoder().encode(password), 'PBKDF2', false, ['deriveKey']);
  return crypto.subtle.deriveKey(
    { name: 'PBKDF2', hash: 'SHA-256', salt, iterations },
    base, { name: 'AES-GCM', length: 256 }, false, ['encrypt', 'decrypt']
  );
}
Back to quick reference ↑
05

Verify identity and serialization outside the primitive

A valid signature proves only that the matching private key signed the exact bytes. It does not establish who controls that key, whether the message is fresh, or whether it is authorized. Pin or validate public keys through a trusted protocol, include domain separation, version, audience, and replay-resistant context in the signed bytes, and define canonical serialization. Algorithm identifiers and signature encodings vary by protocol; do not assume Web Crypto output matches every JOSE, COSE, or external library format.

Sign and verify one canonical byte sequence
const pair = await crypto.subtle.generateKey(
  { name: 'ECDSA', namedCurve: 'P-256' }, false, ['sign', 'verify']
);
const data = new TextEncoder().encode('example/v1\nrequest:42\naction:approve');
const signature = await crypto.subtle.sign({ name: 'ECDSA', hash: 'SHA-256' }, pair.privateKey, data);
const valid = await crypto.subtle.verify({ name: 'ECDSA', hash: 'SHA-256' }, pair.publicKey, signature, data);
Back to quick reference ↑
06

Restrict extractability, usages, storage, and protocol boundaries

CryptoKey carries its type, extractable flag, algorithm, and allowed usages. Mark secret and private keys nonextractable unless a reviewed backup or interchange design requires export; nonextractable reduces accidental extraction but does not defeat malicious same-origin script that can invoke the key. CryptoKey objects can be structured-cloned and stored in IndexedDB where supported. Validate imported JWK alg, kty, crv, key_ops, ext, provenance, and intended usage through a protocol library.

Import a pinned public verification key
async function importVerifier(jwk) {
  if (jwk.kty !== 'EC' || jwk.crv !== 'P-256' || jwk.d) throw new TypeError('Unexpected key');
  return crypto.subtle.importKey(
    'jwk', jwk, { name: 'ECDSA', namedCurve: 'P-256' }, false, ['verify']
  );
}
Back to quick reference ↑

Sources and further reading

References

Authoritative documentation used to verify and expand this cheat sheet.

  1. World Wide Web ConsortiumWeb Cryptography Level 2w3.org
  2. World Wide Web ConsortiumWeb Cryptography API Recommendationw3.org
  3. Internet Engineering Task ForceAuthenticated Encryption with Associated Datarfc-editor.org
  4. Internet Engineering Task ForceHMAC-based Extract-and-Expand Key Derivation Functionrfc-editor.org
  5. Internet Engineering Task ForceJSON Web Keyrfc-editor.org

Help us improve

Found a typo or missing example?

Tell us what would make this cheat sheet clearer, more complete, or more useful.

Share feedback