import { readFileSync } from "node:fs";import { X509Certificate } from "node:crypto"; const cert = new X509Certificate(readFileSync("cert.pem")); // PEM or DER console.log("subject:", cert.subject);console.log("issuer:", cert.issuer);console.log("valid:", cert.validFrom, "->", cert.validTo);console.log("serial:", cert.serialNumber);console.log("SHA-256 fingerprint:", cert.fingerprint256); const key = cert.publicKey;console.log("key type:", key.asymmetricKeyType); // 'ec' for ECDSA certsconsole.log("key details:", key.asymmetricKeyDetails); // { namedCurve: 'prime256v1' }const expiresInDays = (new Date(cert.validTo) - Date.now()) / 86_400_000;console.log("days until expiry:", Math.floor(expiresInDays)); // does this certificate belong to that CA?// console.log(cert.checkIssued(caCert)); // caCert: another X509CertificateHow it works
- The constructor accepts a PEM string/Buffer or raw DER — detection is automatic.
cert.publicKeyis a regularKeyObject: pass it straight tocrypto.verifyor export it as SPKI PEM.asymmetricKeyDetails.namedCurvereports the OpenSSL curve name —prime256v1means P-256.fingerprint256is the SHA-256 hash of the whole DER certificate, colon-separated — the value browsers and audit tools display.cert.rawexposes the certificate's DER bytes as aBuffer— hand them to other parsers or hash them yourself to reproducefingerprint256.
Gotchas
validFrom/validToare human-format strings, notDateobjects — wrap them innew Date(...)before comparing, as the second snippet does.- The certificate fingerprint hashes the entire certificate; the *key* fingerprint hashes only the SPKI. Same key re-issued in a new certificate changes the first, not the second.
- Parsing a certificate proves nothing about trust:
X509Certificatedecodes self-signed and expired certs happily. Chain validation is a separate step (checkIssued, or a TLS library).