File size: 4,665 Bytes
88c4c60 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | 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");
/**
* Check if cert file is expired or expiring within 30 days
*/
function isCertExpired(certPath) {
try {
const cert = forge.pki.certificateFromPem(fs.readFileSync(certPath, "utf8"));
const expiryThreshold = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000);
return cert.validity.notAfter < expiryThreshold;
} catch {
return true; // treat unreadable cert as expired
}
}
/**
* Generate Root CA certificate (only once, auto-regenerate if expired)
* This Root CA will sign all dynamic leaf certificates
*/
async function generateRootCA() {
const exists = fs.existsSync(ROOT_CA_KEY_PATH) && fs.existsSync(ROOT_CA_CERT_PATH);
if (exists && !isCertExpired(ROOT_CA_CERT_PATH)) {
console.log("β
Root CA already exists");
return { key: ROOT_CA_KEY_PATH, cert: ROOT_CA_CERT_PATH };
}
if (exists) {
console.log("π Root CA expired or expiring soon β regenerating...");
try { fs.unlinkSync(ROOT_CA_KEY_PATH); } catch { /* ignore */ }
try { fs.unlinkSync(ROOT_CA_CERT_PATH); } catch { /* ignore */ }
}
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,
isCertExpired,
ROOT_CA_CERT_PATH,
ROOT_CA_KEY_PATH
};
|