import { X509Certificate, createPrivateKey } from "node:crypto"; // Certificate: X509Certificate parses PEM or DER; .raw is the DER, toString() the PEMexport const certPemToDer = (pem) => new X509Certificate(pem).raw;export const certDerToPem = (der) => new X509Certificate(der).toString(); // Private key: DER has no header, so say which structure it holds (pkcs8, sec1, pkcs1)export const keyPemToDer = (pem) => createPrivateKey(pem).export({ type: "pkcs8", format: "der" });export const keyDerToPem = (der) => createPrivateKey({ key: der, format: "der", type: "pkcs8" }).export({ type: "pkcs8", format: "pem" }); How it works
new X509Certificate(pemOrDer)accepts both encodings;.rawis aBufferwith the DER (563 bytes here) andtoString()the PEM, identical to the original file.createPrivateKey(pem)reads a PEM key of any type;export({ type: "pkcs8", format: "der" })returns PKCS#8 DER (138 bytes for this P-256 key), andformat: "pem"the PEM.- Reading DER needs
format: "der"and atype("pkcs8","sec1"or"pkcs1"): unlike PEM, DER has no header that names its structure. - The run checks the certificate's DER with OpenSSL (
openssl x509 -inform DER).
Gotchas
createPrivateKey({ key: der, format: "der" })withouttypethrowsERR_INVALID_ARG_VALUE; Node.js does not guess the structure of DER bytes.type: "sec1"andtype: "pkcs8"give different bytes for the same EC key (121 against 138 bytes); write PKCS#8 unless the consumer asks for SEC1 (BEGIN EC PRIVATE KEY).X509Certificatereads only the first certificate of a PEM bundle — the run getsCN=api.example.comfrom a two-certificate file and no error — so split bundles before converting them one by one.
Related recipes
- How to convert a certificate and a private key between PEM and DER in WebCrypto
- How to convert a certificate and a private key between PEM and DER with OpenSSL
- How to convert a certificate and a private key between PEM and DER in Python
- How to export a public key as PEM in Node.js
- How to decode an X.509 certificate in Node.js