>From dcab70339a397234a809d46d152bf0e547a5723e Mon Sep 17 00:00:00 2001 From: "Endi S. Dewata" Date: Sat, 13 Feb 2016 09:42:14 +0100 Subject: [PATCH] Added PKCS #12 attribute to store certificate trust flags. A new PKCS #12 attribute has been defined to store NSS certificate trust flags in PKCS #12 file. The PKCS12Util has been modified to store the trust flags during export and reset the trust flags in NSS database during import. https://fedorahosted.org/pki/ticket/1742 --- .../netscape/cmstools/pkcs12/PKCS12CertCLI.java | 10 +- .../netscape/cmstools/pkcs12/PKCS12ExportCLI.java | 5 + .../netscape/cmstools/pkcs12/PKCS12ImportCLI.java | 5 + .../com/netscape/cmstools/pkcs12/PKCS12KeyCLI.java | 2 +- base/util/src/netscape/security/pkcs/PKCS12.java | 136 +++++++++++++ .../src/netscape/security/pkcs/PKCS12Util.java | 213 +++++++++++++++------ 6 files changed, 305 insertions(+), 66 deletions(-) create mode 100644 base/util/src/netscape/security/pkcs/PKCS12.java diff --git a/base/java-tools/src/com/netscape/cmstools/pkcs12/PKCS12CertCLI.java b/base/java-tools/src/com/netscape/cmstools/pkcs12/PKCS12CertCLI.java index 2179c186c37ed324933f8f5867d954cc67a158ee..f4d97cd742ec294671008fece537ed4f0a09dc3b 100644 --- a/base/java-tools/src/com/netscape/cmstools/pkcs12/PKCS12CertCLI.java +++ b/base/java-tools/src/com/netscape/cmstools/pkcs12/PKCS12CertCLI.java @@ -18,6 +18,7 @@ package com.netscape.cmstools.pkcs12; +import com.netscape.certsrv.dbs.certdb.CertId; import com.netscape.cmstools.cli.CLI; import netscape.security.pkcs.PKCS12Util.PKCS12CertInfo; @@ -34,8 +35,13 @@ public class PKCS12CertCLI extends CLI { } public static void printCertInfo(PKCS12CertInfo certInfo) throws Exception { + System.out.println(" Serial Number: " + new CertId(certInfo.cert.getSerialNumber()).toHexString()); System.out.println(" Nickname: " + certInfo.nickname); - System.out.println(" Subject: " + certInfo.cert.getSubjectDN()); - System.out.println(" Issuer: " + certInfo.cert.getIssuerDN()); + System.out.println(" Subject DN: " + certInfo.cert.getSubjectDN()); + System.out.println(" Issuer DN: " + certInfo.cert.getIssuerDN()); + + if (certInfo.trustFlags != null) { + System.out.println(" Trust flags: " + certInfo.trustFlags); + } } } 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 1e67740047a8f191b5b38548de8704e81dea431f..e5acd06007b0bd0ef8edb561e6cf861b245a9dcb 100644 --- a/base/java-tools/src/com/netscape/cmstools/pkcs12/PKCS12ExportCLI.java +++ b/base/java-tools/src/com/netscape/cmstools/pkcs12/PKCS12ExportCLI.java @@ -59,6 +59,8 @@ public class PKCS12ExportCLI extends CLI { option.setArgName("path"); options.addOption(option); + options.addOption(null, "no-trust-flags", false, "Do not include trust flags"); + options.addOption("v", "verbose", false, "Run in verbose mode."); options.addOption(null, "debug", false, "Run in debug mode."); options.addOption(null, "help", false, "Show help message."); @@ -120,8 +122,11 @@ public class PKCS12ExportCLI extends CLI { Password password = new Password(passwordString.toCharArray()); + boolean trustFlagsEnabled = !cmd.hasOption("no-trust-flags"); + try { PKCS12Util util = new PKCS12Util(); + util.setTrustFlagsEnabled(trustFlagsEnabled); util.exportData(filename, password); } finally { password.clear(); diff --git a/base/java-tools/src/com/netscape/cmstools/pkcs12/PKCS12ImportCLI.java b/base/java-tools/src/com/netscape/cmstools/pkcs12/PKCS12ImportCLI.java index 8add346fb5ef78adb217061b1d1815a6b4ebd320..4e9ed23fc000842b9df0063b4b61ab59b7b5e8af 100644 --- a/base/java-tools/src/com/netscape/cmstools/pkcs12/PKCS12ImportCLI.java +++ b/base/java-tools/src/com/netscape/cmstools/pkcs12/PKCS12ImportCLI.java @@ -59,6 +59,8 @@ public class PKCS12ImportCLI extends CLI { option.setArgName("path"); options.addOption(option); + options.addOption(null, "no-trust-flags", false, "Do not include trust flags"); + options.addOption("v", "verbose", false, "Run in verbose mode."); options.addOption(null, "debug", false, "Run in debug mode."); options.addOption(null, "help", false, "Show help message."); @@ -120,8 +122,11 @@ public class PKCS12ImportCLI extends CLI { Password password = new Password(passwordString.toCharArray()); + boolean trustFlagsEnabled = !cmd.hasOption("no-trust-flags"); + try { PKCS12Util util = new PKCS12Util(); + util.setTrustFlagsEnabled(trustFlagsEnabled); util.importData(filename, password); } finally { password.clear(); diff --git a/base/java-tools/src/com/netscape/cmstools/pkcs12/PKCS12KeyCLI.java b/base/java-tools/src/com/netscape/cmstools/pkcs12/PKCS12KeyCLI.java index 5297a50adc749672b55c25f613f54de06bfbc7f8..9f0779782b19135d0adbb38803432918ed8ec580 100644 --- a/base/java-tools/src/com/netscape/cmstools/pkcs12/PKCS12KeyCLI.java +++ b/base/java-tools/src/com/netscape/cmstools/pkcs12/PKCS12KeyCLI.java @@ -34,7 +34,7 @@ public class PKCS12KeyCLI extends CLI { } public static void printKeyInfo(PKCS12KeyInfo keyInfo) throws Exception { - System.out.println(" Subject: " + keyInfo.subjectDN); + System.out.println(" Subject DN: " + keyInfo.subjectDN); if (keyInfo.privateKeyInfo != null) { System.out.println(" Algorithm: " + keyInfo.privateKeyInfo.getAlgorithm()); diff --git a/base/util/src/netscape/security/pkcs/PKCS12.java b/base/util/src/netscape/security/pkcs/PKCS12.java new file mode 100644 index 0000000000000000000000000000000000000000..df7fde447745dc9f17ee7c0e0d1ea42de1aab796 --- /dev/null +++ b/base/util/src/netscape/security/pkcs/PKCS12.java @@ -0,0 +1,136 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2016 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.pkcs; + +import org.mozilla.jss.asn1.OBJECT_IDENTIFIER; + +public class PKCS12 { + + // PKI OID: 2.16.840.1.113730.5 + public final static OBJECT_IDENTIFIER PKI_OID = new OBJECT_IDENTIFIER("2.16.840.1.113730.5"); + + // PKCS #12 OID: 2.16.840.1.113730.5.1 + public final static OBJECT_IDENTIFIER PKCS12_OID = PKI_OID.subBranch(1); + + // PKCS #12 attributes OID: 2.16.840.1.113730.5.1.1 + public final static OBJECT_IDENTIFIER PKCS12_ATTRIBUTES_OID = PKCS12_OID.subBranch(1); + + // Certificate trust flags OID: 2.16.840.1.113730.5.1.1.1 + public final static OBJECT_IDENTIFIER CERT_TRUST_FLAGS_OID = PKCS12_ATTRIBUTES_OID.subBranch(1); + + // based on certdb.h in NSS + public final static int TERMINAL_RECORD = 1 << 0; + public final static int TRUSTED = 1 << 1; + public final static int SEND_WARN = 1 << 2; + public final static int VALID_CA = 1 << 3; + public final static int TRUSTED_CA = 1 << 4; + public final static int NS_TRUSTED_CA = 1 << 5; + public final static int USER = 1 << 6; + public final static int TRUSTED_CLIENT_CA = 1 << 7; + public final static int INVISIBLE_CA = 1 << 8; + public final static int GOVT_APPROVED_CA = 1 << 9; + + public static boolean isFlagEnabled(int flag, int flags) { + return (flag & flags) > 0; + } + + // based on printflags() in secutil.c in NSS + public static String encodeFlags(int flags) { + + StringBuffer sb = new StringBuffer(); + + if (isFlagEnabled(VALID_CA, flags) && !isFlagEnabled(TRUSTED_CA, flags) && !isFlagEnabled(TRUSTED_CLIENT_CA, flags)) + sb.append("c"); + + if (isFlagEnabled(TERMINAL_RECORD, flags) && !isFlagEnabled(TRUSTED, flags)) + sb.append("p"); + + if (isFlagEnabled(TRUSTED_CA, flags)) + sb.append("C"); + + if (isFlagEnabled(TRUSTED_CLIENT_CA, flags)) + sb.append("T"); + + if (isFlagEnabled(TRUSTED, flags)) + sb.append("P"); + + if (isFlagEnabled(USER, flags)) + sb.append("u"); + + if (isFlagEnabled(SEND_WARN, flags)) + sb.append("w"); + + if (isFlagEnabled(INVISIBLE_CA, flags)) + sb.append("I"); + + if (isFlagEnabled(GOVT_APPROVED_CA, flags)) + sb.append("G"); + + return sb.toString(); + } + + // based on CERT_DecodeTrustString() in certdb.c in NSS + public static int decodeFlags(String flags) throws Exception { + + int value = 0; + + for (char c : flags.toCharArray()) { + switch (c) { + case 'p': + value = value | TERMINAL_RECORD; + break; + + case 'P': + value = value | TRUSTED | TERMINAL_RECORD; + break; + + case 'w': + value = value | SEND_WARN; + break; + + case 'c': + value = value | VALID_CA; + break; + + case 'T': + value = value | TRUSTED_CLIENT_CA | VALID_CA; + break; + + case 'C' : + value = value | TRUSTED_CA | VALID_CA; + break; + + case 'u': + value = value | USER; + break; + + case 'i': + value = value | INVISIBLE_CA; + break; + case 'g': + value = value | GOVT_APPROVED_CA; + break; + + default: + throw new Exception("Invalid trust flag: " + c); + } + } + + return value; + } +} diff --git a/base/util/src/netscape/security/pkcs/PKCS12Util.java b/base/util/src/netscape/security/pkcs/PKCS12Util.java index bcd48970e3b5f66835987e1b65fee81dfcc4963e..6acace0b96efe3dc1ab641b1271a92aafcc69b66 100644 --- a/base/util/src/netscape/security/pkcs/PKCS12Util.java +++ b/base/util/src/netscape/security/pkcs/PKCS12Util.java @@ -45,6 +45,7 @@ import org.mozilla.jss.crypto.CryptoStore; import org.mozilla.jss.crypto.CryptoToken; import org.mozilla.jss.crypto.EncryptionAlgorithm; import org.mozilla.jss.crypto.IVParameterSpec; +import org.mozilla.jss.crypto.InternalCertificate; import org.mozilla.jss.crypto.KeyGenAlgorithm; import org.mozilla.jss.crypto.KeyGenerator; import org.mozilla.jss.crypto.KeyWrapAlgorithm; @@ -73,6 +74,7 @@ public class PKCS12Util { private static Logger logger = Logger.getLogger(PKCS12Util.class.getName()); PFX pfx; + boolean trustFlagsEnabled = true; public static class PKCS12KeyInfo { public EncryptedPrivateKeyInfo encPrivateKeyInfo; @@ -83,6 +85,42 @@ public class PKCS12Util { public static class PKCS12CertInfo { public X509CertImpl cert; public String nickname; + public String trustFlags; + } + + public boolean isTrustFlagsEnabled() { + return trustFlagsEnabled; + } + + public void setTrustFlagsEnabled(boolean trustFlagsEnabled) { + this.trustFlagsEnabled = trustFlagsEnabled; + } + + public String getTrustFlags(X509Certificate cert) { + + InternalCertificate icert = (InternalCertificate) cert; + + StringBuilder sb = new StringBuilder(); + + sb.append(PKCS12.encodeFlags(icert.getSSLTrust())); + sb.append(","); + sb.append(PKCS12.encodeFlags(icert.getEmailTrust())); + sb.append(","); + sb.append(PKCS12.encodeFlags(icert.getObjectSigningTrust())); + + return sb.toString(); + } + + public void setTrustFlags(X509Certificate cert, String trustFlags) throws Exception { + + InternalCertificate icert = (InternalCertificate) cert; + + String[] flags = trustFlags.split(","); + if (flags.length < 3) throw new Exception("Invalid trust flags: " + trustFlags); + + icert.setSSLTrust(PKCS12.decodeFlags(flags[0])); + icert.setEmailTrust(PKCS12.decodeFlags(flags[1])); + icert.setObjectSigningTrust(PKCS12.decodeFlags(flags[2])); } byte[] getEncodedKey(PrivateKey privateKey) throws Exception { @@ -107,6 +145,8 @@ public class PKCS12Util { public void addKeyBag(PrivateKey privateKey, X509Certificate x509cert, Password pass, byte[] localKeyID, SEQUENCE safeContents) throws Exception { + logger.fine("Creating key bag for " + x509cert.getSubjectDN()); + PasswordConverter passConverter = new PasswordConverter(); byte salt[] = { 0x01, 0x01, 0x01, 0x01 }; byte[] priData = getEncodedKey(privateKey); @@ -118,7 +158,7 @@ public class PKCS12Util { PBEAlgorithm.PBE_SHA1_DES3_CBC, pass, salt, 1, passConverter, pki); - SET keyAttrs = createBagAttrs( + SET keyAttrs = createKeyBagAttrs( x509cert.getSubjectDN().toString(), localKeyID); SafeBag keyBag = new SafeBag(SafeBag.PKCS8_SHROUDED_KEY_BAG, @@ -130,12 +170,18 @@ public class PKCS12Util { public byte[] addCertBag(X509Certificate x509cert, String nickname, SEQUENCE safeContents) throws Exception { + logger.fine("Creating cert bag for " + nickname); + ASN1Value cert = new OCTET_STRING(x509cert.getEncoded()); byte[] localKeyID = createLocalKeyID(x509cert); - SET certAttrs = null; - if (nickname != null) - certAttrs = createBagAttrs(nickname, localKeyID); + String trustFlags = null; + if (trustFlagsEnabled) { + trustFlags = getTrustFlags(x509cert); + logger.fine("Trust flags: " + trustFlags); + } + + SET certAttrs = createCertBagAttrs(nickname, localKeyID, trustFlags); SafeBag certBag = new SafeBag(SafeBag.CERT_BAG, new CertBag(CertBag.X509_CERT_TYPE, cert), certAttrs); @@ -156,27 +202,66 @@ public class PKCS12Util { return md.digest(); } - SET createBagAttrs(String nickname, byte localKeyID[]) + SET createKeyBagAttrs(String subjectDN, byte localKeyID[]) throws Exception { SET attrs = new SET(); + + SEQUENCE subjectAttr = new SEQUENCE(); + subjectAttr.addElement(SafeBag.FRIENDLY_NAME); + + SET subjectSet = new SET(); + subjectSet.addElement(new BMPString(subjectDN)); + subjectAttr.addElement(subjectSet); + + attrs.addElement(subjectAttr); + + SEQUENCE localKeyAttr = new SEQUENCE(); + localKeyAttr.addElement(SafeBag.LOCAL_KEY_ID); + + SET localKeySet = new SET(); + localKeySet.addElement(new OCTET_STRING(localKeyID)); + localKeyAttr.addElement(localKeySet); + + attrs.addElement(localKeyAttr); + + return attrs; + } + + SET createCertBagAttrs(String nickname, byte localKeyID[], String trustFlags) + throws Exception { + + SET attrs = new SET(); + SEQUENCE nicknameAttr = new SEQUENCE(); - nicknameAttr.addElement(SafeBag.FRIENDLY_NAME); + SET nicknameSet = new SET(); - nicknameSet.addElement(new BMPString(nickname)); nicknameAttr.addElement(nicknameSet); + attrs.addElement(nicknameAttr); + SEQUENCE localKeyAttr = new SEQUENCE(); - localKeyAttr.addElement(SafeBag.LOCAL_KEY_ID); + SET localKeySet = new SET(); - localKeySet.addElement(new OCTET_STRING(localKeyID)); localKeyAttr.addElement(localKeySet); + attrs.addElement(localKeyAttr); + if (trustFlags != null && trustFlagsEnabled) { + SEQUENCE trustFlagsAttr = new SEQUENCE(); + trustFlagsAttr.addElement(PKCS12.CERT_TRUST_FLAGS_OID); + + SET trustFlagsSet = new SET(); + trustFlagsSet.addElement(new BMPString(trustFlags)); + trustFlagsAttr.addElement(trustFlagsSet); + + attrs.addElement(trustFlagsAttr); + } + return attrs; } @@ -191,7 +276,7 @@ public class PKCS12Util { SEQUENCE encSafeContents = new SEQUENCE(); SEQUENCE safeContents = new SEQUENCE(); - logger.fine("Loading certificates:"); + logger.fine("Loading certificates"); X509Certificate[] certs = store.getCertificates(); @@ -200,13 +285,13 @@ public class PKCS12Util { try { PrivateKey prikey = cm.findPrivKeyByCert(cert); - logger.fine(" - cert " + nickname + " with private key"); + logger.fine("Found certificate " + nickname + " with private key"); byte localKeyID[] = addCertBag(cert, nickname, safeContents); addKeyBag(prikey, cert, password, localKeyID, encSafeContents); } catch (ObjectNotFoundException e) { - logger.fine(" - cert " + nickname + " without private key"); + logger.fine("Found certificate " + nickname + " without private key"); addCertBag(cert, nickname, safeContents); } } @@ -286,41 +371,47 @@ public class PKCS12Util { public PKCS12CertInfo getCertInfo(SafeBag bag) throws Exception { - CertBag certBag = (CertBag) bag.getInterpretedBagContent(); - - OCTET_STRING str = (OCTET_STRING) certBag.getInterpretedCert(); - byte[] x509cert = str.toByteArray(); - - // find cert's nickname - SET bagAttrs = bag.getBagAttributes(); - String nickname = null; - - if (bagAttrs != null) { - - for (int k = 0; k < bagAttrs.size(); k++) { - - Attribute attr = (Attribute) bagAttrs.elementAt(k); - OBJECT_IDENTIFIER oid = attr.getType(); - - if (!oid.equals(SafeBag.FRIENDLY_NAME)) continue; - SET values = attr.getValues(); - ANY value = (ANY) values.elementAt(0); - - ByteArrayInputStream bbis = new ByteArrayInputStream(value.getEncoded()); - BMPString sss = (BMPString) (new BMPString.Template()).decode(bbis); - nickname = sss.toString(); - - break; - } - } - - X509CertImpl certImpl = new X509CertImpl(x509cert); - - logger.fine("Found certificate " + certImpl.getSubjectDN() + (nickname == null ? "" : " (" + nickname + ")")); - PKCS12CertInfo certInfo = new PKCS12CertInfo(); - certInfo.cert = certImpl; - certInfo.nickname = nickname; + + CertBag certBag = (CertBag) bag.getInterpretedBagContent(); + + OCTET_STRING certStr = (OCTET_STRING) certBag.getInterpretedCert(); + byte[] x509cert = certStr.toByteArray(); + + certInfo.cert = new X509CertImpl(x509cert); + logger.fine("Found certificate " + certInfo.cert.getSubjectDN()); + + SET bagAttrs = bag.getBagAttributes(); + if (bagAttrs == null) return certInfo; + + for (int i = 0; i < bagAttrs.size(); i++) { + + Attribute attr = (Attribute) bagAttrs.elementAt(i); + OBJECT_IDENTIFIER oid = attr.getType(); + + if (oid.equals(SafeBag.FRIENDLY_NAME)) { + + SET values = attr.getValues(); + ANY value = (ANY) values.elementAt(0); + + ByteArrayInputStream is = new ByteArrayInputStream(value.getEncoded()); + BMPString nicknameStr = (BMPString) (new BMPString.Template()).decode(is); + + certInfo.nickname = nicknameStr.toString(); + logger.fine("Nickname: " + certInfo.nickname); + + } else if (oid.equals(PKCS12.CERT_TRUST_FLAGS_OID) && trustFlagsEnabled) { + + SET values = attr.getValues(); + ANY value = (ANY) values.elementAt(0); + + ByteArrayInputStream is = new ByteArrayInputStream(value.getEncoded()); + BMPString trustFlagsStr = (BMPString) (new BMPString.Template()).decode(is); + + certInfo.trustFlags = trustFlagsStr.toString(); + logger.fine("Trust flags: " + certInfo.trustFlags); + } + } return certInfo; } @@ -481,35 +572,31 @@ public class PKCS12Util { } } - public X509Certificate importCert(X509CertImpl cert) throws Exception { + public X509Certificate importCert(X509CertImpl cert, String nickname, String trustFlags) throws Exception { logger.fine("Importing certificate " + cert.getSubjectDN()); CryptoManager cm = CryptoManager.getInstance(); - return cm.importCACertPackage(cert.getEncoded()); - } - public X509Certificate importCert(X509CertImpl cert, String nickname) throws Exception { + X509Certificate xcert; - logger.fine("Importing certificate " + cert.getSubjectDN() + " (" + nickname + ")"); + if (nickname == null) { + xcert = cm.importCACertPackage(cert.getEncoded()); - CryptoManager cm = CryptoManager.getInstance(); - return cm.importUserCACertPackage(cert.getEncoded(), nickname); + } else { + xcert = cm.importUserCACertPackage(cert.getEncoded(), nickname); + } + + if (trustFlags != null && trustFlagsEnabled) + setTrustFlags(xcert, trustFlags); + + return xcert; } public void importCerts(List certInfos) throws Exception { for (PKCS12CertInfo certInfo : certInfos) { - - X509CertImpl cert = certInfo.cert; - String nickname = certInfo.nickname; - - if (nickname == null) { - importCert(cert); - continue; - } - - importCert(cert, nickname); + importCert(certInfo.cert, certInfo.nickname, certInfo.trustFlags); } } -- 2.4.3