>From b37436f7757d3e049dc4368873efa5d7a6a77d50 Mon Sep 17 00:00:00 2001
From: "Endi S. Dewata" <edewata@redhat.com>
Date: Thu, 31 Mar 2016 21:59:25 +0200
Subject: [PATCH] Fixed missing trust flags in certificate backup.

The ConfigurationUtils.backupKeys() has been modified to use
PKCS12Util to export the certificates and their trust flags into
a PKCS #12 file such that the file can be used for cloning.

The code to generate PFX object has been refactored from the
PKCS12Util.storeIntoFile() into a separate generatePFX() method.

The PKCS12Util.loadCertFromNSS() has been modified to provide
options to load a certificate from NSS database without the key
or the certificate chain. The CLIs have been modified to provide
the same options.

The PKCS12Util.getCertInfo() has modified to ignore missing
certificate attributes in the PKCS #12 file and generate a new
local ID.

https://fedorahosted.org/pki/ticket/2255
---
 base/java-tools/bin/pki                            |   3 +
 .../netscape/cmstools/pkcs12/PKCS12CertAddCLI.java |   7 +-
 .../netscape/cmstools/pkcs12/PKCS12ExportCLI.java  |  12 ++-
 .../cms/servlet/csadmin/ConfigurationUtils.java    |  40 ++++----
 .../src/netscape/security/pkcs/PKCS12Util.java     | 108 ++++++++++++---------
 5 files changed, 98 insertions(+), 72 deletions(-)

diff --git a/base/java-tools/bin/pki b/base/java-tools/bin/pki
index e476cfcfe0b4476820354ef1ec8a9ddfbf0f734c..88490f7dad0093cc49f954d3a71d27f2e7fd085d 100644
--- a/base/java-tools/bin/pki
+++ b/base/java-tools/bin/pki
@@ -138,6 +138,9 @@ class PKICLI(pki.cli.CLI):
         if self.token and self.token != 'internal':
             cmd.extend(['--token', self.token])
 
+        if self.verbose:
+            cmd.extend(['--verbose'])
+
         cmd.extend(args)
 
         if self.verbose:
diff --git a/base/java-tools/src/com/netscape/cmstools/pkcs12/PKCS12CertAddCLI.java b/base/java-tools/src/com/netscape/cmstools/pkcs12/PKCS12CertAddCLI.java
index 48e4907cf7c21604465cfb303c6d35edd9489f60..a422b200dcf50920599921a9e60e6b0fb24297d3 100644
--- a/base/java-tools/src/com/netscape/cmstools/pkcs12/PKCS12CertAddCLI.java
+++ b/base/java-tools/src/com/netscape/cmstools/pkcs12/PKCS12CertAddCLI.java
@@ -65,6 +65,8 @@ public class PKCS12CertAddCLI extends CLI {
 
         options.addOption(null, "new-file", false, "Create a new PKCS #12 file");
         options.addOption(null, "no-trust-flags", false, "Do not include trust flags");
+        options.addOption(null, "no-key", false, "Do not include private key");
+        options.addOption(null, "no-chain", false, "Do not include certificate chain");
 
         options.addOption("v", "verbose", false, "Run in verbose mode.");
         options.addOption(null, "debug", false, "Run in debug mode.");
@@ -139,6 +141,8 @@ public class PKCS12CertAddCLI extends CLI {
 
         boolean newFile = cmd.hasOption("new-file");
         boolean includeTrustFlags = !cmd.hasOption("no-trust-flags");
+        boolean includeKey = !cmd.hasOption("no-key");
+        boolean includeChain = !cmd.hasOption("no-chain");
 
         try {
             PKCS12Util util = new PKCS12Util();
@@ -155,7 +159,8 @@ public class PKCS12CertAddCLI extends CLI {
                 pkcs12 = util.loadFromFile(filename, password);
             }
 
-            util.loadCertFromNSS(pkcs12, nickname);
+            // load the specified certificate
+            util.loadCertFromNSS(pkcs12, nickname, includeKey, includeChain);
             util.storeIntoFile(pkcs12, filename, password);
 
         } finally {
diff --git a/base/java-tools/src/com/netscape/cmstools/pkcs12/PKCS12ExportCLI.java b/base/java-tools/src/com/netscape/cmstools/pkcs12/PKCS12ExportCLI.java
index d42c449b40c1b1fc97e921f9635b9e6a8a1e922b..fab5ecdda038ef281348f3e16535e8fdef7bae90 100644
--- a/base/java-tools/src/com/netscape/cmstools/pkcs12/PKCS12ExportCLI.java
+++ b/base/java-tools/src/com/netscape/cmstools/pkcs12/PKCS12ExportCLI.java
@@ -63,6 +63,8 @@ public class PKCS12ExportCLI extends CLI {
 
         options.addOption(null, "new-file", false, "Create a new PKCS #12 file");
         options.addOption(null, "no-trust-flags", false, "Do not include trust flags");
+        options.addOption(null, "no-key", false, "Do not include private key");
+        options.addOption(null, "no-chain", false, "Do not include certificate chain");
 
         options.addOption("v", "verbose", false, "Run in verbose mode.");
         options.addOption(null, "debug", false, "Run in debug mode.");
@@ -127,11 +129,13 @@ public class PKCS12ExportCLI extends CLI {
         Password password = new Password(passwordString.toCharArray());
 
         boolean newFile = cmd.hasOption("new-file");
-        boolean trustFlagsEnabled = !cmd.hasOption("no-trust-flags");
+        boolean includeTrustFlags = !cmd.hasOption("no-trust-flags");
+        boolean includeKey = !cmd.hasOption("no-key");
+        boolean includeChain = !cmd.hasOption("no-chain");
 
         try {
             PKCS12Util util = new PKCS12Util();
-            util.setTrustFlagsEnabled(trustFlagsEnabled);
+            util.setTrustFlagsEnabled(includeTrustFlags);
 
             PKCS12 pkcs12;
 
@@ -149,9 +153,9 @@ public class PKCS12ExportCLI extends CLI {
                 util.loadFromNSS(pkcs12);
 
             } else {
-                // load specified certificates
+                // load the specified certificates
                 for (String nickname : nicknames) {
-                    util.loadCertFromNSS(pkcs12, nickname);
+                    util.loadCertFromNSS(pkcs12, nickname, includeKey, includeChain);
                 }
             }
 
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 51e5f082434b45ab667005ef598b183be4ada189..25838f1f3208f39c7ccf79ad9eb3edb9e5f9b3dc 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
@@ -30,7 +30,6 @@ import java.io.PrintStream;
 import java.math.BigInteger;
 import java.net.URI;
 import java.net.URISyntaxException;
-import java.security.DigestException;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
 import java.security.KeyPair;
@@ -165,6 +164,8 @@ import netscape.ldap.LDAPSearchResults;
 import netscape.ldap.LDAPv3;
 import netscape.security.pkcs.ContentInfo;
 import netscape.security.pkcs.PKCS10;
+import netscape.security.pkcs.PKCS12;
+import netscape.security.pkcs.PKCS12Util;
 import netscape.security.pkcs.PKCS7;
 import netscape.security.pkcs.SignerInfo;
 import netscape.security.util.DerOutputStream;
@@ -3331,11 +3332,8 @@ public class ConfigurationUtils {
         }
     }
 
-    public static void backupKeys(String pwd, String fname) throws EPropertyNotFound, EBaseException,
-            NotInitializedException, ObjectNotFoundException, TokenException, DigestException,
-            InvalidKeyException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidBERException,
-            CertificateEncodingException, IllegalStateException, IllegalBlockSizeException, BadPaddingException,
-            IOException {
+    public static void backupKeys(String pwd, String fname) throws Exception {
+
         CMS.debug("backupKeys(): start");
         IConfigStore cs = CMS.getConfigStore();
         String certlist = cs.getString("preop.cert.list");
@@ -3344,39 +3342,37 @@ public class ConfigurationUtils {
         CryptoManager cm = CryptoManager.getInstance();
 
         Password pass = new org.mozilla.jss.util.Password(pwd.toCharArray());
-        SEQUENCE encSafeContents = new SEQUENCE();
-        SEQUENCE safeContents = new SEQUENCE();
+
+        PKCS12Util util = new PKCS12Util();
+        PKCS12 pkcs12 = new PKCS12();
+
+        // load system certificate (with key but without chain)
         while (st.hasMoreTokens()) {
+
             String t = st.nextToken();
             if (t.equals("sslserver"))
                 continue;
+
             String nickname = cs.getString("preop.cert." + t + ".nickname");
             String modname = cs.getString("preop.module.token");
 
             if (!modname.equals("Internal Key Storage Token"))
                 nickname = modname + ":" + nickname;
 
-            X509Certificate x509cert = cm.findCertByNickname(nickname);
-            byte localKeyId[] = addCertBag(x509cert, nickname, safeContents);
-            PrivateKey pkey = cm.findPrivKeyByCert(x509cert);
-            addKeyBag(pkey, x509cert, pass, localKeyId, encSafeContents);
+            util.loadCertFromNSS(pkcs12, nickname, true, false);
         }
 
-        X509Certificate[] cacerts = cm.getCACerts();
-
-        for (int i = 0; i < cacerts.length; i++) {
-            String nickname = null;
-            addCertBag(cacerts[i], nickname, safeContents);
+        // load CA certificates (without keys or chains)
+        for (X509Certificate caCert : cm.getCACerts()) {
+            util.loadCertFromNSS(pkcs12, caCert, false, false);
         }
 
-        AuthenticatedSafes authSafes = new AuthenticatedSafes();
-        authSafes.addSafeContents(safeContents);
-        authSafes.addSafeContents(encSafeContents);
-        PFX pfx = new PFX(authSafes);
-        pfx.computeMacData(pass, null, 5);
+        PFX pfx = util.generatePFX(pkcs12, pass);
+
         ByteArrayOutputStream bos = new ByteArrayOutputStream();
         pfx.encode(bos);
         byte[] output = bos.toByteArray();
+
         cs.putString("preop.pkcs12", CryptoUtil.byte2string(output));
         pass.clear();
         cs.commit(false);
diff --git a/base/util/src/netscape/security/pkcs/PKCS12Util.java b/base/util/src/netscape/security/pkcs/PKCS12Util.java
index 7c9ab2fb4e9554b18c3bf09c9e7c90f8349918fd..967479b69e6cdada355d77d6a072b3acb6851cf3 100644
--- a/base/util/src/netscape/security/pkcs/PKCS12Util.java
+++ b/base/util/src/netscape/security/pkcs/PKCS12Util.java
@@ -162,13 +162,14 @@ public class PKCS12Util {
     }
 
     BigInteger createLocalID(X509Certificate cert) throws Exception {
-
         // SHA1 hash of the X509Cert DER encoding
-        byte[] certDer = cert.getEncoded();
+        return createLocalID(cert.getEncoded());
+    }
+
+    BigInteger createLocalID(byte[] bytes) throws Exception {
 
         MessageDigest md = MessageDigest.getInstance("SHA");
-
-        md.update(certDer);
+        md.update(bytes);
         return new BigInteger(1, md.digest());
     }
 
@@ -244,21 +245,46 @@ public class PKCS12Util {
         CryptoStore store = token.getCryptoStore();
 
         for (X509Certificate cert : store.getCertificates()) {
-            loadCertChainFromNSS(pkcs12, cert);
+            loadCertFromNSS(pkcs12, cert, true, true);
         }
     }
 
-    public void loadCertFromNSS(PKCS12 pkcs12, String nickname) throws Exception {
+    public void loadCertFromNSS(PKCS12 pkcs12, String nickname, boolean includeKey, boolean includeChain) throws Exception {
 
         CryptoManager cm = CryptoManager.getInstance();
 
         X509Certificate[] certs = cm.findCertsByNickname(nickname);
         for (X509Certificate cert : certs) {
-            loadCertChainFromNSS(pkcs12, cert);
+            loadCertFromNSS(pkcs12, cert, includeKey, includeChain);
         }
     }
 
-    public void loadCertFromNSS(PKCS12 pkcs12, X509Certificate cert, BigInteger id, boolean replace) throws Exception {
+    public void loadCertFromNSS(PKCS12 pkcs12, X509Certificate cert, boolean includeKey, boolean includeChain) throws Exception {
+
+        CryptoManager cm = CryptoManager.getInstance();
+
+        BigInteger id = createLocalID(cert);
+
+        // load cert info
+        loadCertInfoFromNSS(pkcs12, cert, id, true);
+
+        if (includeKey) {
+            // load key info if exists
+            loadKeyInfoFromNSS(pkcs12, cert, id);
+        }
+
+        if (includeChain) {
+            // load cert chain
+            X509Certificate[] certChain = cm.buildCertificateChain(cert);
+            for (int i = 1; i < certChain.length; i++) {
+                X509Certificate c = certChain[i];
+                BigInteger cid = createLocalID(c);
+                loadCertInfoFromNSS(pkcs12, c, cid, false);
+            }
+        }
+    }
+
+    public void loadCertInfoFromNSS(PKCS12 pkcs12, X509Certificate cert, BigInteger id, boolean replace) throws Exception {
 
         String nickname = cert.getNickname();
         logger.info("Loading certificate \"" + nickname + "\" from NSS database");
@@ -272,7 +298,7 @@ public class PKCS12Util {
         pkcs12.addCertInfo(certInfo, replace);
     }
 
-    public void loadCertKeyFromNSS(PKCS12 pkcs12, X509Certificate cert, BigInteger id) throws Exception {
+    public void loadKeyInfoFromNSS(PKCS12 pkcs12, X509Certificate cert, BigInteger id) throws Exception {
 
         String nickname = cert.getNickname();
         logger.info("Loading private key for certificate \"" + nickname + "\" from NSS database");
@@ -298,30 +324,9 @@ public class PKCS12Util {
         }
     }
 
-    public void loadCertChainFromNSS(PKCS12 pkcs12, X509Certificate cert) throws Exception {
+    public PFX generatePFX(PKCS12 pkcs12, Password password) throws Exception {
 
-        CryptoManager cm = CryptoManager.getInstance();
-
-        BigInteger id = createLocalID(cert);
-
-        // load cert key if exists
-        loadCertKeyFromNSS(pkcs12, cert, id);
-
-        // load cert
-        loadCertFromNSS(pkcs12, cert, id, true);
-
-        // load parent certs without key
-        X509Certificate[] certChain = cm.buildCertificateChain(cert);
-        for (int i = 1; i < certChain.length; i++) {
-            X509Certificate c = certChain[i];
-            BigInteger cid = createLocalID(c);
-            loadCertFromNSS(pkcs12, c, cid, false);
-        }
-    }
-
-    public void storeIntoFile(PKCS12 pkcs12, String filename, Password password) throws Exception {
-
-        logger.info("Storing data into PKCS #12 file");
+        logger.info("Generating PKCS #12 data");
 
         SEQUENCE safeContents = new SEQUENCE();
 
@@ -342,6 +347,14 @@ public class PKCS12Util {
         PFX pfx = new PFX(authSafes);
         pfx.computeMacData(password, null, 5);
 
+        return pfx;
+    }
+
+    public void storeIntoFile(PKCS12 pkcs12, String filename, Password password) throws Exception {
+
+        PFX pfx = generatePFX(pkcs12, password);
+
+        logger.info("Storing data into PKCS #12 file");
         ByteArrayOutputStream bos = new ByteArrayOutputStream();
         pfx.encode(bos);
         byte[] data = bos.toByteArray();
@@ -362,7 +375,7 @@ public class PKCS12Util {
         // get key attributes
         SET bagAttrs = bag.getBagAttributes();
 
-        for (int i = 0; i < bagAttrs.size(); i++) {
+        for (int i = 0; bagAttrs != null && i < bagAttrs.size(); i++) {
 
             Attribute attr = (Attribute) bagAttrs.elementAt(i);
             OBJECT_IDENTIFIER oid = attr.getType();
@@ -376,7 +389,7 @@ public class PKCS12Util {
                 BMPString subjectDN = (BMPString) new BMPString.Template().decode(bis);
 
                 keyInfo.subjectDN = subjectDN.toString();
-                logger.fine("Subject DN: " + keyInfo.subjectDN);
+                logger.fine("   Subject DN: " + keyInfo.subjectDN);
 
             } else if (oid.equals(SafeBag.LOCAL_KEY_ID)) {
 
@@ -387,12 +400,10 @@ public class PKCS12Util {
                 OCTET_STRING keyID = (OCTET_STRING) new OCTET_STRING.Template().decode(bis);
 
                 keyInfo.id = new BigInteger(1, keyID.toByteArray());
-                logger.fine("ID: " + keyInfo.id.toString(16));
+                logger.fine("   ID: " + keyInfo.id.toString(16));
             }
         }
 
-        logger.fine("Found private key " + keyInfo.subjectDN);
-
         return keyInfo;
     }
 
@@ -406,12 +417,11 @@ public class PKCS12Util {
         byte[] x509cert = certStr.toByteArray();
 
         certInfo.cert = new X509CertImpl(x509cert);
-        logger.fine("Found certificate " + certInfo.cert.getSubjectDN());
+        logger.fine("   Subject DN: " + certInfo.cert.getSubjectDN());
 
         SET bagAttrs = bag.getBagAttributes();
-        if (bagAttrs == null) return certInfo;
 
-        for (int i = 0; i < bagAttrs.size(); i++) {
+        for (int i = 0; bagAttrs != null && i < bagAttrs.size(); i++) {
 
             Attribute attr = (Attribute) bagAttrs.elementAt(i);
             OBJECT_IDENTIFIER oid = attr.getType();
@@ -425,7 +435,7 @@ public class PKCS12Util {
                 BMPString nickname = (BMPString) (new BMPString.Template()).decode(bis);
 
                 certInfo.nickname = nickname.toString();
-                logger.fine("Nickname: " + certInfo.nickname);
+                logger.fine("   Nickname: " + certInfo.nickname);
 
 
             } else if (oid.equals(SafeBag.LOCAL_KEY_ID)) {
@@ -437,7 +447,7 @@ public class PKCS12Util {
                 OCTET_STRING keyID = (OCTET_STRING) new OCTET_STRING.Template().decode(bis);
 
                 certInfo.id = new BigInteger(1, keyID.toByteArray());
-                logger.fine("ID: " + certInfo.id.toString(16));
+                logger.fine("   ID: " + certInfo.id.toString(16));
 
             } else if (oid.equals(PKCS12.CERT_TRUST_FLAGS_OID) && trustFlagsEnabled) {
 
@@ -448,16 +458,22 @@ public class PKCS12Util {
                 BMPString trustFlags = (BMPString) (new BMPString.Template()).decode(is);
 
                 certInfo.trustFlags = trustFlags.toString();
-                logger.fine("Trust flags: " + certInfo.trustFlags);
+                logger.fine("   Trust flags: " + certInfo.trustFlags);
             }
         }
 
+        if (certInfo.id == null) {
+            logger.fine("   ID not specified, generating new ID");
+            certInfo.id = createLocalID(x509cert);
+            logger.fine("   ID: " + certInfo.id.toString(16));
+        }
+
         return certInfo;
     }
 
     public void getKeyInfos(PKCS12 pkcs12, PFX pfx, Password password) throws Exception {
 
-        logger.fine("Getting private keys");
+        logger.fine("Load private keys:");
 
         AuthenticatedSafes safes = pfx.getAuthSafes();
 
@@ -472,6 +488,7 @@ public class PKCS12Util {
 
                 if (!oid.equals(SafeBag.PKCS8_SHROUDED_KEY_BAG)) continue;
 
+                logger.fine(" - Private key:");
                 PKCS12KeyInfo keyInfo = getKeyInfo(bag, password);
                 pkcs12.addKeyInfo(keyInfo);
             }
@@ -480,7 +497,7 @@ public class PKCS12Util {
 
     public void getCertInfos(PKCS12 pkcs12, PFX pfx, Password password) throws Exception {
 
-        logger.fine("Getting certificates");
+        logger.fine("Loading certificates:");
 
         AuthenticatedSafes safes = pfx.getAuthSafes();
 
@@ -495,6 +512,7 @@ public class PKCS12Util {
 
                 if (!oid.equals(SafeBag.CERT_BAG)) continue;
 
+                logger.fine(" - Certificate:");
                 PKCS12CertInfo certInfo = getCertInfo(bag);
                 pkcs12.addCertInfo(certInfo, true);
             }
-- 
2.7.3

