I'm trying to use Elliptical Curve Diffie-Hellman keys to create a shared secret between a Browser and NodeJS. If I export the browser public key as raw, everything works, but I'm required to export the key as spki and then NodeJS gets mad about it.
In the Browser I do this:
async function generateDHKeys() {
const key_ECDH = await window.crypto.subtle.generateKey(
{ name: 'ECDH', namedCurve: 'P-256' },
true,
['deriveKey'],
);
const publicKeyData = await window.crypto.subtle.exportKey(
'spki',
key_ECDH.publicKey,
);
const publicKeyBytes = new Uint8Array(publicKeyData);
publicKeyB64 = btoa(String.fromCharCode.apply(null, publicKeyBytes));
const privateKeyData = await window.crypto.subtle.exportKey(
'pkcs8',
key_ECDH.privateKey,
);
const privateKeyBytes = new Uint8Array(privateKeyData);
privateKeyB64 = btoa(String.fromCharCode.apply(null, privateKeyBytes));
privateKeyBytes.fill(0);
return { publicKeyB64, privateKeyB64 };
}
const {publicKeyB64} = await generateDHKeys();
So, now I've exported the Public Key and converted it to Base64. I then send it to the NodeJS server, and I try to create a shared secret:
In NodeJS, I do this:
export function generateDHKeys(foreignPublicKeyB64) {
const ecdh = crypto.createECDH("prime256v1");
ecdh.generateKeys();
const publicKeyB64 = ecdh.getPublicKey("base64");
const privateKeyB64 = ecdh.getPrivateKey("base64");
const sharedSecretB64 = ecdh.computeSecret(foreignPublicKeyB64, "base64", "base64");
const sharedSecretHashB64 = crypto
.createHash("sha256")
.update(sharedSecretB64, "base64")
.digest("base64");
return { publicKeyB64, privateKeyB64, sharedSecretB64, sharedSecretHashB64 };
}
And I get an error saying "Public key is not valid for specified curve."
However, if in the Browser code I export the key as raw (instead of spki) it works....
How can I export the public key as spki in the browser, and then use it to generate a shared secret in NodeJS? Or, how can I convert a Base64 SPKI public key into a raw key in Node?
EDIT
It has been discovered that the Browser Crypto APIs are indeed supported in Node v15.0.0+, meaning my browser JS can be simply copied and run in a Node context. Rather than access window.crypto.subtle as I would in a browser, in a Node application I can import the subtle module like so:
const { subtle } = require("crypto").webcrypto;
HOWEVER... as @Topaco pointed out, as of Node v16.2.0, this API is still experimental and subject to change. See @Topaco's answer for additional information and documentation links.