Skip to content

Instantly share code, notes, and snippets.

@mbarneyjr
Created July 22, 2024 02:08
Show Gist options
  • Save mbarneyjr/7d954bf665ec97247ff3176423dc2f37 to your computer and use it in GitHub Desktop.
Save mbarneyjr/7d954bf665ec97247ff3176423dc2f37 to your computer and use it in GitHub Desktop.
A script that will create keypairs and certificates in the form of a root CA, and intermediate CA, and a leaf certificate
import forge from "node-forge";
import crypto, { randomUUID } from "crypto";
import { existsSync, mkdirSync, writeFileSync } from "fs";
const pki = forge.pki;
function generateKeyPair() {
const forgeKeypair = pki.rsa.generateKeyPair(4096);
return {
publicKey: pki.publicKeyToPem(forgeKeypair.publicKey),
privateKey: pki.privateKeyToPem(forgeKeypair.privateKey),
};
}
/**
* @param {crypto.KeyPairSyncResult<string, string>} keyPair
* @param {forge.pki.CertificateField[]} attributes
*/
function generateSelfSignedCertificate(keyPair, attributes) {
const privateKey = pki.privateKeyFromPem(keyPair.privateKey);
const publicKey = pki.publicKeyFromPem(keyPair.publicKey);
const cert = pki.createCertificate();
cert.publicKey = publicKey;
cert.serialNumber = randomUUID().replace(/-/g, "");
cert.validity.notBefore = new Date();
cert.validity.notAfter = new Date();
cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1);
cert.setSubject(attributes);
cert.setIssuer(attributes);
cert.setExtensions([
{
name: "basicConstraints",
cA: true,
},
{
name: "keyUsage",
digitalSignature: true,
keyCertSign: true,
cRLSign: true,
},
]);
cert.sign(privateKey, forge.md.sha256.create());
return pki.certificateToPem(cert);
}
/**
* @param {crypto.KeyPairSyncResult<string, string>} keyPair
* @param {forge.pki.CertificateField[]} attributes
*/
function generateCSR(keyPair, attributes) {
const privateKey = pki.privateKeyFromPem(keyPair.privateKey);
const publicKey = pki.publicKeyFromPem(keyPair.publicKey);
const csr = pki.createCertificationRequest();
csr.publicKey = publicKey;
csr.setSubject(attributes);
csr.sign(privateKey, forge.md.sha256.create());
const pem = pki.certificationRequestToPem(csr);
return pem;
}
/**
* @param {{
* csrPem: string
* issuingKeypair: crypto.KeyPairSyncResult<string, string>
* issuingCaPem: string
* issueCaCert: boolean
* }}
* @param {crypto.KeyPairSyncResult<string, string>} issuingKeypair
* @param {string} issuingCaPem
*/
function issueCert({ csrPem, caCert, issuingKeypair, issuingCaPem }) {
const csr = pki.certificationRequestFromPem(csrPem);
const issuingCa = pki.certificateFromPem(issuingCaPem);
const issuingPrivateKey = pki.privateKeyFromPem(issuingKeypair.privateKey);
if (!csr.verify()) {
throw new Error("CSR not verified");
}
const cert = pki.createCertificate();
cert.serialNumber = randomUUID().replace(/-/g, "");
cert.validity.notBefore = new Date();
cert.validity.notAfter = new Date();
cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1);
cert.setSubject(csr.subject.attributes);
cert.setIssuer(issuingCa.subject.attributes);
cert.setExtensions([
{
name: "basicConstraints",
cA: caCert === true,
},
{
name: "keyUsage",
digitalSignature: true,
keyCertSign: caCert === true,
cRLSign: caCert === true,
},
]);
cert.publicKey = csr.publicKey;
cert.sign(issuingPrivateKey, forge.md.sha256.create());
return pki.certificateToPem(cert);
}
/**
* @param {Record<string, string>} attributes
*/
function getAttributes(options) {
const result = [];
for (let [key, value] of Object.entries(options)) {
result.push({
name: key,
value,
});
}
return result;
}
function main() {
if (!existsSync("out")) {
mkdirSync("out");
}
const rootKeypair = generateKeyPair();
writeFileSync("out/root-private-key.pem", rootKeypair.privateKey);
writeFileSync("out/root-public-key.pem", rootKeypair.publicKey);
const rootCert = generateSelfSignedCertificate(
rootKeypair,
getAttributes({ commonName: "root" }),
);
writeFileSync("out/root-cert.pem", rootCert);
const intKeypair = generateKeyPair();
writeFileSync("out/int-private-key.pem", intKeypair.privateKey);
writeFileSync("out/int-public-key.pem", intKeypair.publicKey);
const intCsr = generateCSR(
intKeypair,
getAttributes({
commonName: "int",
}),
);
const intCert = issueCert({
caCert: true,
csrPem: intCsr,
issuingKeypair: rootKeypair,
issuingCaPem: rootCert,
});
writeFileSync("out/int-cert.pem", intCert);
const leafKeypair = generateKeyPair();
writeFileSync("out/leaf-private-key.pem", leafKeypair.privateKey);
writeFileSync("out/leaf-public-key.pem", leafKeypair.publicKey);
const leafCsr = generateCSR(
leafKeypair,
getAttributes({
commonName: "leaf",
}),
);
const leafCert = issueCert({
caCert: false,
csrPem: leafCsr,
issuingKeypair: intKeypair,
issuingCaPem: intCert,
});
writeFileSync("out/leaf-cert.pem", leafCert);
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment