diff options
9 files changed, 557 insertions, 66 deletions
diff --git a/base/common/python/pki/nss.py b/base/common/python/pki/nss.py index f36b771f8..196fe462f 100644 --- a/base/common/python/pki/nss.py +++ b/base/common/python/pki/nss.py @@ -32,12 +32,19 @@ CSR_FOOTER = '-----END NEW CERTIFICATE REQUEST-----' CERT_HEADER = '-----BEGIN CERTIFICATE-----' CERT_FOOTER = '-----END CERTIFICATE-----' +PKCS7_HEADER = '-----BEGIN PKCS7-----' +PKCS7_FOOTER = '-----END PKCS7-----' + def convert_data(data, input_format, output_format, header=None, footer=None): + if input_format == output_format: + return data + if input_format == 'base64' and output_format == 'pem': # split a single line into multiple lines + data = data.rstrip('\r\n') lines = [data[i:i+64] for i in range(0, len(data), 64)] return '%s\n%s\n%s\n' % (header, '\n'.join(lines), footer) @@ -65,11 +72,32 @@ def convert_cert(cert_data, input_format, output_format): return convert_data(cert_data, input_format, output_format, CERT_HEADER, CERT_FOOTER) +def convert_pkcs7(pkcs7_data, input_format, output_format): + + return convert_data(pkcs7_data, input_format, output_format, PKCS7_HEADER, PKCS7_FOOTER) + +def get_file_type(filename): + + with open(filename, 'r') as f: + data = f.read() + + if data.startswith(CSR_HEADER): + return 'csr' + + if data.startswith(CERT_HEADER): + return 'cert' + + if data.startswith(PKCS7_HEADER): + return 'pkcs7' + + return None + class NSSDatabase(object): - def __init__(self, directory, password=None, password_file=None): + def __init__(self, directory, token='internal', password=None, password_file=None): self.directory = directory + self.token = token self.tmpdir = tempfile.mkdtemp() @@ -88,29 +116,38 @@ class NSSDatabase(object): shutil.rmtree(self.tmpdir) def add_cert(self, - nickname, cert_file, - trust_attributes='u,u,u'): + nickname, + cert_file, + trust_attributes=',,'): - subprocess.check_call([ + cmd = [ 'certutil', '-A', '-d', self.directory, + '-h', self.token, + '-f', self.password_file, '-n', nickname, '-i', cert_file, '-t', trust_attributes - ]) + ] + + subprocess.check_call(cmd) def modify_cert(self, nickname, - trust_attributes='u,u,u'): + trust_attributes): - subprocess.check_call([ + cmd = [ 'certutil', '-M', '-d', self.directory, + '-h', self.token, + '-f', self.password_file, '-n', nickname, '-t', trust_attributes - ]) + ] + + subprocess.check_call(cmd) def create_noise(self, noise_file, size=2048): @@ -123,27 +160,56 @@ class NSSDatabase(object): def create_request(self, subject_dn, - noise_file, - request_file): + request_file, + noise_file=None, + key_type=None, + key_size=None, + curve=None, + hash_alg=None): tmpdir = tempfile.mkdtemp() try: + if not noise_file: + noise_file = os.path.join(tmpdir, 'noise.bin') + if key_size: + size = key_size + else: + size = 2048 + self.create_noise( + noise_file=noise_file, + size=size) + binary_request_file = os.path.join(tmpdir, 'request.bin') - b64_request_file = os.path.join(tmpdir, 'request.b64') - # generate binary request - subprocess.check_call([ + cmd = [ 'certutil', '-R', '-d', self.directory, + '-h', self.token, '-f', self.password_file, '-s', subject_dn, - '-z', noise_file, - '-o', binary_request_file - ]) + '-o', binary_request_file, + '-z', noise_file + ] + + if key_type: + cmd.extend(['-k', key_type]) + + if key_size: + cmd.extend(['-g', str(key_size)]) + + if curve: + cmd.extend(['-q', curve]) + + if hash_alg: + cmd.extend(['-Z', hash_alg]) + + # generate binary request + subprocess.check_call(cmd) # encode binary request in base-64 + b64_request_file = os.path.join(tmpdir, 'request.b64') subprocess.check_call([ 'BtoA', binary_request_file, b64_request_file]) @@ -167,11 +233,12 @@ class NSSDatabase(object): serial='1', validity=240): - p = subprocess.Popen([ + cmd = [ 'certutil', '-C', '-x', '-d', self.directory, + '-h', self.token, '-f', self.password_file, '-c', subject_dn, '-a', @@ -184,7 +251,9 @@ class NSSDatabase(object): '-3', '--extSKID', '--extAIA' - ], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + ] + + p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) keystroke = '' @@ -245,7 +314,7 @@ class NSSDatabase(object): rc = p.wait() if rc: - raise Exception('Failed to generate self-signed CA certificate. RC: %d' + rc) + raise Exception('Failed to generate self-signed CA certificate. RC: %d' % rc) def get_cert(self, nickname, output_format='pem'): @@ -258,13 +327,17 @@ class NSSDatabase(object): else: raise Exception('Unsupported output format: %s' % output_format) - cert_data = subprocess.check_output([ + cmd = [ 'certutil', '-L', '-d', self.directory, + '-h', self.token, + '-f', self.password_file, '-n', nickname, output_format_option - ]) + ] + + cert_data = subprocess.check_output(cmd) if output_format == 'base64': cert_data = base64.b64encode(cert_data) @@ -273,12 +346,127 @@ class NSSDatabase(object): def remove_cert(self, nickname): - subprocess.check_call([ + cmd = [ 'certutil', '-D', '-d', self.directory, + '-h', self.token, + '-f', self.password_file, '-n', nickname - ]) + ] + + subprocess.check_call(cmd) + + def import_cert_chain(self, nickname, cert_chain_file, trust_attributes=None): + + tmpdir = tempfile.mkdtemp() + + try: + file_type = get_file_type(cert_chain_file) + + if file_type == 'cert': # import single PEM cert + self.add_cert( + nickname=nickname, + cert_file=cert_chain_file, + trust_attributes=trust_attributes) + return self.get_cert( + nickname=nickname, + output_format='base64') + + elif file_type == 'pkcs7': # import PKCS #7 cert chain + return self.import_pkcs7( + pkcs7_file=cert_chain_file, + nickname=nickname, + trust_attributes=trust_attributes, + output_format='base64') + + else: # import PKCS #7 data without header/footer + with open(cert_chain_file, 'r') as f: + base64_data = f.read() + pkcs7_data = convert_pkcs7(base64_data, 'base64', 'pem') + + tmp_cert_chain_file = os.path.join(tmpdir, 'cert_chain.p7b') + with open(tmp_cert_chain_file, 'w') as f: + f.write(pkcs7_data) + + self.import_pkcs7( + pkcs7_file=tmp_cert_chain_file, + nickname=nickname, + trust_attributes=trust_attributes) + + return base64_data + + finally: + shutil.rmtree(tmpdir) + + def import_pkcs7(self, pkcs7_file, nickname, trust_attributes=None, output_format='pem'): + + tmpdir = tempfile.mkdtemp() + + try: + # export certs from PKCS #7 into PEM output + output = subprocess.check_output([ + 'openssl', + 'pkcs7', + '-print_certs', + '-in', pkcs7_file + ]) + + # parse PEM output into separate PEM certificates + certs = [] + lines = [] + state = 'header' + + for line in output.splitlines(): + + if state == 'header': + if line != CERT_HEADER: + # ignore header lines + pass + else: + # save cert header + lines.append(line) + state = 'body' + + elif state == 'body': + if line != CERT_FOOTER: + # save cert body + lines.append(line) + else: + # save cert footer + lines.append(line) + + # construct PEM cert + cert = '\n'.join(lines) + certs.append(cert) + lines = [] + state = 'header' + + # import PEM certs into NSS database + counter = 1 + for cert in certs: + + cert_file = os.path.join(tmpdir, 'cert%d.pem' % counter) + with open(cert_file, 'w') as f: + f.write(cert) + + if counter == 1: + n = nickname + else: + n = '%s #%d' % (nickname, counter) + + self.add_cert(n, cert_file, trust_attributes) + + counter += 1 + + # convert PKCS #7 data to the requested format + with open(pkcs7_file, 'r') as f: + data = f.read() + + return convert_pkcs7(data, 'pem', output_format) + + finally: + shutil.rmtree(tmpdir) def import_pkcs12(self, pkcs12_file, pkcs12_password=None, pkcs12_password_file=None): @@ -296,13 +484,16 @@ class NSSDatabase(object): else: raise Exception('Missing PKCS #12 password') - subprocess.check_call([ + cmd = [ 'pk12util', '-d', self.directory, + '-h', self.token, '-k', self.password_file, '-i', pkcs12_file, '-w', password_file - ]) + ] + + subprocess.check_call(cmd) finally: shutil.rmtree(tmpdir) @@ -323,14 +514,16 @@ class NSSDatabase(object): else: raise Exception('Missing PKCS #12 password') - subprocess.check_call([ + cmd = [ 'pk12util', '-d', self.directory, '-k', self.password_file, '-o', pkcs12_file, '-w', password_file, '-n', nickname - ]) + ] + + subprocess.check_call(cmd) finally: shutil.rmtree(tmpdir) diff --git a/base/common/src/com/netscape/certsrv/system/ConfigurationRequest.java b/base/common/src/com/netscape/certsrv/system/ConfigurationRequest.java index 7c6c339f5..8c9da6f37 100644 --- a/base/common/src/com/netscape/certsrv/system/ConfigurationRequest.java +++ b/base/common/src/com/netscape/certsrv/system/ConfigurationRequest.java @@ -178,6 +178,9 @@ public class ConfigurationRequest { protected String adminCert; @XmlElement + protected Boolean external; + + @XmlElement protected String standAlone; @XmlElement @@ -754,6 +757,14 @@ public class ConfigurationRequest { this.adminCert = adminCert; } + public Boolean isExternal() { + return external; + } + + public void setExternal(Boolean external) { + this.external = external; + } + public boolean getStandAlone() { return (standAlone != null && standAlone.equalsIgnoreCase("true")); } @@ -945,6 +956,7 @@ public class ConfigurationRequest { ", adminCert=" + adminCert + ", importAdminCert=" + importAdminCert + ", generateServerCert=" + generateServerCert + + ", external=" + external + ", standAlone=" + standAlone + ", stepTwo=" + stepTwo + ", authdbBaseDN=" + authdbBaseDN + diff --git a/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java b/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java index 88118adf8..91dad159b 100644 --- a/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java +++ b/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java @@ -126,6 +126,7 @@ import com.netscape.certsrv.base.EBaseException; import com.netscape.certsrv.base.EPropertyNotFound; import com.netscape.certsrv.base.IConfigStore; import com.netscape.certsrv.base.ISubsystem; +import com.netscape.certsrv.base.MetaInfo; import com.netscape.certsrv.base.PKIException; import com.netscape.certsrv.base.ResourceNotFoundException; import com.netscape.certsrv.ca.ICertificateAuthority; @@ -133,6 +134,8 @@ import com.netscape.certsrv.client.ClientConfig; import com.netscape.certsrv.client.PKIClient; import com.netscape.certsrv.client.PKIConnection; import com.netscape.certsrv.dbs.IDBSubsystem; +import com.netscape.certsrv.dbs.certdb.ICertRecord; +import com.netscape.certsrv.dbs.certdb.ICertificateRepository; import com.netscape.certsrv.dbs.crldb.ICRLIssuingPointRecord; import com.netscape.certsrv.key.KeyData; import com.netscape.certsrv.ldap.ILdapConnFactory; @@ -2248,6 +2251,54 @@ public class ConfigurationUtils { certObj.setCertChain(certChainStr); } + public static KeyPair loadKeyPair(String nickname) throws Exception { + + CMS.debug("ConfigurationUtils: loadKeyPair(" + nickname + ")"); + + CryptoManager cm = CryptoManager.getInstance(); + + X509Certificate cert = cm.findCertByNickname(nickname); + PublicKey publicKey = cert.getPublicKey(); + PrivateKey privateKey = cm.findPrivKeyByCert(cert); + + return new KeyPair(publicKey, privateKey); + } + + public static void storeKeyPair(IConfigStore config, String tag, KeyPair pair) + throws TokenException, EBaseException { + + CMS.debug("ConfigurationUtils: storeKeyPair(" + tag + ")"); + + PublicKey publicKey = pair.getPublic(); + + if (publicKey instanceof RSAPublicKey) { + + RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey; + + byte modulus[] = rsaPublicKey.getModulus().toByteArray(); + config.putString(PCERT_PREFIX + tag + ".pubkey.modulus", + CryptoUtil.byte2string(modulus)); + + byte exponent[] = rsaPublicKey.getPublicExponent().toByteArray(); + config.putString(PCERT_PREFIX + tag + ".pubkey.exponent", + CryptoUtil.byte2string(exponent)); + + } else { // ECC + + CMS.debug("ConfigurationUtils: Public key class: " + publicKey.getClass().getName()); + byte encoded[] = publicKey.getEncoded(); + config.putString(PCERT_PREFIX + tag + ".pubkey.encoded", CryptoUtil.byte2string(encoded)); + } + + PrivateKey privateKey = (PrivateKey) pair.getPrivate(); + byte id[] = privateKey.getUniqueID(); + String kid = CryptoUtil.byte2string(id); + config.putString(PCERT_PREFIX + tag + ".privkey.id", kid); + + String keyAlgo = config.getString(PCERT_PREFIX + tag + ".signingalgorithm"); + setSigningAlgorithm(tag, keyAlgo, config); + } + public static void createECCKeyPair(String token, String curveName, IConfigStore config, String ct) throws NoSuchAlgorithmException, NoSuchTokenException, TokenException, CryptoManager.NotInitializedException, EPropertyNotFound, EBaseException { @@ -2812,6 +2863,20 @@ public class ConfigurationUtils { } } + public static void loadCertRequest(IConfigStore config, String tag, Cert cert) throws Exception { + + CMS.debug("ConfigurationUtils.loadCertRequest(" + tag + ")"); + + String subjectDN = config.getString(PCERT_PREFIX + tag + ".dn"); + cert.setDN(subjectDN); + + String subsystem = config.getString(PCERT_PREFIX + tag + ".subsystem"); + String certreq = config.getString(subsystem + "." + tag + ".certreq"); + String formattedCertreq = CryptoUtil.reqFormat(certreq); + + cert.setRequest(formattedCertreq); + } + public static void handleCertRequest(IConfigStore config, String certTag, Cert cert) throws EPropertyNotFound, EBaseException, InvalidKeyException, NotInitializedException, TokenException, NoSuchAlgorithmException, NoSuchProviderException, CertificateException, SignatureException, IOException { @@ -2953,6 +3018,42 @@ public class ConfigurationUtils { return pubk; } + public static void loadCert(IConfigStore config, Cert cert) throws Exception { + + String tag = cert.getCertTag(); + CMS.debug("ConfigurationUtils: loadCert(" + tag + ")"); + + CryptoManager cm = CryptoManager.getInstance(); + X509Certificate x509Cert = cm.findCertByNickname(cert.getNickname()); + + if (!x509Cert.getSubjectDN().equals(x509Cert.getIssuerDN())) { + CMS.debug("ConfigurationUtils: " + tag + " cert is not self-signed"); + + String subsystem = config.getString(PCERT_PREFIX + tag + ".subsystem"); + String certChain = config.getString(subsystem + ".external_ca_chain.cert"); + cert.setCertChain(certChain); + + return; + } + + CMS.debug("ConfigurationUtils: " + tag + " cert is self-signed"); + + // When importing existing self-signed CA certificate, create a + // certificate record to reserve the serial number. Otherwise it + // might conflict with system certificates to be created later. + + X509CertImpl x509CertImpl = new X509CertImpl(x509Cert.getEncoded()); + + ICertificateAuthority ca = (ICertificateAuthority) CMS.getSubsystem(ICertificateAuthority.ID); + ICertificateRepository cr = ca.getCertificateRepository(); + + BigInteger serialNo = x509Cert.getSerialNumber(); + MetaInfo meta = new MetaInfo(); + + ICertRecord record = cr.createCertRecord(serialNo, x509CertImpl, meta); + cr.addCertificateRecord(record); + } + public static int handleCerts(Cert cert) throws IOException, EBaseException, CertificateException, NotInitializedException, TokenException, InvalidKeyException { String certTag = cert.getCertTag(); diff --git a/base/server/cms/src/org/dogtagpki/server/rest/SystemConfigService.java b/base/server/cms/src/org/dogtagpki/server/rest/SystemConfigService.java index a0138681a..697196a6e 100644 --- a/base/server/cms/src/org/dogtagpki/server/rest/SystemConfigService.java +++ b/base/server/cms/src/org/dogtagpki/server/rest/SystemConfigService.java @@ -20,6 +20,7 @@ package org.dogtagpki.server.rest; import java.math.BigInteger; import java.net.MalformedURLException; import java.net.URL; +import java.security.KeyPair; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.util.ArrayList; @@ -420,7 +421,13 @@ public class SystemConfigService extends PKIService implements SystemConfigResou } cs.commit(false); - if (!request.getStepTwo()) { + if (request.isExternal() && tag.equals("signing")) { // external/existing CA + // load key pair for existing and externally-signed signing cert + CMS.debug("SystemConfigService: loading signing cert key pair"); + KeyPair pair = ConfigurationUtils.loadKeyPair(certData.getNickname()); + ConfigurationUtils.storeKeyPair(cs, tag, pair); + + } else if (!request.getStepTwo()) { if (keytype.equals("ecc")) { String curvename = certData.getKeyCurveName() != null ? certData.getKeyCurveName() : cs.getString("keys.ecc.curve.default"); @@ -443,7 +450,15 @@ public class SystemConfigService extends PKIService implements SystemConfigResou cert.setSubsystem(cs.getString("preop.cert." + tag + ".subsystem")); cert.setType(cs.getString("preop.cert." + tag + ".type")); - if (!request.getStepTwo()) { + if (request.isExternal() && tag.equals("signing")) { // external/existing CA + + // update configuration for existing or externally-signed signing certificate + String certStr = cs.getString("ca." + tag + ".cert" ); + cert.setCert(certStr); + CMS.debug("SystemConfigService: certificate " + tag + ": " + certStr); + ConfigurationUtils.updateConfig(cs, tag); + + } else if (!request.getStepTwo()) { ConfigurationUtils.configCert(null, null, null, cert); } else { @@ -465,8 +480,16 @@ public class SystemConfigService extends PKIService implements SystemConfigResou CMS.debug("Step 2: certStr for '" + tag + "' is " + certStr); } - // Handle Cert Requests for everything EXCEPT Stand-alone PKI (Step 2) - if (request.getStandAlone()) { + if (request.isExternal() && tag.equals("signing")) { // external/existing CA + + CMS.debug("SystemConfigService: Loading cert request for " + tag + " cert"); + ConfigurationUtils.loadCertRequest(cs, tag, cert); + + CMS.debug("SystemConfigService: Loading cert " + tag); + ConfigurationUtils.loadCert(cs, cert); + + } else if (request.getStandAlone()) { + // Handle Cert Requests for everything EXCEPT Stand-alone PKI (Step 2) if (!request.getStepTwo()) { // Stand-alone PKI (Step 1) ConfigurationUtils.handleCertRequest(cs, tag, cert); @@ -489,6 +512,13 @@ public class SystemConfigService extends PKIService implements SystemConfigResou ConfigurationUtils.updateCloneConfig(); } + if (request.isExternal() && tag.equals("signing")) { // external/existing CA + CMS.debug("SystemConfigService: External CA has signing cert"); + hasSigningCert.setValue(true); + certs.add(cert); + continue; + } + // to determine if we have the signing cert when using an external ca // this will only execute on a ca or stand-alone pki String b64 = certData.getCert(); diff --git a/base/server/etc/default.cfg b/base/server/etc/default.cfg index ddd2d8367..1c1ae92b3 100644 --- a/base/server/etc/default.cfg +++ b/base/server/etc/default.cfg @@ -22,6 +22,7 @@ sensitive_parameters= pki_client_pkcs12_password pki_clone_pkcs12_password pki_ds_password + pki_external_pkcs12_password pki_one_time_pin pki_pin pki_replication_password @@ -365,10 +366,13 @@ pki_req_ext_add=False pki_req_ext_oid=1.3.6.1.4.1.311.20.2 pki_req_ext_critical=False pki_req_ext_data=1E0A00530075006200430041 -pki_external_csr_path=%(pki_instance_configuration_path)s/ca_signing.csr +pki_external_csr_path= pki_external_step_two=False -pki_external_ca_cert_chain_path=%(pki_instance_configuration_path)s/external_ca_chain.cert -pki_external_ca_cert_path=%(pki_instance_configuration_path)s/external_ca.cert +pki_external_ca_cert_chain_path= +pki_external_ca_cert_chain_nickname=caSigningCert External CA +pki_external_ca_cert_path= +pki_external_pkcs12_path= +pki_external_pkcs12_password= pki_import_admin_cert=False pki_ocsp_signing_key_algorithm=SHA256withRSA pki_ocsp_signing_key_size=2048 diff --git a/base/server/python/pki/server/__init__.py b/base/server/python/pki/server/__init__.py index d55a3691d..bf592dcd5 100644 --- a/base/server/python/pki/server/__init__.py +++ b/base/server/python/pki/server/__init__.py @@ -328,10 +328,11 @@ class PKIInstance(object): return password - def open_nssdb(self): + def open_nssdb(self, token='internal'): return pki.nss.NSSDatabase( directory=self.nssdb_dir, - password=self.get_password('internal')) + token=token, + password=self.get_password(token)) def get_subsystem(self, name): for subsystem in self.subsystems: diff --git a/base/server/python/pki/server/deployment/pkihelper.py b/base/server/python/pki/server/deployment/pkihelper.py index 61f04d215..9c9b40454 100644 --- a/base/server/python/pki/server/deployment/pkihelper.py +++ b/base/server/python/pki/server/deployment/pkihelper.py @@ -757,8 +757,7 @@ class ConfigurationFile: # External CA if not self.external_step_two: # External CA (Step 1) - self.confirm_data_exists("pki_external_csr_path") - self.confirm_missing_file("pki_external_csr_path") + # The pki_external_csr_path is optional. # generic extension support in CSR - for external CA if self.add_req_ext: self.confirm_data_exists("pki_req_ext_oid") @@ -766,10 +765,9 @@ class ConfigurationFile: self.confirm_data_exists("pki_req_ext_data") else: # External CA (Step 2) - self.confirm_data_exists("pki_external_ca_cert_chain_path") - self.confirm_file_exists("pki_external_ca_cert_chain_path") - self.confirm_data_exists("pki_external_ca_cert_path") - self.confirm_file_exists("pki_external_ca_cert_path") + # The pki_external_ca_cert_chain_path and + # pki_external_ca_cert_path are optional. + pass elif not self.skip_configuration and self.standalone: if not self.external_step_two: # Stand-alone PKI Admin CSR (Step 1) @@ -3813,17 +3811,7 @@ class ConfigClient: if not isinstance(certs, list): certs = [certs] for cdata in certs: - if (self.subsystem == "CA" and self.external and - not self.external_step_two): - # External CA (Step 1) - if cdata['tag'].lower() == "signing": - # Save 'External CA Signing Certificate' CSR (Step 1) - self.save_system_csr( - cdata['request'], - log.PKI_CONFIG_EXTERNAL_CSR_SAVE, - self.mdict['pki_external_csr_path']) - return - elif self.standalone and not self.external_step_two: + if self.standalone and not self.external_step_two: # Stand-alone PKI (Step 1) if cdata['tag'].lower() == "audit_signing": # Save Stand-alone PKI 'Audit Signing Certificate' CSR @@ -3991,8 +3979,17 @@ class ConfigClient: data.token = self.mdict['pki_token_name'] data.tokenPassword = self.mdict['pki_token_password'] data.subsystemName = self.mdict['pki_subsystem_name'] + + data.external = self.external data.standAlone = self.standalone - data.stepTwo = self.external_step_two + + if self.standalone: + # standalone installation uses two-step process (ticket #1698) + data.stepTwo = self.external_step_two + + else: + # other installations use only one step in the configuration servlet + data.stepTwo = False # Cloning parameters if self.mdict['pki_instance_type'] == "Tomcat": @@ -4122,25 +4119,46 @@ class ConfigClient: self.mdict['pki_req_ext_critical'] cert1.req_ext_data = \ self.mdict['pki_req_ext_data'] - if self.external_step_two: - # External CA (Step 2) or Stand-alone PKI (Step 2) - if not self.subsystem == "CA": - # Stand-alone PKI (Step 2) - cert1 = pki.system.SystemCertData() - cert1.tag = self.mdict['pki_ca_signing_tag'] - # Load the External CA or Stand-alone PKI + + if self.external and self.external_step_two: # external/existing CA step 2 + + # If specified, load the externally-signed CA cert + if self.mdict['pki_external_ca_cert_path']: + self.load_system_cert( + cert1, + log.PKI_CONFIG_EXTERNAL_CA_LOAD, + self.mdict['pki_external_ca_cert_path']) + + # If specified, load the external CA cert chain + if self.mdict['pki_external_ca_cert_chain_path']: + self.load_system_cert_chain( + cert1, + log.PKI_CONFIG_EXTERNAL_CA_CHAIN_LOAD, + self.mdict['pki_external_ca_cert_chain_path']) + + systemCerts.append(cert1) + + elif self.standalone and self.external_step_two: # standalone KRA/OCSP step 2 + + cert1 = pki.system.SystemCertData() + cert1.tag = self.mdict['pki_ca_signing_tag'] + + # Load the stand-alone PKI # 'External CA Signing Certificate' (Step 2) self.load_system_cert( cert1, log.PKI_CONFIG_EXTERNAL_CA_LOAD, self.mdict['pki_external_ca_cert_path']) - # Load the External CA or Stand-alone PKI + + # Load the stand-alone PKI # 'External CA Signing Certificate Chain' (Step 2) self.load_system_cert_chain( cert1, log.PKI_CONFIG_EXTERNAL_CA_CHAIN_LOAD, self.mdict['pki_external_ca_cert_chain_path']) + systemCerts.append(cert1) + elif self.subsystem == "CA": # PKI CA or Subordinate CA systemCerts.append(cert1) diff --git a/base/server/python/pki/server/deployment/scriptlets/configuration.py b/base/server/python/pki/server/deployment/scriptlets/configuration.py index c6e890235..b8b8fc691 100644 --- a/base/server/python/pki/server/deployment/scriptlets/configuration.py +++ b/base/server/python/pki/server/deployment/scriptlets/configuration.py @@ -21,13 +21,18 @@ from __future__ import absolute_import import json +import re # PKI Deployment Imports from .. import pkiconfig as config from .. import pkimessages as log from .. import pkiscriptlet -import pki.system + import pki.encoder +import pki.nss +import pki.server +import pki.system +import pki.util # PKI Deployment Configuration Scriptlet @@ -81,6 +86,127 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet): deployer.mdict['pki_client_secmod_database'], password_file=deployer.mdict['pki_client_password_conf']) + instance = pki.server.PKIInstance(deployer.mdict['pki_instance_name']) + instance.load() + + subsystem = instance.get_subsystem(deployer.mdict['pki_subsystem'].lower()) + + token = deployer.mdict['pki_token_name'] + nssdb = instance.open_nssdb(token) + + external = config.str2bool(deployer.mdict['pki_external']) + step_one = not config.str2bool(deployer.mdict['pki_external_step_two']) + step_two = not step_one + + try: + if external and step_one: # external/existing CA step 1 + + key_type = deployer.mdict['pki_ca_signing_key_type'] + key_alg = deployer.mdict['pki_ca_signing_key_algorithm'] + + if key_type == 'rsa': + key_size = int(deployer.mdict['pki_ca_signing_key_size']) + curve = None + + m = re.match(r'(.*)withRSA', key_alg) + if not m: + raise Exception('Invalid key algorithm: %s' % key_alg) + hash_alg = m.group(1) + + elif key_type == 'ec' or key_type == 'ecc': + key_type = 'ec' + key_size = None + curve = deployer.mdict['pki_ca_signing_key_size'] + + m = re.match(r'(.*)withEC', key_alg) + if not m: + raise Exception('Invalid key algorithm: %s' % key_alg) + hash_alg = m.group(1) + + else: + raise Exception('Invalid key type: %s' % key_type) + + # If filename specified, generate CA cert request and + # import it into CS.cfg. + request_file = deployer.mdict['pki_external_csr_path'] + if request_file: + nssdb.create_request( + subject_dn=deployer.mdict['pki_ca_signing_subject_dn'], + request_file=request_file, + key_type=key_type, + key_size=key_size, + curve=curve, + hash_alg=hash_alg) + with open(request_file) as f: + signing_csr = f.read() + signing_csr = pki.nss.convert_csr(signing_csr, 'pem', 'base64') + subsystem.config['ca.signing.certreq'] = signing_csr + + subsystem.save() + + elif external and step_two: # external/existing CA step 2 + + # If specified, import existing CA cert request into CS.cfg. + request_file = deployer.mdict['pki_external_csr_path'] + if request_file: + with open(request_file) as f: + signing_csr = f.read() + signing_csr = pki.nss.convert_csr(signing_csr, 'pem', 'base64') + subsystem.config['ca.signing.certreq'] = signing_csr + + # If specified, import external CA cert into NSS database. + external_ca_cert_chain_nickname = deployer.mdict['pki_external_ca_cert_chain_nickname'] + external_ca_cert_chain_file = deployer.mdict['pki_external_ca_cert_chain_path'] + if external_ca_cert_chain_file: + cert_chain = nssdb.import_cert_chain( + nickname=external_ca_cert_chain_nickname, + cert_chain_file=external_ca_cert_chain_file, + trust_attributes='CT,C,C') + subsystem.config['ca.external_ca_chain.cert'] = cert_chain + + # If specified, import externally-signed CA cert into NSS database. + signing_nickname = deployer.mdict['pki_ca_signing_nickname'] + signing_cert_file = deployer.mdict['pki_external_ca_cert_path'] + if signing_cert_file: + nssdb.add_cert( + nickname=signing_nickname, + cert_file=signing_cert_file, + trust_attributes='CT,C,C') + + # If specified, import CA cert and key from PKCS #12 file into NSS database. + pkcs12_file = deployer.mdict['pki_external_pkcs12_path'] + if pkcs12_file: + pkcs12_password = deployer.mdict['pki_external_pkcs12_password'] + nssdb.import_pkcs12(pkcs12_file, pkcs12_password) + + # Export CA cert from NSS database and import it into CS.cfg. + signing_cert_data = nssdb.get_cert( + nickname=signing_nickname, + output_format='base64') + subsystem.config['ca.signing.nickname'] = signing_nickname + subsystem.config['ca.signing.tokenname'] = deployer.mdict['pki_ca_signing_token'] + subsystem.config['ca.signing.cert'] = signing_cert_data + subsystem.config['ca.signing.cacertnickname'] = signing_nickname + subsystem.config['ca.signing.defaultSigningAlgorithm'] = deployer.mdict['pki_ca_signing_signing_algorithm'] + + subsystem.save() + + else: # self-signed CA + + # To be implemented in ticket #1692. + + # Generate CA cert request. + # Self sign CA cert. + # Import self-signed CA cert into NSS database. + + pass + + finally: + nssdb.close() + + if external and step_one: + return self.rv + # Start/Restart this Tomcat PKI Process # Optionally prepare to enable a java debugger # (e. g. - 'eclipse'): diff --git a/base/server/python/pki/server/deployment/scriptlets/finalization.py b/base/server/python/pki/server/deployment/scriptlets/finalization.py index 56ddf0219..3c4f469ac 100644 --- a/base/server/python/pki/server/deployment/scriptlets/finalization.py +++ b/base/server/python/pki/server/deployment/scriptlets/finalization.py @@ -67,9 +67,15 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet): if len(deployer.instance.tomcat_instance_subsystems()) == 1: # Modify contents of 'serverCertNick.conf' (if necessary) deployer.servercertnick_conf.modify() - # Optionally, programmatically 'restart' the configured PKI instance - if config.str2bool(deployer.mdict['pki_restart_configured_instance']): - deployer.systemd.restart() + + external = config.str2bool(deployer.mdict['pki_external']) + step_one = not config.str2bool(deployer.mdict['pki_external_step_two']) + + if not (external and step_one): + # Optionally, programmatically 'restart' the configured PKI instance + if config.str2bool(deployer.mdict['pki_restart_configured_instance']): + deployer.systemd.restart() + # Optionally, 'purge' the entire temporary client infrastructure # including the client NSS security databases and password files # |