crypto.subtle is the property that returns a SubtleCrypto object, the low-level toolbox of the Web Crypto API for hashing, signing, encrypting and managing keys. It is defined by the W3C Web Cryptography specification and is exposed only in secure contexts, meaning HTTPS or localhost. Every method returns a Promise, and the work runs outside your JavaScript code.
SubtleCrypto has these methods: digest, encrypt, decrypt, sign, verify, generateKey, deriveKey, deriveBits, importKey, exportKey, wrapKey and unwrapKey. You name the algorithm with a string or an object such as { name: "AES-GCM", iv }. Keys are opaque CryptoKey objects, and you choose at creation whether they are extractable.
const enc = new TextEncoder();
const d = await crypto.subtle.digest('SHA-256', enc.encode('hello'));
console.log(Buffer.from(d).toString('hex'), d.byteLength);
const key = await crypto.subtle.generateKey({ name: 'AES-GCM', length: 256 }, true, ['encrypt', 'decrypt']);
const iv = crypto.getRandomValues(new Uint8Array(12));
const ct = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, enc.encode('hello'));
console.log(ct.byteLength);
console.log(new TextDecoder().decode(await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, key, ct)));
const bad = new Uint8Array(ct); bad[0] ^= 1;
try { await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, key, bad); } catch (e) { console.log(e.name, '|', e.message); }
try { await crypto.subtle.digest('MD5', enc.encode('x')); } catch (e) { console.log(e.name, '|', e.message); }
Output from Node.js 22.22.0 (the ES module top-level await form, where Buffer is only used to print hex):
2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824 32
21
hello
OperationError | The operation failed for an operation-specific reason
NotSupportedError | Unrecognized algorithm name
It is undefined because the page is not a secure context. Browsers hide subtle on plain http: pages, except on localhost. Serve the page over HTTPS to fix it. Test with window.isSecureContext, which is false in that case. Node.js has crypto.subtle without that restriction.
Call crypto.subtle.digest('SHA-256', bytes) and convert the returned ArrayBuffer to hex yourself. There is no built-in hex helper, so map each byte to a two-digit hex string. Hash Generator on this site does the same for pasted text.