How to generate an HTTPS certificate with node-forge
source link: https://advancedweb.hu/how-to-generate-an-https-certificate-with-node-forge/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
How to generate an HTTPS certificate with node-forge
A script that returns a self-signed certificate in Javascript
Generating HTTPS certificates
Many times I need to generate an HTTPS certificate for a development server. For example, I can use the https
module to start a webserver:
import https from "https";
// ... get a certificate somehow
// start HTTPS server
const app = https.createServer({key: cert.privateKey, cert: cert.cert}, async (req, res) => {
res.setHeader("Content-Type", "text/html");
res.writeHead(200);
res.end("Hello world!");
});
app.listen(8081);
But the https.createServer
needs a certificate. How to generate it?
The official documentation as well as most articles recommend running OpenSSL commands in advance:
openssl genrsa -out key.pem
openssl req -new -key key.pem -out csr.pem
openssl x509 -req -days 9999 -in csr.pem -signkey key.pem -out cert.pem
rm csr.pem
Then on the Javascript-side, dealing with certificates is a matter of reading files:
const options = {
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem')
};
But, as usual, I wanted something simpler: an in-process generator that requires no setup and just works when I run the Javascript code.
For this I used to use the pem library. It has a createCertificate
function thats result the https.createServer
can use:
import pem from "pem";
const keys = await pem.promisified.createCertificate({days: 1, selfSigned: true});
https.createServer({key: keys.clientKey, cert: keys.certificate}, (req, res) => {
// ...
}).listen(8081);
This works great, but under the hood it simply calls OpenSSL. I always found it a possible problem.
And indeed it broke recently because of this. So I continued searching for some other solution.
Node-forge
During this search, I stumbled upon this article: https://www.techengineer.one/how-to-generate-x-509v3-self-signed-certificate-in-pem-format-with-node-js/. It describes the solution for my problem.
With a few extra tweaks, this is the solution I ended up with:
import forge from "node-forge";
import crypto from "crypto";
export const generateCert = ({altNameIPs, altNameURIs, validityDays}) => {
const keys = forge.pki.rsa.generateKeyPair(2048);
const cert = forge.pki.createCertificate();
cert.publicKey = keys.publicKey;
// NOTE: serialNumber is the hex encoded value of an ASN.1 INTEGER.
// Conforming CAs should ensure serialNumber is:
// - no more than 20 octets
// - non-negative (prefix a '00' if your value starts with a '1' bit)
cert.serialNumber = '01' + crypto.randomBytes(19).toString("hex"); // 1 octet = 8 bits = 1 byte = 2 hex chars
cert.validity.notBefore = new Date();
cert.validity.notAfter = new Date(new Date().getTime() + 1000 * 60 * 60 * 24 * (validityDays ?? 1));
const attrs = [{
name: 'countryName',
value: 'AU'
}, {
shortName: 'ST',
value: 'Some-State'
}, {
name: 'organizationName',
value: 'Internet Widgits Pty Ltd'
}];
cert.setSubject(attrs);
cert.setIssuer(attrs);
// add alt names so that the browser won't complain
cert.setExtensions([{
name: 'subjectAltName',
altNames: [
...(altNameURIs !== undefined ?
altNameURIs.map((uri) => ({type: 6, value: uri})) :
[]
),
...(altNameIPs !== undefined ?
altNameIPs.map((uri) => ({type: 7, ip: uri})) :
[]
)
]
}]);
// self-sign certificate
cert.sign(keys.privateKey);
// convert a Forge certificate and private key to PEM
const pem = forge.pki.certificateToPem(cert);
const privateKey = forge.pki.privateKeyToPem(keys.privateKey)
return {
cert: pem,
privateKey
};
}
This generates an https
-compatible certificate:
const cert = generateCert({altNameIPs: ["127.0.0.1", "127.0.0.2"], validityDays: 2});
const app = https.createServer({key: cert.privateKey, cert: cert.cert}, async (req, res) => {
res.setHeader("Content-Type", "text/html");
res.writeHead(200);
res.end("Hello world!");
});
app.listen(8081);
I added the ability to specify the alt name IP and domains, as well as a validity in days. But otherwise, this is the perfect solution: fully JS-based, no outside dependency, and does not require any additional setup.
How does it work?
It uses node-forge which is a pure JS implementation of all primitives related to TLS and other cryptographic tools. It can do a lot more than just generating and signing certificates, and its documentation contains a ton of examples.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK