>From faaa4357c951f7cd57e5678d6c5156040bdeec3d Mon Sep 17 00:00:00 2001
From: "Endi S. Dewata" <edewata@redhat.com>
Date: Wed, 11 May 2016 19:33:51 +0200
Subject: [PATCH] Fixed support for generic CSR extensions.

The deployment tool has been modified to support adding Subordinate
CA extension into the CSR for Microsoft CA, and also adding generic
extensions to any system certificate.

https://fedorahosted.org/pki/ticket/2312
---
 base/common/python/pki/nssdb.py                    | 27 ++++++++++-
 .../cms/servlet/csadmin/ConfigurationUtils.java    | 29 ++++++-----
 .../dogtagpki/server/rest/SystemConfigService.java | 19 +-------
 .../server/deployment/scriptlets/configuration.py  | 25 +++++++++-
 .../deployment/scriptlets/security_databases.py    | 49 +++++++++++++++++++
 .../com/netscape/cmsutil/crypto/CryptoUtil.java    | 56 +++++++++++-----------
 6 files changed, 144 insertions(+), 61 deletions(-)

diff --git a/base/common/python/pki/nssdb.py b/base/common/python/pki/nssdb.py
index 7908461b1b0735ddd8f678f6b89efef0b25127bc..2504a95797cae1eebe491df398d41c4129467650 100644
--- a/base/common/python/pki/nssdb.py
+++ b/base/common/python/pki/nssdb.py
@@ -171,7 +171,8 @@ class NSSDatabase(object):
                        key_type=None, key_size=None, curve=None,
                        hash_alg=None,
                        basic_constraints_ext=None,
-                       key_usage_ext=None):
+                       key_usage_ext=None,
+                       generic_exts=None):
 
         tmpdir = tempfile.mkdtemp()
 
@@ -251,6 +252,30 @@ class NSSDatabase(object):
 
                 keystroke += '\n'
 
