From 9e8f05c6912b1fe1dd007e025c5301d3087fceee Mon Sep 17 00:00:00 2001 From: Brian Muenzenmeyer Date: Fri, 28 Aug 2026 06:10:13 -0500 Subject: [PATCH 1/5] crypto: add crypto.parsePKCS12() Return the private key, end-entity certificate, and CA certificates from a PKCS#12 (.p12/.pfx) bundle as a KeyObject and X509Certificate instances. Node.js already parses PKCS#12 in SecureContext::LoadPKCS12, which backs tls's `pfx` option, but the results are consumed directly into an SSL_CTX and never reach JavaScript. Callers who need the key or the certificates for anything other than an immediate TLS connection have to shell out to `openssl pkcs12` or take a userland dependency. The binding wraps d2i_PKCS12_bio() and PKCS12_parse() and follows their semantics, matching the existing TLS path: the first private key is returned, the end-entity certificate is the one associated with that key, and any remaining certificates are returned through `ca`. A bundle containing no private key reports `cert` as null and returns its certificates through `ca`. Absent and empty passphrases are kept distinct, since OpenSSL treats them differently. Bundles that require OpenSSL's legacy provider throw ERR_CRYPTO_UNSUPPORTED_OPERATION, reusing the error added for the TLS path. Signed-off-by: bmuenzenmeyer --- doc/api/crypto.md | 72 ++++++++++ lib/crypto.js | 2 + lib/internal/crypto/keys.js | 54 ++++++++ node.gyp | 2 + src/crypto/crypto_pkcs12.cc | 205 ++++++++++++++++++++++++++++ src/crypto/crypto_pkcs12.h | 23 ++++ src/node_crypto.cc | 1 + src/node_crypto.h | 1 + test/parallel/test-crypto-pkcs12.js | 131 ++++++++++++++++++ 9 files changed, 491 insertions(+) create mode 100644 src/crypto/crypto_pkcs12.cc create mode 100644 src/crypto/crypto_pkcs12.h create mode 100644 test/parallel/test-crypto-pkcs12.js diff --git a/doc/api/crypto.md b/doc/api/crypto.md index 8d80bdc299aa..bba0c2358f4b 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -5338,6 +5338,75 @@ const derivedKey = hkdfSync('sha512', 'key', 'salt', 'info', 64); console.log(Buffer.from(derivedKey).toString('hex')); // '24156e2...5391653' ``` +### `crypto.parsePKCS12(bundle[, options])` + + + +* `bundle` {ArrayBuffer|Buffer|TypedArray|DataView} The DER-encoded PKCS#12 + bundle. +* `options` {Object} + * `passphrase` {string|ArrayBuffer|Buffer|TypedArray|DataView} The passphrase + protecting the bundle. Omit for bundles with no passphrase. Omitting this + option is **not** equivalent to passing an empty string; the two are + handled differently, and a bundle created with one will not open with the + other. +* Returns: {Object} + * `key` {KeyObject|null} The private key, or `null` if the bundle contains + none. + * `cert` {X509Certificate|null} The end-entity certificate, or `null` if the + bundle contains none. + * `ca` {X509Certificate\[]} Any additional certificates in the bundle, such + as intermediates and roots. May be empty. + +Parses a PKCS#12 bundle — commonly seen with the `.p12` or `.pfx` extension — +and returns its contents. + +```mjs +import { parsePKCS12 } from 'node:crypto'; +import { readFileSync } from 'node:fs'; + +const { key, cert, ca } = parsePKCS12( + readFileSync('bundle.p12'), + { passphrase: 'secret' }, +); + +console.log(cert.subject); +console.log(key.export({ type: 'pkcs8', format: 'pem' })); +``` + +```cjs +const { parsePKCS12 } = require('node:crypto'); +const { readFileSync } = require('node:fs'); + +const { key, cert, ca } = parsePKCS12( + readFileSync('bundle.p12'), + { passphrase: 'secret' }, +); + +console.log(cert.subject); +console.log(key.export({ type: 'pkcs8', format: 'pem' })); +``` + +A PKCS#12 bundle may technically contain more than one private key. This API +returns only the first, matching the behavior of OpenSSL's `PKCS12_parse()`. + +The end-entity certificate is identified by its association with the private +key. A bundle containing no private key therefore reports `cert` as `null` and +returns all of its certificates through `ca`. + +Bundles encrypted with older algorithms — notably RC2 and PBE-SHA1 variants +produced by legacy Windows tooling and older versions of `keytool` — require +OpenSSL's legacy provider. Reading these throws an error with the code +[`ERR_CRYPTO_UNSUPPORTED_OPERATION`][]; starting Node.js with +[`--openssl-legacy-provider`][] may allow them to be read, subject to the +security implications of enabling that provider. + +To use a PKCS#12 bundle directly for a TLS connection, prefer the `pfx` option +of [`tls.createSecureContext()`][] rather than parsing and re-supplying the +parts. + ### `crypto.pbkdf2(password, salt, iterations, keylen, digest, callback)`