>From ef96c0915c1aac6e937fb8fbda71b41a56781cd0 Mon Sep 17 00:00:00 2001
From: Ade Lee <alee@redhat.com>
Date: Wed, 30 Oct 2013 15:50:28 -0400
Subject: [PATCH] Fixed logic for setting admin cert signing algorithm

Should now be SHA256 by default.
Bugzilla BZ 1024445
---
 base/ca/shared/conf/CS.cfg.in                      |   1 +
 base/ca/shared/profiles/ca/caAdminCert.cfg         |   2 +-
 .../com/netscape/cms/servlet/csadmin/CertUtil.java | 123 +++++++++++++--------
 3 files changed, 81 insertions(+), 45 deletions(-)

diff --git a/base/ca/shared/conf/CS.cfg.in b/base/ca/shared/conf/CS.cfg.in
index 2f12b5173fc68ebaa42b33e7bb329489930e117f..69d98e3dc439669b3c4f518878170bf8cb7a20ff 100644
--- a/base/ca/shared/conf/CS.cfg.in
+++ b/base/ca/shared/conf/CS.cfg.in
@@ -660,6 +660,7 @@ ca.notification.requestInQ.senderEmail=
 ca.ocsp_signing.cacertnickname=ocspSigningCert cert-[PKI_INSTANCE_NAME]
 ca.ocsp_signing.defaultSigningAlgorithm=SHA256withRSA
 ca.ocsp_signing.tokenname=internal
+ca.profiles.defaultSigningAlgsAllowed==SHA256withRSA,SHA1withRSA,SHA512withRSA,MD5withRSA,MD2withRSA,SHA1withDSA,SHA256withEC,SHA1withEC,SHA384withEC,SHA512withEC
 ca.publish.createOwnDNEntry=false
 ca.publish.queue.enable=true
 ca.publish.queue.maxNumberOfThreads=3
diff --git a/base/ca/shared/profiles/ca/caAdminCert.cfg b/base/ca/shared/profiles/ca/caAdminCert.cfg
index c44079a1eeb9f21603a3bd1e7a799be98f11d709..cd2970397caaba6799bc563497da54f98ac2234d 100644
--- a/base/ca/shared/profiles/ca/caAdminCert.cfg
+++ b/base/ca/shared/profiles/ca/caAdminCert.cfg
@@ -81,7 +81,7 @@ policyset.adminCertSet.7.default.params.exKeyUsageCritical=false
 policyset.adminCertSet.7.default.params.exKeyUsageOIDs=1.3.6.1.5.5.7.3.2,1.3.6.1.5.5.7.3.4
 policyset.adminCertSet.8.constraint.class_id=signingAlgConstraintImpl
 policyset.adminCertSet.8.constraint.name=No Constraint
-policyset.adminCertSet.8.constraint.params.signingAlgsAllowed=SHA1withRSA,SHA256withRSA,SHA512withRSA,MD5withRSA,MD2withRSA,SHA1withDSA,SHA1withEC,SHA256withEC,SHA384withEC,SHA512withEC
+policyset.adminCertSet.8.constraint.params.signingAlgsAllowed=SHA256withRSA,SHA1withRSA,SHA512withRSA,MD5withRSA,MD2withRSA,SHA1withDSA,SHA256withEC,SHA1withEC,SHA384withEC,SHA512withEC
 policyset.adminCertSet.8.default.class_id=signingAlgDefaultImpl
 policyset.adminCertSet.8.default.name=Signing Alg
 policyset.adminCertSet.8.default.params.signingAlg=-
diff --git a/base/server/cms/src/com/netscape/cms/servlet/csadmin/CertUtil.java b/base/server/cms/src/com/netscape/cms/servlet/csadmin/CertUtil.java
index 789c0aab59afdd206d8d85fa2def9b262ee6fee3..7b772ba565f5700037545372a8e894f7b5679629 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/csadmin/CertUtil.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/csadmin/CertUtil.java
@@ -17,14 +17,15 @@
 // --- END COPYRIGHT BLOCK ---
 package com.netscape.cms.servlet.csadmin;
 
-import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
-import java.io.DataInputStream;
 import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.io.InputStreamReader;
 import java.math.BigInteger;
 import java.util.Date;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.Set;
 
 import javax.servlet.http.HttpServletResponse;
 
@@ -36,6 +37,8 @@ import netscape.security.x509.X509CertImpl;
 import netscape.security.x509.X509CertInfo;
 import netscape.security.x509.X509Key;
 
+import org.apache.commons.lang.ArrayUtils;
+import org.apache.commons.lang.StringUtils;
 import org.apache.velocity.context.Context;
 import org.mozilla.jss.CryptoManager;
 import org.mozilla.jss.crypto.PrivateKey;
