somewhat guest login
This commit is contained in:
172
zefie_wtvp_minisrv/tools/generate_msn_san_cert.js
Normal file
172
zefie_wtvp_minisrv/tools/generate_msn_san_cert.js
Normal file
@@ -0,0 +1,172 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const forge = require('node-forge');
|
||||
|
||||
const workspaceRoot = __dirname;
|
||||
const httpsDir = path.join(workspaceRoot, '..', 'includes', 'ServiceDeps', 'https');
|
||||
const msnDir = path.join(workspaceRoot, '..', 'includes', 'ServiceDeps', 'msntv2');
|
||||
const domainsFile = path.join(msnDir, 'msn_domains.txt');
|
||||
|
||||
const defaultCaCertPath = path.join(msnDir, 'msntv2.crt');
|
||||
const defaultCaKeyPath = path.join(msnDir, 'msntv2.key');
|
||||
const defaultOutCertPath = path.join(msnDir, 'msn_domains.crt');
|
||||
const defaultOutKeyPath = path.join(msnDir, 'msn_domains.key');
|
||||
|
||||
function parseArgs(argv) {
|
||||
const out = {};
|
||||
for (let i = 2; i < argv.length; i++) {
|
||||
const part = argv[i];
|
||||
if (!part.startsWith('--')) continue;
|
||||
const key = part.slice(2);
|
||||
const next = argv[i + 1];
|
||||
if (!next || next.startsWith('--')) {
|
||||
out[key] = true;
|
||||
continue;
|
||||
}
|
||||
out[key] = next;
|
||||
i += 1;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function extractDomainsFromRedirectMap(text) {
|
||||
const found = [];
|
||||
const seen = new Set();
|
||||
const re = /"([A-Za-z0-9.-]+\.)"\s*:\s*self\.redirect_ip/g;
|
||||
let match;
|
||||
while ((match = re.exec(text))) {
|
||||
const clean = match[1].replace(/\.$/, '').toLowerCase();
|
||||
if (!seen.has(clean)) {
|
||||
seen.add(clean);
|
||||
found.push(clean);
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
function loadDomains(args) {
|
||||
if (args['from-map-file']) {
|
||||
const mapText = fs.readFileSync(path.resolve(workspaceRoot, args['from-map-file']), 'utf8');
|
||||
const domains = extractDomainsFromRedirectMap(mapText);
|
||||
if (!domains.length) {
|
||||
throw new Error('No domains were extracted from --from-map-file.');
|
||||
}
|
||||
return domains;
|
||||
}
|
||||
|
||||
if (!fs.existsSync(domainsFile)) {
|
||||
throw new Error('Domain file not found: ' + domainsFile);
|
||||
}
|
||||
|
||||
const domains = fs.readFileSync(domainsFile, 'utf8')
|
||||
.split(/\r?\n/)
|
||||
.map((s) => s.trim().toLowerCase())
|
||||
.filter((s) => s && !s.startsWith('#'));
|
||||
|
||||
return Array.from(new Set(domains));
|
||||
}
|
||||
|
||||
function loadPemOrThrow(filePath, label) {
|
||||
if (!fs.existsSync(filePath)) {
|
||||
throw new Error(label + ' file missing: ' + filePath);
|
||||
}
|
||||
return fs.readFileSync(filePath, 'utf8');
|
||||
}
|
||||
|
||||
function ensureDirFor(filePath) {
|
||||
const dir = path.dirname(filePath);
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
function signAlgorithm(sigName) {
|
||||
const lower = String(sigName || 'sha1').toLowerCase();
|
||||
if (lower === 'sha256') return forge.md.sha256.create();
|
||||
if (lower === 'sha384') return forge.md.sha384.create();
|
||||
if (lower === 'sha512') return forge.md.sha512.create();
|
||||
return forge.md.sha1.create();
|
||||
}
|
||||
|
||||
function generateCert({ domains, caCertPem, caKeyPem, outCertPath, outKeyPath, years, sig }) {
|
||||
const caCert = forge.pki.certificateFromPem(caCertPem);
|
||||
const caKey = forge.pki.privateKeyFromPem(caKeyPem);
|
||||
|
||||
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));
|
||||
|
||||
const now = new Date();
|
||||
cert.validity.notBefore = new Date(now.getTime() - 24 * 60 * 60 * 1000);
|
||||
cert.validity.notAfter = new Date(now.getTime() + years * 365 * 24 * 60 * 60 * 1000);
|
||||
|
||||
const cn = domains[0] || 'headwaiter.trusted.msntv.msn.com';
|
||||
cert.setSubject([
|
||||
{ name: 'commonName', value: cn },
|
||||
{ name: 'organizationName', value: 'Zefie Networks' },
|
||||
{ name: 'countryName', value: 'US' }
|
||||
]);
|
||||
cert.setIssuer(caCert.subject.attributes);
|
||||
|
||||
cert.setExtensions([
|
||||
{ name: 'basicConstraints', cA: false },
|
||||
{ name: 'keyUsage', digitalSignature: true, keyEncipherment: true },
|
||||
{ name: 'extKeyUsage', serverAuth: true },
|
||||
{
|
||||
name: 'subjectAltName',
|
||||
altNames: domains.map((d) => ({ type: 2, value: d }))
|
||||
}
|
||||
]);
|
||||
|
||||
cert.sign(caKey, signAlgorithm(sig));
|
||||
|
||||
ensureDirFor(outCertPath);
|
||||
ensureDirFor(outKeyPath);
|
||||
fs.writeFileSync(outKeyPath, forge.pki.privateKeyToPem(keys.privateKey), 'utf8');
|
||||
fs.writeFileSync(outCertPath, forge.pki.certificateToPem(cert), 'utf8');
|
||||
|
||||
return { cn, count: domains.length };
|
||||
}
|
||||
|
||||
function main() {
|
||||
const args = parseArgs(process.argv);
|
||||
|
||||
const caCertPath = path.resolve(workspaceRoot, args['ca-cert'] || defaultCaCertPath);
|
||||
const caKeyPath = path.resolve(workspaceRoot, args['ca-key'] || defaultCaKeyPath);
|
||||
const outCertPath = path.resolve(workspaceRoot, args['out-cert'] || defaultOutCertPath);
|
||||
const outKeyPath = path.resolve(workspaceRoot, args['out-key'] || defaultOutKeyPath);
|
||||
const years = Number(args.years || 15);
|
||||
const sig = String(args.sig || 'sha1');
|
||||
|
||||
const domains = loadDomains(args);
|
||||
const caCertPem = loadPemOrThrow(caCertPath, 'CA cert');
|
||||
const caKeyPem = loadPemOrThrow(caKeyPath, 'CA key');
|
||||
|
||||
const result = generateCert({
|
||||
domains,
|
||||
caCertPem,
|
||||
caKeyPem,
|
||||
outCertPath,
|
||||
outKeyPath,
|
||||
years,
|
||||
sig
|
||||
});
|
||||
|
||||
console.log('[msn-san-cert] generated cert:', outCertPath);
|
||||
console.log('[msn-san-cert] generated key :', outKeyPath);
|
||||
console.log('[msn-san-cert] domains :', result.count);
|
||||
console.log('[msn-san-cert] common name :', result.cn);
|
||||
console.log('[msn-san-cert] signature alg :', sig);
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
try {
|
||||
main();
|
||||
} catch (err) {
|
||||
console.error('[msn-san-cert] error:', err.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user