WebCrypto
Crypto / SubtleCrypto / CryptoKey) built directly on Node's native node:crypto. It is the toolkit's injectable crypto engine, presented in the standard WebCrypto shape, so operators, and every higher structure (X.509, CMS, OCSP), reach for one familiar surface.
Unlike the browser's built-in crypto.subtle, this engine is **PQC-first without being PQC-only**: the FIPS 204 ML-DSA and FIPS 205 SLH-DSA signature suites sit alongside the full classical set PKI still runs on -- RSASSA-PKCS1-v1_5, RSA-PSS, RSA-OAEP, ECDSA, ECDH, Ed25519 / Ed448, AES-GCM / CBC / KW, HMAC, HKDF, PBKDF2, the SHA family (including legacy SHA-1 for old certificates and signatures), and the SHAKE128 / SHAKE256 extendable-output functions as message digests. FIPS 203 ML-KEM key generation, encoding, certificate/PKCS#8 import, and encapsulation/decapsulation (encapsulateBits / decapsulateBits over Node's crypto.encapsulate/decapsulate) are all available. Because it is OpenSSL-backed, every key and signature it emits is interoperable with OpenSSL, NSS, and other PKI implementations.
pki.webcrypto.CryptoKey
new pki.webcrypto.CryptoKey(type, extractable, algorithm, usages, handle)
Opaque handle to key material, matching the W3C CryptoKey shape: { type, extractable, algorithm, usages }. The underlying node:crypto KeyObject is non-enumerable and never serialized -- extract material only through subtle.exportKey, and only when the key was created extractable. Instances are produced by subtle.generateKey / subtle.importKey; the constructor is rarely called directly.
Example
async function example() {
var kp = await pki.webcrypto.subtle.generateKey({ name: "Ed25519" }, true, ["sign", "verify"]);
kp.publicKey.type; // "public"
kp.publicKey.algorithm; // { name: "Ed25519" }
}
example();
References
pki.webcrypto.subtle
await pki.webcrypto.subtle.exportKey(format, key)
Export a CryptoKey to spki (public), pkcs8 (private), jwk (either), or raw (symmetric, or an uncompressed EC / OKP public point). Throws unless the key was created extractable.
raw is defined for public and secret keys only; asking for it on a private key throws webcrypto/not-supported instead of answering with the public half. This matters through wrapKey, which forwards the caller's format here: wrapping a private key as raw would otherwise escrow the public key, and unwrapping it returns a handle announcing usages: ["sign"] that cannot sign, with the private key gone. Use pkcs8 or jwk to serialize a private key.
A private jwk round-trips as a private key for every algorithm, ML-DSA, ML-KEM and SLH-DSA included: those are kty: "AKP" and carry the private half in priv in place of the d an EC or OKP key uses.
Example
async function example() {
var keyPair = await pki.webcrypto.subtle.generateKey({ name: "Ed25519" }, true, ["sign", "verify"]);
var spki = await pki.webcrypto.subtle.exportKey("spki", keyPair.publicKey);
}
example();
References
- spec W3C WebCrypto sec. subtlecrypto
- spec FIPS 186-5
- spec FIPS 203
- spec FIPS 204
- spec FIPS 205
- spec RFC 8017
pki.webcrypto
pki.webcrypto.getRandomValues(typedArray) / pki.webcrypto.subtle
A ready Crypto instance (the shape of globalThis.crypto) exposing getRandomValues, randomUUID, and subtle. Construct additional instances with new pki.webcrypto.Crypto().
Example
var iv = pki.webcrypto.getRandomValues(new Uint8Array(12));
References
- spec W3C WebCrypto