// --- 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) 2007 Red Hat, Inc. // All rights reserved. // --- END COPYRIGHT BLOCK --- package com.netscape.cmstools; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.security.MessageDigest; 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; /** * Tool for creating PKCS12 file * *

* * @version $Revision$, $Date$ * */ public class PKCS12Export { private static boolean debugMode = false; private static void debug(String s) { if (debugMode) System.out.println("PKCS12Export debug: " + s); } private static void printUsage() { System.out.println( "Usage: PKCS12Export -d -p -w -o "); System.out.println(""); System.out.println("If you want to turn on debug, do the following:"); System.out.println( "Usage: PKCS12Export -debug -d -p -w -o "); } private static byte[] getEncodedKey(org.mozilla.jss.crypto.PrivateKey pkey) { try { 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); byte[] recovered = c.doFinal(enckey); return recovered; } catch (Exception e) { debug("PKCS12Export getEncodedKey: Exception=" + e.toString()); System.exit(1); } return null; } private static void addKeyBag(org.mozilla.jss.crypto.PrivateKey pkey, X509Certificate x509cert, Password pass, byte[] localKeyId, SEQUENCE safeContents) { try { 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); } catch (Exception e) { debug("PKCS12Export addKeyBag: Exception=" + e.toString()); System.exit(1); } } private static byte[] addCertBag(X509Certificate x509cert, String nickname, SEQUENCE safeContents) throws IOException { byte[] localKeyId = null; try { ASN1Value cert = new OCTET_STRING(x509cert.getEncoded()); 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); } catch (Exception e) { debug("PKCS12Export addCertBag: " + e.toString()); System.exit(1); } return localKeyId; } private static byte[] createLocalKeyId(X509Certificate cert) { try { // SHA1 hash of the X509Cert der encoding byte certDer[] = cert.getEncoded(); MessageDigest md = MessageDigest.getInstance("SHA"); md.update(certDer); return md.digest(); } catch (Exception e) { debug("PKCS12Export createLocalKeyId: Exception: " + e.toString()); System.exit(1); } return null; } private static SET createBagAttrs(String nickName, byte localKeyId[]) throws IOException { try { 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; } catch (Exception e) { debug("PKCS12Export createBagAttrs: Exception=" + e.toString()); System.exit(1); } return null; } public static void main(String args[]) { if (args.length < 8) { printUsage(); System.exit(1); } String pwdfile = null; String dir = null; String pk12pwdfile = null; String pk12output = null; for (int i = 0; i < args.length; i++) { if (args[i].equals("-d")) { dir = args[i + 1]; } else if (args[i].equals("-p")) { pwdfile = args[i + 1]; } else if (args[i].equals("-s")) { // snickname = args[i + 1]; } else if (args[i].equals("-w")) { pk12pwdfile = args[i + 1]; } else if (args[i].equals("-o")) { pk12output = args[i + 1]; } else if (args[i].equals("-debug")) { debugMode = true; } } debug("The directory for certdb/keydb is " + dir); debug("The password file for keydb is " + pwdfile); // get password String pwd = null; BufferedReader in = null; try { in = new BufferedReader(new FileReader(pwdfile)); pwd = in.readLine(); if (pwd == null) { pwd = ""; } } catch (Exception e) { debug("Failed to read the keydb password from the file. Exception: " + e.toString()); System.exit(1); } finally { if (in != null) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } } String pk12pwd = null; try { in = new BufferedReader(new FileReader(pk12pwdfile)); pk12pwd = in.readLine(); if (pk12pwd == null) { pk12pwd = ""; } } catch (Exception e) { debug("Failed to read the keydb password from the file. Exception: " + e.toString()); System.exit(1); } finally { if (in != null) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } } CryptoManager cm = null; try { CryptoManager.InitializationValues vals = new CryptoManager.InitializationValues(dir, "", "", "secmod.db"); CryptoManager.initialize(vals); cm = CryptoManager.getInstance(); } catch (Exception e) { debug("Failed to initialize the certdb."); System.exit(1); } SEQUENCE encSafeContents = new SEQUENCE(); SEQUENCE safeContents = new SEQUENCE(); try { CryptoToken token = cm.getInternalKeyStorageToken(); Password pass = new Password(pwd.toCharArray()); token.login(pass); CryptoStore store = token.getCryptoStore(); X509Certificate[] certs = store.getCertificates(); debug("Number of user certificates = " + certs.length); Password pass12 = new Password(pk12pwd.toCharArray()); for (int i = 0; i < certs.length; i++) { String nickname = certs[i].getNickname(); debug("Certificate nickname = " + nickname); org.mozilla.jss.crypto.PrivateKey prikey = null; try { prikey = cm.findPrivKeyByCert(certs[i]); } catch (Exception e) { debug("PKCS12Export Exception: " + e.toString()); } if (prikey == null) { debug("Private key is null"); addCertBag(certs[i], null, safeContents); } else { debug("Private key is not null"); byte localKeyId[] = addCertBag(certs[i], nickname, safeContents); addKeyBag(prikey, certs[i], pass12, localKeyId, encSafeContents); } } AuthenticatedSafes authSafes = new AuthenticatedSafes(); authSafes.addSafeContents(safeContents); authSafes.addSafeContents(encSafeContents); PFX pfx = new PFX(authSafes); pfx.computeMacData(pass12, null, 5); ByteArrayOutputStream bos = new ByteArrayOutputStream(); pfx.encode(bos); FileOutputStream fos = new FileOutputStream(pk12output); fos.write(bos.toByteArray()); fos.flush(); fos.close(); pass.clear(); pass12.clear(); } catch (Exception e) { debug("PKCS12Export Exception: " + e.toString()); System.exit(1); } } }