Skip to content
ecdsa.com

Public key from a certificate · Node.js

How to extract the public key from an X.509 certificate in Node.js

To extract the public key from an X.509 certificate in Node.js, parse it with crypto.X509Certificate and export cert.publicKey with { type: "spki", format: "pem" }. Nothing but node:crypto is needed. This recipe prints the SPKI PEM, computes the key's SHA-256 fingerprint and base64 pin, and checks a private key against the certificate.

Tested with Node.js 24.22.0 (node:crypto X509Certificate and KeyObject, OpenSSL 3.5.8).

extract.mjs — SPKI PEM, fingerprint, pin and the key-pair check
import { X509Certificate, createHash, createPrivateKey, createPublicKey } from "node:crypto";
import { readFileSync } from "node:fs";
const cert = new X509Certificate(readFileSync("cert.pem")); // PEM or DER
const publicKey = cert.publicKey; // a KeyObject of type "public"
const spkiPem = publicKey.export({ type: "spki", format: "pem" });
const spkiDer = publicKey.export({ type: "spki", format: "der" });
process.stdout.write(spkiPem);
const digest = createHash("sha256").update(spkiDer).digest();
console.log("SPKI SHA-256:", digest.toString("hex").match(/../g).join(":"));
console.log("SPKI pin (base64):", digest.toString("base64"));
// Does key.pem belong to this certificate? (checkPrivateKey compares the public halves)
const privateKey = createPrivateKey(readFileSync("key.pem"));
console.log("key.pem matches:", cert.checkPrivateKey(privateKey));
console.log("same key object:", createPublicKey(privateKey).equals(publicKey));

How it works

  1. new X509Certificate(buffer) reads PEM or DER; cert.publicKey is a KeyObject of type public, usable directly with crypto.verify.
  2. publicKey.export({ type: "spki", format: "pem" }) gives the BEGIN PUBLIC KEY block; format: "der" the bytes to hash.
  3. SHA-256 of the SPKI DER is the key fingerprint (45:ca:a7:e3:…:7b:5f), and its base64 (Rcqn43J0…e18=) the SPKI pin.
  4. cert.checkPrivateKey(privateKey) answers whether key.pem belongs to the certificate; KeyObject.equals compares two public keys.

Gotchas

  • cert.fingerprint256 hashes the whole certificate (CD:B1:A1:40…), not its key; it changes at every renewal, the SPKI hash does not.
  • publicKey.asymmetricKeyDetails names the curve the OpenSSL way (prime256v1), not P-256 as JOSE and WebCrypto do.

Related recipes