summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEndi S. Dewata <edewata@redhat.com>2015-11-07 00:09:19 +0100
committerMatthew Harmsen <mharmsen@pki.usersys.redhat.com>2016-02-22 20:19:30 -0700
commitbc0de424aa8c56d2278e41b7786ca202b7e64cc3 (patch)
tree35800e3d43bcdb58e7c561ab0a058674475aa7c7
parent4a81377c26e68c48b78c90f2a61970373dd1a6fa (diff)
downloadpki-bc0de424aa8c56d2278e41b7786ca202b7e64cc3.tar.gz
pki-bc0de424aa8c56d2278e41b7786ca202b7e64cc3.tar.xz
pki-bc0de424aa8c56d2278e41b7786ca202b7e64cc3.zip
Added mechanism to import existing CA certificate.
The deployment procedure for external CA has been modified such that it generates the CA CSR before starting the server. This allows the same procedure to be used to import CA certificate from an existing server. It also removes the requirement to keep the server running while waiting to get the CSR signed by an external CA. https://fedorahosted.org/pki/ticket/456 (cherry picked from commit 20c985ae773b26f653cac6d22bd9d93923e18c8e)
-rw-r--r--base/common/python/pki/nss.py247
-rw-r--r--base/common/src/com/netscape/certsrv/system/ConfigurationRequest.java12
-rw-r--r--base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java101
-rw-r--r--base/server/cms/src/org/dogtagpki/server/rest/SystemConfigService.java38
-rw-r--r--base/server/etc/default.cfg10
-rw-r--r--base/server/python/pki/server/__init__.py5
-rw-r--r--base/server/python/pki/server/deployment/pkihelper.py70
-rw-r--r--base/server/python/pki/server/deployment/scriptlets/configuration.py128
-rw-r--r--base/server/python/pki/server/deployment/scriptlets/finalization.py12
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 a981b9a76..32e10275f 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;
@@ -2266,6 +2269,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 {
@@ -2830,6 +2881,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 {
@@ -2971,6 +3036,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 0cc267826..5942115a3 100644
--- a/base/server/python/pki/server/__init__.py
+++ b/base/server/python/pki/server/__init__.py
@@ -304,10 +304,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 b6ee61b27..f349b74da 100644
--- a/base/server/python/pki/server/deployment/pkihelper.py
+++ b/base/server/python/pki/server/deployment/pkihelper.py
@@ -742,8 +742,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")
@@ -751,10 +750,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)
@@ -3779,17 +3777,7 @@ class ConfigClient:
if not isinstance(certs, types.ListType):
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
@@ -3956,8 +3944,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":
@@ -4085,25 +4082,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 fbcb1ccaa..6539de8e1 100644
--- a/base/server/python/pki/server/deployment/scriptlets/configuration.py
+++ b/base/server/python/pki/server/deployment/scriptlets/configuration.py
@@ -20,13 +20,18 @@
#
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
@@ -80,6 +85,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 b92965929..4c98cc499 100644
--- a/base/server/python/pki/server/deployment/scriptlets/finalization.py
+++ b/base/server/python/pki/server/deployment/scriptlets/finalization.py
@@ -65,9 +65,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
#