2 min read

Web Crypto API

The Web Crypto API provides a set of low-level cryptographic primitives that are controlled by the script engine. It allows web applications to perform cryptographic operations such as hashing, signature generation and verification, and encryption and decryption.

1. The SubtleCrypto Interface

The core of the API is accessed through the window.crypto.subtle property. All operations are asynchronous and return Promises.

2. Generating Random Numbers

For cryptographic purposes (like generating IVs or salts), you should never use Math.random(). Instead, use crypto.getRandomValues().

const array = new Uint32Array(10);
window.crypto.getRandomValues(array);

console.log("Your lucky numbers:");
for (const num of array) {
  console.log(num);
}

3. Hashing (Digesting)

To create a hash (digest) of data, use digest().

async function sha256(message) {
  // encode as (utf-8) Uint8Array
  const msgBuffer = new TextEncoder().encode(message);

  // hash the message
  const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);

  // convert ArrayBuffer to Array
  const hashArray = Array.from(new Uint8Array(hashBuffer));

  // convert bytes to hex string
  const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
  return hashHex;
}

sha256('Hello World').then(digest => console.log(digest));

4. Generating Keys

You can generate symmetric or asymmetric keys.

async function generateKey() {
  const key = await window.crypto.subtle.generateKey(
    {
      name: "AES-GCM",
      length: 256
    },
    true, // extractable
    ["encrypt", "decrypt"]
  );
  return key;
}

5. Encryption and Decryption

Example using AES-GCM (Galois/Counter Mode).

async function encryptMessage(key, message) {
  const encoded = new TextEncoder().encode(message);

  // The IV (Initialization Vector) must be unique for every encryption
  const iv = window.crypto.getRandomValues(new Uint8Array(12));

  const ciphertext = await window.crypto.subtle.encrypt(
    {
      name: "AES-GCM",
      iv: iv
    },
    key,
    encoded
  );

  return { ciphertext, iv };
}

async function decryptMessage(key, ciphertext, iv) {
  const decrypted = await window.crypto.subtle.decrypt(
    {
      name: "AES-GCM",
      iv: iv
    },
    key,
    ciphertext
  );

  return new TextDecoder().decode(decrypted);
}

// Usage
(async () => {
  const key = await generateKey();
  const { ciphertext, iv } = await encryptMessage(key, "Secret Message");

  console.log("Encrypted buffer:", ciphertext);

  const decrypted = await decryptMessage(key, ciphertext, iv);
  console.log("Decrypted:", decrypted);
})();

programming/javascript/vanilla/javascript programming/javascript/vanilla/promises-async-await programming/javascript/vanilla/typed-arrays