const path = require("path"); const fs = require("fs"); const forge = require("node-forge"); const { MITM_DIR } = require("../paths"); const ROOT_CA_KEY_PATH = path.join(MITM_DIR, "rootCA.key"); const ROOT_CA_CERT_PATH = path.join(MITM_DIR, "rootCA.crt"); /** * Generate Root CA certificate (only once) * This Root CA will sign all dynamic leaf certificates */ async function generateRootCA() { if (fs.existsSync(ROOT_CA_KEY_PATH) && fs.existsSync(ROOT_CA_CERT_PATH)) { console.log("✅ Root CA already exists"); return { key: ROOT_CA_KEY_PATH, cert: ROOT_CA_CERT_PATH }; } if (!fs.existsSync(MITM_DIR)) { fs.mkdirSync(MITM_DIR, { recursive: true }); } console.log("🔐 Generating Root CA certificate..."); // Generate RSA key pair const keys = forge.pki.rsa.generateKeyPair(2048); // Create Root CA certificate const cert = forge.pki.createCertificate(); cert.publicKey = keys.publicKey; cert.serialNumber = "01"; cert.validity.notBefore = new Date(); cert.validity.notAfter = new Date(); cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 10); const attrs = [ { name: "commonName", value: "9Router MITM Root CA" }, { name: "organizationName", value: "9Router" }, { name: "countryName", value: "US" } ]; cert.setSubject(attrs); cert.setIssuer(attrs); // Self-signed cert.setExtensions([ { name: "basicConstraints", cA: true, critical: true }, { name: "keyUsage", keyCertSign: true, cRLSign: true, critical: true }, { name: "subjectKeyIdentifier" } ]); // Self-sign the certificate cert.sign(keys.privateKey, forge.md.sha256.create()); // Save to disk const privateKeyPem = forge.pki.privateKeyToPem(keys.privateKey); const certPem = forge.pki.certificateToPem(cert); fs.writeFileSync(ROOT_CA_KEY_PATH, privateKeyPem); fs.writeFileSync(ROOT_CA_CERT_PATH, certPem); console.log("✅ Root CA generated successfully"); return { key: ROOT_CA_KEY_PATH, cert: ROOT_CA_CERT_PATH }; } /** * Load Root CA from disk */ function loadRootCA() { if (!fs.existsSync(ROOT_CA_KEY_PATH) || !fs.existsSync(ROOT_CA_CERT_PATH)) { throw new Error("Root CA not found. Generate it first."); } const keyPem = fs.readFileSync(ROOT_CA_KEY_PATH, "utf8"); const certPem = fs.readFileSync(ROOT_CA_CERT_PATH, "utf8"); return { key: forge.pki.privateKeyFromPem(keyPem), cert: forge.pki.certificateFromPem(certPem) }; } /** * Generate leaf certificate for a specific domain, signed by Root CA */ function generateLeafCert(domain, rootCA) { // Generate key pair for leaf cert const keys = forge.pki.rsa.generateKeyPair(2048); // Create leaf certificate const cert = forge.pki.createCertificate(); cert.publicKey = keys.publicKey; cert.serialNumber = Math.floor(Math.random() * 1000000).toString(); cert.validity.notBefore = new Date(); cert.validity.notAfter = new Date(); cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1); cert.setSubject([ { name: "commonName", value: domain } ]); cert.setIssuer(rootCA.cert.subject.attributes); cert.setExtensions([ { name: "basicConstraints", cA: false }, { name: "keyUsage", digitalSignature: true, keyEncipherment: true }, { name: "extKeyUsage", serverAuth: true, clientAuth: true }, { name: "subjectAltName", altNames: [ { type: 2, value: domain }, // DNS { type: 2, value: `*.${domain}` } // Wildcard ] } ]); // Sign with Root CA cert.sign(rootCA.key, forge.md.sha256.create()); return { key: forge.pki.privateKeyToPem(keys.privateKey), cert: forge.pki.certificateToPem(cert) }; } module.exports = { generateRootCA, loadRootCA, generateLeafCert, ROOT_CA_CERT_PATH, ROOT_CA_KEY_PATH };