MeshCentral/certoperations.js

236 lines
12 KiB
JavaScript

/**
* @description Certificate generator
* @author Joko Sastriawan / Ylian Saint-Hilaire
* @version v0.0.1
*/
module.exports.CertificateOperations = function () {
var obj = {};
obj.fs = require('fs');
obj.forge = require('node-forge');
obj.pki = obj.forge.pki;
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; } }
obj.fileExists = function(filePath) { try { return obj.fs.statSync(filePath).isFile(); } catch (err) { return false; } }
// Return the SHA256 hash of the certificate public key
obj.getPublicKeyHash = function(cert) {
var publickey = obj.pki.certificateFromPem(cert).publicKey;
return obj.pki.getPublicKeyFingerprint(publickey, { encoding: 'hex', md: obj.forge.md.sha256.create() });
}
// Create a self-signed certificate
obj.GenerateRootCertificate = function (addThumbPrintToName, commonName, country, organization) {
var keys = obj.pki.rsa.generateKeyPair(2048);
var cert = obj.pki.createCertificate();
cert.publicKey = keys.publicKey;
cert.serialNumber = '' + Math.floor((Math.random() * 100000) + 1); ;
cert.validity.notBefore = new Date();
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.
cert.validity.notAfter = new Date();
cert.validity.notAfter.setFullYear(cert.validity.notAfter.getFullYear() + 30);
if (addThumbPrintToName == true) { commonName += '-' + obj.pki.getPublicKeyFingerprint(cert.publicKey, { encoding: 'hex' }).substring(0, 6); }
var attrs = [ { name: 'commonName', value: commonName } ];
if (country != undefined) attrs.push({ name: 'countryName', value: country });
if (organization != undefined) attrs.push({ name: 'organizationName', value: organization });
cert.setSubject(attrs);
cert.setIssuer(attrs);
cert.setExtensions([
{ name: 'basicConstraints', cA: true },
{
name: 'nsCertType',
client: false,
server: false,
email: false,
objsign: false,
sslCA: true,
emailCA: false,
objCA: true
}
]);
cert.sign(keys.privateKey, obj.forge.md.sha256.create());
return { cert: cert, key: keys.privateKey };
}
// Issue a certificate from a root
obj.IssueWebServerCertificate = function (rootcert, addThumbPrintToName, commonName, country, organization) {
var keys = obj.pki.rsa.generateKeyPair(2048);
var cert = obj.pki.createCertificate();
cert.publicKey = keys.publicKey;
cert.serialNumber = '' + Math.floor((Math.random() * 100000) + 1); ;
cert.validity.notBefore = new Date();
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.
cert.validity.notAfter = new Date();
cert.validity.notAfter.setFullYear(cert.validity.notAfter.getFullYear() + 30);
if (addThumbPrintToName == true) { commonName += '-' + obj.pki.getPublicKeyFingerprint(cert.publicKey, { encoding: 'hex' }).substring(0, 6); }
var attrs = [ { name: 'commonName', value: commonName }];
if (country != undefined) attrs.push({ name: 'countryName', value: country });
if (organization != undefined) attrs.push({ name: 'organizationName', value: organization });
cert.setSubject(attrs);
cert.setIssuer(rootcert.cert.subject.attributes);
cert.setExtensions([{
name: 'basicConstraints',
cA: false
}, {
name: 'keyUsage',
keyCertSign: true,
digitalSignature: true,
nonRepudiation: true,
keyEncipherment: true,
dataEncipherment: true
}, {
name: 'extKeyUsage',
serverAuth: true,
clientAuth: false,
codeSigning: false,
emailProtection: false,
timeStamping: false
}, {
name: 'nsCertType',
client: false,
server: true,
email: false,
objsign: false,
sslCA: false,
emailCA: false,
objCA: false
}, {
name: 'subjectAltName',
altNames: [{
type: 6, // URI
value: 'http://' + commonName + '/'
}, {
type: 6, // URL
value: 'http://localhost/'
}]
}, {
name: 'subjectKeyIdentifier'
}]);
cert.sign(rootcert.key, obj.forge.md.sha256.create());
return { cert: cert, key: keys.privateKey };
}
// Returns the web server TLS certificate and private key, if not present, create demonstration ones.
obj.GetMeshServerCertificate = function (directory, certargs, func) {
// commonName, country, organization
// If the certificates directory does not exist, create it.
if (!obj.dirExists(directory)) { obj.fs.mkdirSync(directory); }
var r = {}, rcount = 0;
// If the root certificate already exist, load it
if (obj.fileExists(directory + '/root-cert-public.crt') && obj.fileExists(directory + '/root-cert-private.key')) {
var rootCertificate = obj.fs.readFileSync(directory + '/root-cert-public.crt', 'utf8');
var rootPrivateKey = obj.fs.readFileSync(directory + '/root-cert-private.key', 'utf8');
r.root = { cert: rootCertificate, key: rootPrivateKey };
rcount++;
}
// If the web certificate already exist, load it
if (obj.fileExists(directory + '/webserver-cert-public.crt') && obj.fileExists(directory + '/webserver-cert-private.key')) {
var webCertificate = obj.fs.readFileSync(directory + '/webserver-cert-public.crt', 'utf8');
var webPrivateKey = obj.fs.readFileSync(directory + '/webserver-cert-private.key', 'utf8');
r.web = { cert: webCertificate, key: webPrivateKey };
rcount++;
}
// If the bin certificate already exist, load it
if (obj.fileExists(directory + '/mpsserver-cert-public.crt') && obj.fileExists(directory + '/mpsserver-cert-private.key')) {
var mpsCertificate = obj.fs.readFileSync(directory + '/mpsserver-cert-public.crt', 'utf8');
var mpsPrivateKey = obj.fs.readFileSync(directory + '/mpsserver-cert-private.key', 'utf8');
r.mps = { cert: mpsCertificate, key: mpsPrivateKey };
rcount++;
}
// If the bin certificate already exist, load it
if (obj.fileExists(directory + '/agentserver-cert-public.crt') && obj.fileExists(directory + '/agentserver-cert-private.key')) {
var agentCertificate = obj.fs.readFileSync(directory + '/agentserver-cert-public.crt', 'utf8');
var agentPrivateKey = obj.fs.readFileSync(directory + '/agentserver-cert-private.key', 'utf8');
r.agent = { cert: agentCertificate, key: agentPrivateKey };
rcount++;
}
// Decode certificate arguments
var commonName = 'un-configured', country, organization;
if (certargs != undefined) {
var args = certargs.split(',');
if (args.length > 0) commonName = args[0];
if (args.length > 1) country = args[1];
if (args.length > 2) organization = args[2];
}
if (rcount == 4) {
// Fetch the name of the server
var webCertificate = obj.pki.certificateFromPem(r.web.cert);
r.CommonName = webCertificate.subject.getField('CN').value;
var rootCertificate = obj.pki.certificateFromPem(r.root.cert);
r.RootName = rootCertificate.subject.getField('CN').value;
if (certargs == undefined) { if (func != undefined) { func(r); } return r }; // If no certificate arguments are given, keep the certificate
var xcountry, xcountryField = webCertificate.subject.getField('C');
if (xcountryField != null) { xcountry = xcountryField.value; }
var xorganization, xorganizationField = webCertificate.subject.getField('O');
if (xorganizationField != null) { xorganization = xorganizationField.value; }
if ((r.CommonName == commonName) && (xcountry == country) && (xorganization == organization)) { if (func != undefined) { func(r); } return r; } // If the certificate matches what we want, keep it.
}
console.log('Generating certificates...');
var rootCertAndKey, rootCertificate, rootPrivateKey, rootName;
if (r.root == undefined) {
// If the root certificate does not exist, create one
rootCertAndKey = obj.GenerateRootCertificate(true, 'MeshCentralRoot');
rootCertificate = obj.pki.certificateToPem(rootCertAndKey.cert);
rootPrivateKey = obj.pki.privateKeyToPem(rootCertAndKey.key);
obj.fs.writeFileSync(directory + '/root-cert-public.crt', rootCertificate);
obj.fs.writeFileSync(directory + '/root-cert-private.key', rootPrivateKey);
} else {
// Keep the root certificate we have
rootCertAndKey = { cert: obj.pki.certificateFromPem(r.root.cert), key: obj.pki.privateKeyFromPem(r.root.key) };
rootCertificate = r.root.cert
rootPrivateKey = r.root.key
}
var rootName = rootCertAndKey.cert.subject.getField('CN').value;
// If the web certificate does not exist, create one
var webCertAndKey, webCertificate, webPrivateKey;
webCertAndKey = obj.IssueWebServerCertificate(rootCertAndKey, false, commonName, country, organization);
webCertificate = obj.pki.certificateToPem(webCertAndKey.cert);
webPrivateKey = obj.pki.privateKeyToPem(webCertAndKey.key);
obj.fs.writeFileSync(directory + '/webserver-cert-public.crt', webCertificate);
obj.fs.writeFileSync(directory + '/webserver-cert-private.key', webPrivateKey);
// If the Intel AMT MPS certificate does not exist, create one
var mpsCertAndKey, mpsCertificate, mpsPrivateKey;
mpsCertAndKey = obj.IssueWebServerCertificate(rootCertAndKey, false, commonName, country, organization);
mpsCertificate = obj.pki.certificateToPem(mpsCertAndKey.cert);
mpsPrivateKey = obj.pki.privateKeyToPem(mpsCertAndKey.key);
obj.fs.writeFileSync(directory + '/mpsserver-cert-public.crt', mpsCertificate);
obj.fs.writeFileSync(directory + '/mpsserver-cert-private.key', mpsPrivateKey);
// If the mesh agent server certificate does not exist, create one
var agentCertAndKey, agentCertificate, agentPrivateKey;
if (r.agent == undefined) {
agentCertAndKey = obj.IssueWebServerCertificate(rootCertAndKey, true, 'MeshCentralAgentServer');
agentCertificate = obj.pki.certificateToPem(agentCertAndKey.cert);
agentPrivateKey = obj.pki.privateKeyToPem(agentCertAndKey.key);
obj.fs.writeFileSync(directory + '/agentserver-cert-public.crt', agentCertificate);
obj.fs.writeFileSync(directory + '/agentserver-cert-private.key', agentPrivateKey);
} else {
// Keep the mesh agent server certificate we have
agentCertAndKey = { cert: obj.pki.certificateFromPem(r.agent.cert), key: obj.pki.privateKeyFromPem(r.agent.key) };
agentCertificate = r.agent.cert
agentPrivateKey = r.agent.key
}
var r = { root: { cert: rootCertificate, key: rootPrivateKey }, web: { cert: webCertificate, key: webPrivateKey }, mps: { cert: mpsCertificate, key: mpsPrivateKey }, agent: { cert: agentCertificate, key: agentPrivateKey }, CommonName: commonName, RootName: rootName };
if (func != undefined) { func(r); }
return r;
}
return obj;
};