Skip to main content

Overview

Node.js provides an implementation of the Web Crypto API standard, offering browser-compatible cryptographic operations.
const { subtle } = globalThis.crypto;

(async function() {
  const key = await subtle.generateKey({
    name: 'HMAC',
    hash: 'SHA-256',
    length: 256,
  }, true, ['sign', 'verify']);

  const enc = new TextEncoder();
  const message = enc.encode('I love cupcakes');

  const digest = await subtle.sign({
    name: 'HMAC',
  }, key, message);
})();

Accessing Web Crypto

Access the Web Crypto API through:
  • globalThis.crypto (browser-compatible)
  • require('node:crypto').webcrypto

Modern Algorithms

Node.js supports modern cryptographic algorithms:

Symmetric Encryption

  • AES-OCB - Authenticated encryption with associated data
  • ChaCha20-Poly1305 - Modern authenticated encryption

Hashing

  • SHA3-256, SHA3-384, SHA3-512 - SHA-3 family
  • cSHAKE128, cSHAKE256 - Customizable SHAKE

Key Derivation

  • Argon2d, Argon2i, Argon2id - Memory-hard password hashing

Post-Quantum Cryptography

  • ML-DSA-44, ML-DSA-65, ML-DSA-87 - Post-quantum signatures
  • ML-KEM-512, ML-KEM-768, ML-KEM-1024 - Post-quantum key encapsulation

MAC

  • KMAC128, KMAC256 - Keccak-based MAC

Secure Curves

  • Ed448, X448 - Edwards-curve operations

Class: Crypto

The global crypto object provides cryptographic functionality:

crypto.subtle

Access to the SubtleCrypto API for cryptographic operations:
const { subtle } = globalThis.crypto;

crypto.getRandomValues(typedArray)

Generate cryptographically strong random values:
const array = new Uint8Array(32);
globalThis.crypto.getRandomValues(array);
console.log(array);
Parameters:
  • typedArray - Integer-based TypedArray to fill with random values
Returns: Reference to the filled TypedArray
Maximum array size is 65,536 bytes. Float arrays are not supported.

crypto.randomUUID()

Generate RFC 4122 version 4 UUID:
const uuid = globalThis.crypto.randomUUID();
console.log(uuid);
// '36b8f84d-df4e-4d49-b662-bcde71a8764f'

Class: SubtleCrypto

Provides low-level cryptographic primitives:

SubtleCrypto.supports(operation, algorithm[, length])

Check runtime algorithm support:
const { SubtleCrypto } = globalThis;

if (SubtleCrypto.supports?.('importKey', 'Argon2id')) {
  console.log('Argon2id is supported');
}

if (SubtleCrypto.supports?.('encrypt', 'AES-OCB')) {
  console.log('AES-OCB is supported');
}
Parameters:
  • operation - Operation name (e.g., ‘encrypt’, ‘sign’)
  • algorithm - Algorithm identifier
  • length - Optional key length or additional algorithm parameter

Generating Keys

subtle.generateKey(algorithm, extractable, keyUsages)

Generate symmetric or asymmetric keys: AES Keys:
const key = await subtle.generateKey({
  name: 'AES-GCM',
  length: 256,
}, true, ['encrypt', 'decrypt']);
ECDSA Key Pairs:
const { publicKey, privateKey } = await subtle.generateKey({
  name: 'ECDSA',
  namedCurve: 'P-521',
}, true, ['sign', 'verify']);
Ed25519 Key Pairs:
const keyPair = await subtle.generateKey({
  name: 'Ed25519',
}, true, ['sign', 'verify']);
RSA Key Pairs:
const { publicKey, privateKey } = await subtle.generateKey({
  name: 'RSA-PSS',
  modulusLength: 2048,
  publicExponent: new Uint8Array([1, 0, 1]),
  hash: 'SHA-256',
}, true, ['sign', 'verify']);
HMAC Keys:
const key = await subtle.generateKey({
  name: 'HMAC',
  hash: 'SHA-512',
}, true, ['sign', 'verify']);

Encryption and Decryption

subtle.encrypt(algorithm, key, data)

Encrypt data:
const crypto = globalThis.crypto;

