2017-08-28 19:27:45 +03:00
/ * *
* @ description Certificate generator
* @ author Joko Sastriawan / Ylian Saint - Hilaire
2019-01-04 03:22:15 +03:00
* @ copyright Intel Corporation 2018 - 2019
2018-01-04 23:15:21 +03:00
* @ license Apache - 2.0
2017-08-28 19:27:45 +03:00
* @ version v0 . 0.1
* /
2018-01-04 23:15:21 +03:00
2018-08-30 03:40:30 +03:00
/*xjslint node: true */
/*xjslint plusplus: true */
/*xjslint maxlen: 256 */
/*jshint node: true */
/*jshint strict: false */
/*jshint esversion: 6 */
"use strict" ;
2018-08-27 22:24:15 +03:00
2019-02-03 01:54:36 +03:00
module . exports . CertificateOperations = function ( parent ) {
2017-08-28 19:27:45 +03:00
var obj = { } ;
2019-02-03 01:54:36 +03:00
obj . parent = parent ;
2018-08-30 03:40:30 +03:00
obj . fs = require ( "fs" ) ;
obj . forge = require ( "node-forge" ) ;
obj . crypto = require ( "crypto" ) ;
2018-11-01 02:03:09 +03:00
obj . tls = require ( 'tls' ) ;
2017-08-28 19:27:45 +03:00
obj . pki = obj . forge . pki ;
2018-08-30 03:40:30 +03: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 19:27:45 +03:00
2018-11-01 02:03:09 +03:00
// Return the certificate of the remote HTTPS server
obj . loadCertificate = function ( url , tag , func ) {
2018-12-01 03:42:58 +03:00
const u = require ( 'url' ) . parse ( url ) ;
2018-11-01 02:03:09 +03:00
if ( u . protocol == 'https:' ) {
2018-12-01 03:42:58 +03:00
// 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 ( ) ; } ) ;
2018-11-01 02:03:09 +03:00
tlssocket . xxurl = url ;
tlssocket . xxfunc = func ;
tlssocket . xxtag = tag ;
2018-12-01 03:42:58 +03:00
tlssocket . on ( 'end' , function ( ) { this . xxfunc ( this . xxurl , this . xxcert . raw . toString ( 'binary' ) , this . xxtag ) ; } ) ;
2018-11-01 02:03:09 +03:00
tlssocket . on ( 'error' , function ( ) { this . xxfunc ( this . xxurl , null , this . xxtag ) ; } ) ;
2018-12-01 03:42:58 +03:00
} 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 ) ) {
2019-01-03 05:03:34 +03:00
func ( url , Buffer . from ( data . substring ( x1 + 27 , x2 ) , 'base64' ) . toString ( 'binary' ) , tag ) ;
2018-12-01 03:42:58 +03:00
} else {
func ( url , data , tag ) ;
}
} ) ;
2018-11-01 02:03:09 +03:00
} else { func ( url , null , tag ) ; }
} ;
2019-02-03 01:54:36 +03:00
// 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-02 01:01:21 +03:00
// Return the SHA384 hash of the certificate public key
2017-10-15 09:22:19 +03:00
obj . getPublicKeyHash = function ( cert ) {
var publickey = obj . pki . certificateFromPem ( cert ) . publicKey ;
2018-08-30 03:40:30 +03:00
return obj . pki . getPublicKeyFingerprint ( publickey , { encoding : "hex" , md : obj . forge . md . sha384 . create ( ) } ) ;
} ;
2017-09-30 00:26:51 +03:00
2018-12-01 03:42:58 +03:00
// 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 ) ) {
2019-01-03 05:03:34 +03:00
return obj . crypto . createHash ( 'sha384' ) . update ( Buffer . from ( cert . substring ( x1 + 27 , x2 ) , 'base64' ) ) . digest ( 'hex' ) ;
2018-12-01 03:42:58 +03:00
} 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 ) ) {
2019-01-03 05:03:34 +03:00
return obj . crypto . createHash ( 'sha384' ) . update ( Buffer . from ( cert . substring ( x1 + 27 , x2 ) , 'base64' ) ) . digest ( 'binary' ) ;
2018-12-01 03:42:58 +03:00
} else { console . log ( 'ERROR: Unable to decode certificate.' ) ; return null ; }
}
} ;
2017-08-28 19:27:45 +03:00
// Create a self-signed certificate
2017-10-19 02:28:05 +03:00
obj . GenerateRootCertificate = function ( addThumbPrintToName , commonName , country , organization , strong ) {
2019-02-12 01:41:15 +03:00
var keys = obj . pki . rsa . generateKeyPair ( { bits : ( strong == true ) ? 3072 : 2048 , e : 0x10001 } ) ;
2017-08-28 19:27:45 +03:00
var cert = obj . pki . createCertificate ( ) ;
cert . publicKey = keys . publicKey ;
2018-08-30 03:40:30 +03:00
cert . serialNumber = String ( Math . floor ( ( Math . random ( ) * 100000 ) + 1 ) ) ;
2017-08-28 19:27:45 +03:00
cert . validity . notBefore = new Date ( ) ;
2018-08-30 03:40:30 +03: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 19:27:45 +03:00
cert . validity . notAfter = new Date ( ) ;
cert . validity . notAfter . setFullYear ( cert . validity . notAfter . getFullYear ( ) + 30 ) ;
2018-08-30 03:40:30 +03:00
if ( addThumbPrintToName === true ) { commonName += "-" + obj . pki . getPublicKeyFingerprint ( cert . publicKey , { encoding : "hex" } ) . substring ( 0 , 6 ) ; }
2018-09-01 23:00:53 +03:00
if ( country == null ) { country = "unknown" ; }
if ( organization == null ) { organization = "unknown" ; }
2018-08-30 03:40:30 +03:00
var attrs = [ { name : "commonName" , value : commonName } , { name : "organizationName" , value : organization } , { name : "countryName" , value : country } ] ;
2017-08-28 19:27:45 +03:00
cert . setSubject ( attrs ) ;
cert . setIssuer ( attrs ) ;
2017-10-03 00:12:29 +03:00
// Create a root certificate
2018-12-22 01:39:26 +03:00
//cert.setExtensions([{ name: "basicConstraints", cA: true }, { name: "nsCertType", sslCA: true, emailCA: true, objCA: true }, { name: "subjectKeyIdentifier" }]);
cert . setExtensions ( [ { name : "basicConstraints" , cA : true } , { name : "subjectKeyIdentifier" } ] ) ;
2017-10-15 09:22:19 +03:00
cert . sign ( keys . privateKey , obj . forge . md . sha384 . create ( ) ) ;
2017-08-28 19:27:45 +03:00
return { cert : cert , key : keys . privateKey } ;
2018-08-30 03:40:30 +03:00
} ;
2017-08-28 19:27:45 +03:00
// Issue a certificate from a root
2017-10-15 09:22:19 +03:00
obj . IssueWebServerCertificate = function ( rootcert , addThumbPrintToName , commonName , country , organization , extKeyUsage , strong ) {
2019-02-12 01:41:15 +03:00
var keys = obj . pki . rsa . generateKeyPair ( { bits : ( strong == true ) ? 3072 : 2048 , e : 0x10001 } ) ;
2017-08-28 19:27:45 +03:00
var cert = obj . pki . createCertificate ( ) ;
cert . publicKey = keys . publicKey ;
2018-08-30 03:40:30 +03:00
cert . serialNumber = String ( Math . floor ( ( Math . random ( ) * 100000 ) + 1 ) ) ;
2017-08-28 19:27:45 +03:00
cert . validity . notBefore = new Date ( ) ;
2018-08-30 03:40:30 +03: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 19:27:45 +03:00
cert . validity . notAfter = new Date ( ) ;
cert . validity . notAfter . setFullYear ( cert . validity . notAfter . getFullYear ( ) + 30 ) ;
2018-08-30 03:40:30 +03:00
if ( addThumbPrintToName === true ) { commonName += "-" + obj . pki . getPublicKeyFingerprint ( cert . publicKey , { encoding : "hex" } ) . substring ( 0 , 6 ) ; }
var attrs = [ { name : "commonName" , value : commonName } ] ;
2018-09-01 23:00:53 +03:00
if ( country != null ) { attrs . push ( { name : "countryName" , value : country } ) ; }
if ( organization != null ) { attrs . push ( { name : "organizationName" , value : organization } ) ; }
2017-08-28 19:27:45 +03:00
cert . setSubject ( attrs ) ;
cert . setIssuer ( rootcert . cert . subject . attributes ) ;
2017-09-30 00:26:51 +03:00
2018-08-30 03:40:30 +03:00
if ( extKeyUsage == null ) { extKeyUsage = { name : "extKeyUsage" , serverAuth : true } ; } else { extKeyUsage . name = "extKeyUsage" ; }
2018-12-22 01:39:26 +03:00
//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" } ] ;
2019-03-17 00:49:48 +03:00
if ( extKeyUsage . serverAuth === true ) {
2019-03-19 19:54:52 +03:00
// Set subjectAltName according to commonName parsing.
2019-03-17 00:49:48 +03:00
// Ideally, we should let opportunity in given interface to set any type of altNames according to node_forge library
2019-03-19 19:54:52 +03:00
// such as type 2, 6 and 7. (2 -> DNS, 6 -> URI, 7 -> IP)
2019-03-17 00:49:48 +03:00
var altNames = [ ] ;
// According to commonName parsing (IP or DNS), add URI and DNS and/or IP altNames
if ( require ( 'net' ) . isIP ( commonName ) ) {
// set both IP and DNS when commonName is an IP@
altNames . push ( { type : 7 , ip : commonName } ) ;
altNames . push ( { type : 2 , value : commonName } ) ;
} else {
// set only DNS when commonName is a FQDN
altNames . push ( { type : 2 , value : commonName } ) ;
}
altNames . push ( { type : 6 , value : "http://" + commonName + "/" } )
2019-03-19 19:54:52 +03:00
// Add localhost stuff for easy testing on localhost ;)
2019-03-17 00:49:48 +03:00
altNames . push ( { type : 2 , value : "localhost" } ) ;
altNames . push ( { type : 6 , value : "http://localhost/" } ) ;
altNames . push ( { type : 7 , ip : "127.0.0.1" } ) ;
extensions . push ( { name : "subjectAltName" , altNames : altNames } ) ;
}
2018-12-22 01:39:26 +03:00
2017-09-30 00:26:51 +03:00
cert . setExtensions ( extensions ) ;
2017-10-15 09:22:19 +03:00
cert . sign ( rootcert . key , obj . forge . md . sha384 . create ( ) ) ;
2018-08-30 03:40:30 +03:00
2017-08-28 19:27:45 +03:00
return { cert : cert , key : keys . privateKey } ;
2018-08-30 03:40:30 +03:00
} ;
2017-08-28 19:27:45 +03:00
2019-01-12 01:01:36 +03:00
// Make sure a string with Mac style CR endo of line is changed to Linux LF style.
function fixEndOfLines ( str ) {
2019-01-15 20:20:21 +03:00
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.
2019-01-12 01:01:36 +03:00
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.
}
2019-03-10 01:28:08 +03:00
// Return true if the name is found in the certificates names, we support wildcard certificates
function compareCertificateNames ( certNames , name ) {
if ( certNames == null ) return false ;
if ( certNames . indexOf ( name . toLowerCase ( ) ) >= 0 ) return true ;
for ( var i in certNames ) {
if ( ( certNames [ i ] . startsWith ( '*.' ) == true ) && ( name . endsWith ( certNames [ i ] . substring ( 1 ) ) == true ) ) { return true ; }
if ( certNames [ i ] . startsWith ( 'http://*.' ) == true ) {
if ( name . endsWith ( certNames [ i ] . substring ( 8 ) ) == true ) { return true ; }
if ( ( certNames [ i ] . endsWith ( '/' ) == true ) && ( name . endsWith ( certNames [ i ] . substring ( 8 , certNames [ i ] . length - 1 ) ) == true ) ) { return true ; }
}
}
return false ;
}
2017-08-28 19:27:45 +03:00
// Returns the web server TLS certificate and private key, if not present, create demonstration ones.
2019-02-03 01:54:36 +03:00
obj . GetMeshServerCertificate = function ( args , config , func ) {
2018-08-30 03:40:30 +03:00
var i = 0 ;
2017-10-03 00:12:29 +03:00
var certargs = args . cert ;
2018-03-07 04:50:44 +03:00
var mpscertargs = args . mpscert ;
2017-10-19 02:28:05 +03:00
var strongCertificate = ( args . fastcert ? false : true ) ;
2018-12-20 23:12:24 +03:00
var rcountmax = 4 ;
2018-08-30 03:40:30 +03:00
var caindex = 1 ;
var caok = false ;
var calist = [ ] ;
var dnsname = null ;
2017-08-28 19:27:45 +03:00
// commonName, country, organization
2018-08-30 03:40:30 +03:00
2017-08-28 19:27:45 +03:00
// If the certificates directory does not exist, create it.
2018-07-14 05:18:43 +03:00
if ( ! obj . dirExists ( parent . datapath ) ) { obj . fs . mkdirSync ( parent . datapath ) ; }
2018-08-30 03:40:30 +03:00
var r = { } ;
var rcount = 0 ;
2017-08-28 19:27:45 +03:00
// If the root certificate already exist, load it
2019-02-03 01:54:36 +03:00
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 19:27:45 +03:00
r . root = { cert : rootCertificate , key : rootPrivateKey } ;
rcount ++ ;
}
2017-10-03 00:12:29 +03:00
2018-11-02 01:01:21 +03:00
if ( args . tlsoffload ) {
2017-10-03 00:12:29 +03:00
// If the web certificate already exist, load it. Load just the certificate since we are in TLS offload situation
2019-02-03 01:54:36 +03:00
if ( obj . fileExists ( "webserver-cert-public.crt" ) ) {
r . web = { cert : obj . fileLoad ( "webserver-cert-public.crt" , "utf8" ) } ;
2017-10-03 00:12:29 +03:00
rcount ++ ;
}
} else {
// If the web certificate already exist, load it. Load both certificate and private key
2019-02-03 01:54:36 +03:00
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" ) } ;
2017-10-03 00:12:29 +03:00
rcount ++ ;
}
2017-08-28 19:27:45 +03:00
}
2018-08-30 03:40:30 +03:00
2017-10-03 00:12:29 +03:00
// If the mps certificate already exist, load it
2019-02-03 01:54:36 +03:00
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 19:27:45 +03:00
rcount ++ ;
}
2018-08-30 03:40:30 +03:00
2017-10-03 00:12:29 +03:00
// If the agent certificate already exist, load it
2019-02-03 01:54:36 +03:00
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 19:27:45 +03:00
rcount ++ ;
}
2017-09-08 02:01:44 +03:00
2017-11-04 03:01:30 +03:00
// If the swarm server certificate exist, load it (This is an optional certificate)
2019-02-03 01:54:36 +03:00
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" ) } ;
2017-11-04 03:01:30 +03:00
}
// If the swarm server root certificate exist, load it (This is an optional certificate)
2019-02-03 01:54:36 +03:00
if ( obj . fileExists ( "swarmserverroot-cert-public.crt" ) ) {
r . swarmserverroot = { cert : obj . fileLoad ( "swarmserverroot-cert-public.crt" , "utf8" ) } ;
2017-11-04 03:01:30 +03:00
}
2017-09-08 02:01:44 +03:00
// If CA certificates are present, load them
2018-08-30 03:40:30 +03:00
do {
caok = false ;
2019-02-03 01:54:36 +03:00
if ( obj . fileExists ( "webserver-cert-chain" + caindex + ".crt" ) ) {
calist . push ( obj . fileLoad ( "webserver-cert-chain" + caindex + ".crt" , "utf8" ) ) ;
2018-08-30 03:40:30 +03:00
caok = true ;
}
caindex ++ ;
} while ( caok === true ) ;
if ( r . web != null ) { r . web . ca = calist ; }
2018-01-03 03:52:49 +03:00
2017-08-28 19:27:45 +03:00
// Decode certificate arguments
2018-08-30 03:40:30 +03:00
var commonName = "un-configured" ;
var country = null ;
var organization = null ;
var forceWebCertGen = 0 ;
var forceMpsCertGen = 0 ;
2017-08-28 19:27:45 +03:00
if ( certargs != undefined ) {
2018-08-30 03:40:30 +03: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 19:27:45 +03:00
}
2017-10-03 00:12:29 +03:00
2018-03-07 04:50:44 +03:00
// Decode MPS certificate arguments, this is for the Intel AMT CIRA server
2018-08-30 03:40:30 +03: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-07 04:50:44 +03:00
}
2019-03-10 01:28:08 +03:00
if ( rcount === rcountmax ) {
// Fetch the certificates names for the main certificate
r . AmtMpsName = obj . pki . certificateFromPem ( r . mps . cert ) . subject . getField ( "CN" ) . value ;
var webCertificate = obj . pki . certificateFromPem ( r . web . cert ) ;
r . WebIssuer = webCertificate . issuer . getField ( "CN" ) . value ;
r . CommonName = webCertificate . subject . getField ( "CN" ) . value ;
if ( r . CommonName . startsWith ( '*.' ) ) {
if ( commonName . indexOf ( '.' ) == - 1 ) { console . log ( "ERROR: Must specify a server full domain name in Config.json->Settings->Cert when using a wildcard certificate." ) ; process . exit ( 0 ) ; return ; }
if ( commonName . startsWith ( '*.' ) ) { console . log ( "ERROR: Server can't use a wildcard name: " + commonName ) ; process . exit ( 0 ) ; return ; }
r . CommonName = commonName ;
}
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 ( ) ) ; } }
var rootCertificate = obj . pki . certificateFromPem ( r . root . cert ) ;
r . RootName = rootCertificate . subject . getField ( "CN" ) . value ;
}
2018-01-03 03:52:49 +03:00
// Look for domains that have DNS names and load their certificates
r . dns = { } ;
2018-09-01 02:43:09 +03:00
for ( i in config . domains ) {
2018-08-30 03:40:30 +03:00
if ( ( i != "" ) && ( config . domains [ i ] != null ) && ( config . domains [ i ] . dns != null ) ) {
dnsname = config . domains [ i ] . dns ;
2019-03-10 01:28:08 +03:00
// Check if this domain matches a parent wildcard cert, if so, use the parent cert.
if ( compareCertificateNames ( r . CommonNames , dnsname ) == true ) {
r . dns [ i ] = { cert : obj . fileLoad ( "webserver-cert-public.crt" , "utf8" ) , key : obj . fileLoad ( "webserver-cert-private.key" , "utf8" ) } ;
2018-01-03 03:52:49 +03:00
} else {
2019-03-10 01:28:08 +03: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-" + i + "-cert-public.crt" ) ) {
r . dns [ i ] = { cert : obj . fileLoad ( "webserver-" + i + "-cert-public.crt" , "utf8" ) } ;
config . domains [ i ] . certs = r . dns [ i ] ;
} else {
console . log ( "WARNING: File \"webserver-" + i + "-cert-public.crt\" missing, domain \"" + i + "\" will not work correctly." ) ;
}
2018-01-03 03:52:49 +03:00
} else {
2019-03-10 01:28:08 +03:00
// 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" ) } ;
config . domains [ i ] . certs = r . dns [ i ] ;
// If CA certificates are present, load them
caindex = 1 ;
r . dns [ i ] . ca = [ ] ;
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" ) ) ;
caok = true ;
}
caindex ++ ;
} while ( caok === true ) ;
} else {
rcountmax ++ ; // This certificate must be generated
}
2018-01-03 03:52:49 +03:00
}
}
}
}
2018-08-30 03:40:30 +03:00
if ( rcount === rcountmax ) {
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 19:27:45 +03:00
if ( xcountryField != null ) { xcountry = xcountryField . value ; }
2018-08-30 03:40:30 +03:00
var xorganization , xorganizationField = webCertificate . subject . getField ( "O" ) ;
2017-08-28 19:27:45 +03:00
if ( xorganizationField != null ) { xorganization = xorganizationField . value ; }
2018-03-07 04:50:44 +03:00
if ( certargs == null ) { commonName = r . CommonName ; country = xcountry ; organization = xorganization ; }
// Check if we have correct certificates
2019-03-10 01:28:08 +03:00
if ( compareCertificateNames ( r . CommonNames , commonName ) == false ) { forceWebCertGen = 1 ; }
if ( r . AmtMpsName != mpsCommonName ) { forceMpsCertGen = 1 ; }
// If the certificates matches what we want, use them.
if ( ( forceWebCertGen == 0 ) && ( forceMpsCertGen == 0 ) ) {
2018-08-30 03:40:30 +03:00
if ( func !== undefined ) { func ( r ) ; }
return r ;
2018-07-24 03:34:24 +03:00
}
2017-08-28 19:27:45 +03:00
}
2019-02-03 01:54:36 +03:00
if ( parent . configurationFiles != null ) { console . log ( "Error: Database missing some certificates." ) ; process . exit ( 0 ) ; return null ; }
2018-08-30 03:40:30 +03:00
console . log ( "Generating certificates, may take a few minutes..." ) ;
parent . updateServerState ( "state" , "generatingcertificates" ) ;
2017-10-19 02:28:05 +03: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-30 03:40:30 +03:00
commonName = webCertificate . subject . getField ( "CN" ) . value ;
var xcountryField = webCertificate . subject . getField ( "C" ) ;
2017-10-19 02:28:05 +03:00
if ( xcountryField != null ) { country = xcountryField . value ; }
2018-08-30 03:40:30 +03:00
var xorganizationField = webCertificate . subject . getField ( "O" ) ;
2017-10-19 02:28:05 +03:00
if ( xorganizationField != null ) { organization = xorganizationField . value ; }
}
2017-08-28 19:27:45 +03:00
var rootCertAndKey , rootCertificate , rootPrivateKey , rootName ;
2018-09-01 23:00:53 +03:00
if ( r . root == null ) {
2017-08-28 19:27:45 +03:00
// If the root certificate does not exist, create one
2018-08-30 03:40:30 +03:00
console . log ( "Generating root certificate..." ) ;
rootCertAndKey = obj . GenerateRootCertificate ( true , "MeshCentralRoot" , null , null , strongCertificate ) ;
2017-08-28 19:27:45 +03:00
rootCertificate = obj . pki . certificateToPem ( rootCertAndKey . cert ) ;
rootPrivateKey = obj . pki . privateKeyToPem ( rootCertAndKey . key ) ;
2018-08-30 03:40:30 +03:00
obj . fs . writeFileSync ( parent . getConfigFilePath ( "root-cert-public.crt" ) , rootCertificate ) ;
obj . fs . writeFileSync ( parent . getConfigFilePath ( "root-cert-private.key" ) , rootPrivateKey ) ;
2017-08-28 19:27:45 +03: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-30 03:40:30 +03:00
rootCertificate = r . root . cert ;
rootPrivateKey = r . root . key ;
2017-08-28 19:27:45 +03:00
}
2018-08-30 03:40:30 +03:00
var rootName = rootCertAndKey . cert . subject . getField ( "CN" ) . value ;
2017-08-28 19:27:45 +03:00
// If the web certificate does not exist, create one
var webCertAndKey , webCertificate , webPrivateKey ;
2017-10-11 22:20:59 +03:00
if ( ( r . web == null ) || ( forceWebCertGen == 1 ) ) {
2018-08-30 03:40:30 +03:00
console . log ( "Generating HTTPS certificate..." ) ;
2017-10-19 02:28:05 +03:00
webCertAndKey = obj . IssueWebServerCertificate ( rootCertAndKey , false , commonName , country , organization , null , strongCertificate ) ;
2017-09-30 00:26:51 +03:00
webCertificate = obj . pki . certificateToPem ( webCertAndKey . cert ) ;
webPrivateKey = obj . pki . privateKeyToPem ( webCertAndKey . key ) ;
2018-08-30 03:40:30 +03:00
obj . fs . writeFileSync ( parent . getConfigFilePath ( "webserver-cert-public.crt" ) , webCertificate ) ;
obj . fs . writeFileSync ( parent . getConfigFilePath ( "webserver-cert-private.key" ) , webPrivateKey ) ;
2017-09-30 00:26:51 +03:00
} else {
// Keep the console certificate we have
webCertAndKey = { cert : obj . pki . certificateFromPem ( r . web . cert ) , key : obj . pki . privateKeyFromPem ( r . web . key ) } ;
2018-08-30 03:40:30 +03:00
webCertificate = r . web . cert ;
webPrivateKey = r . web . key ;
2017-09-30 00:26:51 +03:00
}
2018-08-30 03:40:30 +03:00
var webIssuer = webCertAndKey . cert . issuer . getField ( "CN" ) . value ;
2017-09-30 00:26:51 +03:00
2017-10-19 02:28:05 +03:00
// If the mesh agent server certificate does not exist, create one
var agentCertAndKey , agentCertificate , agentPrivateKey ;
if ( r . agent == null ) {
2018-08-30 03:40:30 +03:00
console . log ( "Generating MeshAgent certificate..." ) ;
2018-12-22 01:39:26 +03:00
agentCertAndKey = obj . IssueWebServerCertificate ( rootCertAndKey , true , "MeshCentralAgentServer" , country , organization , { } , strongCertificate ) ;
2017-10-19 02:28:05 +03:00
agentCertificate = obj . pki . certificateToPem ( agentCertAndKey . cert ) ;
agentPrivateKey = obj . pki . privateKeyToPem ( agentCertAndKey . key ) ;
2018-08-30 03:40:30 +03:00
obj . fs . writeFileSync ( parent . getConfigFilePath ( "agentserver-cert-public.crt" ) , agentCertificate ) ;
obj . fs . writeFileSync ( parent . getConfigFilePath ( "agentserver-cert-private.key" ) , agentPrivateKey ) ;
2017-10-19 02:28:05 +03: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-30 03:40:30 +03:00
agentCertificate = r . agent . cert ;
agentPrivateKey = r . agent . key ;
2017-10-19 02:28:05 +03:00
}
2017-08-28 19:27:45 +03:00
// If the Intel AMT MPS certificate does not exist, create one
var mpsCertAndKey , mpsCertificate , mpsPrivateKey ;
2018-03-07 04:50:44 +03:00
if ( ( r . mps == null ) || ( forceMpsCertGen == 1 ) ) {
2018-08-30 03:40:30 +03:00
console . log ( "Generating Intel AMT MPS certificate..." ) ;
2018-03-07 04:50:44 +03:00
mpsCertAndKey = obj . IssueWebServerCertificate ( rootCertAndKey , false , mpsCommonName , mpsCountry , mpsOrganization , null , false ) ;
2017-09-30 00:26:51 +03:00
mpsCertificate = obj . pki . certificateToPem ( mpsCertAndKey . cert ) ;
mpsPrivateKey = obj . pki . privateKeyToPem ( mpsCertAndKey . key ) ;
2018-08-30 03:40:30 +03:00
obj . fs . writeFileSync ( parent . getConfigFilePath ( "mpsserver-cert-public.crt" ) , mpsCertificate ) ;
obj . fs . writeFileSync ( parent . getConfigFilePath ( "mpsserver-cert-private.key" ) , mpsPrivateKey ) ;
2017-09-30 00:26:51 +03:00
} else {
// Keep the console certificate we have
mpsCertAndKey = { cert : obj . pki . certificateFromPem ( r . mps . cert ) , key : obj . pki . privateKeyFromPem ( r . mps . key ) } ;
2018-08-30 03:40:30 +03:00
mpsCertificate = r . mps . cert ;
mpsPrivateKey = r . mps . key ;
2017-09-30 00:26:51 +03:00
}
2018-12-20 23:12:24 +03: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-03 03:52:49 +03:00
2019-03-10 01:28:08 +03:00
// Fetch the certificates names for the main certificate
var webCertificate = obj . pki . certificateFromPem ( r . web . cert ) ;
r . WebIssuer = webCertificate . issuer . getField ( "CN" ) . value ;
r . CommonName = webCertificate . subject . getField ( "CN" ) . value ;
if ( r . CommonName . startsWith ( '*.' ) ) {
if ( commonName . indexOf ( '.' ) == - 1 ) { console . log ( "ERROR: Must specify a server full domain name in Config.json->Settings->Cert when using a wildcard certificate." ) ; process . exit ( 0 ) ; return ; }
if ( commonName . startsWith ( '*.' ) ) { console . log ( "ERROR: Server can't use a wildcard name: " + commonName ) ; process . exit ( 0 ) ; return ; }
r . CommonName = commonName ;
}
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 ( ) ) ; } }
var rootCertificate = obj . pki . certificateFromPem ( r . root . cert ) ;
r . RootName = rootCertificate . subject . getField ( "CN" ) . value ;
2018-01-03 03:52:49 +03:00
// Look for domains with DNS names that have no certificates and generated them.
2018-09-01 02:43:09 +03:00
for ( i in config . domains ) {
2018-08-30 03:40:30 +03:00
if ( ( i != "" ) && ( config . domains [ i ] != null ) && ( config . domains [ i ] . dns != null ) ) {
dnsname = config . domains [ i ] . dns ;
2019-03-10 01:28:08 +03:00
// Check if this domain matches a parent wildcard cert, if so, use the parent cert.
if ( compareCertificateNames ( r . CommonNames , dnsname ) == true ) {
r . dns [ i ] = { cert : obj . fileLoad ( "webserver-cert-public.crt" , "utf8" ) , key : obj . fileLoad ( "webserver-cert-private.key" , "utf8" ) } ;
} else {
if ( ! args . tlsoffload ) {
// 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 ) ) {
console . log ( "Generating HTTPS certificate for " + i + "..." ) ;
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 ) ;
obj . fs . writeFileSync ( parent . getConfigFilePath ( "webserver-" + i + "-cert-public.crt" ) , xwebCertificate ) ;
obj . fs . writeFileSync ( parent . getConfigFilePath ( "webserver-" + i + "-cert-private.key" ) , xwebPrivateKey ) ;
r . dns [ i ] = { cert : xwebCertificate , key : xwebPrivateKey } ;
config . domains [ i ] . certs = r . dns [ i ] ;
// If CA certificates are present, load them
caindex = 1 ;
r . dns [ i ] . ca = [ ] ;
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" ) ) ) ;
caok = true ;
}
caindex ++ ;
} while ( caok === true ) ;
}
2018-01-03 03:52:49 +03:00
}
}
}
}
2018-03-07 04:50:44 +03:00
// If the swarm server certificate exist, load it (This is an optional certificate)
2019-02-03 01:54:36 +03:00
if ( obj . fileExists ( "swarmserver-cert-public.crt" ) && obj . fileExists ( "swarmserver-cert-private.key" ) ) {
2019-01-12 01:01:36 +03:00
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-07 04:50:44 +03:00
}
// If the swarm server root certificate exist, load it (This is an optional certificate)
2019-02-03 01:54:36 +03:00
if ( obj . fileExists ( "swarmserverroot-cert-public.crt" ) ) {
2019-01-12 01:01:36 +03:00
r . swarmserverroot = { cert : fixEndOfLines ( obj . fs . readFileSync ( parent . getConfigFilePath ( "swarmserverroot-cert-public.crt" ) , "utf8" ) ) } ;
2018-03-07 04:50:44 +03:00
}
// If CA certificates are present, load them
if ( r . web != null ) {
2018-08-30 03:40:30 +03:00
caindex = 1 ;
r . web . ca = [ ] ;
2018-03-07 04:50:44 +03:00
do {
caok = false ;
2019-02-03 01:54:36 +03:00
if ( obj . fileExists ( "webserver-cert-chain" + caindex + ".crt" ) ) {
2019-01-12 01:01:36 +03:00
r . web . ca . push ( fixEndOfLines ( obj . fs . readFileSync ( parent . getConfigFilePath ( "webserver-cert-chain" + caindex + ".crt" ) , "utf8" ) ) ) ;
2018-03-07 04:50:44 +03:00
caok = true ;
}
caindex ++ ;
2018-08-30 03:40:30 +03:00
} while ( caok === true ) ;
2018-03-07 04:50:44 +03:00
}
2017-08-28 19:27:45 +03:00
if ( func != undefined ) { func ( r ) ; }
return r ;
2018-08-30 03:40:30 +03:00
} ;
2017-08-28 19:27:45 +03:00
2018-01-12 22:41:26 +03:00
// Accelerators, used to dispatch work to other processes
2018-08-30 03:40:30 +03:00
const fork = require ( "child_process" ) . fork ;
const program = require ( "path" ) . join ( _ _dirname , "meshaccelerator.js" ) ;
const acceleratorTotalCount = require ( "os" ) . cpus ( ) . length ;
2018-01-12 22:41:26 +03:00
var acceleratorCreateCount = acceleratorTotalCount ;
2018-01-10 07:13:41 +03:00
var freeAccelerators = [ ] ;
2018-01-12 22:41:26 +03:00
var pendingAccelerator = [ ] ;
obj . acceleratorCertStore = null ;
2018-01-10 07:13:41 +03:00
// Create a new accelerator module
2018-01-12 22:41:26 +03:00
obj . getAccelerator = function ( ) {
if ( obj . acceleratorCertStore == null ) { return null ; }
2018-01-10 07:13:41 +03:00
if ( freeAccelerators . length > 0 ) { return freeAccelerators . pop ( ) ; }
if ( acceleratorCreateCount > 0 ) {
2018-01-12 22:41:26 +03:00
acceleratorCreateCount -- ;
2018-08-30 03:40:30 +03:00
var accelerator = fork ( program , [ ] , { stdio : [ "pipe" , "pipe" , "pipe" , "ipc" ] } ) ;
2018-08-21 21:02:35 +03:00
accelerator . accid = acceleratorCreateCount ;
2018-08-30 03:40:30 +03:00
accelerator . on ( "message" , function ( message ) {
2018-08-21 21:02:35 +03:00
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-30 03:40:30 +03:00
accelerator . send ( { action : "setState" , certs : obj . acceleratorCertStore } ) ;
2018-01-10 07:13:41 +03:00
return accelerator ;
2018-08-30 03:40:30 +03:00
2018-01-10 07:13:41 +03:00
}
return null ;
2018-08-30 03:40:30 +03:00
} ;
2018-01-10 07:13:41 +03:00
2018-08-30 03:40:30 +03:00
// Set the state of the accelerators. This way, we don"t have to send certificate & keys to them each time.
2018-01-12 22:41:26 +03:00
obj . acceleratorStart = function ( certificates ) {
2018-08-30 03:40:30 +03:00
if ( obj . acceleratorCertStore != null ) { console . error ( "ERROR: Accelerators can only be started once." ) ; return ; }
2018-01-10 07:13:41 +03: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-30 03:40:30 +03:00
} ;
2018-01-10 07:13:41 +03:00
// Perform any RSA signature, just pass in the private key and data.
2018-08-21 21:02:35 +03:00
obj . acceleratorPerformSignature = function ( privatekey , data , tag , func ) {
2018-01-12 22:41:26 +03:00
if ( acceleratorTotalCount <= 1 ) {
2018-01-10 07:13:41 +03:00
// No accelerators available
2018-08-30 03:40:30 +03:00
if ( typeof privatekey == "number" ) { privatekey = obj . acceleratorCertStore [ privatekey ] . key ; }
const sign = obj . crypto . createSign ( "SHA384" ) ;
2019-01-03 05:03:34 +03:00
sign . end ( Buffer . from ( data , "binary" ) ) ;
2018-08-30 03:40:30 +03:00
func ( tag , sign . sign ( privatekey ) . toString ( "binary" ) ) ;
2018-01-10 07:13:41 +03:00
} else {
2018-01-12 22:41:26 +03:00
var acc = obj . getAccelerator ( ) ;
if ( acc == null ) {
// Add to pending accelerator workload
2018-08-30 03:40:30 +03:00
pendingAccelerator . push ( { action : "sign" , key : privatekey , data : data , tag : tag } ) ;
2018-01-12 22:41:26 +03:00
} else {
// Send to accelerator now
acc . func = func ;
2018-08-21 21:02:35 +03:00
acc . tag = tag ;
2018-08-30 03:40:30 +03:00
acc . send ( { action : "sign" , key : privatekey , data : data } ) ;
2018-01-12 22:41:26 +03:00
}
2018-01-10 07:13:41 +03:00
}
2018-08-30 03:40:30 +03:00
} ;
2018-01-10 07:13:41 +03:00
2017-08-28 19:27:45 +03:00
return obj ;
} ;