Added support for PKCS#8 encrypted private keys.

This commit is contained in:
Ylian Saint-Hilaire 2021-08-20 12:03:52 -07:00
parent 24b456e4fd
commit e47e050149
3 changed files with 36 additions and 11 deletions

View File

@ -743,6 +743,29 @@ module.exports.CertificateOperations = function (parent) {
return true; return true;
} }
// Decrypt private key if needed
obj.decryptPrivateKey = function (key) {
if (typeof key != 'string') return key;
var i = key.indexOf('-----BEGIN ENCRYPTED PRIVATE KEY-----');
var j = key.indexOf('-----END ENCRYPTED PRIVATE KEY-----');
if ((i >= 0) && (j > i)) {
var passwords = parent.config.settings.certificateprivatekeypassword;
if (parent.config.settings.certificateprivatekeypassword == null) { passwords = []; }
else if (typeof parent.config.settings.certificateprivatekeypassword == 'string') { passwords = [parent.config.settings.certificateprivatekeypassword ]; }
var privateKey = null;
for (var k in passwords) { if (privateKey == null) { try { privateKey = obj.pki.decryptRsaPrivateKey(key, passwords[k]); } catch (ex) { } } }
if (privateKey == null) {
console.log("Private certificate key is encrypted, but no correct password was found.");
console.log("Add the password to the \"certificatePrivateKeyPassword\" value in the Settings section of the config.json.");
console.log("Example: \"certificatePrivateKeyPassword\": [ \"MyPassword\" ]");
process.exit();
return null;
}
return obj.pki.privateKeyToPem(privateKey);
}
return key;
}
// Returns the web server TLS certificate and private key, if not present, create demonstration ones. // Returns the web server TLS certificate and private key, if not present, create demonstration ones.
obj.GetMeshServerCertificate = function (args, config, func) { obj.GetMeshServerCertificate = function (args, config, func) {
var i = 0; var i = 0;
@ -764,7 +787,7 @@ module.exports.CertificateOperations = function (parent) {
// If the root certificate already exist, load it // If the root certificate already exist, load it
if (obj.fileExists('root-cert-public.crt') && obj.fileExists('root-cert-private.key')) { if (obj.fileExists('root-cert-public.crt') && obj.fileExists('root-cert-private.key')) {
var rootCertificate = obj.fileLoad('root-cert-public.crt', 'utf8'); var rootCertificate = obj.fileLoad('root-cert-public.crt', 'utf8');
var rootPrivateKey = obj.fileLoad('root-cert-private.key', 'utf8'); var rootPrivateKey = obj.decryptPrivateKey(obj.fileLoad('root-cert-private.key', 'utf8'));
r.root = { cert: rootCertificate, key: rootPrivateKey }; r.root = { cert: rootCertificate, key: rootPrivateKey };
rcount++; rcount++;
@ -786,7 +809,7 @@ module.exports.CertificateOperations = function (parent) {
// If web certificate exist, load it as default. This is useful for agent-only port. Load both certificate and private key // If web certificate exist, load it as default. This is useful for agent-only port. Load both certificate and private key
if (obj.fileExists('webserver-cert-public.crt') && obj.fileExists('webserver-cert-private.key')) { if (obj.fileExists('webserver-cert-public.crt') && obj.fileExists('webserver-cert-private.key')) {
r.webdefault = { cert: obj.fileLoad('webserver-cert-public.crt', 'utf8'), key: obj.fileLoad('webserver-cert-private.key', 'utf8') }; r.webdefault = { cert: obj.fileLoad('webserver-cert-public.crt', 'utf8'), key: obj.decryptPrivateKey(obj.fileLoad('webserver-cert-private.key', 'utf8')) };
if (obj.checkCertificate(r.webdefault.cert, r.webdefault.key) == false) { delete r.webdefault; } if (obj.checkCertificate(r.webdefault.cert, r.webdefault.key) == false) { delete r.webdefault; }
} }
@ -798,27 +821,27 @@ module.exports.CertificateOperations = function (parent) {
} }
} else { } else {
// If the web certificate already exist, load it. Load both certificate and private key // 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')) { if (obj.fileExists('webserver-cert-public.crt') && obj.decryptPrivateKey(obj.fileExists('webserver-cert-private.key'))) {
r.web = { cert: obj.fileLoad('webserver-cert-public.crt', 'utf8'), key: obj.fileLoad('webserver-cert-private.key', 'utf8') }; r.web = { cert: obj.fileLoad('webserver-cert-public.crt', 'utf8'), key: obj.decryptPrivateKey(obj.fileLoad('webserver-cert-private.key', 'utf8')) };
if (obj.checkCertificate(r.web.cert, r.web.key) == false) { delete r.web; } else { rcount++; } if (obj.checkCertificate(r.web.cert, r.web.key) == false) { delete r.web; } else { rcount++; }
} }
} }
// If the mps certificate already exist, load it // If the mps certificate already exist, load it
if (obj.fileExists('mpsserver-cert-public.crt') && obj.fileExists('mpsserver-cert-private.key')) { 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') }; r.mps = { cert: obj.fileLoad('mpsserver-cert-public.crt', 'utf8'), key: obj.decryptPrivateKey(obj.fileLoad('mpsserver-cert-private.key', 'utf8')) };
if (obj.checkCertificate(r.mps.cert, r.mps.key) == false) { delete r.mps; } else { rcount++; } if (obj.checkCertificate(r.mps.cert, r.mps.key) == false) { delete r.mps; } else { rcount++; }
} }
// If the agent certificate already exist, load it // If the agent certificate already exist, load it
if (obj.fileExists("agentserver-cert-public.crt") && obj.fileExists("agentserver-cert-private.key")) { 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') }; r.agent = { cert: obj.fileLoad("agentserver-cert-public.crt", 'utf8'), key: obj.decryptPrivateKey(obj.fileLoad("agentserver-cert-private.key", 'utf8')) };
if (obj.checkCertificate(r.agent.cert, r.agent.key) == false) { delete r.agent; } else { rcount++; } if (obj.checkCertificate(r.agent.cert, r.agent.key) == false) { delete r.agent; } else { rcount++; }
} }
// If the swarm server certificate exist, load it (This is an optional certificate) // 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')) { 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') }; r.swarmserver = { cert: obj.fileLoad('swarmserver-cert-public.crt', 'utf8'), key: obj.decryptPrivateKey(obj.fileLoad('swarmserver-cert-private.key', 'utf8')) };
if (obj.checkCertificate(r.swarmserver.cert, r.swarmserver.key) == false) { delete r.swarmserver; } if (obj.checkCertificate(r.swarmserver.cert, r.swarmserver.key) == false) { delete r.swarmserver; }
} }
@ -894,7 +917,7 @@ module.exports.CertificateOperations = function (parent) {
dnsname = config.domains[i].dns; dnsname = config.domains[i].dns;
// Check if this domain matches a parent wildcard cert, if so, use the parent cert. // Check if this domain matches a parent wildcard cert, if so, use the parent cert.
if (obj.compareCertificateNames(r.CommonNames, dnsname) == true) { if (obj.compareCertificateNames(r.CommonNames, dnsname) == true) {
r.dns[i] = { cert: obj.fileLoad('webserver-cert-public.crt', 'utf8'), key: obj.fileLoad('webserver-cert-private.key', 'utf8') }; r.dns[i] = { cert: obj.fileLoad('webserver-cert-public.crt', 'utf8'), key: obj.decryptPrivateKey(obj.fileLoad('webserver-cert-private.key', 'utf8')) };
} else { } else {
if (args.tlsoffload) { if (args.tlsoffload) {
// If the web certificate already exist, load it. Load just the certificate since we are in TLS offload situation // If the web certificate already exist, load it. Load just the certificate since we are in TLS offload situation
@ -907,7 +930,7 @@ module.exports.CertificateOperations = function (parent) {
} else { } else {
// If the web certificate already exist, load it. Load both certificate and private key // 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')) { 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') }; r.dns[i] = { cert: obj.fileLoad('webserver-' + i + '-cert-public.crt', 'utf8'), key: obj.decryptPrivateKey(obj.fileLoad('webserver-' + i + '-cert-private.key', 'utf8')) };
config.domains[i].certs = r.dns[i]; config.domains[i].certs = r.dns[i];
// If CA certificates are present, load them // If CA certificates are present, load them
caindex = 1; caindex = 1;
@ -1062,7 +1085,7 @@ module.exports.CertificateOperations = function (parent) {
dnsname = config.domains[i].dns; dnsname = config.domains[i].dns;
// Check if this domain matches a parent wildcard cert, if so, use the parent cert. // Check if this domain matches a parent wildcard cert, if so, use the parent cert.
if (obj.compareCertificateNames(r.CommonNames, dnsname) == true) { if (obj.compareCertificateNames(r.CommonNames, dnsname) == true) {
r.dns[i] = { cert: obj.fileLoad('webserver-cert-public.crt', 'utf8'), key: obj.fileLoad('webserver-cert-private.key', 'utf8') }; r.dns[i] = { cert: obj.fileLoad('webserver-cert-public.crt', 'utf8'), key: obj.decryptPrivateKey(obj.fileLoad('webserver-cert-private.key', 'utf8')) };
} else { } else {
if (!args.tlsoffload) { if (!args.tlsoffload) {
// If the web certificate does not exist, create it // If the web certificate does not exist, create it

View File

@ -58,8 +58,9 @@
"WANonly": { "type": "boolean", "default": false, "description": "When enabled, only MeshCentral WAN features are enabled and agents will connect to the server using a well known DNS name." }, "WANonly": { "type": "boolean", "default": false, "description": "When enabled, only MeshCentral WAN features are enabled and agents will connect to the server using a well known DNS name." },
"LANonly": { "type": "boolean", "default": false, "description": "When enabled, only MeshCentral LAN features are enabled and agents will find the server using multicast LAN packets." }, "LANonly": { "type": "boolean", "default": false, "description": "When enabled, only MeshCentral LAN features are enabled and agents will find the server using multicast LAN packets." },
"maintenanceMode": { "type": "boolean", "default": false, "description": "When enabled the server is in maintenance mode, only administrators can login. Use the maintenance command in server console to change." }, "maintenanceMode": { "type": "boolean", "default": false, "description": "When enabled the server is in maintenance mode, only administrators can login. Use the maintenance command in server console to change." },
"certificatePrivateKeyPassword": { "type": "array", "default": null, "description": "List of passwords used to decrypt PKCK#8 .key files that are in the meshcentral-data folder." },
"sessionTime": { "type": "integer", "default": 60, "description": "Duration of a session cookie in minutes. Changing this affects how often the session needs to be automatically refreshed." }, "sessionTime": { "type": "integer", "default": 60, "description": "Duration of a session cookie in minutes. Changing this affects how often the session needs to be automatically refreshed." },
"sessionKey": { "type": "string" }, "sessionKey": { "type": "string", "default": null, "description": "Password used to encrypt the MeshCentral web session cookies. If null, a random one is generated each time the server starts." },
"sessionSameSite": { "type": "string" }, "sessionSameSite": { "type": "string" },
"dbEncryptKey": { "type": "string" }, "dbEncryptKey": { "type": "string" },
"dbRecordsEncryptKey": { "type": "string", "default": null }, "dbRecordsEncryptKey": { "type": "string", "default": null },

View File

@ -10,6 +10,7 @@
"_LANonly": true, "_LANonly": true,
"_sessionKey": "MyReallySecretPassword1", "_sessionKey": "MyReallySecretPassword1",
"_sessionSameSite": "strict", "_sessionSameSite": "strict",
"_certificatePrivateKeyPassword": [ "password1", "password2" ],
"_dbEncryptKey": "MyReallySecretPassword2", "_dbEncryptKey": "MyReallySecretPassword2",
"_dbRecordsEncryptKey": "MyReallySecretPassword", "_dbRecordsEncryptKey": "MyReallySecretPassword",
"_dbRecordsDecryptKey": "MyReallySecretPassword", "_dbRecordsDecryptKey": "MyReallySecretPassword",