const key = await crypto.subtle.generateKey({
  name: 'AES-GCM',
  length: 256,
}, true, ['encrypt', 'decrypt']);

const iv = crypto.getRandomValues(new Uint8Array(12));
const data = new TextEncoder().encode('secret message');

const encrypted = await crypto.subtle.encrypt({
  name: 'AES-GCM',
  iv,
}, key, data);

subtle.decrypt(algorithm, key, data)

Decrypt data:
const decrypted = await crypto.subtle.decrypt({
  name: 'AES-GCM',
  iv,
}, key, encrypted);

const message = new TextDecoder().decode(decrypted);

Signing and Verification

subtle.sign(algorithm, key, data)

Sign data:
const { publicKey, privateKey } = await subtle.generateKey({
  name: 'ECDSA',
  namedCurve: 'P-384',
}, true, ['sign', 'verify']);

const data = new TextEncoder().encode('message to sign');
const signature = await subtle.sign({
  name: 'ECDSA',
  hash: 'SHA-384',
}, privateKey, data);

subtle.verify(algorithm, key, signature, data)

Verify signature:
const isValid = await subtle.verify({
  name: 'ECDSA',
  hash: 'SHA-384',
}, publicKey, signature, data);

console.log('Signature valid:', isValid);

Hashing

subtle.digest(algorithm, data)

