Web Crypto
crypto.subtle is Node's own Web Crypto implementation (lib/internal/crypto/webcrypto.js) ported to TypeScript and written over the node:crypto module: the algorithm is normalized at run time, every failure rejects the returned promise with Node's error, and crypto.subtle is an ordinary value. node:crypto's natives come from the backend picked with -crypto (OpenSSL libcrypto by default; Apple CommonCrypto on macOS covers the symmetric algorithms only). getRandomValues and randomUUID need only a CSPRNG and no external library.
crypto.getRandomValues(view)
Works, with caveatsFills an integer TypedArray's (or whole ArrayBuffer's) bytes in place from the in-house CSPRNG — arc4random_buf on macOS/BSD, getrandom() on Linux — and returns the same view. The spec's 65,536-byte quota is enforced (an oversize view throws QuotaExceededError), and a float TypedArray is a compile-time rejection matching the spec's TypeMismatchError. The legacy plain number[] form still works, writing one random byte value (0–255) per element.
Parameters- viewTypedArray | ArrayBuffer
- The buffer to fill in place.
TypedArray — The same view, now filled.
const bytes = new Uint8Array(16)
crypto.getRandomValues(bytes)- A whole
ArrayBufferis accepted as a deliberate extension (real JS throwsTypeMismatchError— only integer TypedArrays are spec-legal).
crypto.randomUUID()
ExactReturns a standard RFC 4122 version-4 UUID string. The version nibble (4) and variant nibble (one of 8/9/a/b) are fixed by the spec's bit layout; the rest is CSPRNG-random.
Returnsstring — A 36-character v4 UUID.
const id1: string = crypto.randomUUID()
const id2: string = crypto.randomUUID()crypto.subtle.digest(algo, data)
supportedHashes any BufferSource with SHA-1, SHA-256, SHA-384 or SHA-512, returning a Promise<ArrayBuffer>; an unknown algorithm rejects with NotSupportedError.
Parameters- algostring | { name: string }
- Hash name literal.
- dataTypedArray | ArrayBuffer
- Bytes to hash.
Promise<ArrayBuffer> — The digest bytes.
const sha256 = new Uint8Array(await crypto.subtle.digest("SHA-256", data))crypto.subtle.encrypt / .decrypt
Works, with caveatsAES-CTR (counter, length), AES-CBC, AES-GCM (iv, additionalData, tagLength) and RSA-OAEP (label), each returning a Promise<ArrayBuffer>; a failed decryption rejects with OperationError.
Parameters- algo{ name: string, ... }
- Algorithm literal with its parameters (iv, label, …).
- keyCryptoKey
- The key to use.
- dataTypedArray | ArrayBuffer
- Plaintext or ciphertext.
Promise<ArrayBuffer> — Ciphertext or plaintext bytes.
const ct = await crypto.subtle.encrypt({ name: "AES-GCM", iv: iv }, aesKey, secret)
const pt = await crypto.subtle.decrypt({ name: "AES-GCM", iv: iv }, aesKey, ct)- On -crypto=commoncrypto the asymmetric algorithms (RSA, EC, Ed25519, X25519) fail: they run over node:crypto's KeyObject, which that backend does not provide.
crypto.subtle.sign / .verify
Works, with caveatsHMAC, RSASSA-PKCS1-v1_5, RSA-PSS (saltLength), ECDSA over P-256/P-384/P-521 (IEEE P1363 r‖s signatures) and Ed25519. sign returns Promise<ArrayBuffer>, verify returns Promise<boolean>.
Parameters- algostring | { name: string, ... }
- Algorithm literal (hash/saltLength as needed).
- keyCryptoKey
- Signing or verifying key.
- dataTypedArray | ArrayBuffer
- Message bytes (verify also takes the signature).
Promise<ArrayBuffer> | Promise<boolean> — Signature bytes, or a verification result.
const sig = new Uint8Array(await crypto.subtle.sign("HMAC", hmacKey, msg))
console.log(await crypto.subtle.verify("HMAC", hmacKey, sig, msg)) // true- On -crypto=commoncrypto the asymmetric algorithms (RSA, EC, Ed25519, X25519) fail: they run over node:crypto's KeyObject, which that backend does not provide.
crypto.subtle.generateKey
Works, with caveatsAES-CTR/CBC/GCM/KW, HMAC, RSASSA-PKCS1-v1_5/RSA-PSS/RSA-OAEP with any publicExponent, ECDSA/ECDH, Ed25519 and X25519, returning a Promise<CryptoKey> or Promise<CryptoKeyPair>; usages are checked and split across a pair as Node splits them.
Parameters- algo{ name: string, ... }
- Algorithm literal with its key parameters.
- extractableboolean
- Whether the key may be exported.
- usagesstring[]
- Permitted operations.
Promise<CryptoKey | CryptoKeyPair> — The generated key or key pair.
const aesKey = await crypto.subtle.generateKey(
{ name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt"])- On -crypto=commoncrypto the asymmetric algorithms (RSA, EC, Ed25519, X25519) fail: they run over node:crypto's KeyObject, which that backend does not provide.
crypto.subtle.importKey / .exportKey
Works, with caveatsraw (secret keys; EC points, compressed or not; Ed25519/X25519), pkcs8, spki and jwk for every algorithm, with Node's JWK checks and member order. CryptoKey's type, extractable, algorithm and usages are all readable.
Parameters- format"raw" | "pkcs8" | "spki" | "jwk"
- Key encoding.
- keyDataTypedArray | Map<string,string> | object
- The key material (import only).
- usagesstring[]
- Permitted operations (import only).
Promise<CryptoKey | ArrayBuffer | Map<string,string>> — A CryptoKey, or exported bytes / a JWK object.
const hmacKey = await crypto.subtle.importKey("raw", rawKey,
{ name: "HMAC", hash: "SHA-256" }, true, ["sign", "verify"])
const jwk = await crypto.subtle.exportKey("jwk", hmacKey)- On -crypto=commoncrypto the asymmetric algorithms (RSA, EC, Ed25519, X25519) fail: they run over node:crypto's KeyObject, which that backend does not provide.
crypto.subtle.deriveKey / .deriveBits
Works, with caveatsPBKDF2 and HKDF (non-extractable base keys, as Node requires), ECDH and X25519. deriveBits returns a Promise<ArrayBuffer>; deriveKey makes any key the target algorithm can make.
Parameters- algo{ name: string, ... }
- Derivation algorithm literal (salt, iterations, hash, …).
- baseKeyCryptoKey
- The imported base key material.
Promise<CryptoKey | ArrayBuffer> — The derived key or raw bits.
const aes = await crypto.subtle.deriveKey(
{ name: "PBKDF2", salt: salt, iterations: 100000, hash: "SHA-256" },
base, { name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt"])- On -crypto=commoncrypto the asymmetric algorithms (RSA, EC, Ed25519, X25519) fail: they run over node:crypto's KeyObject, which that backend does not provide.
crypto.subtle.wrapKey / unwrapKey
Works, with caveatsExports a key in any format and encrypts it with AES-KW, AES-GCM/CBC/CTR or RSA-OAEP, and the reverse.
ReturnsPromise<ArrayBuffer> | Promise<CryptoKey> — The wrapped key, or the unwrapped CryptoKey.
const wrapped = await crypto.subtle.wrapKey('raw', aesKey, kek, 'AES-KW')
const back = await crypto.subtle.unwrapKey('raw', wrapped, kek, 'AES-KW', 'AES-GCM', true, ['encrypt'])- On -crypto=commoncrypto the asymmetric algorithms (RSA, EC, Ed25519, X25519) fail: they run over node:crypto's KeyObject, which that backend does not provide.
crypto.createHash / createHmac / hash / getHashes
Works, with caveatsNode's Hash and Hmac objects: .update(data) (chainable, string or bytes), .digest(encoding?) and Hash's .copy(). Every digest the crypto backend has is available, including shake128/shake256 with an outputLength. Validation and errors are Node's: a second digest throws ERR_CRYPTO_HASH_FINALIZED, and an unknown algorithm throws 'Digest method not supported'.
ReturnsHash | Hmac | string — A digest handle, or the one-shot digest.
import crypto from 'crypto'
const accept = crypto.createHash('sha1')
.update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
.digest('base64')
const sig = crypto.createHmac('sha256', secret).update(msg).digest('hex')- On -crypto=commoncrypto only md5, sha1, sha224, sha256, sha384 and sha512 exist; SHAKE, SHA-3, SHA-512/t, RIPEMD-160 and BLAKE2 throw 'Digest method not supported'.
crypto.createCipheriv / createDecipheriv / getCiphers
Works, with caveatsSymmetric encryption with any cipher the backend has. update/final, setAutoPadding, and setAAD, getAuthTag and setAuthTag for AEAD modes such as GCM. A wrong key or IV length, a bad final block, and a failed authentication throw the errors Node throws.
ReturnsCipheriv | Decipheriv — A cipher handle.
const e = crypto.createCipheriv('aes-256-gcm', key, iv)
const ct = Buffer.concat([e.update('hello'), e.final()])
const tag = e.getAuthTag()- On -crypto=commoncrypto only AES-128/192/256 in CBC, ECB, CTR and GCM exist; other ciphers throw ERR_CRYPTO_UNKNOWN_CIPHER.
crypto.pbkdf2 / scrypt / hkdf (+ Sync)
Works, with caveatsKey derivation. The Sync forms return a Buffer (hkdfSync an ArrayBuffer); the callback forms run on the thread pool and call back with (err, key), as Node's do.
ReturnsBuffer | ArrayBuffer | void — The derived key, or nothing for the callback form.
const key = crypto.scryptSync('password', 'salt', 32)- On -crypto=commoncrypto the digests are the six createHash has there.
crypto.randomBytes / randomFill / randomInt / timingSafeEqual / constants
ExactCSPRNG bytes and integers. randomBytes and randomFill fill on the thread pool when given a callback; randomInt is Node's rejection-sampling draw. timingSafeEqual compares in constant time and throws Node's RangeError on a length mismatch. constants is Node's table.
ReturnsBuffer | number | boolean — Random bytes, a random integer, or the comparison.
const token = crypto.randomBytes(32).toString('hex')
const die = crypto.randomInt(1, 7)crypto.generateKeyPair / createSign / createVerify / sign / verify
Works, with caveatsKey pairs of every type but dh — rsa, rsa-pss, dsa, ec, ed25519, ed448, x25519, x448 — as KeyObjects, or in any encoding (pem/der/jwk with pkcs1/spki/pkcs8/sec1 and an optional cipher). Signatures over a KeyObject, PEM or { key, passphrase, padding, saltLength, dsaEncoding }: Sign/Verify streams and the one-shot sign/verify (null algorithm for Ed25519). The callback form of generateKeyPair runs on the thread pool.
Returns{ publicKey, privateKey } | Buffer | boolean — A key pair (KeyObjects or encoded keys), a signature, or the verification.
const { publicKey, privateKey } = crypto.generateKeyPairSync('ed25519')
const sig = crypto.sign(null, Buffer.from('msg'), privateKey)
crypto.verify(null, Buffer.from('msg'), publicKey, sig) // true- dh key pairs are not generated.
- On -crypto=commoncrypto: RSA keys of 1024 bits or more with exponent 65537, EC keys on P-256/P-384/P-521, ed25519 and x25519 only; encodings other than SPKI/PKCS#8 PEM and the padding/saltLength/dsaEncoding sign options fail.
KeyObject / createPrivateKey / createPublicKey / createSecretKey
Works, with caveatsKeys from PEM, DER (pkcs1/spki/pkcs8/sec1, encrypted PKCS#8 with a passphrase) or JWK (RSA, EC, OKP), and export back to each. type, asymmetricKeyType, asymmetricKeyDetails, symmetricKeySize and equals answer as in Node, with Node's error codes, OpenSSL's ERR_OSSL_* ones included.
ReturnsKeyObject — A SecretKeyObject, PublicKeyObject or PrivateKeyObject.
const key = crypto.createPrivateKey(pem)
const jwk = key.export({ format: 'jwk' })
crypto.createPublicKey(key).asymmetricKeyType // 'ec'- A secp256k1 JWK cannot be imported (P-256/P-384/P-521 can; export covers all four).
- On -crypto=commoncrypto, creating or exporting an asymmetric key fails.
createECDH / diffieHellman / publicEncrypt / privateDecrypt / getCurves
Works, with caveatsECDH key agreement on every built-in curve, with compressed, uncompressed or hybrid points; diffieHellman over EC, X25519 and X448 KeyObjects; RSA encryption with OAEP (oaepHash, oaepLabel), PKCS#1 or no padding, and the private-encrypt/public-decrypt pair. getCurves lists the curves.
ReturnsECDH | Buffer | string[] — The ECDH object, a shared secret or ciphertext, or the curve names.
const alice = crypto.createECDH('prime256v1'), bob = crypto.createECDH('prime256v1')
const a = alice.generateKeys(), b = bob.generateKeys()
alice.computeSecret(b).equals(bob.computeSecret(a)) // true- On -crypto=commoncrypto each of these fails.