reorg
This commit is contained in:
@@ -0,0 +1,563 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const tls = require("tls");
|
||||
const net = require("net");
|
||||
const http = require("http");
|
||||
const https = require("https");
|
||||
const url = require("url");
|
||||
const forge = require("node-forge");
|
||||
const os = require("os");
|
||||
const { exec } = require("child_process");
|
||||
const { logDebug, logError, logWarn, logInfo } = require("../infrastructure/logger");
|
||||
const state = require("../infrastructure/state");
|
||||
const certsDir = process.env.CERTS_DIR || "./certs";
|
||||
const caKeyPath = path.join(certsDir, "ca.key.pem");
|
||||
const caCertPath = path.join(certsDir, "ca.cert.pem");
|
||||
const caFetchPromises = new Map(); // domain => Promise for CA chain fetch
|
||||
// Create certs directory if not exists
|
||||
if (!fs.existsSync(certsDir)) {
|
||||
fs.mkdirSync(certsDir);
|
||||
}
|
||||
// Generate or validate Root CA
|
||||
let regenerated = false;
|
||||
if (fs.existsSync(caKeyPath) && fs.existsSync(caCertPath)) {
|
||||
const caCertPem = fs.readFileSync(caCertPath, "utf8");
|
||||
const cert = forge.pki.certificateFromPem(caCertPem);
|
||||
if (cert.validity.notAfter > new Date()) {
|
||||
logDebug("CA", "Existing Root CA is valid.");
|
||||
} else {
|
||||
logWarn("CA", "Existing Root CA expired, regenerating.");
|
||||
regenerated = true;
|
||||
}
|
||||
} else {
|
||||
regenerated = true;
|
||||
}
|
||||
if (regenerated) {
|
||||
const keys = forge.pki.rsa.generateKeyPair(2048);
|
||||
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: "P2NS CA" },
|
||||
{ name: "countryName", value: "US" },
|
||||
{ shortName: "ST", value: "CA" },
|
||||
{ name: "localityName", value: "San Francisco" },
|
||||
{ name: "organizationName", value: "P2NS" },
|
||||
{ shortName: "OU", value: "P2NS Root CA" },
|
||||
];
|
||||
cert.setSubject(attrs);
|
||||
cert.setIssuer(attrs);
|
||||
const skid = forge.pki.getPublicKeyFingerprint(cert.publicKey, {
|
||||
md: forge.md.sha1.create(),
|
||||
});
|
||||
cert.setExtensions([
|
||||
{ name: "basicConstraints", cA: true, pathLenConstraint: 0 },
|
||||
{ name: "keyUsage", keyCertSign: true, cRLSign: true },
|
||||
{ name: "subjectKeyIdentifier" },
|
||||
{ name: "authorityKeyIdentifier", keyIdentifier: skid.getBytes() },
|
||||
]);
|
||||
cert.sign(keys.privateKey, forge.md.sha256.create(), {
|
||||
padding: forge.pki.rsa.PKCS1_v1_5,
|
||||
});
|
||||
fs.writeFileSync(caKeyPath, forge.pki.privateKeyToPem(keys.privateKey));
|
||||
fs.writeFileSync(caCertPath, forge.pki.certificateToPem(cert));
|
||||
logInfo("CA", "Root CA generated.");
|
||||
}
|
||||
// Install Root CA
|
||||
function installRootCA() {
|
||||
const platform = os.platform();
|
||||
const caPath = path.resolve(caCertPath);
|
||||
const commonName = "P2NS CA";
|
||||
if (platform === "darwin") {
|
||||
exec(
|
||||
`security find-certificate -c "${commonName}" -a /Library/Keychains/System.keychain`,
|
||||
(err, stdout) => {
|
||||
const installed = !err && stdout.trim() !== "";
|
||||
if (!regenerated && installed) {
|
||||
logInfo("CA", "Root CA already installed on macOS.");
|
||||
return;
|
||||
}
|
||||
if (installed) {
|
||||
exec(
|
||||
`security delete-certificate -c "${commonName}" /Library/Keychains/System.keychain`,
|
||||
(err, stdout, stderr) => {
|
||||
if (err) {
|
||||
logError("CA", `Error deleting old CA on macOS: ${stderr}`);
|
||||
} else {
|
||||
logDebug("CA", "Removed old CA on macOS.");
|
||||
}
|
||||
addCA();
|
||||
}
|
||||
);
|
||||
} else {
|
||||
addCA();
|
||||
}
|
||||
function addCA() {
|
||||
const command = `security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "${caPath}"`;
|
||||
exec(command, (err, stdout, stderr) => {
|
||||
if (err) {
|
||||
logError("CA", `Error installing root CA on macOS: ${stderr}`);
|
||||
} else {
|
||||
logInfo("CA", "Root CA installed successfully on macOS.");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
} else if (platform === "linux") {
|
||||
const caTargetPath = "/usr/local/share/ca-certificates/p2ns-ca.crt";
|
||||
const installed = fs.existsSync(caTargetPath);
|
||||
if (!regenerated && installed) {
|
||||
logInfo("CA", "Root CA already installed on Linux.");
|
||||
return;
|
||||
}
|
||||
if (installed) {
|
||||
fs.unlinkSync(caTargetPath);
|
||||
logDebug("CA", "Removed old CA file on Linux.");
|
||||
}
|
||||
const copyCmd = `cp "${caPath}" ${caTargetPath}`;
|
||||
exec(copyCmd, (err, stdout, stderr) => {
|
||||
if (err) {
|
||||
logError("CA", `Error copying CA on Linux: ${stderr}`);
|
||||
return;
|
||||
}
|
||||
exec("update-ca-certificates", (err, stdout, stderr) => {
|
||||
if (err) {
|
||||
logError("CA", `Error updating CA on Linux: ${stderr}`);
|
||||
} else {
|
||||
logInfo("CA", "Root CA installed on Linux.");
|
||||
}
|
||||
});
|
||||
});
|
||||
} else if (platform === "win32") {
|
||||
exec(`certutil -store ROOT | findstr "${commonName}"`, (err, stdout) => {
|
||||
const installed = !err && stdout.trim() !== "";
|
||||
if (!regenerated && installed) {
|
||||
logInfo("CA", "Root CA already installed on Windows.");
|
||||
return;
|
||||
}
|
||||
if (installed) {
|
||||
exec(
|
||||
`certutil -delstore ROOT "${commonName}"`,
|
||||
(err, stdout, stderr) => {
|
||||
if (err) {
|
||||
logError("CA", `Error deleting old CA on Windows: ${stderr}`);
|
||||
} else {
|
||||
logDebug("CA", "Removed old CA on Windows.");
|
||||
}
|
||||
addCA();
|
||||
}
|
||||
);
|
||||
} else {
|
||||
addCA();
|
||||
}
|
||||
function addCA() {
|
||||
const command = `certutil -addstore -f "ROOT" "${caPath}"`;
|
||||
exec(command, (err, stdout, stderr) => {
|
||||
if (err) {
|
||||
logError("CA", `Error installing root CA on Windows: ${stderr}`);
|
||||
} else {
|
||||
logInfo("CA", "Root CA installed successfully on Windows.");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
logWarn("CA", "Unsupported platform for auto-install CA");
|
||||
}
|
||||
}
|
||||
// Function to regenerate Root CA
|
||||
function regenerateRootCA() {
|
||||
if (fs.existsSync(caKeyPath)) fs.unlinkSync(caKeyPath);
|
||||
if (fs.existsSync(caCertPath)) fs.unlinkSync(caCertPath);
|
||||
const keys = forge.pki.rsa.generateKeyPair(2048);
|
||||
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: "P2NS CA" },
|
||||
{ name: "countryName", value: "US" },
|
||||
{ shortName: "ST", value: "CA" },
|
||||
{ name: "localityName", value: "San Francisco" },
|
||||
{ name: "organizationName", value: "P2NS" },
|
||||
{ shortName: "OU", value: "P2NS Root CA" },
|
||||
];
|
||||
cert.setSubject(attrs);
|
||||
cert.setIssuer(attrs);
|
||||
const skid = forge.pki.getPublicKeyFingerprint(cert.publicKey, {
|
||||
md: forge.md.sha1.create(),
|
||||
});
|
||||
cert.setExtensions([
|
||||
{ name: "basicConstraints", cA: true, pathLenConstraint: 0 },
|
||||
{ name: "keyUsage", keyCertSign: true, cRLSign: true },
|
||||
{ name: "subjectKeyIdentifier" },
|
||||
{ name: "authorityKeyIdentifier", keyIdentifier: skid.getBytes() },
|
||||
]);
|
||||
cert.sign(keys.privateKey, forge.md.sha256.create(), {
|
||||
padding: forge.pki.rsa.PKCS1_v1_5,
|
||||
});
|
||||
fs.writeFileSync(caKeyPath, forge.pki.privateKeyToPem(keys.privateKey));
|
||||
fs.writeFileSync(caCertPath, forge.pki.certificateToPem(cert));
|
||||
logInfo("CA", "Root CA regenerated.");
|
||||
}
|
||||
// Certificate revocation list (in-memory, could be persisted)
|
||||
const certificateRevocationList = new Set();
|
||||
|
||||
// Certificate expiration monitoring
|
||||
const certificateExpirationCheckInterval = 24 * 60 * 60 * 1000; // 24 hours
|
||||
let expirationCheckInterval = null;
|
||||
|
||||
// Start certificate expiration monitoring
|
||||
function startCertificateExpirationMonitoring() {
|
||||
if (expirationCheckInterval) {
|
||||
clearInterval(expirationCheckInterval);
|
||||
}
|
||||
|
||||
// Check immediately on start
|
||||
checkCertificateExpirations();
|
||||
|
||||
// Then check periodically
|
||||
expirationCheckInterval = setInterval(() => {
|
||||
checkCertificateExpirations();
|
||||
}, certificateExpirationCheckInterval);
|
||||
|
||||
logInfo("CA", "Certificate expiration monitoring started");
|
||||
}
|
||||
|
||||
// Check all certificates for expiration
|
||||
function checkCertificateExpirations() {
|
||||
try {
|
||||
if (!fs.existsSync(certsDir)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const domains = fs.readdirSync(certsDir, { withFileTypes: true })
|
||||
.filter(dirent => dirent.isDirectory())
|
||||
.map(dirent => dirent.name);
|
||||
|
||||
const renewalThreshold = 30 * 24 * 60 * 60 * 1000; // 30 days before expiration
|
||||
const now = Date.now();
|
||||
|
||||
for (const domain of domains) {
|
||||
const certPath = path.join(certsDir, domain, "cert.pem");
|
||||
if (!fs.existsSync(certPath)) continue;
|
||||
|
||||
try {
|
||||
const certPem = fs.readFileSync(certPath, "utf8");
|
||||
// Extract the first certificate from the chain
|
||||
const certMatch = certPem.match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/);
|
||||
if (!certMatch) continue;
|
||||
|
||||
const cert = forge.pki.certificateFromPem(certMatch[0]);
|
||||
const expirationDate = cert.validity.notAfter;
|
||||
const timeUntilExpiration = expirationDate.getTime() - now;
|
||||
|
||||
if (timeUntilExpiration < 0) {
|
||||
logWarn("CA", `Certificate for ${domain} has expired. Auto-renewing...`);
|
||||
renewDomainCertificate(domain);
|
||||
} else if (timeUntilExpiration < renewalThreshold) {
|
||||
logInfo("CA", `Certificate for ${domain} expires in ${Math.floor(timeUntilExpiration / (24 * 60 * 60 * 1000))} days. Auto-renewing...`);
|
||||
renewDomainCertificate(domain);
|
||||
}
|
||||
} catch (err) {
|
||||
logError("CA", `Error checking certificate expiration for ${domain}: ${err.message}`);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
logError("CA", `Error in certificate expiration check: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Renew a domain certificate
|
||||
function renewDomainCertificate(domain) {
|
||||
try {
|
||||
const domainDir = path.join(certsDir, domain);
|
||||
const keyPath = path.join(domainDir, "key.pem");
|
||||
const certPath = path.join(domainDir, "cert.pem");
|
||||
|
||||
// Remove old certificate
|
||||
if (fs.existsSync(keyPath)) fs.unlinkSync(keyPath);
|
||||
if (fs.existsSync(certPath)) fs.unlinkSync(certPath);
|
||||
|
||||
// Get IP from state if available
|
||||
const ip = state.domainToIPMap?.get(domain);
|
||||
const altNames = [{ type: 2, value: domain }];
|
||||
|
||||
// Regenerate certificate
|
||||
getOrCreateDomainCert(domain, ip, altNames);
|
||||
logInfo("CA", `Certificate for ${domain} renewed successfully`);
|
||||
} catch (err) {
|
||||
logError("CA", `Error renewing certificate for ${domain}: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if certificate is revoked
|
||||
function isCertificateRevoked(domain) {
|
||||
return certificateRevocationList.has(domain);
|
||||
}
|
||||
|
||||
// Revoke a certificate
|
||||
function revokeCertificate(domain) {
|
||||
certificateRevocationList.add(domain);
|
||||
logInfo("CA", `Certificate for ${domain} has been revoked`);
|
||||
|
||||
// Optionally persist CRL to disk
|
||||
const crlPath = path.join(certsDir, "crl.json");
|
||||
try {
|
||||
const crl = Array.from(certificateRevocationList);
|
||||
fs.writeFileSync(crlPath, JSON.stringify(crl, null, 2));
|
||||
} catch (err) {
|
||||
logError("CA", `Error saving CRL: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Load CRL from disk
|
||||
function loadCRL() {
|
||||
const crlPath = path.join(certsDir, "crl.json");
|
||||
if (fs.existsSync(crlPath)) {
|
||||
try {
|
||||
const crl = JSON.parse(fs.readFileSync(crlPath, "utf8"));
|
||||
crl.forEach(domain => certificateRevocationList.add(domain));
|
||||
logInfo("CA", `Loaded ${crl.length} revoked certificates from CRL`);
|
||||
} catch (err) {
|
||||
logError("CA", `Error loading CRL: ${err.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Function to get or create domain certificate
|
||||
function getOrCreateDomainCert(
|
||||
domain,
|
||||
ipInput,
|
||||
altNames = [{ type: 2, value: domain }]
|
||||
) {
|
||||
const domainDir = path.join(certsDir, domain);
|
||||
if (!fs.existsSync(domainDir)) fs.mkdirSync(domainDir, { recursive: true });
|
||||
const keyPath = path.join(domainDir, "key.pem");
|
||||
const certPath = path.join(domainDir, "cert.pem");
|
||||
|
||||
// Check if certificate is revoked
|
||||
if (isCertificateRevoked(domain)) {
|
||||
logWarn("CA", `Certificate for ${domain} is revoked, regenerating...`);
|
||||
if (fs.existsSync(keyPath)) fs.unlinkSync(keyPath);
|
||||
if (fs.existsSync(certPath)) fs.unlinkSync(certPath);
|
||||
}
|
||||
|
||||
if (fs.existsSync(keyPath) && fs.existsSync(certPath)) {
|
||||
// Check expiration
|
||||
try {
|
||||
const certPem = fs.readFileSync(certPath, "utf8");
|
||||
const certMatch = certPem.match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/);
|
||||
if (certMatch) {
|
||||
const cert = forge.pki.certificateFromPem(certMatch[0]);
|
||||
if (cert.validity.notAfter > new Date()) {
|
||||
return {
|
||||
key: fs.readFileSync(keyPath, "utf8"),
|
||||
cert: fs.readFileSync(certPath, "utf8"),
|
||||
};
|
||||
} else {
|
||||
logWarn("CA", `Certificate for ${domain} has expired, regenerating...`);
|
||||
if (fs.existsSync(keyPath)) fs.unlinkSync(keyPath);
|
||||
if (fs.existsSync(certPath)) fs.unlinkSync(certPath);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
logError("CA", `Error checking certificate expiration for ${domain}: ${err.message}`);
|
||||
}
|
||||
}
|
||||
const caKeyPem = fs.readFileSync(caKeyPath, "utf8");
|
||||
const caCertPem = fs.readFileSync(caCertPath, "utf8");
|
||||
const caKey = forge.pki.privateKeyFromPem(caKeyPem);
|
||||
const caCert = forge.pki.certificateFromPem(caCertPem);
|
||||
const keys = forge.pki.rsa.generateKeyPair(2048);
|
||||
const cert = forge.pki.createCertificate();
|
||||
cert.publicKey = keys.publicKey;
|
||||
cert.serialNumber = "" + Date.now();
|
||||
cert.validity.notBefore = new Date();
|
||||
cert.validity.notAfter = new Date();
|
||||
cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1);
|
||||
const attrs = [{ name: "commonName", value: domain }];
|
||||
cert.setSubject(attrs);
|
||||
cert.setIssuer(caCert.subject.attributes);
|
||||
const akid = forge.pki.getPublicKeyFingerprint(caCert.publicKey, {
|
||||
md: forge.md.sha1.create(),
|
||||
});
|
||||
const skid = forge.pki.getPublicKeyFingerprint(cert.publicKey, {
|
||||
md: forge.md.sha1.create(),
|
||||
});
|
||||
// Only include domain in SAN, not IPs (as requested)
|
||||
const finalAltNames = [...altNames];
|
||||
// Removed IP addition to SAN - only domain certificate is provided
|
||||
cert.setExtensions([
|
||||
{ name: "basicConstraints", cA: false },
|
||||
{ name: "keyUsage", digitalSignature: true, keyEncipherment: true },
|
||||
{ name: "extKeyUsage", serverAuth: true },
|
||||
{ name: "subjectAltName", altNames: finalAltNames },
|
||||
{ name: "authorityKeyIdentifier", keyIdentifier: akid.getBytes() },
|
||||
{ name: "subjectKeyIdentifier" },
|
||||
]);
|
||||
try {
|
||||
cert.sign(caKey, forge.md.sha256.create(), {
|
||||
padding: forge.pki.rsa.PKCS1_v1_5,
|
||||
});
|
||||
} catch (err) {
|
||||
logError("CERT", `Error signing certificate for ${domain}: ${err.message}`);
|
||||
throw err;
|
||||
}
|
||||
const pemKey = forge.pki.privateKeyToPem(keys.privateKey);
|
||||
const pemCert = forge.pki.certificateToPem(cert);
|
||||
const pemChain = pemCert + caCertPem;
|
||||
fs.writeFileSync(keyPath, pemKey);
|
||||
fs.writeFileSync(certPath, pemChain);
|
||||
return { key: pemKey, cert: pemChain };
|
||||
}
|
||||
// Async function to fetch missing intermediate certificates using AIA
|
||||
async function getCaChain(domain, ip) {
|
||||
if (state.caForDomain.has(domain)) {
|
||||
return state.caForDomain.get(domain);
|
||||
}
|
||||
if (caFetchPromises.has(domain)) {
|
||||
return await caFetchPromises.get(domain);
|
||||
}
|
||||
const fetchPromise = (async () => {
|
||||
let socket;
|
||||
try {
|
||||
socket = await new Promise((resolve, reject) => {
|
||||
const s = tls.connect(
|
||||
{
|
||||
host: ip,
|
||||
port: 443,
|
||||
servername: domain,
|
||||
rejectUnauthorized: false,
|
||||
},
|
||||
() => resolve(s)
|
||||
);
|
||||
s.on("error", reject);
|
||||
});
|
||||
let chain = [];
|
||||
let cert = socket.getPeerCertificate(true);
|
||||
let seenFingerprints = new Set();
|
||||
while (cert) {
|
||||
if (seenFingerprints.has(cert.fingerprint)) break;
|
||||
seenFingerprints.add(cert.fingerprint);
|
||||
chain.push(cert);
|
||||
cert = cert.issuerCertificate;
|
||||
}
|
||||
socket.end();
|
||||
let fullChain = [...chain];
|
||||
let current = chain[chain.length - 1];
|
||||
seenFingerprints = new Set(chain.map((c) => c.fingerprint));
|
||||
while (!selfSigned(current)) {
|
||||
let pem = forge.pki.certificateToPem(
|
||||
forge.pki.certificateFromAsn1(
|
||||
forge.asn1.fromDer(current.raw.toString("binary"))
|
||||
)
|
||||
);
|
||||
let fCert = forge.pki.certificateFromPem(pem);
|
||||
let aia = fCert.getExtension({ name: "authorityInfoAccess" });
|
||||
if (!aia || !aia.altNames) {
|
||||
logWarn(
|
||||
"CERT",
|
||||
`No AIA extension for ${domain}, falling back to no CA chain`
|
||||
);
|
||||
return null;
|
||||
}
|
||||
let caIssuers = aia.altNames.find((alt) => alt.type === 6);
|
||||
if (!caIssuers) {
|
||||
logWarn(
|
||||
"CERT",
|
||||
`No caIssuers in AIA for ${domain}, falling back to no CA chain`
|
||||
);
|
||||
return null;
|
||||
}
|
||||
let uri = caIssuers.value;
|
||||
let buf = await fetchUri(uri);
|
||||
let asn1 = forge.asn1.fromDer(buf.toString("binary"));
|
||||
let issuerCert = forge.pki.certificateFromAsn1(asn1);
|
||||
let issuerPem = forge.pki.certificateToPem(issuerCert);
|
||||
let issuerObj = {
|
||||
raw: buf,
|
||||
fingerprint: issuerCert.generateFingerprint().toLowerCase(),
|
||||
issuerCertificate: null,
|
||||
};
|
||||
fullChain.push(issuerObj);
|
||||
current = fullChain[fullChain.length - 1];
|
||||
if (seenFingerprints.has(current.fingerprint)) break;
|
||||
seenFingerprints.add(current.fingerprint);
|
||||
}
|
||||
let caPems = fullChain
|
||||
.slice(1)
|
||||
.map((c) =>
|
||||
forge.pki.certificateToPem(
|
||||
forge.pki.certificateFromAsn1(
|
||||
forge.asn1.fromDer(c.raw.toString("binary"))
|
||||
)
|
||||
)
|
||||
);
|
||||
return caPems;
|
||||
} catch (err) {
|
||||
logError(
|
||||
"CERT",
|
||||
`Failed to fetch CA chain for ${domain}: ${err.message}`
|
||||
);
|
||||
if (socket) socket.end();
|
||||
return null;
|
||||
}
|
||||
})();
|
||||
caFetchPromises.set(domain, fetchPromise);
|
||||
const result = await fetchPromise;
|
||||
caFetchPromises.delete(domain);
|
||||
state.caForDomain.set(domain, result);
|
||||
return result;
|
||||
}
|
||||
function selfSigned(cert) {
|
||||
let pem = forge.pki.certificateToPem(
|
||||
forge.pki.certificateFromAsn1(
|
||||
forge.asn1.fromDer(cert.raw.toString("binary"))
|
||||
)
|
||||
);
|
||||
let fCert = forge.pki.certificateFromPem(pem);
|
||||
return fCert.isIssuer(fCert);
|
||||
}
|
||||
async function fetchUri(uri) {
|
||||
const parsed = url.parse(uri);
|
||||
const protocol = parsed.protocol === "http:" ? http : https;
|
||||
return new Promise((resolve, reject) => {
|
||||
const req = protocol.get(uri, (res) => {
|
||||
if (res.statusCode !== 200) {
|
||||
reject(new Error(`Bad status code: ${res.statusCode}`));
|
||||
return;
|
||||
}
|
||||
const data = [];
|
||||
res.on("data", (chunk) => data.push(chunk));
|
||||
res.on("end", () => resolve(Buffer.concat(data)));
|
||||
});
|
||||
req.on("error", reject);
|
||||
});
|
||||
}
|
||||
// Load CRL on module initialization
|
||||
loadCRL();
|
||||
|
||||
// Start expiration monitoring
|
||||
startCertificateExpirationMonitoring();
|
||||
|
||||
module.exports = {
|
||||
installRootCA,
|
||||
regenerateRootCA,
|
||||
getOrCreateDomainCert,
|
||||
getCaChain,
|
||||
revokeCertificate,
|
||||
isCertificateRevoked,
|
||||
renewDomainCertificate,
|
||||
checkCertificateExpirations,
|
||||
};
|
||||
Reference in New Issue
Block a user