Search code examples
javascripttypescriptcryptographywebcrypto-apiwebcrypto

How to generate a private/public key pair and sign data with the private one using the Webcrypto API?


I want to sign data, return the signature and the public key ( and "forget" the private key ). I want to use the Webcrypto API

Based on the docs I started with the following

const generateKeyPair = async function() {
    return crypto.subtle.generateKey(
        {
            name: "ECDSA",
            namedCurve: "P-384",
        },
        true,
        ["sign", "verify"]
    );
}

const sign = async function(privateKey: CryptoKey, buffer: BufferSource) {
    return crypto.subtle.sign(privateKey.algorithm, privateKey, buffer);
}

Based on my tests I think the function generateKeyPair is fine. But when I try the sign function like so

import { describe, it, expect } from "vitest";
import { generateKeyPair, sign } from "../../src";

describe("sign", () => {
    it('returns a signature.', async () => {
        const { privateKey } = await generateKeyPair();

        const data = { foo: 'bar' };
        const stringifiedData = JSON.stringify(data);
        const buffer = Buffer.from(stringifiedData);

        await expect(sign(privateKey, buffer)).resolves.toBeTruthy() // expect signature here
    });
});

the test fails with the error

Error: promise rejected "TypeError [ERR_MISSING_OPTION]: algorithm.hash is required" instead of resolving

so I guess privateKey.algorithm is lacking the internal hash field. Am I using the wrong algorithm? I also tried to add "hash": "SHA-512" to the options in generateKeyPair.

I basically want to generate a key pair and use the private one for signing and the public one for reading.

Do you have any ideas?


Solution

  • You need to specify a name and hash for the signature algorithm to use. In the case of an ECDSA signature, you need to pass an EcdsaParams object to the sign() function:

    const sign = async function(privateKey: CryptoKey, buffer: BufferSource) {
        return crypto.subtle.sign({
          name: 'ECDSA',
          hash: 'SHA-512'
        }, privateKey, buffer);
    };
    

    Playground link