'NodeJS load PFX certificate from file
I am writing a small project using Node.JS and TypeScript, once of the requirements is to read a PFX certificate from a .pfx file and use this in the code to encrypt the payload body
I have a certificate public/private key file called cert1.pfx, my code requires this certificate as below
...
const cert = loadPfx("cert1.pfx");
const p: Payload = new Payload();
p.addReaderCertificate(cert);
...
I have searched around but cannot find a way to load the PFX for my use case, I have seen examples of loading a PFX for HTTPS server or Express.JS, I looked a node-x509 but that is for BASE64 encoded CER or PEM certificates, I also looked at node-rsa but thats for encrypt/decrypt using public/private keys.
Does anyone know if this is possible? If so would appreciate some pointers on how to accomplish.
Solution 1:[1]
So after a LOT of research and trawling the Google archives I came across a package called pem and this has the following method:
pem.readPkcs12(bufferOrPath, [options], callback)
This can read a PKCS#12 file (or in other words a *.pfx or *.p12 file) amongst other things, I must have missed this in my earlier research.
Usage:
const pem = require("pem");
const fs = require("fs");
const pfx = fs.readFileSync(__dirname + "/test.pfx");
pem.readPkcs12(pfx, { p12Password: "password" }, (err, cert) => {
console.log(cert);
});
Output:
{ cert: "...", ca: ["subca", "rootca"], key: "..." }
Solution 2:[2]
It sounds like you only need to use Node's own https capabilities. Node can read the PFX file directly. (Https.createServer, SSL Options)
Example from Node.js site:
const https = require('https');
const fs = require('fs');
const options = {
pfx: fs.readFileSync('test/fixtures/test_cert.pfx'),
passphrase: 'sample'
};
https.createServer(options, (req, res) => {
res.writeHead(200);
res.end('hello world\n');
}).listen(8000);
Solution 3:[3]
I was also stuck on a similar problem @neil-stevens solution helped me to read the .pfx file but what i find is one feature/bug ( i don't know exactly what it is) of pem that it returns encrypted private key mostly in RSA , but if you need actual private key you need to export encrypted key into pkcs8,which can be done using Node RSA.
Usage :
const RSAKey = cert.key;
const key = new NodeRSA(RSAKey);
const privateKey = key.exportKey("pkcs8");
Solution 4:[4]
Complementing the answer of Neil Stevens (Sorry, idk how to quote).
- I hade that issue of getting a empty object only, but so i just observe that i was not loging the err. So i did that, after that i was able to resolve my problems
1º You need to have installed openSSL or libressl how described on https://github.com/Dexus/pem.
- Tip: To install if you already have configured and instaled Chocolatey. Just Run the command: "choco install openssl". (with eleveted permissions)
2º I had to use pem.config() to set exatcly path of openSSL folder. I think you can also define in the Environment Variables, i just not tested this way yet.
3º Finally my code it was left like this:
const pem = require("pem");
const fs = require("fs");
const pfx = fs.readFileSync("./cert.pfx");
let certificate = '';
pem.config({
pathOpenSSL: 'C:\\Program Files\\OpenSSL-Win64\\bin\\openssl'
})
const getPrivateKey = async () => {
return new Promise(async (resolve, reject) => {
pem.readPkcs12(pfx, { p12Password: 'myPassWordStr' }, (err, cert) => {
console.log('err::: ', err);
// console.log('cert::: ', cert);
resolve(cert);
});
});
}
const start = async () => {
certificate = await getPrivateKey();
console.log('privateKey::: ', certificate);
}
start();
Solution 5:[5]
I arrived here while trying to find a way to configure a local web server with HTTPS for local development using the development certificate generated by the .NET CLI (as this is easily created / trusted / removed).
Thanks to Neil Stephen's answer I was able to create a working solution on Windows, using a combination of npm, dotnet CLI, openssl and npm package pem.
Git ships with a copy of OpenSSL, so I didn't need to install it separately :)
.env
OPENSSL_PATH=C:\Program Files\Git\usr\bin\openssl
CERT_PASSWORD=SecurePassword123
CERT_PFX=cert/localhost.pfx
CERT_PEM=cert/localhost.pem
CERT_KEY=cert/localhost.key
PORT=443
ENTRYPOINT=src/index.html
package.json
"scripts": {
"start": "env-cmd -x parcel $ENTRYPOINT --https --cert $CERT_PEM --key $CERT_KEY --port $PORT --open",
"build": "env-cmd -x parcel build $ENTRYPOINT",
"dev-certs": "run-s dev-certs:create dev-certs:convert",
"dev-certs:create": "env-cmd -x dotnet dev-certs https -ep $CERT_PFX -p $CERT_PASSWORD --verbose --trust",
"dev-certs:convert": "node ./cli/cert.mjs",
"dev-certs:clean": "dotnet dev-certs https --clean"
},
cert.mjs
import pem from "pem";
import { PFX2PEM } from "pem/lib/convert.js";
import fs from "fs";
import "dotenv/config";
pem.config({
pathOpenSSL: process.env.OPENSSL_PATH
});
const pass = process.env.CERT_PASSWORD;
// GET .KEY FILE - without this, HMR won't work
const pfx = fs.readFileSync(process.env.CERT_PFX);
pem.readPkcs12(pfx, { p12Password: pass }, (err, cert) => {
if (!!err) {
console.error(err);
return;
}
// console.log(cert.key);
fs.writeFileSync(process.env.CERT_KEY, cert.key);
});
// GET .PEM FILE
PFX2PEM(process.env.CERT_PFX, process.env.CERT_PEM, pass, (errPem, successPem) => {
if (!successPem) {
console.error(errPem);
return;
}
console.log(`Certificate '${process.env.CERT_PEM}' created!`);
});
Repository is here: https://github.com/zplume/parcel-https and the README contains details of how it works.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | gerichhome |
| Solution 2 | Constablebrew |
| Solution 3 | mondyfy |
| Solution 4 | Maaelphd |
| Solution 5 |
