const fs = require('fs'); const path = require('path'); const forge = require('node-forge'); const rootDir = path.resolve(__dirname, '..'); const rawConfigText = fs.readFileSync(path.join(rootDir, 'config.json'), 'utf8'); const configText = rawConfigText.charCodeAt(0) === 0xfeff ? rawConfigText.slice(1) : rawConfigText; const config = JSON.parse(configText); const certDir = path.resolve(rootDir, config.certDir || 'certs'); const keyPath = path.join(certDir, 'rootCA.key.pem'); const certPath = path.join(certDir, 'rootCA.cert.pem'); const certDerPath = path.join(certDir, 'rootCA.cert.cer'); fs.mkdirSync(certDir, { recursive: true }); if (fs.existsSync(keyPath) || fs.existsSync(certPath) || fs.existsSync(certDerPath)) { console.error('Root CA already exists. Delete certs/rootCA.* first if you intentionally want to regenerate it.'); process.exit(1); } console.log('Generating 2048-bit RSA root CA...'); const keys = forge.pki.rsa.generateKeyPair(2048); const cert = forge.pki.createCertificate(); cert.publicKey = keys.publicKey; cert.serialNumber = forge.util.bytesToHex(forge.random.getBytesSync(16)).replace(/^00/, '01'); cert.validity.notBefore = new Date(Date.now() - 60 * 1000); cert.validity.notAfter = new Date(Date.now() + 10 * 365 * 24 * 60 * 60 * 1000); cert.setSubject([ { name: 'commonName', value: 'Local Node MITM Proxy Root CA' }, { name: 'organizationName', value: 'Local Development' }, { shortName: 'OU', value: 'Debug Proxy' }, ]); cert.setIssuer(cert.subject.attributes); cert.setExtensions([ { name: 'basicConstraints', cA: true, critical: true }, { name: 'keyUsage', keyCertSign: true, cRLSign: true, critical: true }, { name: 'subjectKeyIdentifier' }, ]); cert.sign(keys.privateKey, forge.md.sha256.create()); const keyPem = forge.pki.privateKeyToPem(keys.privateKey); const certPem = forge.pki.certificateToPem(cert); const certDer = Buffer.from(forge.asn1.toDer(forge.pki.certificateToAsn1(cert)).getBytes(), 'binary'); fs.writeFileSync(keyPath, keyPem, 'utf8'); fs.writeFileSync(certPath, certPem, 'utf8'); fs.writeFileSync(certDerPath, certDer); console.log(`Created private key: ${keyPath}`); console.log(`Created PEM cert: ${certPath}`); console.log(`Created DER cert: ${certDerPath}`); console.log('Keep rootCA.key.pem private. Anyone with it can mint trusted certificates on this machine.');