package fr.mch.mdo.security.util; import java.io.BufferedReader; import java.io.Console; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.OutputStream; import java.security.Key; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.util.Enumeration; /** * This class can be used to import a key/certificate pair from a pkcs12 file * into a regular JKS format keystore for use with jetty and other java based * SSL applications, etc. * * <PRE> * usage: java PKCS12Import {pkcs12file} [newjksfile] * </PRE> * * If you don't supply newjksfile, newstore.jks will be used. This can be an * existing JKS keystore. * <P> * Upon execution, you will be prompted for the password for the pkcs12 keystore * as well as the password for the jdk file. After execution you should have a * JKS keystore file that contains the private key and certificate that were in * the pkcs12 * <P> * You can generate a pkcs12 file from PEM encoded certificate and key files * using the following openssl command: * * <PRE> * openssl pkcs12 -export -out keystore.pkcs12 -in www.crt -inkey www.key * </PRE> * * then run: * * <PRE> * java PKCS12Import keystore.pkcs12 keytore.jks * </PRE> * * @author Jason Gilbert <jason@doozer.com> */ public class PKCS12Import { private static int exitCount = 0; public static final String PKCS12_KEY_STORE_INSTANCE_TYPE = "pkcs12"; public static final String JKS_KEY_STORE_INSTANCE_TYPE = "jks"; public static final String KEY_STORE_TYPE_IN_EXCEPTION_MESSAGE = "Could not retrieve PKCS12 key store instance"; public static final String KEY_STORE_TYPE_OUT_EXCEPTION_MESSAGE = "Could not retrieve JKS key store instance"; public static final String ILLEGAL_FILE_IN_NOT_FOUND_EXCEPTION_MESSAGE = "Could not found the input file"; public static final String ILLEGAL_KEY_STORE_LOAD_FILE_IN = "Could not load key store input file"; public static final String ILLEGAL_KEY_STORE_LOAD_FILE_OUT = "Could not load key store output file"; public static final String ILLEGAL_NUMBER_ARGUMENT_EXCEPTION_MESSAGE = "Number of arguments is between 1 and 3:\n Usage: java PKCS12Import {pkcs12file} [newjksfile] [passfile]"; public static final String ILLEGAL_FILE_READ_EXCEPTION_MESSAGE = "File can not be read"; public static final String ILLEGAL_FILE_WRITE_EXCEPTION_MESSAGE = "File can not be written"; public static final String NO_SUCH_FIELD_EXCEPTION_MESSAGE = "No Such field"; public static final String DEFAULT_OUTPUT_JSK_FILE = "newstore.jks"; /** * This method checks parameters and returns an arrays of files * * @param args * parameters to be checked * @return arrays of files */ private static File[] processParameters(String... args) { File fileIn = null; File fileOut = null; File filePass = null; switch (args.length) { case 3: filePass = new File(args[2]); case 2: fileOut = new File(args[1]); case 1: fileIn = new File(args[0]); if (fileOut == null) { fileOut = new File(PKCS12Import.DEFAULT_OUTPUT_JSK_FILE); } break; default: System.err.println("Usage: java PKCS12Import {pkcs12file} [newjksfile] [passfile]"); throw new IllegalArgumentException(PKCS12Import.ILLEGAL_NUMBER_ARGUMENT_EXCEPTION_MESSAGE); } if (!fileIn.canRead()) { System.err.println("Unable to access input keystore: " + fileIn.getPath()); throw new IllegalArgumentException(PKCS12Import.ILLEGAL_FILE_READ_EXCEPTION_MESSAGE); } if (fileOut.exists() && !fileOut.canWrite()) { System.err.println("Output file is not writable: " + fileOut.getPath()); throw new IllegalArgumentException(PKCS12Import.ILLEGAL_FILE_WRITE_EXCEPTION_MESSAGE); } if (filePass != null && !filePass.canRead()) { System.err.println("Password file is not readable: " + filePass.getPath()); throw new IllegalArgumentException(PKCS12Import.ILLEGAL_FILE_READ_EXCEPTION_MESSAGE); } return new File[] { fileIn, fileOut, filePass }; } /** * This method retrieves an array of passwords * * @param filePass * a password file * @return an array of passwords */ private static char[][] processPassPhrases(File filePass) { char[] inPhrase = null; char[] outPhrase = null; if (filePass != null) { BufferedReader bufferedReader = null; try { bufferedReader = new BufferedReader(new FileReader(filePass)); } catch (FileNotFoundException e) { System.err.println("This could not be happened because of processParameters method.\nFile Not Found: " + filePass.getAbsolutePath()); throw new IllegalArgumentException(PKCS12Import.ILLEGAL_FILE_READ_EXCEPTION_MESSAGE); } if (bufferedReader != null) { // Read input keystore passphrase String inPhraseString = null; try { inPhraseString = bufferedReader.readLine(); } catch (IOException e) { System.err.println("IOException Could not retrieve the input password phrase"); throw new NoSuchFieldError(PKCS12Import.NO_SUCH_FIELD_EXCEPTION_MESSAGE); } if (inPhraseString != null) { inPhraseString = inPhraseString.trim(); inPhrase = inPhraseString.toCharArray(); } else { System.err.println("Could not retrieve the input password phrase"); throw new NoSuchFieldError(PKCS12Import.NO_SUCH_FIELD_EXCEPTION_MESSAGE); } // Read output keystore passphrase String outPhraseString = null; try { outPhraseString = bufferedReader.readLine(); } catch (IOException e) { System.err.println("IOException Could not retrieve the output password phrase"); throw new NoSuchFieldError(PKCS12Import.NO_SUCH_FIELD_EXCEPTION_MESSAGE); } if (outPhraseString != null) { outPhraseString = outPhraseString.trim(); outPhrase = outPhraseString.toCharArray(); } else { System.err.println("Could not retrieve the output password phrase"); throw new NoSuchFieldError(PKCS12Import.NO_SUCH_FIELD_EXCEPTION_MESSAGE); } try { bufferedReader.close(); } catch (IOException e) { System.err.println("Could not close the file " + filePass.getAbsolutePath()); } } } else { inPhrase = readPassphrase("Enter input keystore passphrase: "); outPhrase = readPassphrase("Enter output keystore passphrase: "); } return new char[][] { inPhrase, outPhrase }; } /** * This is the main method to convert the PKCS12 to JKS * @param fileIn the PKCS12 input file * @param inPhrase the pass phrase of the PKCS12 input file * @param fileOut the JKS output file * @param outPhrase the pass phrase of the JKS output file */ private static void processKeyStore(String keySoreTypeIn, File fileIn, char[] inPhrase, String keySoreTypeOut, File fileOut, char[] outPhrase) { FileInputStream pkcs12FileInputStream = null; FileInputStream jksFileInputStream = null; OutputStream jksOutputStream = null; KeyStore kspkcs12 = null; try { // To close all open stream try { kspkcs12 = KeyStore.getInstance(keySoreTypeIn); } catch (KeyStoreException e) { System.err.println("Could not retrieve pkcs12 key store instance"); throw new IllegalArgumentException(PKCS12Import.KEY_STORE_TYPE_IN_EXCEPTION_MESSAGE); } KeyStore ksjks = null; try { ksjks = KeyStore.getInstance(keySoreTypeOut); } catch (KeyStoreException e) { System.err.println("Could not retrieve jks key store instance"); throw new IllegalArgumentException(PKCS12Import.KEY_STORE_TYPE_OUT_EXCEPTION_MESSAGE); } try { if (fileIn==null) { throw new FileNotFoundException(); } pkcs12FileInputStream = new FileInputStream(fileIn); } catch (FileNotFoundException e) { System.err.println("File Not Found " + fileIn); throw new IllegalArgumentException(PKCS12Import.ILLEGAL_FILE_IN_NOT_FOUND_EXCEPTION_MESSAGE); } try { kspkcs12.load(pkcs12FileInputStream, inPhrase); } catch (NoSuchAlgorithmException e) { System.err.println("Could not load pkcs12 NoSuchAlgorithmException"); System.exit(++exitCount); } catch (CertificateException e) { System.err.println("Could not load pkcs12 CertificateException"); System.exit(++exitCount); } catch (IOException e) { System.err.println("Could not load pkcs12 IOException"); throw new IllegalArgumentException(PKCS12Import.ILLEGAL_KEY_STORE_LOAD_FILE_IN); } try { if (fileOut == null) { fileOut = new File(PKCS12Import.DEFAULT_OUTPUT_JSK_FILE); } jksFileInputStream = new FileInputStream(fileOut); } catch (Exception e) { System.out.println("WARNING File Not Found " + fileOut.getAbsolutePath()); } try { ksjks.load(jksFileInputStream, outPhrase); } catch (NoSuchAlgorithmException e) { System.err.println("Could not load jks NoSuchAlgorithmException"); System.exit(++exitCount); } catch (CertificateException e) { System.err.println("Could not load jks CertificateException"); System.exit(++exitCount); } catch (IOException e) { System.err.println("Could not load jks IOException"); throw new IllegalArgumentException(PKCS12Import.ILLEGAL_KEY_STORE_LOAD_FILE_OUT); } Enumeration<String> eAliases = null; try { eAliases = kspkcs12.aliases(); } catch (KeyStoreException e) { System.err.println("Could get pkcs12 aliases"); System.exit(++exitCount); } if (eAliases != null) { int n = 0; while (eAliases.hasMoreElements()) { String strAlias = (String) eAliases.nextElement(); System.out.println("Alias " + n++ + ": " + strAlias); boolean isKeyEntry = false; try { isKeyEntry = kspkcs12.isKeyEntry(strAlias); } catch (KeyStoreException e) { System.err.println("pkcs12 not initialize"); System.exit(++exitCount); } if (isKeyEntry) { System.out.println("Adding key for alias " + strAlias); Key key = null; try { key = kspkcs12.getKey(strAlias, inPhrase); } catch (UnrecoverableKeyException e) { System.err .println("UnrecoverableKeyException Could not get pkcs12 key with alias " + strAlias); System.exit(++exitCount); } catch (KeyStoreException e) { System.err .println("KeyStoreException Could not get pkcs12 key with alias " + strAlias); System.exit(++exitCount); } catch (NoSuchAlgorithmException e) { System.err .println("NoSuchAlgorithmException Could not get pkcs12 key with alias " + strAlias); System.exit(++exitCount); } Certificate[] chain = null; try { chain = kspkcs12.getCertificateChain(strAlias); } catch (KeyStoreException e) { System.err .println("Could not get pkcs12 certificate chain with alias " + strAlias); System.exit(++exitCount); } try { ksjks.setKeyEntry(strAlias, key, outPhrase, chain); } catch (KeyStoreException e) { System.err .println("Could not set jks entry key with alias " + strAlias); System.exit(++exitCount); } } } try { jksOutputStream = new FileOutputStream(fileOut); } catch (FileNotFoundException e) { System.err.println("File Not Found " + fileOut.getAbsolutePath()); System.exit(++exitCount); } try { ksjks.store(jksOutputStream, outPhrase); } catch (KeyStoreException e) { System.err .println("KeyStoreException Could not store jsk in " + fileOut.getAbsolutePath()); System.exit(++exitCount); } catch (NoSuchAlgorithmException e) { System.err .println("NoSuchAlgorithmException Could not store jsk in " + fileOut.getAbsolutePath()); System.exit(++exitCount); } catch (CertificateException e) { System.err .println("CertificateException Could not store jsk in " + fileOut.getAbsolutePath()); System.exit(++exitCount); } catch (IOException e) { System.err.println("IOException Could not store jsk in " + fileOut.getAbsolutePath()); System.exit(++exitCount); } try { jksOutputStream.close(); } catch (IOException e) { System.err.println("Could not close " + fileOut.getAbsolutePath()); System.exit(++exitCount); } } else { System.err.println("Could get pkcs12 aliases"); System.exit(++exitCount); } } finally { // To close all open stream try { jksOutputStream.close(); } catch (Exception e) { System.err.println("Could not close the JKS OutputStream " + fileOut); } try { jksFileInputStream.close(); } catch (Exception e) { System.err.println("Could not close the JKS InputStream " + fileOut); } try { pkcs12FileInputStream.close(); } catch (Exception e) { System.err.println("Could not close the PKCS12 InputStream " + fileIn); } } } /** * Entry point to convert PKCS12 to JKS * @param args the parameters used to convert PKCS12 to JKS */ public static void processImport(String... args) { File[] files = processParameters(args); File fileIn = files[0]; File fileOut = files[1]; File filePass = files[2]; //Could be null char[][] passPhrases = processPassPhrases(filePass); processKeyStore(PKCS12Import.PKCS12_KEY_STORE_INSTANCE_TYPE, fileIn, passPhrases[0], PKCS12Import.JKS_KEY_STORE_INSTANCE_TYPE, fileOut, passPhrases[1]); } /** * Entry point for this class * @param args the parameters used to convert PKCS12 to JKS * @throws Exception when any errors occur */ public static void main(String... args) throws Exception { PKCS12Import.processImport(args); } private static char[] readPassphrase(String label) { Console console = System.console(); char[] phrase = null; if (console != null) { phrase = console.readPassword(label); } else { System.err.println("Console not found"); } if (phrase == null || phrase.length == 0) { System.err.println("Could not retrieve the input password phrase"); throw new NoSuchFieldError(PKCS12Import.NO_SUCH_FIELD_EXCEPTION_MESSAGE); } return phrase; } }