+            if generic_exts:
+
+                cmd.extend(['--extGeneric'])
+
+                counter = 0
+                exts = []
+
+                for generic_ext in generic_exts:
+
+                    data_file = os.path.join(tmpdir, 'csr-ext-%d' % counter)
+                    with open(data_file, 'w') as f:
+                        f.write(generic_ext['data'])
+
+                    critical = 'critical' if generic_ext['critical'] else 'not-critical'
+
+                    ext = generic_ext['oid']
+                    ext += ':' + critical
+                    ext += ':' + data_file
+
+                    exts.append(ext)
+                    counter += 1
+
+                cmd.append(','.join(exts))
+
             # generate binary request
             p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
                                  stderr=subprocess.STDOUT)
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 131f565adbd2d208b7cd14ed1ab9084336d7e70b..2da4e48658354116eb8ef1427133bc928226bd0f 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
@@ -2936,12 +2936,15 @@ public class ConfigurationUtils {
 
         cert.setDN(caDN);
 
-        Extensions exts = null;
+        Extensions exts = new Extensions();
         if (certTag.equals("signing")) {
             CMS.debug("generateCertRequest: generating basic CA extensions");
-            exts = createBasicCAExtensions(config);
+            createBasicCAExtensions(config, exts);
         }
 
+        CMS.debug("generateCertRequest: generating generic extensions");
+        createGenericExtensions(config, certTag, exts);
+
         CMS.debug("generateCertRequest: generating PKCS #10 request");
         PKCS10 certReq = CryptoUtil.createCertificationRequest(caDN, pubk, privk, algorithm, exts);
 
@@ -2961,8 +2964,7 @@ public class ConfigurationUtils {
      * createBasicCAExtensions creates the basic Extensions needed for a CSR to a
      * CA signing certificate
      */
-    private static Extensions createBasicCAExtensions(IConfigStore config) throws Exception {
-        Extensions exts = new Extensions();
+    private static void createBasicCAExtensions(IConfigStore config, Extensions exts) throws Exception {
         CMS.debug("ConfigurationUtils: createBasicCAExtensions: begins");
 
         // create BasicConstraintsExtension
@@ -2991,15 +2993,18 @@ public class ConfigurationUtils {
         NSCertTypeExtension nsctExt = new NSCertTypeExtension(false, nsBits);
         exts.add(nsctExt);
         */
+    }
 
-        // add a generic extension
+    private static void createGenericExtensions(IConfigStore config, String tag, Extensions exts) throws Exception {
+        CMS.debug("ConfigurationUtils: createGenericExtensions: begins");
+        // if specified, add a generic extension
         try {
-            String oidString = config.getString(PCERT_PREFIX + "signing.ext.oid");
-            String dataString = config.getString(PCERT_PREFIX + "signing.ext.data");
+            String oidString = config.getString(PCERT_PREFIX + tag + ".ext.oid");
+            String dataString = config.getString(PCERT_PREFIX + tag + ".ext.data");
 
             if (oidString != null && dataString != null) {
-                CMS.debug("ConfigurationUtils: createBasicCAExtensions: processing generic extension");
-                boolean critical = config.getBoolean("preop.cert.signing.ext.critical");
+                CMS.debug("ConfigurationUtils: createGenericExtensions: adding generic extension for " + tag);
+                boolean critical = config.getBoolean(PCERT_PREFIX + tag + ".ext.critical");
                 ObjectIdentifier oid = new ObjectIdentifier(oidString);
 
                 byte data[] = CryptoUtil.hexString2Bytes(dataString);
@@ -3010,18 +3015,16 @@ public class ConfigurationUtils {
                 out.close();
 
                 exts.add(genExt);
-                CMS.debug("ConfigurationUtils: createBasicCAExtensions: generic extension added: " + oidString);
+                CMS.debug("ConfigurationUtils: createGenericExtensions: generic extension added: " + oidString);
             }
 
         } catch (EPropertyNotFound e) {
             // generic extension not specified, ignore
 
         } catch (EBaseException e) {
-            CMS.debug("ConfigurationUtils: createBasicCAExtensions: Unable to add generic extension: " + e);
+            CMS.debug("ConfigurationUtils: createGenericExtensions: Unable to add generic extension: " + e);
             throw new BadRequestException("Unable to add generic certificate extension: " + e, e);
         }
-
-        return exts;
     }
 
     public static X509Key getECCX509Key(IConfigStore config, String certTag) throws EPropertyNotFound, EBaseException,
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 57e0372326c0c823e9347931a059a9459e0eddaf..3720116b9e25c3e636248e342226d55fc234f70c 100644
--- a/base/server/cms/src/org/dogtagpki/server/rest/SystemConfigService.java
+++ b/base/server/cms/src/org/dogtagpki/server/rest/SystemConfigService.java
@@ -327,15 +327,6 @@ public class SystemConfigService extends PKIService implements SystemConfigResou
                 for (SystemCertData systemCert : request.getSystemCerts()) {
                     if (systemCert.getTag().equals(tag)) {
                         certData = systemCert;
-                        CMS.debug("Found data for '" + tag + "'");
-                        if (tag.equals("signing") &&
-                                certData.getReqExtOID() != null &&
-                                certData.getReqExtData() != null) {
-                            CMS.debug("SystemConfigService:processCerts: adding request extension to config");
-                            cs.putString("preop.cert.signing.ext.oid", certData.getReqExtOID());
-                            cs.putString("preop.cert.signing.ext.data", certData.getReqExtData());
-                            cs.putBoolean("preop.cert.signing.ext.critical", certData.getReqExtCritical());
-                        }
                         break;
                     }
                 }
@@ -399,16 +390,12 @@ public class SystemConfigService extends PKIService implements SystemConfigResou
                 }
 
                 String signingalgorithm = certData.getSigningAlgorithm() != null ? certData.getSigningAlgorithm() : keyalgorithm;
-                String nickname = certData.getNickname() != null ? certData.getNickname() :
-                    cs.getString("preop.cert." + tag + ".nickname");
-                String dn = certData.getSubjectDN() != null ? certData.getSubjectDN() :
-                    cs.getString("preop.cert." + tag + ".dn");
+                String nickname = cs.getString("preop.cert." + tag + ".nickname");
+                String dn = cs.getString("preop.cert." + tag + ".dn");
 
                 cs.putString("preop.cert." + tag + ".keytype", keytype);
                 cs.putString("preop.cert." + tag + ".keyalgorithm", keyalgorithm);
                 cs.putString("preop.cert." + tag + ".signingalgorithm", signingalgorithm);
-                cs.putString("preop.cert." + tag + ".nickname", nickname);
-                cs.putString("preop.cert." + tag + ".dn", dn);
 
                 // support injecting SAN into server cert
                 if ( tag.equals("sslserver") && certData.getServerCertSAN() != null) {
@@ -578,10 +565,8 @@ public class SystemConfigService extends PKIService implements SystemConfigResou
         cs.putString("preop.cert." + tag + ".pubkey.modulus", CryptoUtil.byte2string(modulus));
         cs.putString("preop.cert." + tag + ".pubkey.exponent", CryptoUtil.byte2string(exponent));
         cs.putString("preop.cert." + tag + ".privkey.id", CryptoUtil.byte2string(privk.getUniqueID()));
-        cs.putString("preop.cert." + tag + ".dn", cdata.getSubjectDN());
         cs.putString("preop.cert." + tag + ".keyalgorithm", cdata.getKeyAlgorithm());
         cs.putString("preop.cert." + tag + ".keytype", cdata.getKeyType());
-        cs.putString("preop.cert." + tag + ".nickname", cdata.getNickname());
     }
 
     private void updateConfiguration(ConfigurationRequest data, SystemCertData cdata, String tag) {
diff --git a/base/server/python/pki/server/deployment/scriptlets/configuration.py b/base/server/python/pki/server/deployment/scriptlets/configuration.py
index 6da08c587086260ac0b9af212fbf0ecf66e927fb..b8505dd9b7d59a527f21c07f2fb55bde1f46eafa 100644
--- a/base/server/python/pki/server/deployment/scriptlets/configuration.py
+++ b/base/server/python/pki/server/deployment/scriptlets/configuration.py
@@ -19,6 +19,7 @@
 #
 
 from __future__ import absolute_import
+import binascii
 import json
 import re
 
@@ -97,6 +98,8 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
         try:
             if external and step_one:  # external CA step 1 only
 
+                subject_dn = subsystem.config['preop.cert.signing.dn']
+
                 # Determine CA signing key type and algorithm
 
                 key_type = deployer.mdict['pki_ca_signing_key_type']
@@ -149,15 +152,33 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
                         'critical': True
                     }
 
+                    # if specified, add generic CSR extension
+                    generic_exts = None
+
+                    if 'preop.cert.signing.ext.oid' in subsystem.config and \
+                       'preop.cert.signing.ext.data' in subsystem.config:
+
+                        data = subsystem.config['preop.cert.signing.ext.data']
+                        critical = subsystem.config['preop.cert.signing.ext.critical']
+
+                        generic_ext = {
+                            'oid': subsystem.config['preop.cert.signing.ext.oid'],
+                            'data': binascii.unhexlify(data),
+                            'critical': config.str2bool(critical)
+                        }
+
+                        generic_exts = [generic_ext]
+
                     nssdb.create_request(
-                        subject_dn=deployer.mdict['pki_ca_signing_subject_dn'],
+                        subject_dn=subject_dn,
                         request_file=external_csr_path,
                         key_type=key_type,
                         key_size=key_size,
                         curve=curve,
                         hash_alg=hash_alg,
                         basic_constraints_ext=basic_constraints_ext,
-                        key_usage_ext=key_usage_ext)
+                        key_usage_ext=key_usage_ext,
+                        generic_exts=generic_exts)
 
                     with open(external_csr_path) as f:
                         signing_csr = f.read()
diff --git a/base/server/python/pki/server/deployment/scriptlets/security_databases.py b/base/server/python/pki/server/deployment/scriptlets/security_databases.py
index c3ae890907ecbb18c51cf0340543691c16d32fe4..18fc3e1efb9ebf64308f7fdf7f647c9ac973ba35 100644
--- a/base/server/python/pki/server/deployment/scriptlets/security_databases.py
+++ b/base/server/python/pki/server/deployment/scriptlets/security_databases.py
@@ -43,6 +43,12 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
         config.pki_log.info(log.SECURITY_DATABASES_SPAWN_1, __name__,
                             extra=config.PKI_INDENTATION_LEVEL_1)
 
+        instance = pki.server.PKIInstance(deployer.mdict['pki_instance_name'])
+        instance.load()
+
+        subsystem = instance.get_subsystem(
+            deployer.mdict['pki_subsystem'].lower())
+
         if config.str2bool(deployer.mdict['pki_hsm_enable']):
             deployer.password.create_hsm_password_conf(
                 deployer.mdict['pki_shared_password_conf'],
@@ -158,6 +164,49 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
         # Always delete the temporary 'pfile'
         deployer.file.delete(deployer.mdict['pki_shared_pfile'])
 
+        # Store system cert parameters in installation step to guarantee the
+        # parameters exist during configuration step and to allow customization.
+
+        certs = subsystem.find_system_certs()
+        for cert in certs:
+
+            # get CS.cfg tag and pkispawn tag
+            config_tag = cert['id']
+            deploy_tag = config_tag
+
+            if config_tag == 'signing':  # for CA and OCSP
+                deploy_tag = subsystem.name + '_signing'
+
+            elif config_tag == 'sslserver':
+                deploy_tag = 'ssl_server'
+
+            # store nickname
+            nickname = deployer.mdict['pki_%s_nickname' % deploy_tag]
+            subsystem.config['preop.cert.%s.nickname' % config_tag] = nickname
+
+            # store subject DN
+            subject_dn = deployer.mdict['pki_%s_subject_dn' % deploy_tag]
+            subsystem.config['preop.cert.%s.dn' % config_tag] = subject_dn
+
+            # TODO: move more system cert params here
+
+        # If specified in the deployment parameter, add generic CA signing cert
+        # extension parameters into the CS.cfg. Generic extension for other
+        # system certs can be added directly into CS.cfg after before the
+        # configuration step.
+
+        if subsystem.type == 'CA':
+            if deployer.configuration_file.add_req_ext:
+
+                subsystem.config['preop.cert.signing.ext.oid'] = \
+                    deployer.configuration_file.req_ext_oid
+                subsystem.config['preop.cert.signing.ext.data'] = \
+                    deployer.configuration_file.req_ext_data
+                subsystem.config['preop.cert.signing.ext.critical'] = \
+                    deployer.configuration_file.req_ext_critical.lower()
+
+        subsystem.save()
+
     def update_external_certs_conf(self, external_path, deployer):
         external_certs = pki.server.PKIInstance.read_external_certs(
             external_path)
diff --git a/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java b/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java
index 7ac3e4e9ad2702e09c704638b6ab773bb2dccb49..979b047d7c76451e6c404b8b87402c880e2b0cd5 100644
--- a/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java
+++ b/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java
@@ -49,33 +49,6 @@ 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;
-import netscape.security.util.DerValue;
-import netscape.security.util.ObjectIdentifier;
-import netscape.security.x509.AlgorithmId;
-import netscape.security.x509.CertificateAlgorithmId;
-import netscape.security.x509.CertificateChain;
-import netscape.security.x509.CertificateExtensions;
-import netscape.security.x509.CertificateIssuerName;
-import netscape.security.x509.CertificateSerialNumber;
-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;
-import netscape.security.x509.X509CertInfo;
-import netscape.security.x509.X509Key;
-
 import org.mozilla.jss.CryptoManager;
 import org.mozilla.jss.CryptoManager.NotInitializedException;
 import org.mozilla.jss.NoSuchTokenException;
@@ -135,6 +108,33 @@ import org.mozilla.jss.util.Password;
 import com.netscape.cmsutil.util.Cert;
 import com.netscape.cmsutil.util.Utils;
 
+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;
+import netscape.security.util.DerValue;
+import netscape.security.util.ObjectIdentifier;
+import netscape.security.x509.AlgorithmId;
+import netscape.security.x509.CertificateAlgorithmId;
+import netscape.security.x509.CertificateChain;
+import netscape.security.x509.CertificateExtensions;
+import netscape.security.x509.CertificateIssuerName;
+import netscape.security.x509.CertificateSerialNumber;
+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;
+import netscape.security.x509.X509CertInfo;
+import netscape.security.x509.X509Key;
+
 @SuppressWarnings("serial")
 public class CryptoUtil {
 
@@ -1466,7 +1466,7 @@ public class CryptoUtil {
         sig.initSign(prik);
         PKCS10 pkcs10 = null;
 
-        if (exts != null) {
+        if (exts != null && !exts.isEmpty()) {
             PKCS10Attribute attr = new
                     PKCS10Attribute(PKCS9Attribute.EXTENSION_REQUEST_OID,
                             exts);
-- 
2.4.11

