package fr.mch.mdo.security.util; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import junit.framework.Test; import junit.framework.TestSuite; import fr.mch.mdo.test.MdoTestCase; /** * 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 PKCS12ImportTest extends MdoTestCase { /** Dummy file for testing */ private static final String DUMMY_FILE = "src/test/resources/generated/dummy.file"; /** The PKCS12 file to be converted */ private static final String PKCS12_INPUT_FILE = "src/test/resources/mdo-keystore.pkcs12"; /** The converted JKS file */ private static final String JKS_OUTPUT_FILE = "src/test/resources/generated/mdo-keystore.jks"; /** The pass phrases file for PKCS12_INPUT_FILE and JKS_OUTPUT_FILE*/ private static final String PASS_PHRASES_INPUT_FILE_2 = "src/test/resources/keystore-pkcs12_2.pass"; /** The pass phrases file for PKCS12_INPUT_FILE and JKS_OUTPUT_FILE*/ private static final String PASS_PHRASES_INPUT_FILE_1 = "src/test/resources/keystore-pkcs12_1.pass"; /** The pass phrases file for PKCS12_INPUT_FILE and JKS_OUTPUT_FILE*/ private static final String PASS_PHRASES_INPUT_FILE_0 = "src/test/resources/keystore-pkcs12_0.pass"; /** * Create the test case * * @param testName * name of the test case */ public PKCS12ImportTest(String testName) { super(testName); } /** * @return the suite of tests being tested */ public static Test suite() { return new TestSuite(PKCS12ImportTest.class); } public void testProcessImport() { File jksFile = new File(PKCS12ImportTest.JKS_OUTPUT_FILE); assertFalse("The JKS file must not exist", jksFile.exists()); PKCS12Import.processImport(PKCS12ImportTest.PKCS12_INPUT_FILE, PKCS12ImportTest.JKS_OUTPUT_FILE, PKCS12ImportTest.PASS_PHRASES_INPUT_FILE_2); assertTrue("The JKS file must be generated", jksFile.exists()); assertTrue("The JKS file must be deleted", jksFile.delete()); } public void testProcessParameters() { //0 argument try { PKCS12Import.processImport(); } catch(IllegalArgumentException e) { assertEquals("The method PKCS12Import.processImport() must throw an Exception", PKCS12Import.ILLEGAL_NUMBER_ARGUMENT_EXCEPTION_MESSAGE, e.getMessage()); } //4 arguments try { PKCS12Import.processImport(PKCS12ImportTest.PKCS12_INPUT_FILE, PKCS12ImportTest.JKS_OUTPUT_FILE, PKCS12ImportTest.PASS_PHRASES_INPUT_FILE_2, PKCS12ImportTest.DUMMY_FILE); } catch(IllegalArgumentException e) { assertEquals("The method PKCS12Import.processImport() must throw an Exception", PKCS12Import.ILLEGAL_NUMBER_ARGUMENT_EXCEPTION_MESSAGE, e.getMessage()); } //1 arguments file can not be read File pkcs12File = new File(PKCS12ImportTest.DUMMY_FILE); try { assertFalse("This file can not be read", pkcs12File.canRead()); PKCS12Import.processImport(PKCS12ImportTest.DUMMY_FILE); } catch(IllegalArgumentException e) { assertEquals("The method PKCS12Import.processImport() must throw an Exception", PKCS12Import.ILLEGAL_FILE_READ_EXCEPTION_MESSAGE, e.getMessage()); } //2 arguments file can not be written File jksFile = new File(PKCS12ImportTest.JKS_OUTPUT_FILE); try { if(!jksFile.exists()) { try { jksFile.createNewFile(); jksFile.setReadOnly(); } catch (IOException e) { fail("Could not create JKS file" + e.getMessage()); } } assertFalse("This file can not be written", jksFile.canWrite()); PKCS12Import.processImport(PKCS12ImportTest.PKCS12_INPUT_FILE, PKCS12ImportTest.JKS_OUTPUT_FILE); } catch(IllegalArgumentException e) { assertEquals("The method PKCS12Import.processImport() must throw an Exception", PKCS12Import.ILLEGAL_FILE_WRITE_EXCEPTION_MESSAGE, e.getMessage()); } if(jksFile.exists()) { jksFile.delete(); } //3 arguments password file can not be read try { PKCS12Import.processImport(PKCS12ImportTest.PKCS12_INPUT_FILE, PKCS12ImportTest.JKS_OUTPUT_FILE, PKCS12ImportTest.DUMMY_FILE); } catch(IllegalArgumentException e) { assertEquals("The method PKCS12Import.processImport() must throw an Exception", PKCS12Import.ILLEGAL_FILE_READ_EXCEPTION_MESSAGE, e.getMessage()); } } public void testProcessPassPhrases() { //pass phrases file does not exist try { // Call private method PKCS12Import.processPassPhrases invokeProcessPassPhrases(new File(PKCS12ImportTest.DUMMY_FILE)); } catch (InvocationTargetException e) { assertTrue("The method PKCS12Import.processImport() must throw an IllegalArgumentException", e.getTargetException() instanceof IllegalArgumentException); assertEquals("The method PKCS12Import.processImport() must throw an Exception", PKCS12Import.ILLEGAL_FILE_READ_EXCEPTION_MESSAGE, e.getMessage()); } //No input password phrase try { PKCS12Import.processImport(PKCS12ImportTest.PKCS12_INPUT_FILE, PKCS12ImportTest.JKS_OUTPUT_FILE, PKCS12ImportTest.PASS_PHRASES_INPUT_FILE_0); } catch(NoSuchFieldError e) { assertEquals("The method PKCS12Import.processImport() must throw an Exception", PKCS12Import.NO_SUCH_FIELD_EXCEPTION_MESSAGE, e.getMessage()); } //No output password phrase try { PKCS12Import.processImport(PKCS12ImportTest.PKCS12_INPUT_FILE, PKCS12ImportTest.JKS_OUTPUT_FILE, PKCS12ImportTest.PASS_PHRASES_INPUT_FILE_1); } catch(NoSuchFieldError e) { assertEquals("The method PKCS12Import.processImport() must throw an Exception", PKCS12Import.NO_SUCH_FIELD_EXCEPTION_MESSAGE, e.getMessage()); } //No pass phrases file try { PKCS12Import.processImport(PKCS12ImportTest.PKCS12_INPUT_FILE, PKCS12ImportTest.JKS_OUTPUT_FILE); } catch(NoSuchFieldError e) { assertEquals("The method PKCS12Import.processImport() must throw an Exception", PKCS12Import.NO_SUCH_FIELD_EXCEPTION_MESSAGE, e.getMessage()); } } public void testProcessKeyStore() { File dummyFile = new File(PKCS12ImportTest.DUMMY_FILE); File pkcs12File = new File(PKCS12ImportTest.PKCS12_INPUT_FILE); try { invokeProcessKeyStore(PKCS12Import.PKCS12_KEY_STORE_INSTANCE_TYPE + "3", null, null, null, null, null); } catch(InvocationTargetException e) { assertTrue("The method PKCS12Import.processImport() must throw an IllegalArgumentException", e.getTargetException() instanceof IllegalArgumentException); assertEquals("The method PKCS12Import.processImport() must throw an Exception", PKCS12Import.KEY_STORE_TYPE_IN_EXCEPTION_MESSAGE, e.getMessage()); } try { invokeProcessKeyStore(PKCS12Import.PKCS12_KEY_STORE_INSTANCE_TYPE, null, null, PKCS12Import.JKS_KEY_STORE_INSTANCE_TYPE + "3", null, null); } catch(InvocationTargetException e) { assertTrue("The method PKCS12Import.processImport() must throw an IllegalArgumentException", e.getTargetException() instanceof IllegalArgumentException); assertEquals("The method PKCS12Import.processImport() must throw an Exception", PKCS12Import.KEY_STORE_TYPE_OUT_EXCEPTION_MESSAGE, e.getMessage()); } try { // Null input file invokeProcessKeyStore(PKCS12Import.PKCS12_KEY_STORE_INSTANCE_TYPE, null, null, PKCS12Import.JKS_KEY_STORE_INSTANCE_TYPE , null, null); } catch(InvocationTargetException e) { assertTrue("The method PKCS12Import.processImport() must throw an IllegalArgumentException", e.getTargetException() instanceof IllegalArgumentException); assertEquals("The method PKCS12Import.processImport() must throw an Exception", PKCS12Import.ILLEGAL_FILE_IN_NOT_FOUND_EXCEPTION_MESSAGE, e.getMessage()); } try { // Dummy input file invokeProcessKeyStore(PKCS12Import.PKCS12_KEY_STORE_INSTANCE_TYPE, dummyFile, null, PKCS12Import.JKS_KEY_STORE_INSTANCE_TYPE , null, null); } catch(InvocationTargetException e) { assertTrue("The method PKCS12Import.processImport() must throw an IllegalArgumentException", e.getTargetException() instanceof IllegalArgumentException); assertEquals("The method PKCS12Import.processImport() must throw an Exception", PKCS12Import.ILLEGAL_FILE_IN_NOT_FOUND_EXCEPTION_MESSAGE, e.getMessage()); } try { // Load pkcs12 NoSuchAlgorithmException invokeProcessKeyStore(PKCS12Import.PKCS12_KEY_STORE_INSTANCE_TYPE, pkcs12File, new char[] {'K', 'I', 'M', 'S', 'A', 'N'}, PKCS12Import.JKS_KEY_STORE_INSTANCE_TYPE , null, null); } catch(InvocationTargetException e) { assertTrue("The method PKCS12Import.processImport() must throw an IllegalArgumentException", e.getTargetException() instanceof IllegalArgumentException); assertEquals("The method PKCS12Import.processImport() must throw an Exception", PKCS12Import.ILLEGAL_KEY_STORE_LOAD_FILE_IN, e.getMessage()); } try { // Load jks IOException invokeProcessKeyStore(PKCS12Import.PKCS12_KEY_STORE_INSTANCE_TYPE, pkcs12File, new char[] {'k', 'i', 'm', 's', 'a', 'n'}, PKCS12Import.JKS_KEY_STORE_INSTANCE_TYPE , pkcs12File, null); } catch(InvocationTargetException e) { assertTrue("The method PKCS12Import.processImport() must throw an IllegalArgumentException", e.getTargetException() instanceof IllegalArgumentException); assertEquals("The method PKCS12Import.processImport() must throw an Exception", PKCS12Import.ILLEGAL_KEY_STORE_LOAD_FILE_OUT, e.getMessage()); } } private Object invokeProcessKeyStore(String keySoreTypeIn, File fileIn, char[] inPhrase, String keySoreTypeOut, File fileOut, char[] outPhrase) throws InvocationTargetException { // Purposely pass null values to the method, to make sure it throws // NullPointerException Class<?>[] argClasses = { String.class, File.class, char[].class, String.class, File.class, char[].class }; Object[] argObjects = { keySoreTypeIn, fileIn, inPhrase, keySoreTypeOut, fileOut, outPhrase }; return invokeStaticMethod(PKCS12Import.class, "processKeyStore", argClasses, argObjects); } private Object invokeProcessPassPhrases(File filePass) throws InvocationTargetException { // Purposely pass null values to the method, to make sure it throws // NullPointerException Class<?>[] argClasses = { File.class }; Object[] argObjects = { filePass }; return invokeStaticMethod(PKCS12Import.class, "processPassPhrases", argClasses, argObjects); } }