From de125310d5cfc6e62ba463d643e771b0bae0394d Mon Sep 17 00:00:00 2001 From: "Endi S. Dewata" Date: Tue, 9 Feb 2016 18:41:40 +0100 Subject: [PATCH] Refactored PKCS12Export. The code to export NSS database into PKCS #12 file in PKCS12Export tool has been refactored into PKCS12Util class to simplify further enhancements. The PKCS12Export tool has also been modified to use Java Logging API. A default logging configuration file has been added. The command-line wrapper has been modified to get the path to the logging configuration file from pki.conf. https://fedorahosted.org/pki/ticket/1742 --- base/common/CMakeLists.txt | 1 + base/common/share/etc/logging.properties | 28 +++ base/common/share/etc/pki.conf | 3 + .../src/com/netscape/cmstools/PKCS12Export.java | 215 ++------------------- .../templates/pki_java_command_wrapper.in | 7 +- .../src/netscape/security/pkcs/PKCS12Util.java | 214 ++++++++++++++++++++ 6 files changed, 273 insertions(+), 195 deletions(-) create mode 100644 base/common/share/etc/logging.properties create mode 100644 base/util/src/netscape/security/pkcs/PKCS12Util.java diff --git a/base/common/CMakeLists.txt b/base/common/CMakeLists.txt index ee401f201429167b67348c35c9c89c3d52b3c3fd..121392512de0b4695c5508fa432bca4f18d167cf 100644 --- a/base/common/CMakeLists.txt +++ b/base/common/CMakeLists.txt @@ -13,6 +13,7 @@ configure_file( install( FILES + ${CMAKE_CURRENT_SOURCE_DIR}/share/etc/logging.properties ${CMAKE_CURRENT_BINARY_DIR}/share/etc/pki.conf DESTINATION ${DATA_INSTALL_DIR}/etc/ diff --git a/base/common/share/etc/logging.properties b/base/common/share/etc/logging.properties new file mode 100644 index 0000000000000000000000000000000000000000..bd5b5b627903e0daa2c8b70a0569a5cc78321765 --- /dev/null +++ b/base/common/share/etc/logging.properties @@ -0,0 +1,28 @@ +# --- BEGIN COPYRIGHT BLOCK --- +# Copyright (C) 2016 Red Hat, Inc. +# All rights reserved. +# Modifications: configuration parameters +# --- END COPYRIGHT BLOCK --- + +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +handlers = java.util.logging.ConsoleHandler + +java.util.logging.ConsoleHandler.level = ALL +java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter +java.util.logging.SimpleFormatter.format = %4$s: %5$s%6$s%n + +.level = WARNING diff --git a/base/common/share/etc/pki.conf b/base/common/share/etc/pki.conf index a43d1d6c144379d655da06f77e8b5056bc787e6a..57cb83e5a5087f4d8efea2743a1b3b7cc95c0489 100644 --- a/base/common/share/etc/pki.conf +++ b/base/common/share/etc/pki.conf @@ -1,2 +1,5 @@ # JNI jar file location JNI_JAR_DIR=/usr/lib/java + +# logging configuration location +LOGGING_CONFIG=/usr/share/pki/etc/logging.properties diff --git a/base/java-tools/src/com/netscape/cmstools/PKCS12Export.java b/base/java-tools/src/com/netscape/cmstools/PKCS12Export.java index b8999fe99f57af644643011345323484c7b6e4ac..c23aa1fe1aa00ff43eb3c1da46bb8811c3ecbe31 100644 --- a/base/java-tools/src/com/netscape/cmstools/PKCS12Export.java +++ b/base/java-tools/src/com/netscape/cmstools/PKCS12Export.java @@ -18,39 +18,16 @@ package com.netscape.cmstools; import java.io.BufferedReader; -import java.io.ByteArrayOutputStream; -import java.io.FileOutputStream; import java.io.FileReader; -import java.security.MessageDigest; +import java.util.logging.Level; +import java.util.logging.Logger; import org.mozilla.jss.CryptoManager; -import org.mozilla.jss.asn1.ASN1Util; -import org.mozilla.jss.asn1.ASN1Value; -import org.mozilla.jss.asn1.BMPString; -import org.mozilla.jss.asn1.OCTET_STRING; -import org.mozilla.jss.asn1.SEQUENCE; -import org.mozilla.jss.asn1.SET; -import org.mozilla.jss.crypto.Cipher; -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.KeyGenAlgorithm; -import org.mozilla.jss.crypto.KeyGenerator; -import org.mozilla.jss.crypto.KeyWrapAlgorithm; -import org.mozilla.jss.crypto.KeyWrapper; -import org.mozilla.jss.crypto.PBEAlgorithm; -import org.mozilla.jss.crypto.SymmetricKey; -import org.mozilla.jss.crypto.X509Certificate; -import org.mozilla.jss.pkcs12.AuthenticatedSafes; -import org.mozilla.jss.pkcs12.CertBag; -import org.mozilla.jss.pkcs12.PFX; -import org.mozilla.jss.pkcs12.PasswordConverter; -import org.mozilla.jss.pkcs12.SafeBag; -import org.mozilla.jss.pkix.primitive.EncryptedPrivateKeyInfo; -import org.mozilla.jss.pkix.primitive.PrivateKeyInfo; import org.mozilla.jss.util.Password; +import netscape.security.pkcs.PKCS12Util; + /** * Tool for creating PKCS12 file * @@ -61,7 +38,7 @@ import org.mozilla.jss.util.Password; */ public class PKCS12Export { - boolean debug; + private static Logger logger = Logger.getLogger(PKCS12Export.class.getName()); String databaseDirectory; String databasePasswordFilename; @@ -69,14 +46,6 @@ public class PKCS12Export { String pkcs12PasswordFilename; String pkcs12OutputFilename; - public boolean isDebug() { - return debug; - } - - public void setDebug(boolean debug) { - this.debug = debug; - } - public String getDatabaseDirectory() { return databaseDirectory; } @@ -108,152 +77,9 @@ public class PKCS12Export { this.pkcs12OutputFilename = pkcs12OutputFilename; } - void debug(String s) { - if (debug) - System.out.println("PKCS12Export: " + s); - } - - byte[] getEncodedKey(org.mozilla.jss.crypto.PrivateKey pkey) throws Exception { - - CryptoManager cm = CryptoManager.getInstance(); - CryptoToken token = cm.getInternalKeyStorageToken(); - - KeyGenerator kg = token.getKeyGenerator(KeyGenAlgorithm.DES3); - SymmetricKey sk = kg.generate(); - - KeyWrapper wrapper = token.getKeyWrapper(KeyWrapAlgorithm.DES3_CBC_PAD); - byte iv[] = { 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 }; - IVParameterSpec param = new IVParameterSpec(iv); - wrapper.initWrap(sk, param); - byte[] enckey = wrapper.wrap(pkey); - - Cipher c = token.getCipherContext(EncryptionAlgorithm.DES3_CBC_PAD); - c.initDecrypt(sk, param); - return c.doFinal(enckey); - } - - void addKeyBag(org.mozilla.jss.crypto.PrivateKey pkey, X509Certificate x509cert, - Password pass, byte[] localKeyId, SEQUENCE safeContents) throws Exception { - - PasswordConverter passConverter = new PasswordConverter(); - byte salt[] = { 0x01, 0x01, 0x01, 0x01 }; - byte[] priData = getEncodedKey(pkey); - - PrivateKeyInfo pki = (PrivateKeyInfo) - ASN1Util.decode(PrivateKeyInfo.getTemplate(), priData); - - ASN1Value key = EncryptedPrivateKeyInfo.createPBE( - PBEAlgorithm.PBE_SHA1_DES3_CBC, - pass, salt, 1, passConverter, pki); - - SET keyAttrs = createBagAttrs( - x509cert.getSubjectDN().toString(), localKeyId); - - SafeBag keyBag = new SafeBag(SafeBag.PKCS8_SHROUDED_KEY_BAG, - key, keyAttrs); - - safeContents.addElement(keyBag); - } - - byte[] addCertBag(X509Certificate x509cert, String nickname, - SEQUENCE safeContents) throws Exception { - - ASN1Value cert = new OCTET_STRING(x509cert.getEncoded()); - byte[] localKeyId = createLocalKeyId(x509cert); - - SET certAttrs = null; - if (nickname != null) - certAttrs = createBagAttrs(nickname, localKeyId); - - SafeBag certBag = new SafeBag(SafeBag.CERT_BAG, - new CertBag(CertBag.X509_CERT_TYPE, cert), certAttrs); - - safeContents.addElement(certBag); - - return localKeyId; - } - - byte[] createLocalKeyId(X509Certificate cert) throws Exception { - - // SHA1 hash of the X509Cert der encoding - byte certDer[] = cert.getEncoded(); - - MessageDigest md = MessageDigest.getInstance("SHA"); - - md.update(certDer); - return md.digest(); - } - - SET createBagAttrs(String nickName, byte localKeyId[]) - 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); - - return attrs; - } - - public byte[] generatePKCS12Data(Password password) throws Exception { - - debug("Generating PKCS #12 data"); - - CryptoManager cm = CryptoManager.getInstance(); - CryptoToken token = cm.getInternalKeyStorageToken(); - CryptoStore store = token.getCryptoStore(); - - X509Certificate[] certs = store.getCertificates(); - - SEQUENCE encSafeContents = new SEQUENCE(); - SEQUENCE safeContents = new SEQUENCE(); - - for (int i = 0; i < certs.length; i++) { - String nickname = certs[i].getNickname(); - debug(" * Certificate: " + nickname); - try { - org.mozilla.jss.crypto.PrivateKey prikey = cm.findPrivKeyByCert(certs[i]); - - debug(" Private key exists"); - byte localKeyId[] = - addCertBag(certs[i], nickname, safeContents); - addKeyBag(prikey, certs[i], password, localKeyId, encSafeContents); - - } catch (org.mozilla.jss.crypto.ObjectNotFoundException e) { - debug(" Private key does not exist"); - addCertBag(certs[i], null, safeContents); - } - } - - AuthenticatedSafes authSafes = new AuthenticatedSafes(); - authSafes.addSafeContents(safeContents); - authSafes.addSafeContents(encSafeContents); - - PFX pfx = new PFX(authSafes); - pfx.computeMacData(password, null, 5); - - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - pfx.encode(bos); - - return bos.toByteArray(); - } - public void initDatabase() throws Exception { - debug("Initializing database in " + databaseDirectory); + logger.info("Initializing database in " + databaseDirectory); CryptoManager.InitializationValues vals = new CryptoManager.InitializationValues( @@ -263,7 +89,7 @@ public class PKCS12Export { CryptoManager cm = CryptoManager.getInstance(); CryptoToken token = cm.getInternalKeyStorageToken(); - debug("Reading database password from " + databasePasswordFilename); + logger.info("Reading database password from " + databasePasswordFilename); String line; try (BufferedReader in = new BufferedReader(new FileReader(databasePasswordFilename))) { @@ -274,7 +100,7 @@ public class PKCS12Export { } Password password = new Password(line.toCharArray()); - debug("Logging into security token"); + logger.info("Logging into security token"); try { token.login(password); @@ -285,7 +111,7 @@ public class PKCS12Export { public void exportData() throws Exception { - debug("Reading PKCS #12 password from " + pkcs12PasswordFilename); + logger.info("Reading PKCS #12 password from " + pkcs12PasswordFilename); String line; try (BufferedReader in = new BufferedReader(new FileReader(pkcs12PasswordFilename))) { @@ -296,18 +122,14 @@ public class PKCS12Export { } Password password = new Password(line.toCharArray()); - byte[] data; + logger.info("Exporting NSS database into " + pkcs12OutputFilename); + try { - data = generatePKCS12Data(password); + PKCS12Util util = new PKCS12Util(); + util.exportData(pkcs12OutputFilename, password); } finally { password.clear(); } - - debug("Storing PKCS #12 data into " + pkcs12OutputFilename); - - try (FileOutputStream fos = new FileOutputStream(pkcs12OutputFilename)) { - fos.write(data); - } } public static void printUsage() { @@ -355,11 +177,16 @@ public class PKCS12Export { } } + if (debug) { + Logger.getLogger("org.dogtagpki").setLevel(Level.FINE); + Logger.getLogger("com.netscape").setLevel(Level.FINE); + Logger.getLogger("netscape").setLevel(Level.FINE); + } + // TODO: validate parameters try { PKCS12Export tool = new PKCS12Export(); - tool.setDebug(debug); tool.setDatabaseDirectory(databaseDirectory); tool.setDatabasePasswordFilename(databasePasswordFilename); tool.setPkcs12PasswordFilename(pkcs12PasswordFilename); @@ -370,9 +197,9 @@ public class PKCS12Export { } catch (Exception e) { if (debug) { - e.printStackTrace(); + logger.log(Level.SEVERE, "Unable to export PKCS #12 file", e); } else { - System.err.println("ERROR: " + e); + logger.severe("Unable to export PKCS #12 file: " + e.getMessage()); } System.exit(1); } diff --git a/base/java-tools/templates/pki_java_command_wrapper.in b/base/java-tools/templates/pki_java_command_wrapper.in index 404bcf0a12914f714b59f9e8fe286fd75a42367a..56ca9f1fcbdd95a2e5e2be11e1ff42c0d1f0b22b 100644 --- a/base/java-tools/templates/pki_java_command_wrapper.in +++ b/base/java-tools/templates/pki_java_command_wrapper.in @@ -124,12 +124,17 @@ CP=/usr/share/java/${PRODUCT}/pki-cmsutil.jar:${CP} CP=/usr/share/java/${PRODUCT}/pki-tools.jar:${CP} export CP +LOGGING_CONFIG=`source /usr/share/pki/etc/pki.conf && source /etc/pki/pki.conf && echo $LOGGING_CONFIG` ############################################################################### ## (6) Execute the java command specified by this java command wrapper ## ## based upon the preset LD_LIBRARY_PATH and CP environment variables. ## ############################################################################### -${JAVA} ${JAVA_OPTIONS} -cp ${CP} com.netscape.cmstools.${COMMAND} "$@" +${JAVA} ${JAVA_OPTIONS} \ + -cp ${CP} \ + -Djava.util.logging.config.file=${LOGGING_CONFIG} \ + com.netscape.cmstools.${COMMAND} "$@" + exit $? diff --git a/base/util/src/netscape/security/pkcs/PKCS12Util.java b/base/util/src/netscape/security/pkcs/PKCS12Util.java new file mode 100644 index 0000000000000000000000000000000000000000..63051858e05a23e2f8b39503e2eaad07ad7dae1a --- /dev/null +++ b/base/util/src/netscape/security/pkcs/PKCS12Util.java @@ -0,0 +1,214 @@ +// --- 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 java.io.ByteArrayOutputStream; +import java.io.FileOutputStream; +import java.security.MessageDigest; +import java.util.logging.Logger; + +import org.mozilla.jss.CryptoManager; +import org.mozilla.jss.asn1.ASN1Util; +import org.mozilla.jss.asn1.ASN1Value; +import org.mozilla.jss.asn1.BMPString; +import org.mozilla.jss.asn1.OCTET_STRING; +import org.mozilla.jss.asn1.SEQUENCE; +import org.mozilla.jss.asn1.SET; +import org.mozilla.jss.crypto.Cipher; +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.KeyGenAlgorithm; +import org.mozilla.jss.crypto.KeyGenerator; +import org.mozilla.jss.crypto.KeyWrapAlgorithm; +import org.mozilla.jss.crypto.KeyWrapper; +import org.mozilla.jss.crypto.ObjectNotFoundException; +import org.mozilla.jss.crypto.PBEAlgorithm; +import org.mozilla.jss.crypto.PrivateKey; +import org.mozilla.jss.crypto.SymmetricKey; +import org.mozilla.jss.crypto.X509Certificate; +import org.mozilla.jss.pkcs12.AuthenticatedSafes; +import org.mozilla.jss.pkcs12.CertBag; +import org.mozilla.jss.pkcs12.PFX; +import org.mozilla.jss.pkcs12.PasswordConverter; +import org.mozilla.jss.pkcs12.SafeBag; +import org.mozilla.jss.pkix.primitive.EncryptedPrivateKeyInfo; +import org.mozilla.jss.pkix.primitive.PrivateKeyInfo; +import org.mozilla.jss.util.Password; + +public class PKCS12Util { + + private static Logger logger = Logger.getLogger(PKCS12Util.class.getName()); + + PFX pfx; + + byte[] getEncodedKey(PrivateKey privateKey) throws Exception { + + CryptoManager cm = CryptoManager.getInstance(); + CryptoToken token = cm.getInternalKeyStorageToken(); + + KeyGenerator kg = token.getKeyGenerator(KeyGenAlgorithm.DES3); + SymmetricKey sk = kg.generate(); + + KeyWrapper wrapper = token.getKeyWrapper(KeyWrapAlgorithm.DES3_CBC_PAD); + byte[] iv = { 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 }; + IVParameterSpec param = new IVParameterSpec(iv); + wrapper.initWrap(sk, param); + byte[] enckey = wrapper.wrap(privateKey); + + Cipher c = token.getCipherContext(EncryptionAlgorithm.DES3_CBC_PAD); + c.initDecrypt(sk, param); + return c.doFinal(enckey); + } + + public void addKeyBag(PrivateKey privateKey, X509Certificate x509cert, + Password pass, byte[] localKeyID, SEQUENCE safeContents) throws Exception { + + PasswordConverter passConverter = new PasswordConverter(); + byte salt[] = { 0x01, 0x01, 0x01, 0x01 }; + byte[] priData = getEncodedKey(privateKey); + + PrivateKeyInfo pki = (PrivateKeyInfo) + ASN1Util.decode(PrivateKeyInfo.getTemplate(), priData); + + ASN1Value key = EncryptedPrivateKeyInfo.createPBE( + PBEAlgorithm.PBE_SHA1_DES3_CBC, + pass, salt, 1, passConverter, pki); + + SET keyAttrs = createBagAttrs( + x509cert.getSubjectDN().toString(), localKeyID); + + SafeBag keyBag = new SafeBag(SafeBag.PKCS8_SHROUDED_KEY_BAG, + key, keyAttrs); + + safeContents.addElement(keyBag); + } + + public byte[] addCertBag(X509Certificate x509cert, String nickname, + SEQUENCE safeContents) throws Exception { + + ASN1Value cert = new OCTET_STRING(x509cert.getEncoded()); + byte[] localKeyID = createLocalKeyID(x509cert); + + SET certAttrs = null; + if (nickname != null) + certAttrs = createBagAttrs(nickname, localKeyID); + + SafeBag certBag = new SafeBag(SafeBag.CERT_BAG, + new CertBag(CertBag.X509_CERT_TYPE, cert), certAttrs); + + safeContents.addElement(certBag); + + return localKeyID; + } + + byte[] createLocalKeyID(X509Certificate cert) throws Exception { + + // SHA1 hash of the X509Cert DER encoding + byte[] certDer = cert.getEncoded(); + + MessageDigest md = MessageDigest.getInstance("SHA"); + + md.update(certDer); + return md.digest(); + } + + SET createBagAttrs(String nickname, byte localKeyID[]) + 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); + + return attrs; + } + + public void loadFromNSS(Password password) throws Exception { + + logger.info("Loading data from NSS database"); + + CryptoManager cm = CryptoManager.getInstance(); + CryptoToken token = cm.getInternalKeyStorageToken(); + CryptoStore store = token.getCryptoStore(); + + SEQUENCE encSafeContents = new SEQUENCE(); + SEQUENCE safeContents = new SEQUENCE(); + + logger.fine("Loading certificates:"); + + X509Certificate[] certs = store.getCertificates(); + + for (X509Certificate cert : certs) { + String nickname = cert.getNickname(); + + try { + PrivateKey prikey = cm.findPrivKeyByCert(cert); + logger.fine(" - cert " + 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"); + addCertBag(cert, nickname, safeContents); + } + } + + AuthenticatedSafes authSafes = new AuthenticatedSafes(); + authSafes.addSafeContents(safeContents); + authSafes.addSafeContents(encSafeContents); + + pfx = new PFX(authSafes); + } + + public void storeIntoPKCS12(String filename, Password password) throws Exception { + + logger.info("Storing data into PKCS #12 file"); + + pfx.computeMacData(password, null, 5); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + pfx.encode(bos); + byte[] data = bos.toByteArray(); + + try (FileOutputStream fos = new FileOutputStream(filename)) { + fos.write(data); + } + } + + public void exportData(String filename, Password password) throws Exception { + + loadFromNSS(password); + storeIntoPKCS12(filename, password); + } +} -- 2.4.3