@@ -271,52 +274,84 @@ public class CertUtil {
     }
 
     /**
-     * reads from the admin cert profile caAdminCert.profile and takes the first
-     * entry in the list of allowed algorithms. Users that wish a different algorithm
-     * can specify it in the profile using default.params.signingAlg
+     * reads from the admin cert profile caAdminCert.profile and determines the algorithm as follows:
+     *
+     * 1.  First gets list of allowed algorithms from profile (constraint.params.signingAlgsAllowed)
+     *     If entry does not exist, uses entry "ca.profiles.defaultSigningAlgsAllowed" from CS.cfg
+     *     If that entry does not exist, uses basic default
+     *
+     * 2.  Gets default.params.signingAlg from profile.
+     *     If entry does not exist, selects first algorithm in allowed algorithm list that matches
+     *     CA signing key type
+     *     Otherwise returns entry if it matches signing CA key type.
+     *
+     * @throws EBaseException
+     * @throws IOException
+     * @throws FileNotFoundException
      */
 
-    public static String getAdminProfileAlgorithm(IConfigStore config) {
-        String algorithm = "SHA256withRSA";
-        try {
-            String caSigningKeyType = config.getString("preop.cert.signing.keytype", "rsa");
-            String pfile = config.getString("profile.caAdminCert.config");
-            FileInputStream fis = new FileInputStream(pfile);
-            DataInputStream in = new DataInputStream(fis);
-            BufferedReader br = new BufferedReader(new InputStreamReader(in));
-
-            String strLine;
-            while ((strLine = br.readLine()) != null) {
-                String marker2 = "default.params.signingAlg=";
-                int indx = strLine.indexOf(marker2);
-                if (indx != -1) {
-                    String alg = strLine.substring(indx + marker2.length());
-                    if ((alg.length() > 0) && (!alg.equals("-"))) {
-                        algorithm = alg;
-                        break;
-                    }
-                    ;
-                }
-                ;
-
-                String marker = "signingAlgsAllowed=";
-                indx = strLine.indexOf(marker);
-                if (indx != -1) {
-                    String[] algs = strLine.substring(indx + marker.length()).split(",");
-                    for (int i = 0; i < algs.length; i++) {
-                        if ((caSigningKeyType.equals("rsa") && (algs[i].indexOf("RSA") != -1)) ||
-                                (caSigningKeyType.equals("ecc") && (algs[i].indexOf("EC") != -1))) {
-                            algorithm = algs[i];
-                            break;
-                        }
-                    }
+    public static String getAdminProfileAlgorithm(IConfigStore config) throws EBaseException, FileNotFoundException,
+            IOException {
+        String caSigningKeyType = config.getString("preop.cert.signing.keytype", "rsa");
+        String pfile = config.getString("profile.caAdminCert.config");
+        Properties props = new Properties();
+        props.load(new FileInputStream(pfile));
+
+        Set<String> keys = props.stringPropertyNames();
+        Iterator<String> iter = keys.iterator();
+        String defaultAlg = null;
+        String[] algsAllowed = null;
+
+        while (iter.hasNext()) {
+            String key = iter.next();
+            if (key.endsWith("default.params.signingAlg")) {
+                defaultAlg = props.getProperty(key);
+            }
+            if (key.endsWith("constraint.params.signingAlgsAllowed")) {
+                algsAllowed = StringUtils.split(props.getProperty(key), ",");
+            }
+        }
+
+        if (algsAllowed == null) { //algsAllowed not defined in profile, use a global setting
+            algsAllowed = StringUtils.split(config.getString("ca.profiles.defaultSigningAlgsAllowed",
+                    "SHA256withRSA,SHA256withEC,SHA1withDSA"), ",");
+        }
+
+        if (ArrayUtils.isEmpty(algsAllowed)) {
+            throw new EBaseException("No allowed signing algorithms defined.");
+        }
+
+        if (StringUtils.isNotEmpty(defaultAlg) && !defaultAlg.equals("-")) {
+            // check if the defined default algorithm is valid
+            if (! isAlgorithmValid(caSigningKeyType, defaultAlg)) {
+                throw new EBaseException("Administrator cert cannot be signed by specfied algorithm." +
+                                         "Algorithm incompatible with signing key");
+            }
+
+            for (String alg : algsAllowed) {
+                if (defaultAlg.trim().equals(alg.trim())) {
+                    return defaultAlg;
                 }
             }
-            in.close();
-        } catch (Exception e) {
-            CMS.debug("getAdminProfleAlgorithm: exception: " + e);
+            throw new EBaseException(
+                    "Administrator Certificate cannot be signed by the specified algorithm " +
+                    "as it is not one of the allowed signing algorithms.  Check the admin cert profile.");
         }
-        return algorithm;
+
+        // no algorithm specified.  Pick the first allowed algorithm.
+        for (String alg : algsAllowed) {
+            if (isAlgorithmValid(caSigningKeyType, alg)) return alg;
+        }
+
+        throw new EBaseException(
+                "Admin certificate cannot be signed by any of the specified possible algorithms." +
+                "Algorithm is incompatible with the CA signing key type" );
+    }
+
+    private static boolean isAlgorithmValid(String signingKeyType, String algorithm) {
+       return ((signingKeyType.equals("rsa") && algorithm.contains("RSA")) ||
+               (signingKeyType.equals("ecc") && algorithm.contains("EC"))  ||
+               (signingKeyType.equals("dsa") && algorithm.contains("DSA")));
     }
 
     public static X509CertImpl createLocalCert(IConfigStore config, X509Key x509key,
-- 
1.8.3.1

