The private key is generated using Elliptic Curve. None of the methods from the SubtleCrypto interface of the Web Crypto API seem to be able to derive a public key from a private key, correct me if I'm wrong. Do I have to use a 3rd party library for that?
WebCrypto is a low level API with only a relatively small feature set. To my knowledge there is no dedicated method for deriving a public key from a private key.
However, you can export the private CryptoKey as JWK (JSON Web Key), remove the private portions, and re-import the remaining portion, which thereby becomes the public CryptoKey. The following code shows this for an ECDSA key:
async function getPublic(privateKey){
const jwkPrivate = await crypto.subtle.exportKey("jwk", privateKey);
delete jwkPrivate.d;
jwkPrivate.key_ops = ["verify"];
return crypto.subtle.importKey("jwk", jwkPrivate, {name: "ECDSA", namedCurve: "P-256"}, true, ["verify"]);
}
async function test(){
// Generate test key pair
const keyPair = await crypto.subtle.generateKey({name: "ECDSA", namedCurve: "P-256"}, true, ["sign", "verify"]);
// Derive public from private key
const publicKeyFromPrivate = await getPublic(keyPair.privateKey)
// Compare CryptoKeys
console.log(keyPair.publicKey);
console.log(publicKeyFromPrivate)
// Compare keys (in JWK format)
const jwkPublic = await crypto.subtle.exportKey("jwk", keyPair.publicKey);
const jwkPublicFromPrivate = await crypto.subtle.exportKey("jwk", publicKeyFromPrivate);
console.log(jwkPublic);
console.log(jwkPublicFromPrivate);
}
(async () => {
await test()
})();
As you can see, the original and the reconstructed public key are identical.
However, it should be mentioned that this solutin has one drawback: the private key must be exportable.
This post shows the same approach for RSA.