MeshCentral/certoperations.js

547 lines
31 KiB
JavaScript
Raw Normal View History

2017-08-28 12:27:45 -04:00
/**
* @description Certificate generator
* @author Joko Sastriawan / Ylian Saint-Hilaire
2019-01-03 19:22:15 -05:00
* @copyright Intel Corporation 2018-2019
2018-01-04 15:15:21 -05:00
* @license Apache-2.0
2017-08-28 12:27:45 -04:00
* @version v0.0.1
*/
2018-01-04 15:15:21 -05:00
2018-08-29 20:40:30 -04:00
/*xjslint node: true */
/*xjslint plusplus: true */
/*xjslint maxlen: 256 */
/*jshint node: true */
/*jshint strict: false */
/*jshint esversion: 6 */
"use strict";
2018-08-27 15:24:15 -04:00
module.exports.CertificateOperations = function (parent) {
2017-08-28 12:27:45 -04:00
var obj = {};
obj.parent = parent;
2018-08-29 20:40:30 -04:00
obj.fs = require("fs");
obj.forge = require("node-forge");
obj.crypto = require("crypto");
obj.tls = require('tls');
2017-08-28 12:27:45 -04:00
obj.pki = obj.forge.pki;
2018-08-29 20:40:30 -04:00
obj.dirExists = function (filePath) { try { return obj.fs.statSync(filePath).isDirectory(); } catch (err) { return false; } };
obj.getFilesizeInBytes = function (filename) { try { return obj.fs.statSync(filename).size; } catch (err) { return -1; } };
2017-08-28 12:27:45 -04:00
// Return the certificate of the remote HTTPS server
obj.loadCertificate = function (url, tag, func) {
const u = require('url').parse(url);
if (u.protocol == 'https:') {
// Read the certificate from HTTPS
const tlssocket = obj.tls.connect((u.port ? u.port : 443), u.hostname, { servername: u.hostname, rejectUnauthorized: false }, function () { this.xxcert = this.getPeerCertificate(); this.end(); });
tlssocket.xxurl = url;
tlssocket.xxfunc = func;
tlssocket.xxtag = tag;
tlssocket.on('end', function () { this.xxfunc(this.xxurl, this.xxcert.raw.toString('binary'), this.xxtag); });
tlssocket.on('error', function () { this.xxfunc(this.xxurl, null, this.xxtag); });
} else if (u.protocol == 'file:') {
// Read the certificate from a file
obj.fs.readFile(url.substring(7), 'utf8', function (err, data) {
if (err) { func(url, null, tag); return; }
var x1 = data.indexOf('-----BEGIN CERTIFICATE-----'), x2 = data.indexOf('-----END CERTIFICATE-----');
if ((x1 >= 0) && (x2 > x1)) {
func(url, Buffer.from(data.substring(x1 + 27, x2), 'base64').toString('binary'), tag);
} else {
func(url, data, tag);
}
});
} else { func(url, null, tag); }
};
// Check if a configuration file exists
obj.fileExists = function (filename) {
if ((parent.configurationFiles != null) && (parent.configurationFiles[filename] != null)) { return true; }
var filePath = parent.getConfigFilePath(filename);
try { return obj.fs.statSync(filePath).isFile(); } catch (err) { return false; }
};
// Load a configuration file
obj.fileLoad = function (filename, encoding) {
if ((parent.configurationFiles != null) && (parent.configurationFiles[filename] != null)) {
return fixEndOfLines(parent.configurationFiles[filename].toString());
} else {
return fixEndOfLines(obj.fs.readFileSync(parent.getConfigFilePath(filename), encoding));
}
}
2018-11-01 18:01:21 -04:00
// Return the SHA384 hash of the certificate public key
obj.getPublicKeyHash = function (cert) {
var publickey = obj.pki.certificateFromPem(cert).publicKey;
2018-08-29 20:40:30 -04:00
return obj.pki.getPublicKeyFingerprint(publickey, { encoding: "hex", md: obj.forge.md.sha384.create() });
};
// Return the SHA384 hash of the certificate, return hex
obj.getCertHash = function (cert) {
try {
var md = obj.forge.md.sha384.create();
md.update(obj.forge.asn1.toDer(obj.pki.certificateToAsn1(obj.pki.certificateFromPem(cert))).getBytes());
return md.digest().toHex();
} catch (ex) {
// If this is not an RSA certificate, hash the raw PKCS7 out of the PEM file
var x1 = cert.indexOf('-----BEGIN CERTIFICATE-----'), x2 = cert.indexOf('-----END CERTIFICATE-----');
if ((x1 >= 0) && (x2 > x1)) {
return obj.crypto.createHash('sha384').update(Buffer.from(cert.substring(x1 + 27, x2), 'base64')).digest('hex');
} else { console.log('ERROR: Unable to decode certificate.'); return null; }
}
};
// Return the SHA384 hash of the certificate public key
obj.getPublicKeyHashBinary = function (cert) {
var publickey = obj.pki.certificateFromPem(cert).publicKey;
return obj.pki.getPublicKeyFingerprint(publickey, { encoding: "binary", md: obj.forge.md.sha384.create() });
};
// Return the SHA384 hash of the certificate, return binary
obj.getCertHashBinary = function (cert) {
try {
// If this is a RSA certificate, we can use Forge to hash the ASN1
var md = obj.forge.md.sha384.create();
md.update(obj.forge.asn1.toDer(obj.pki.certificateToAsn1(obj.pki.certificateFromPem(cert))).getBytes());
return md.digest().getBytes();
} catch (ex) {
// If this is not an RSA certificate, hash the raw PKCS7 out of the PEM file
var x1 = cert.indexOf('-----BEGIN CERTIFICATE-----'), x2 = cert.indexOf('-----END CERTIFICATE-----');
if ((x1 >= 0) && (x2 > x1)) {
return obj.crypto.createHash('sha384').update(Buffer.from(cert.substring(x1 + 27, x2), 'base64')).digest('binary');
} else { console.log('ERROR: Unable to decode certificate.'); return null; }
}
};
2017-08-28 12:27:45 -04:00
// Create a self-signed certificate
2017-10-18 19:28:05 -04:00
obj.GenerateRootCertificate = function (addThumbPrintToName, commonName, country, organization, strong) {
// TODO: Use Async key generation to use web workers and go a lot faster.
// rsa.generateKeyPair({ bits: 3072, e: 0x10001, workers: -1 }, function (err, keypair) { /*keypair.privateKey, keypair.publicKey*/ });
var keys = obj.pki.rsa.generateKeyPair({ bits: (strong == true) ? 3072 : 2048, e: 0x10001 });
2017-08-28 12:27:45 -04:00
var cert = obj.pki.createCertificate();
cert.publicKey = keys.publicKey;
2018-08-29 20:40:30 -04:00
cert.serialNumber = String(Math.floor((Math.random() * 100000) + 1));
2017-08-28 12:27:45 -04:00
cert.validity.notBefore = new Date();
2018-08-29 20:40:30 -04:00
cert.validity.notBefore.setFullYear(cert.validity.notBefore.getFullYear() - 1); // Create a certificate that is valid one year before, to make sure out-of-sync clocks don"t reject this cert.
2017-08-28 12:27:45 -04:00
cert.validity.notAfter = new Date();
cert.validity.notAfter.setFullYear(cert.validity.notAfter.getFullYear() + 30);
2018-08-29 20:40:30 -04:00
if (addThumbPrintToName === true) { commonName += "-" + obj.pki.getPublicKeyFingerprint(cert.publicKey, { encoding: "hex" }).substring(0, 6); }
2018-09-01 16:00:53 -04:00
if (country == null) { country = "unknown"; }
if (organization == null) { organization = "unknown"; }
2018-08-29 20:40:30 -04:00
var attrs = [{ name: "commonName", value: commonName }, { name: "organizationName", value: organization }, { name: "countryName", value: country }];
2017-08-28 12:27:45 -04:00
cert.setSubject(attrs);
cert.setIssuer(attrs);
// Create a root certificate
//cert.setExtensions([{ name: "basicConstraints", cA: true }, { name: "nsCertType", sslCA: true, emailCA: true, objCA: true }, { name: "subjectKeyIdentifier" }]);
cert.setExtensions([{ name: "basicConstraints", cA: true }, { name: "subjectKeyIdentifier" }]);
cert.sign(keys.privateKey, obj.forge.md.sha384.create());
2017-08-28 12:27:45 -04:00
return { cert: cert, key: keys.privateKey };
2018-08-29 20:40:30 -04:00
};
2017-08-28 12:27:45 -04:00
// Issue a certificate from a root
obj.IssueWebServerCertificate = function (rootcert, addThumbPrintToName, commonName, country, organization, extKeyUsage, strong) {
var keys = obj.pki.rsa.generateKeyPair({ bits: (strong == true) ? 3072 : 2048, e: 0x10001 });
2017-08-28 12:27:45 -04:00
var cert = obj.pki.createCertificate();
cert.publicKey = keys.publicKey;
2018-08-29 20:40:30 -04:00
cert.serialNumber = String(Math.floor((Math.random() * 100000) + 1));
2017-08-28 12:27:45 -04:00
cert.validity.notBefore = new Date();
2018-08-29 20:40:30 -04:00
cert.validity.notBefore.setFullYear(cert.validity.notAfter.getFullYear() - 1); // Create a certificate that is valid one year before, to make sure out-of-sync clocks don"t reject this cert.
2017-08-28 12:27:45 -04:00
cert.validity.notAfter = new Date();
cert.validity.notAfter.setFullYear(cert.validity.notAfter.getFullYear() + 30);
2018-08-29 20:40:30 -04:00
if (addThumbPrintToName === true) { commonName += "-" + obj.pki.getPublicKeyFingerprint(cert.publicKey, { encoding: "hex" }).substring(0, 6); }
var attrs = [{ name: "commonName", value: commonName }];
2018-09-01 16:00:53 -04:00
if (country != null) { attrs.push({ name: "countryName", value: country }); }
if (organization != null) { attrs.push({ name: "organizationName", value: organization }); }
2017-08-28 12:27:45 -04:00
cert.setSubject(attrs);
cert.setIssuer(rootcert.cert.subject.attributes);
2018-08-29 20:40:30 -04:00
if (extKeyUsage == null) { extKeyUsage = { name: "extKeyUsage", serverAuth: true }; } else { extKeyUsage.name = "extKeyUsage"; }
//var extensions = [{ name: "basicConstraints", cA: false }, { name: "keyUsage", keyCertSign: true, digitalSignature: true, nonRepudiation: true, keyEncipherment: true, dataEncipherment: true }, extKeyUsage, { name: "nsCertType", client: false, server: true, email: false, objsign: false, sslCA: false, emailCA: false, objCA: false }, { name: "subjectKeyIdentifier" }];
var extensions = [{ name: "basicConstraints", cA: false }, { name: "keyUsage", keyCertSign: false, digitalSignature: true, nonRepudiation: false, keyEncipherment: true, dataEncipherment: (extKeyUsage.serverAuth !== true) }, extKeyUsage, { name: "subjectKeyIdentifier" }];
if (extKeyUsage.serverAuth === true) { extensions.push({ name: "subjectAltName", altNames: [{ type: 6, value: "http://" + commonName + "/" }, { type: 6, value: "http://localhost/" }] }); }
cert.setExtensions(extensions);
cert.sign(rootcert.key, obj.forge.md.sha384.create());
2018-08-29 20:40:30 -04:00
2017-08-28 12:27:45 -04:00
return { cert: cert, key: keys.privateKey };
2018-08-29 20:40:30 -04:00
};
2017-08-28 12:27:45 -04:00
// Make sure a string with Mac style CR endo of line is changed to Linux LF style.
function fixEndOfLines(str) {
if (typeof (str) != 'string') return str; // If this is not a string, do nothing.
var i = str.indexOf('-----'); // Remove everything before "-----".
if (i > 0) { str = str.substring(i); } // this solves problems with editors that save text file type indicators ahead of the text.
if ((typeof(str) != 'string') || (str.indexOf('\n') > 0)) return str; // If there is a \n in the file, keep the file as-is.
return str.split('\r').join('\n'); // If there is no \n, replace all \r with \n.
}
2017-08-28 12:27:45 -04:00
// Returns the web server TLS certificate and private key, if not present, create demonstration ones.
obj.GetMeshServerCertificate = function (args, config, func) {
2018-08-29 20:40:30 -04:00
var i = 0;
var certargs = args.cert;
2018-03-06 20:50:44 -05:00
var mpscertargs = args.mpscert;
2017-10-18 19:28:05 -04:00
var strongCertificate = (args.fastcert ? false : true);
2018-12-20 15:12:24 -05:00
var rcountmax = 4;
2018-08-29 20:40:30 -04:00
var caindex = 1;
var caok = false;
var calist = [];
var dnsname = null;
2017-08-28 12:27:45 -04:00
// commonName, country, organization
2018-08-29 20:40:30 -04:00
2017-08-28 12:27:45 -04:00
// If the certificates directory does not exist, create it.
2018-07-13 22:18:43 -04:00
if (!obj.dirExists(parent.datapath)) { obj.fs.mkdirSync(parent.datapath); }
2018-08-29 20:40:30 -04:00
var r = {};
var rcount = 0;
2017-08-28 12:27:45 -04:00
// If the root certificate already exist, load it
if (obj.fileExists("root-cert-public.crt") && obj.fileExists("root-cert-private.key")) {
var rootCertificate = obj.fileLoad("root-cert-public.crt", "utf8");
var rootPrivateKey = obj.fileLoad("root-cert-private.key", "utf8");
2017-08-28 12:27:45 -04:00
r.root = { cert: rootCertificate, key: rootPrivateKey };
rcount++;
}
2018-11-01 18:01:21 -04:00
if (args.tlsoffload) {
// If the web certificate already exist, load it. Load just the certificate since we are in TLS offload situation
if (obj.fileExists("webserver-cert-public.crt")) {
r.web = { cert: obj.fileLoad("webserver-cert-public.crt", "utf8") };
rcount++;
}
} else {
// If the web certificate already exist, load it. Load both certificate and private key
if (obj.fileExists("webserver-cert-public.crt") && obj.fileExists("webserver-cert-private.key")) {
r.web = { cert: obj.fileLoad("webserver-cert-public.crt", "utf8"), key: obj.fileLoad("webserver-cert-private.key", "utf8") };
rcount++;
}
2017-08-28 12:27:45 -04:00
}
2018-08-29 20:40:30 -04:00
// If the mps certificate already exist, load it
if (obj.fileExists("mpsserver-cert-public.crt") && obj.fileExists("mpsserver-cert-private.key")) {
r.mps = { cert: obj.fileLoad("mpsserver-cert-public.crt", "utf8"), key: obj.fileLoad("mpsserver-cert-private.key", "utf8") };
2017-08-28 12:27:45 -04:00
rcount++;
}
2018-08-29 20:40:30 -04:00
// If the agent certificate already exist, load it
if (obj.fileExists("agentserver-cert-public.crt") && obj.fileExists("agentserver-cert-private.key")) {
r.agent = { cert: obj.fileLoad("agentserver-cert-public.crt", "utf8"), key: obj.fileLoad("agentserver-cert-private.key", "utf8") };
2017-08-28 12:27:45 -04:00
rcount++;
}
// If the swarm server certificate exist, load it (This is an optional certificate)
if (obj.fileExists("swarmserver-cert-public.crt") && obj.fileExists("swarmserver-cert-private.key")) {
r.swarmserver = { cert: obj.fileLoad("swarmserver-cert-public.crt", "utf8"), key: obj.fileLoad("swarmserver-cert-private.key", "utf8") };
}
// If the swarm server root certificate exist, load it (This is an optional certificate)
if (obj.fileExists("swarmserverroot-cert-public.crt")) {
r.swarmserverroot = { cert: obj.fileLoad("swarmserverroot-cert-public.crt", "utf8") };
}
// If CA certificates are present, load them
2018-08-29 20:40:30 -04:00
do {
caok = false;
if (obj.fileExists("webserver-cert-chain" + caindex + ".crt")) {
calist.push(obj.fileLoad("webserver-cert-chain" + caindex + ".crt", "utf8"));
2018-08-29 20:40:30 -04:00
caok = true;
}
caindex++;
} while (caok === true);
if (r.web != null) { r.web.ca = calist; }
2018-01-02 19:52:49 -05:00
2017-08-28 12:27:45 -04:00
// Decode certificate arguments
2018-08-29 20:40:30 -04:00
var commonName = "un-configured";
var country = null;
var organization = null;
var forceWebCertGen = 0;
var forceMpsCertGen = 0;
2017-08-28 12:27:45 -04:00
if (certargs != undefined) {
2018-08-29 20:40:30 -04:00
var xargs = certargs.split(",");
if (xargs.length > 0) { commonName = xargs[0]; }
if (xargs.length > 1) { country = xargs[1]; }
if (xargs.length > 2) { organization = xargs[2]; }
2017-08-28 12:27:45 -04:00
}
2018-03-06 20:50:44 -05:00
// Decode MPS certificate arguments, this is for the Intel AMT CIRA server
2018-08-29 20:40:30 -04:00
var mpsCommonName = commonName;
var mpsCountry = country;
var mpsOrganization = organization;
if (mpscertargs !== undefined) {
var xxargs = mpscertargs.split(",");
if (xxargs.length > 0) { mpsCommonName = xxargs[0]; }
if (xxargs.length > 1) { mpsCountry = xxargs[1]; }
if (xxargs.length > 2) { mpsOrganization = xxargs[2]; }
2018-03-06 20:50:44 -05:00
}
2018-01-02 19:52:49 -05:00
// Look for domains that have DNS names and load their certificates
r.dns = {};
2018-08-31 19:43:09 -04:00
for (i in config.domains) {
2018-08-29 20:40:30 -04:00
if ((i != "") && (config.domains[i] != null) && (config.domains[i].dns != null)) {
dnsname = config.domains[i].dns;
2018-11-01 18:01:21 -04:00
if (args.tlsoffload) {
2018-01-02 19:52:49 -05:00
// If the web certificate already exist, load it. Load just the certificate since we are in TLS offload situation
if (obj.fileExists("webserver-" + i + "-cert-public.crt")) {
r.dns[i] = { cert: obj.fileLoad("webserver-" + i + "-cert-public.crt", "utf8") };
2018-01-02 19:52:49 -05:00
config.domains[i].certs = r.dns[i];
} else {
2018-08-29 20:40:30 -04:00
console.log("WARNING: File \"webserver-" + i + "-cert-public.crt\" missing, domain \"" + i + "\" will not work correctly.");
2018-01-02 19:52:49 -05:00
}
} else {
// If the web certificate already exist, load it. Load both certificate and private key
if (obj.fileExists("webserver-" + i + "-cert-public.crt") && obj.fileExists("webserver-" + i + "-cert-private.key")) {
r.dns[i] = { cert: obj.fileLoad("webserver-" + i + "-cert-public.crt", "utf8"), key: obj.fileLoad("webserver-" + i + "-cert-private.key", "utf8") };
2018-01-02 19:52:49 -05:00
config.domains[i].certs = r.dns[i];
// If CA certificates are present, load them
2018-08-29 20:40:30 -04:00
caindex = 1;
r.dns[i].ca = [];
2018-01-02 19:52:49 -05:00
do {
caok = false;
if (obj.fileExists("webserver-" + i + "-cert-chain" + caindex + ".crt")) {
r.dns[i].ca.push(obj.fileLoad("webserver-" + i + "-cert-chain" + caindex + ".crt", "utf8"));
2018-01-02 19:52:49 -05:00
caok = true;
}
caindex++;
2018-08-29 20:40:30 -04:00
} while (caok === true);
2018-01-02 19:52:49 -05:00
} else {
rcountmax++; // This certificate must be generated
}
}
}
}
2018-08-29 20:40:30 -04:00
if (rcount === rcountmax) {
// Fetch the Intel AMT MPS common name
2018-08-29 20:40:30 -04:00
r.AmtMpsName = obj.pki.certificateFromPem(r.mps.cert).subject.getField("CN").value;
2017-08-28 12:27:45 -04:00
// Fetch the name of the server
var webCertificate = obj.pki.certificateFromPem(r.web.cert);
2018-08-29 20:40:30 -04:00
r.WebIssuer = webCertificate.issuer.getField("CN").value;
r.CommonName = webCertificate.subject.getField("CN").value;
r.CommonNames = [r.CommonName.toLowerCase()];
var altNames = webCertificate.getExtension("subjectAltName");
if (altNames) { for (i = 0; i < altNames.altNames.length; i++) { r.CommonNames.push(altNames.altNames[i].value.toLowerCase()); } }
2017-08-28 12:27:45 -04:00
var rootCertificate = obj.pki.certificateFromPem(r.root.cert);
2018-08-29 20:40:30 -04:00
r.RootName = rootCertificate.subject.getField("CN").value;
2018-03-06 20:50:44 -05:00
2018-08-29 20:40:30 -04:00
if ((certargs == null) && (mpscertargs == null)) { if (func != undefined) { func(r); } return r; } // If no certificate arguments are given, keep the certificate
var xcountry, xcountryField = webCertificate.subject.getField("C");
2017-08-28 12:27:45 -04:00
if (xcountryField != null) { xcountry = xcountryField.value; }
2018-08-29 20:40:30 -04:00
var xorganization, xorganizationField = webCertificate.subject.getField("O");
2017-08-28 12:27:45 -04:00
if (xorganizationField != null) { xorganization = xorganizationField.value; }
2018-03-06 20:50:44 -05:00
if (certargs == null) { commonName = r.CommonName; country = xcountry; organization = xorganization; }
// Check if we have correct certificates
if ((r.CommonNames.indexOf(commonName.toLowerCase()) >= 0) && (r.AmtMpsName == mpsCommonName)) {
2018-03-06 20:50:44 -05:00
// Certificate matches what we want, keep it.
2018-08-29 20:40:30 -04:00
if (func !== undefined) { func(r); }
return r;
2018-03-06 20:50:44 -05:00
} else {
// Check what certificates we really need to re-generate.
if ((r.CommonNames.indexOf(commonName.toLowerCase()) < 0)) { forceWebCertGen = 1; }
2018-03-06 20:50:44 -05:00
if (r.AmtMpsName != mpsCommonName) { forceMpsCertGen = 1; }
}
2017-08-28 12:27:45 -04:00
}
if (parent.configurationFiles != null) { console.log("Error: Database missing some certificates."); process.exit(0); return null; }
2018-08-29 20:40:30 -04:00
console.log("Generating certificates, may take a few minutes...");
parent.updateServerState("state", "generatingcertificates");
2017-10-18 19:28:05 -04:00
// If a certificate is missing, but web certificate is present and --cert is not used, set the names to be the same as the web certificate
if ((certargs == null) && (r.web != null)) {
var webCertificate = obj.pki.certificateFromPem(r.web.cert);
2018-08-29 20:40:30 -04:00
commonName = webCertificate.subject.getField("CN").value;
var xcountryField = webCertificate.subject.getField("C");
2017-10-18 19:28:05 -04:00
if (xcountryField != null) { country = xcountryField.value; }
2018-08-29 20:40:30 -04:00
var xorganizationField = webCertificate.subject.getField("O");
2017-10-18 19:28:05 -04:00
if (xorganizationField != null) { organization = xorganizationField.value; }
}
2017-08-28 12:27:45 -04:00
var rootCertAndKey, rootCertificate, rootPrivateKey, rootName;
2018-09-01 16:00:53 -04:00
if (r.root == null) {
2017-08-28 12:27:45 -04:00
// If the root certificate does not exist, create one
2018-08-29 20:40:30 -04:00
console.log("Generating root certificate...");
rootCertAndKey = obj.GenerateRootCertificate(true, "MeshCentralRoot", null, null, strongCertificate);
2017-08-28 12:27:45 -04:00
rootCertificate = obj.pki.certificateToPem(rootCertAndKey.cert);
rootPrivateKey = obj.pki.privateKeyToPem(rootCertAndKey.key);
2018-08-29 20:40:30 -04:00
obj.fs.writeFileSync(parent.getConfigFilePath("root-cert-public.crt"), rootCertificate);
obj.fs.writeFileSync(parent.getConfigFilePath("root-cert-private.key"), rootPrivateKey);
2017-08-28 12:27:45 -04:00
} else {
// Keep the root certificate we have
rootCertAndKey = { cert: obj.pki.certificateFromPem(r.root.cert), key: obj.pki.privateKeyFromPem(r.root.key) };
2018-08-29 20:40:30 -04:00
rootCertificate = r.root.cert;
rootPrivateKey = r.root.key;
2017-08-28 12:27:45 -04:00
}
2018-08-29 20:40:30 -04:00
var rootName = rootCertAndKey.cert.subject.getField("CN").value;
2017-08-28 12:27:45 -04:00
// If the web certificate does not exist, create one
var webCertAndKey, webCertificate, webPrivateKey;
if ((r.web == null) || (forceWebCertGen == 1)) {
2018-08-29 20:40:30 -04:00
console.log("Generating HTTPS certificate...");
2017-10-18 19:28:05 -04:00
webCertAndKey = obj.IssueWebServerCertificate(rootCertAndKey, false, commonName, country, organization, null, strongCertificate);
webCertificate = obj.pki.certificateToPem(webCertAndKey.cert);
webPrivateKey = obj.pki.privateKeyToPem(webCertAndKey.key);
2018-08-29 20:40:30 -04:00
obj.fs.writeFileSync(parent.getConfigFilePath("webserver-cert-public.crt"), webCertificate);
obj.fs.writeFileSync(parent.getConfigFilePath("webserver-cert-private.key"), webPrivateKey);
} else {
// Keep the console certificate we have
webCertAndKey = { cert: obj.pki.certificateFromPem(r.web.cert), key: obj.pki.privateKeyFromPem(r.web.key) };
2018-08-29 20:40:30 -04:00
webCertificate = r.web.cert;
webPrivateKey = r.web.key;
}
2018-08-29 20:40:30 -04:00
var webIssuer = webCertAndKey.cert.issuer.getField("CN").value;
2017-10-18 19:28:05 -04:00
// If the mesh agent server certificate does not exist, create one
var agentCertAndKey, agentCertificate, agentPrivateKey;
if (r.agent == null) {
2018-08-29 20:40:30 -04:00
console.log("Generating MeshAgent certificate...");
agentCertAndKey = obj.IssueWebServerCertificate(rootCertAndKey, true, "MeshCentralAgentServer", country, organization, { }, strongCertificate);
2017-10-18 19:28:05 -04:00
agentCertificate = obj.pki.certificateToPem(agentCertAndKey.cert);
agentPrivateKey = obj.pki.privateKeyToPem(agentCertAndKey.key);
2018-08-29 20:40:30 -04:00
obj.fs.writeFileSync(parent.getConfigFilePath("agentserver-cert-public.crt"), agentCertificate);
obj.fs.writeFileSync(parent.getConfigFilePath("agentserver-cert-private.key"), agentPrivateKey);
2017-10-18 19:28:05 -04:00
} else {
// Keep the mesh agent server certificate we have
agentCertAndKey = { cert: obj.pki.certificateFromPem(r.agent.cert), key: obj.pki.privateKeyFromPem(r.agent.key) };
2018-08-29 20:40:30 -04:00
agentCertificate = r.agent.cert;
agentPrivateKey = r.agent.key;
2017-10-18 19:28:05 -04:00
}
2017-08-28 12:27:45 -04:00
// If the Intel AMT MPS certificate does not exist, create one
var mpsCertAndKey, mpsCertificate, mpsPrivateKey;
2018-03-06 20:50:44 -05:00
if ((r.mps == null) || (forceMpsCertGen == 1)) {
2018-08-29 20:40:30 -04:00
console.log("Generating Intel AMT MPS certificate...");
2018-03-06 20:50:44 -05:00
mpsCertAndKey = obj.IssueWebServerCertificate(rootCertAndKey, false, mpsCommonName, mpsCountry, mpsOrganization, null, false);
mpsCertificate = obj.pki.certificateToPem(mpsCertAndKey.cert);
mpsPrivateKey = obj.pki.privateKeyToPem(mpsCertAndKey.key);
2018-08-29 20:40:30 -04:00
obj.fs.writeFileSync(parent.getConfigFilePath("mpsserver-cert-public.crt"), mpsCertificate);
obj.fs.writeFileSync(parent.getConfigFilePath("mpsserver-cert-private.key"), mpsPrivateKey);
} else {
// Keep the console certificate we have
mpsCertAndKey = { cert: obj.pki.certificateFromPem(r.mps.cert), key: obj.pki.privateKeyFromPem(r.mps.key) };
2018-08-29 20:40:30 -04:00
mpsCertificate = r.mps.cert;
mpsPrivateKey = r.mps.key;
}
2018-12-20 15:12:24 -05:00
r = { root: { cert: rootCertificate, key: rootPrivateKey }, web: { cert: webCertificate, key: webPrivateKey, ca: [] }, mps: { cert: mpsCertificate, key: mpsPrivateKey }, agent: { cert: agentCertificate, key: agentPrivateKey }, ca: calist, CommonName: commonName, RootName: rootName, AmtMpsName: mpsCommonName, dns: {}, WebIssuer: webIssuer };
2018-01-02 19:52:49 -05:00
// Look for domains with DNS names that have no certificates and generated them.
2018-08-31 19:43:09 -04:00
for (i in config.domains) {
2018-08-29 20:40:30 -04:00
if ((i != "") && (config.domains[i] != null) && (config.domains[i].dns != null)) {
dnsname = config.domains[i].dns;
2018-11-01 18:01:21 -04:00
if (!args.tlsoffload) {
2018-01-02 19:52:49 -05:00
// If the web certificate does not exist, create it
if ((obj.fileExists("webserver-" + i + "-cert-public.crt") === false) || (obj.fileExists("webserver-" + i + "-cert-private.key") === false)) {
2018-08-29 20:40:30 -04:00
console.log("Generating HTTPS certificate for " + i + "...");
2018-01-02 19:52:49 -05:00
var xwebCertAndKey = obj.IssueWebServerCertificate(rootCertAndKey, false, dnsname, country, organization, null, strongCertificate);
var xwebCertificate = obj.pki.certificateToPem(xwebCertAndKey.cert);
var xwebPrivateKey = obj.pki.privateKeyToPem(xwebCertAndKey.key);
2018-08-29 20:40:30 -04:00
obj.fs.writeFileSync(parent.getConfigFilePath("webserver-" + i + "-cert-public.crt"), xwebCertificate);
obj.fs.writeFileSync(parent.getConfigFilePath("webserver-" + i + "-cert-private.key"), xwebPrivateKey);
2018-01-02 19:52:49 -05:00
r.dns[i] = { cert: xwebCertificate, key: xwebPrivateKey };
config.domains[i].certs = r.dns[i];
// If CA certificates are present, load them
2018-08-29 20:40:30 -04:00
caindex = 1;
r.dns[i].ca = [];
2018-01-02 19:52:49 -05:00
do {
caok = false;
if (obj.fileExists("webserver-" + i + "-cert-chain" + caindex + ".crt")) {
r.dns[i].ca.push(fixEndOfLines(obj.fs.readFileSync(parent.getConfigFilePath("webserver-" + i + "-cert-chain" + caindex + ".crt"), "utf8")));
2018-01-02 19:52:49 -05:00
caok = true;
}
caindex++;
2018-08-29 20:40:30 -04:00
} while (caok === true);
2018-01-02 19:52:49 -05:00
}
}
}
}
2018-03-06 20:50:44 -05:00
// If the swarm server certificate exist, load it (This is an optional certificate)
if (obj.fileExists("swarmserver-cert-public.crt") && obj.fileExists("swarmserver-cert-private.key")) {
r.swarmserver = { cert: fixEndOfLines(obj.fs.readFileSync(parent.getConfigFilePath("swarmserver-cert-public.crt"), "utf8")), key: fixEndOfLines(obj.fs.readFileSync(parent.getConfigFilePath("swarmserver-cert-private.key"), "utf8")) };
2018-03-06 20:50:44 -05:00
}
// If the swarm server root certificate exist, load it (This is an optional certificate)
if (obj.fileExists("swarmserverroot-cert-public.crt")) {
r.swarmserverroot = { cert: fixEndOfLines(obj.fs.readFileSync(parent.getConfigFilePath("swarmserverroot-cert-public.crt"), "utf8")) };
2018-03-06 20:50:44 -05:00
}
// If CA certificates are present, load them
if (r.web != null) {
2018-08-29 20:40:30 -04:00
caindex = 1;
r.web.ca = [];
2018-03-06 20:50:44 -05:00
do {
caok = false;
if (obj.fileExists("webserver-cert-chain" + caindex + ".crt")) {
r.web.ca.push(fixEndOfLines(obj.fs.readFileSync(parent.getConfigFilePath("webserver-cert-chain" + caindex + ".crt"), "utf8")));
2018-03-06 20:50:44 -05:00
caok = true;
}
caindex++;
2018-08-29 20:40:30 -04:00
} while (caok === true);
2018-03-06 20:50:44 -05:00
}
2017-08-28 12:27:45 -04:00
if (func != undefined) { func(r); }
return r;
2018-08-29 20:40:30 -04:00
};
2017-08-28 12:27:45 -04:00
// Accelerators, used to dispatch work to other processes
2018-08-29 20:40:30 -04:00
const fork = require("child_process").fork;
const program = require("path").join(__dirname, "meshaccelerator.js");
const acceleratorTotalCount = require("os").cpus().length;
var acceleratorCreateCount = acceleratorTotalCount;
2018-01-09 23:13:41 -05:00
var freeAccelerators = [];
var pendingAccelerator = [];
obj.acceleratorCertStore = null;
2018-01-09 23:13:41 -05:00
// Create a new accelerator module
obj.getAccelerator = function () {
if (obj.acceleratorCertStore == null) { return null; }
2018-01-09 23:13:41 -05:00
if (freeAccelerators.length > 0) { return freeAccelerators.pop(); }
if (acceleratorCreateCount > 0) {
acceleratorCreateCount--;
2018-08-29 20:40:30 -04:00
var accelerator = fork(program, [], { stdio: ["pipe", "pipe", "pipe", "ipc"] });
accelerator.accid = acceleratorCreateCount;
2018-08-29 20:40:30 -04:00
accelerator.on("message", function (message) {
this.func(this.tag, message);
delete this.tag;
if (pendingAccelerator.length > 0) {
var x = pendingAccelerator.shift();
if (x.tag) { this.tag = x.tag; delete x.tag; }
accelerator.send(x);
} else { freeAccelerators.push(this); }
});
2018-08-29 20:40:30 -04:00
accelerator.send({ action: "setState", certs: obj.acceleratorCertStore });
2018-01-09 23:13:41 -05:00
return accelerator;
2018-08-29 20:40:30 -04:00
2018-01-09 23:13:41 -05:00
}
return null;
2018-08-29 20:40:30 -04:00
};
2018-01-09 23:13:41 -05:00
2018-08-29 20:40:30 -04:00
// Set the state of the accelerators. This way, we don"t have to send certificate & keys to them each time.
obj.acceleratorStart = function (certificates) {
2018-08-29 20:40:30 -04:00
if (obj.acceleratorCertStore != null) { console.error("ERROR: Accelerators can only be started once."); return; }
2018-01-09 23:13:41 -05:00
obj.acceleratorCertStore = [{ cert: certificates.agent.cert, key: certificates.agent.key }];
if (certificates.swarmserver != null) { obj.acceleratorCertStore.push({ cert: certificates.swarmserver.cert, key: certificates.swarmserver.key }); }
2018-08-29 20:40:30 -04:00
};
2018-01-09 23:13:41 -05:00
// Perform any RSA signature, just pass in the private key and data.
obj.acceleratorPerformSignature = function (privatekey, data, tag, func) {
if (acceleratorTotalCount <= 1) {
2018-01-09 23:13:41 -05:00
// No accelerators available
2018-08-29 20:40:30 -04:00
if (typeof privatekey == "number") { privatekey = obj.acceleratorCertStore[privatekey].key; }
const sign = obj.crypto.createSign("SHA384");
sign.end(Buffer.from(data, "binary"));
2018-08-29 20:40:30 -04:00
func(tag, sign.sign(privatekey).toString("binary"));
2018-01-09 23:13:41 -05:00
} else {
var acc = obj.getAccelerator();
if (acc == null) {
// Add to pending accelerator workload
2018-08-29 20:40:30 -04:00
pendingAccelerator.push({ action: "sign", key: privatekey, data: data, tag: tag });
} else {
// Send to accelerator now
acc.func = func;
acc.tag = tag;
2018-08-29 20:40:30 -04:00
acc.send({ action: "sign", key: privatekey, data: data });
}
2018-01-09 23:13:41 -05:00
}
2018-08-29 20:40:30 -04:00
};
2018-01-09 23:13:41 -05:00
2017-08-28 12:27:45 -04:00
return obj;
};