From 7da4d9802f058f2f78777928c7e259578ad6daef Mon Sep 17 00:00:00 2001
From: Christina Fu <cfu@redhat.com>
Date: Thu, 25 Sep 2014 14:26:11 -0700
Subject: [PATCH] ticket #1110 pkispawn (configuration) does not provide CA
extensions in subordinate certificate signing requests (CSR)
---
.../netscape/certsrv/system/SystemCertData.java | 40 ++++++++++++
.../cms/servlet/csadmin/ConfigurationUtils.java | 76 +++++++++++++++++++++-
.../cms/servlet/csadmin/SystemConfigService.java | 10 +++
base/server/etc/default.cfg | 5 ++
.../python/pki/server/deployment/pkihelper.py | 25 +++++++
.../python/pki/server/deployment/pkiparser.py | 3 +
.../com/netscape/cmsutil/crypto/CryptoUtil.java | 53 ++++++++++++++-
7 files changed, 208 insertions(+), 4 deletions(-)
diff --git a/base/common/src/com/netscape/certsrv/system/SystemCertData.java b/base/common/src/com/netscape/certsrv/system/SystemCertData.java
index a509e3f..064d8e1 100644
--- a/base/common/src/com/netscape/certsrv/system/SystemCertData.java
+++ b/base/common/src/com/netscape/certsrv/system/SystemCertData.java
@@ -43,6 +43,9 @@ public class SystemCertData {
public static final String SUBJECT_DN = "subjectDN";
public static final String CERT = "cert";
public static final String CERT_CHAIN = "certChain";
+ public static final String REQUEST_EXT_OID = "req_ext_oid";
+ public static final String REQUEST_EXT_CRITICAL = "req_ext_critial";
+ public static final String REQUEST_EXT_DATA = "req_ext_data";
@XmlElement
protected String tag;
@@ -80,6 +83,15 @@ public class SystemCertData {
@XmlElement
protected String certChain;
+ @XmlElement
+ protected String req_ext_oid;
+
+ @XmlElement
+ protected String req_ext_critical;
+
+ @XmlElement
+ protected String req_ext_data;
+
public SystemCertData() {
// required for JAXB
}
@@ -97,6 +109,10 @@ public class SystemCertData {
subjectDN = form.getFirst(SUBJECT_DN);
cert = form.getFirst(CERT);
certChain = form.getFirst(CERT_CHAIN);
+ //support extension in CSR
+ req_ext_oid = form.getFirst(REQUEST_EXT_OID);
+ req_ext_critical = form.getFirst(REQUEST_EXT_CRITICAL);
+ req_ext_data = form.getFirst(REQUEST_EXT_DATA);
}
/**
@@ -267,4 +283,28 @@ public class SystemCertData {
this.certChain = certChain;
}
+ /**
+ * @return the req_ext_oid
+ */
+ public String getReqExtOID() {
+ return req_ext_oid;
+ }
+
+ /**
+ * @return the req_ext_data
+ */
+ public String getReqExtData() {
+ return req_ext_data;
+ }
+
+ /**
+ * @return the req_ext_critical
+ */
+ public boolean getReqExtCritical() {
+ if (req_ext_critical.equals("true"))
+ return true;
+ else
+ return false;
+ }
+
}
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 9f112ea..2ac2344 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
@@ -71,8 +71,14 @@ import netscape.security.pkcs.ContentInfo;
import netscape.security.pkcs.PKCS10;
import netscape.security.pkcs.PKCS7;
import netscape.security.pkcs.SignerInfo;
+import netscape.security.util.DerOutputStream;
+import netscape.security.util.ObjectIdentifier;
import netscape.security.x509.AlgorithmId;
+import netscape.security.x509.BasicConstraintsExtension;
import netscape.security.x509.CertificateChain;
+import netscape.security.x509.Extension;
+import netscape.security.x509.Extensions;
+import netscape.security.x509.KeyUsageExtension;
import netscape.security.x509.X500Name;
import netscape.security.x509.X509CertImpl;
import netscape.security.x509.X509Key;
@@ -2598,6 +2604,7 @@ public class ConfigurationUtils {
EBaseException, InvalidKeyException, NotInitializedException, TokenException, NoSuchAlgorithmException,
NoSuchProviderException, CertificateException, SignatureException, IOException {
+ CMS.debug("ConfigurationUtils: handleCertRequest() begins");
// get public key
String pubKeyType = config.getString(PCERT_PREFIX + certTag + ".keytype");
String algorithm = config.getString(PCERT_PREFIX + certTag + ".keyalgorithm");
@@ -2631,7 +2638,12 @@ public class ConfigurationUtils {
String caDN = config.getString(PCERT_PREFIX + certTag + ".dn");
cert.setDN(caDN);
- PKCS10 certReq = CryptoUtil.createCertificationRequest(caDN, pubk, privk, algorithm);
+ Extensions exts = null;
+ if (certTag.equals("signing")) {
+ CMS.debug("handleCertRequest: certTag is siging -- about to call createBasicCAExtensions()");
+ exts = createBasicCAExtensions(config);
+ }
+ PKCS10 certReq = CryptoUtil.createCertificationRequest(caDN, pubk, privk, algorithm, exts);
CMS.debug("handleCertRequest: created cert request");
byte[] certReqb = certReq.toByteArray();
@@ -2645,6 +2657,68 @@ public class ConfigurationUtils {
}
+ /*
+ * createBasicCAExtensions creates the basic Extensions needed for a CSR to a
+ * CA signing certificate
+ */
+ private static Extensions createBasicCAExtensions(IConfigStore config) throws IOException {
+ Extensions exts = new Extensions();
+ CMS.debug("ConfigurationUtils: createBasicCAExtensions: begins");
+
+ // create BasicConstraintsExtension
+ BasicConstraintsExtension bcExt = new BasicConstraintsExtension(true, -1);
+ exts.add(bcExt);
+
+ // create KeyUsageExtension
+ boolean[] kuBits = new boolean[KeyUsageExtension.NBITS];
+ for (int i = 0; i < kuBits.length; i++) {
+ kuBits[i] = false;
+ }
+ kuBits[KeyUsageExtension.DIGITAL_SIGNATURE_BIT] = true;
+ kuBits[KeyUsageExtension.NON_REPUDIATION_BIT] = true;
+ kuBits[KeyUsageExtension.KEY_CERTSIGN_BIT] = true;
+ kuBits[KeyUsageExtension.CRL_SIGN_BIT] = true;
+ KeyUsageExtension kuExt = new KeyUsageExtension(true, kuBits);
+ exts.add(kuExt);
+ /* save this for later when we want to allow more selection for pkispawn configuration
+ // create NSCertTypeExtension
+ boolean[] nsBits = new boolean[NSCertTypeExtension.NBITS];
+ for (int i = 0; i < nsBits.length; i++) {
+ nsBits[i] = false;
+ }
+ nsBits[NSCertTypeExtension.SSL_CA_BIT] = true;
+ NSCertTypeExtension nsctExt = new NSCertTypeExtension(false, nsBits);
+ exts.add(nsctExt);
+ */
+
+ // add a generic extension
+ Extension genExt = null;
+ try {
+ String oidString = config.getString(PCERT_PREFIX + "signing.ext.oid");
+ String dataString = config.getString(PCERT_PREFIX + "signing.ext.data");
+ boolean critical = false;
+ if (oidString != null && dataString != null) {
+ CMS.debug("ConfigurationUtils: createBasicCAExtensions: processing generic extension");
+ critical = config.getBoolean("preop.cert.signing.ext.critical");
+ ObjectIdentifier oid = new ObjectIdentifier(oidString);
+
+ byte data[] = CryptoUtil.hexString2Bytes(dataString);
+ DerOutputStream out = new DerOutputStream();
+ out.putOctetString(data);
+ genExt = new Extension(oid, critical, out.toByteArray());
+ out.close();
+
+ exts.add(genExt);
+ CMS.debug("ConfigurationUtils: createBasicCAExtensions: generic extension added: " + oidString);
+ }
+ } catch (EBaseException e) {
+ CMS.debug("ConfigurationUtils: createBasicCAExtensions: generic extension not processed:" + e);
+ }
+
+ return exts;
+ }
+
+
public static X509Key getECCX509Key(IConfigStore config, String certTag) throws EPropertyNotFound, EBaseException,
InvalidKeyException {
X509Key pubk = null;
diff --git a/base/server/cms/src/com/netscape/cms/servlet/csadmin/SystemConfigService.java b/base/server/cms/src/com/netscape/cms/servlet/csadmin/SystemConfigService.java
index 252a584..b44cdf9 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/csadmin/SystemConfigService.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/csadmin/SystemConfigService.java
@@ -275,6 +275,15 @@ public class SystemConfigService extends PKIService implements SystemConfigResou
if (cdata.getTag().equals(ct)) {
cdata_found = true;
CMS.debug("Found data for '" + ct + "'");
+ if (ct.equals("signing") &&
+ cdata.getReqExtOID() != null &&
+ cdata.getReqExtData() != null) {
+ CMS.debug("SystemConfigService:processCerts: adding request extension to config");
+ cs.putString("preop.cert.signing.ext.oid", cdata.getReqExtOID());
+ cs.putString("preop.cert.signing.ext.data", cdata.getReqExtData());
+ cs.putBoolean("preop.cert.signing.ext.critical", cdata.getReqExtCritical());
+ }
+
break;
}
}
@@ -342,6 +351,7 @@ public class SystemConfigService extends PKIService implements SystemConfigResou
cs.putString("preop.cert." + ct + ".signingalgorithm", signingalgorithm);
cs.putString("preop.cert." + ct + ".nickname", nickname);
cs.putString("preop.cert." + ct + ".dn", dn);
+ cs.commit(false);
if (!data.getStepTwo()) {
if (keytype.equals("ecc")) {
diff --git a/base/server/etc/default.cfg b/base/server/etc/default.cfg
index 94d34b2..ba1f466 100644
--- a/base/server/etc/default.cfg
+++ b/base/server/etc/default.cfg
@@ -369,6 +369,11 @@ pki_external_csr_path=%(pki_instance_configuration_path)s/ca_signing.csr
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_req_ext_add=False
+# MS subca request ext data
+pki_req_ext_oid=1.3.6.1.4.1.311.20.2
+pki_req_ext_critical=False
+pki_req_ext_data=1E0A00530075006200430041
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/deployment/pkihelper.py b/base/server/python/pki/server/deployment/pkihelper.py
index 3d34edc..091c4de 100644
--- a/base/server/python/pki/server/deployment/pkihelper.py
+++ b/base/server/python/pki/server/deployment/pkihelper.py
@@ -432,7 +432,16 @@ class ConfigurationFile:
self.master_dict = deployer.master_dict
# set useful 'boolean' object variables for this class
self.clone = config.str2bool(self.master_dict['pki_clone'])
+ # generic extension support in CSR - for external CA
+ self.add_req_ext = config.str2bool(
+ self.master_dict['pki_req_ext_add'])
self.external = config.str2bool(self.master_dict['pki_external'])
+ if self.external:
+ # generic extension support in CSR - for external CA
+ if self.add_req_ext:
+ self.req_ext_oid = self.master_dict['pki_req_ext_oid']
+ self.req_ext_critical = self.master_dict['pki_req_ext_critical']
+ self.req_ext_data = self.master_dict['pki_req_ext_data']
self.external_step_two = config.str2bool(
self.master_dict['pki_external_step_two'])
self.skip_configuration = config.str2bool(
@@ -657,6 +666,11 @@ class ConfigurationFile:
# External CA (Step 1)
self.confirm_data_exists("pki_external_csr_path")
self.confirm_missing_file("pki_external_csr_path")
+ # generic extension support in CSR - for external CA
+ if self.add_req_ext:
+ self.confirm_data_exists("pki_req_ext_oid")
+ self.confirm_data_exists("pki_req_ext_critical")
+ self.confirm_data_exists("pki_req_ext_data")
else:
# External CA (Step 2)
self.confirm_data_exists("pki_external_ca_cert_chain_path")
@@ -3178,6 +3192,9 @@ class ConfigClient:
self.subordinate = config.str2bool(self.master_dict['pki_subordinate'])
# set useful 'string' object variables for this class
self.subsystem = self.master_dict['pki_subsystem']
+ # generic extension support in CSR - for external CA
+ self.add_req_ext = config.str2bool(
+ self.master_dict['pki_req_ext_add'])
def configure_pki_data(self, data):
config.pki_log.info(log.PKI_CONFIG_CONFIGURING_PKI_DATA,
@@ -3486,6 +3503,14 @@ class ConfigClient:
cert1 = self.create_system_cert("ca_signing")
cert1.signingAlgorithm = \
self.master_dict['pki_ca_signing_signing_algorithm']
+ # generic extension support in CSR - for external CA
+ if self.add_req_ext:
+ cert1.req_ext_oid = \
+ self.master_dict['pki_req_ext_oid']
+ cert1.req_ext_critical = \
+ self.master_dict['pki_req_ext_critical']
+ cert1.req_ext_data = \
+ self.master_dict['pki_req_ext_data']
if self.external_step_two:
# External CA (Step 2) or Stand-alone PKI (Step 2)
if not self.subsystem == "CA":
diff --git a/base/server/python/pki/server/deployment/pkiparser.py b/base/server/python/pki/server/deployment/pkiparser.py
index b7cece7..df636d4 100644
--- a/base/server/python/pki/server/deployment/pkiparser.py
+++ b/base/server/python/pki/server/deployment/pkiparser.py
@@ -542,6 +542,9 @@ class PKIConfigParser:
if not self.pki_master_dict.has_key('pki_external') or\
not len(self.pki_master_dict['pki_external']):
self.pki_master_dict['pki_external'] = "false"
+ if not self.pki_master_dict.has_key('pki_req_ext_add') or\
+ not len(self.pki_master_dict['pki_req_ext_add']):
+ self.pki_master_dict['pki_req_ext_add'] = "false"
if not self.pki_master_dict.has_key('pki_external_step_two') or\
not len(self.pki_master_dict['pki_external_step_two']):
self.pki_master_dict['pki_external_step_two'] = "false"
diff --git a/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java b/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java
index 5e8e323..bcdb404 100644
--- a/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java
+++ b/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java
@@ -45,7 +45,10 @@ import java.util.Vector;
import javax.crypto.SecretKey;
import netscape.security.pkcs.PKCS10;
+import netscape.security.pkcs.PKCS10Attribute;
+import netscape.security.pkcs.PKCS10Attributes;
import netscape.security.pkcs.PKCS7;
+import netscape.security.pkcs.PKCS9Attribute;
import netscape.security.util.BigInt;
import netscape.security.util.DerInputStream;
import netscape.security.util.DerOutputStream;
@@ -61,6 +64,7 @@ import netscape.security.x509.CertificateSubjectName;
import netscape.security.x509.CertificateValidity;
import netscape.security.x509.CertificateVersion;
import netscape.security.x509.CertificateX509Key;
+import netscape.security.x509.Extensions;
import netscape.security.x509.X500Name;
import netscape.security.x509.X500Signer;
import netscape.security.x509.X509CertImpl;
@@ -1176,14 +1180,38 @@ public class CryptoUtil {
public static PKCS10 createCertificationRequest(String subjectName,
X509Key pubk, PrivateKey prik, String alg)
throws NoSuchAlgorithmException, NoSuchProviderException,
- InvalidKeyException, IOException, CertificateException,
- SignatureException {
+ InvalidKeyException, IOException, CertificateException,
+ SignatureException {
+ return createCertificationRequest(subjectName, pubk, prik, alg, null);
+ }
+
+ /*
+ * This createCertificationRequest() allows extensions to be added to the CSR
+ */
+ public static PKCS10 createCertificationRequest(String subjectName,
+ X509Key pubk, PrivateKey prik, String alg, Extensions exts)
+ throws NoSuchAlgorithmException, NoSuchProviderException,
+ InvalidKeyException, IOException, CertificateException,
+ SignatureException {
X509Key key = pubk;
java.security.Signature sig = java.security.Signature.getInstance(alg,
"Mozilla-JSS");
sig.initSign(prik);
- PKCS10 pkcs10 = new PKCS10(key);
+ PKCS10 pkcs10 = null;
+
+ if (exts != null) {
+ PKCS10Attribute attr = new
+ PKCS10Attribute(PKCS9Attribute.EXTENSION_REQUEST_OID,
+ exts);
+ PKCS10Attributes attrs = new PKCS10Attributes();
+
+ attrs.setAttribute(attr.getAttributeValue().getName(), attr);
+
+ pkcs10 = new PKCS10(key, attrs);
+ } else {
+ pkcs10 = new PKCS10(key);
+ }
X500Name name = new X500Name(subjectName);
X500Signer signer = new X500Signer(sig, name);
@@ -1345,6 +1373,25 @@ public class CryptoUtil {
}
/**
+ * Converts string containing pairs of characters in the range of '0'
+ * to '9', 'a' to 'f' to an array of bytes such that each pair of
+ * characters in the string represents an individual byte
+ */
+ public static byte[] hexString2Bytes(String string) {
+ if (string == null)
+ return null;
+ int stringLength = string.length();
+ if ((stringLength == 0) || ((stringLength % 2) != 0))
+ return null;
+ byte[] bytes = new byte[(stringLength / 2)];
+ for (int i = 0, b = 0; i < stringLength; i += 2, ++b) {
+ String nextByte = string.substring(i, (i + 2));
+ bytes[b] = (byte) Integer.parseInt(nextByte, 0x10);
+ }
+ return bytes;
+ }
+
+ /**
* Retrieves a private key from a unique key ID.
*/
public static PrivateKey findPrivateKeyFromID(byte id[])
--
1.8.4.2