Asymmetric encryption with the Web Cryptography API

The Web Cryptography API lets us generate RSA and EC keypairs, but these keys don’t support encryption/decryption! The only algorithms supporting encryption and decryption are symmetric (and the obscure-sounding RSA-OAEP). Instead of directly providing asymmetric encryption/decryption, the Web Cryptography API provides the deriveKey method, which lets two communicators derive a shared symmetric secret. This post shows how to derive a shared AES key given two ECDH keys. This code should log true, asserting that Alice’s derived AES key is the same as Bob’s derived AES key:

const aliceKeyPair = await genKeyPair();
const bobKeyPair   = await genKeyPair();
const aliceSecret  = await deriveKey(aliceKeyPair.privateKey, bobKeyPair.publicKey  );
const bobSecret    = await deriveKey(  bobKeyPair.privateKey, aliceKeyPair.publicKey);
console.log((await exportKey(aliceSecret)).k === (await exportKey(bobSecret)).k);

Here’s the accompanying code which calls the crypto.subtle API:

function genKeyPair() {
  return crypto.subtle.generateKey({name:"ECDH", namedCurve: "P-256"}, true, ["deriveKey"]);
function deriveKey(privKey, pubKey) {
  return crypto.subtle.deriveKey(
    {"name": "ECDH", "public": pubKey},
    {name:"AES-CTR", length: 256},
    ["encrypt", "decrypt"]
async function exportKey(k) {
  return JSON.stringify(await crypto.subtle.exportKey("jwk", k));

After Alice and Bob have derived their shared symmetric secret, they can use this to communicate using normal symmetric crypto.

I wrote this because I felt like it. This post is not associated with my employer.