Compute hash digest:
const data = new TextEncoder().encode('hello world');
const hashBuffer = await subtle.digest('SHA-256', data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
console.log(hashHex);
Supported hash algorithms:
  • 'SHA-1' (not recommended for security)
  • 'SHA-256'
  • 'SHA-384'
  • 'SHA-512'
  • 'SHA3-256'
  • 'SHA3-384'
  • 'SHA3-512'
  • 'cSHAKE128'
  • 'cSHAKE256'

Key Derivation

subtle.deriveBits(algorithm, baseKey, length)

Derive bits from a key:
const password = new TextEncoder().encode('user-password');
const keyMaterial = await subtle.importKey(
  'raw',
  password,
  'PBKDF2',
  false,
  ['deriveBits']
);

const salt = crypto.getRandomValues(new Uint8Array(16));
const bits = await subtle.deriveBits({
  name: 'PBKDF2',
  hash: 'SHA-512',
  salt,
  iterations: 100000,
}, keyMaterial, 256);

subtle.deriveKey(algorithm, baseKey, derivedKeyAlgorithm, extractable, keyUsages)

Derive a key from another key:
const derivedKey = await subtle.deriveKey({
  name: 'PBKDF2',
  hash: 'SHA-512',
  salt,
  iterations: 100000,
}, keyMaterial, {
  name: 'AES-GCM',
  length: 256,
}, true, ['encrypt', 'decrypt']);
Argon2 Example:
if (SubtleCrypto.supports?.('importKey', 'Argon2id')) {
  const passwordKey = await crypto.subtle.importKey(
    'raw-secret',
    new TextEncoder().encode('password'),
    'Argon2id',
    false,
    ['deriveKey']
  );

  const key = await crypto.subtle.deriveKey({
    name: 'Argon2id',
    nonce: crypto.getRandomValues(new Uint8Array(16)),
    parallelism: 4,
    memory: 2 ** 21,
    passes: 1,
  }, passwordKey, {
    name: 'AES-GCM',
    length: 256,
  }, false, ['encrypt', 'decrypt']);
}

Key Import and Export

subtle.importKey(format, keyData, algorithm, extractable, keyUsages)

Import a key:
const jwk = {
  kty: 'oct',
  k: 'Y0zt37HgOx-BY7SQjYVmrqhPkO44Ii2Jcb9yydUDPfE',
  alg: 'HS256',
  ext: true,
};

const key = await subtle.importKey(
  'jwk',
  jwk,
  { name: 'HMAC', hash: 'SHA-256' },
  true,
  ['sign', 'verify']
);
Supported formats:
  • 'raw' - Raw binary key data
  • 'pkcs8' - PKCS#8 private key
  • 'spki' - SubjectPublicKeyInfo
  • 'jwk' - JSON Web Key
  • 'raw-public' - Raw public key (modern algorithms)
  • 'raw-secret' - Raw secret key (modern algorithms)
  • 'raw-seed' - Raw seed (modern algorithms)

subtle.exportKey(format, key)

Export a key:
const exportedKey = await subtle.exportKey('jwk', key);
console.log(JSON.stringify(exportedKey, null, 2));

Key Wrapping

subtle.wrapKey(format, key, wrappingKey, wrapAlgorithm)

Wrap (encrypt) a key:
const keyToWrap = await subtle.generateKey({
  name: 'HMAC',
  hash: 'SHA-512',
}, true, ['sign', 'verify']);

const wrappingKey = await subtle.generateKey({
  name: 'AES-KW',
  length: 256,
}, true, ['wrapKey', 'unwrapKey']);

const wrappedKey = await subtle.wrapKey(
  'jwk',
  keyToWrap,
  wrappingKey,
  'AES-KW'
);

subtle.unwrapKey(format, wrappedKey, unwrappingKey, unwrapAlgorithm, unwrappedKeyAlgorithm, extractable, keyUsages)

Unwrap (decrypt) a key:
const unwrappedKey = await subtle.unwrapKey(
  'jwk',
  wrappedKey,
  wrappingKey,
  'AES-KW',
  { name: 'HMAC', hash: 'SHA-512' },
  true,
  ['sign', 'verify']
);

Key Encapsulation (Post-Quantum)

subtle.encapsulateBits(algorithm, publicKey, length)

Encapsulate random bits using ML-KEM:
const { publicKey, privateKey } = await subtle.generateKey({
  name: 'ML-KEM-768',
}, true, ['encapsulateBits', 'decapsulateBits']);

const { sharedSecret, ciphertext } = await subtle.encapsulateBits(
  { name: 'ML-KEM-768' },
  publicKey,
  256
);

subtle.decapsulateBits(algorithm, privateKey, ciphertext, length)

Decapsulate shared secret:
const decapsulatedSecret = await subtle.decapsulateBits(
  { name: 'ML-KEM-768' },
  privateKey,
  ciphertext,
  256
);

subtle.getPublicKey(privateKey)

Extract public key from private key:
const publicKey = await subtle.getPublicKey(privateKey);

Class: CryptoKey

Represents a cryptographic key:

Properties

cryptoKey.type

  • 'secret' - Symmetric key
  • 'private' - Private asymmetric key
  • 'public' - Public asymmetric key

cryptoKey.extractable

Boolean indicating if key can be exported

cryptoKey.algorithm

Object describing the key’s algorithm and parameters

cryptoKey.usages

Array of permitted operations:
  • 'encrypt', 'decrypt'
  • 'sign', 'verify'
  • 'deriveKey', 'deriveBits'
  • 'wrapKey', 'unwrapKey'
  • 'encapsulateBits', 'decapsulateBits'
  • 'encapsulateKey', 'decapsulateKey'

Supported Algorithms

Encryption Algorithms

AlgorithmKey GenerationEncrypt/Decrypt
AES-CBC
AES-CTR
AES-GCM
AES-OCB
ChaCha20-Poly1305
RSA-OAEP

Signature Algorithms

AlgorithmKey GenerationSign/Verify
HMAC
KMAC128
KMAC256
RSASSA-PKCS1-v1_5
RSA-PSS
ECDSA
Ed25519
Ed448
ML-DSA-44
ML-DSA-65
ML-DSA-87

Key Derivation Algorithms

AlgorithmImport KeyDerive
PBKDF2
HKDF
ECDH
X25519
X448
Argon2d
Argon2i
Argon2id

Key Encapsulation

AlgorithmKey GenerationEncapsulate/Decapsulate
ML-KEM-512
ML-KEM-768
ML-KEM-1024

Security Best Practices

SHA-1 is cryptographically broken. Use SHA-256 or stronger for security-critical applications.
For password hashing, prefer Argon2id when available, or PBKDF2 with at least 100,000 iterations.
Always use unique initialization vectors (IVs) for each encryption operation. Never reuse IVs with the same key.

Browser Compatibility

The Web Crypto API implementation in Node.js aims for compatibility with browser implementations, making it easier to share cryptographic code between server and client.
  • Crypto - Node.js native cryptography
  • TLS/SSL - Secure network communications
  • Permissions - Control access to cryptographic operations