1/* 2 * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24/* 25 * @test 26 * @bug 8008296 27 * @summary Store and retrieve user passwords using PKCS#12 keystore 28 */ 29 30import java.io.*; 31import java.security.*; 32import java.util.*; 33import javax.crypto.*; 34import javax.crypto.spec.*; 35 36/* 37 * Store and retrieve passwords protected by a selection of PBE algorithms, 38 * using a PKCS#12 keystore. 39 */ 40public class StorePasswords { 41 42 private static final String[] PBE_ALGORITHMS = new String[] { 43 "default PBE algorithm", 44 "PBEWithMD5AndDES", 45 "PBEWithSHA1AndDESede", 46 "PBEWithSHA1AndRC2_40", 47 "PBEWithSHA1AndRC2_128", 48 "PBEWithSHA1AndRC4_40", 49 "PBEWithSHA1AndRC4_128", 50 "PBEWithHmacSHA1AndAES_128", 51 "PBEWithHmacSHA224AndAES_128", 52 "PBEWithHmacSHA256AndAES_128", 53 "PBEWithHmacSHA384AndAES_128", 54 "PBEWithHmacSHA512AndAES_128", 55 "PBEWithHmacSHA1AndAES_256", 56 "PBEWithHmacSHA224AndAES_256", 57 "PBEWithHmacSHA256AndAES_256", 58 "PBEWithHmacSHA384AndAES_256", 59 "PBEWithHmacSHA512AndAES_256" 60 }; 61 62 private static final String KEYSTORE = "mykeystore.p12"; 63 private static final char[] KEYSTORE_PWD = "changeit".toCharArray(); 64 private static final char[] ENTRY_PWD = "protectit".toCharArray(); 65 private static final char[] USER_PWD = "hello1".toCharArray(); 66 67 public static void main(String[] args) throws Exception { 68 69 new File(KEYSTORE).delete(); 70 71 int storeCount = store(); 72 int recoverCount = recover(); 73 74 if (recoverCount != storeCount) { 75 throw new Exception("Stored " + storeCount + " user passwords, " + 76 "recovered " + recoverCount + " user passwords"); 77 } 78 System.out.println("\nStored " + storeCount + " user passwords, " + 79 "recovered " + recoverCount + " user passwords"); 80 81 new File(KEYSTORE).delete(); 82 } 83 84 private static int store() throws Exception { 85 int count = 0; 86 // Load an empty PKCS#12 keystore 87 KeyStore keystore = KeyStore.getInstance("PKCS12"); 88 System.out.println("\nLoading PKCS#12 keystore..."); 89 keystore.load(null, null); 90 91 // Derive a PBE key from the password 92 PBEKeySpec keySpec = new PBEKeySpec(USER_PWD); 93 SecretKeyFactory factory = SecretKeyFactory.getInstance("PBE"); 94 SecretKey key = factory.generateSecret(keySpec); 95 PBEParameterSpec specWithEightByteSalt = 96 new PBEParameterSpec("NaClNaCl".getBytes(), 1024); 97 98 // Store the user password in a keystore entry (for each algorithm) 99 for (String algorithm : PBE_ALGORITHMS) { 100 101 try { 102 System.out.println("Storing user password '" + 103 new String(USER_PWD) + "' (protected by " + algorithm + 104 ")"); 105 106 if (algorithm.equals("default PBE algorithm")) { 107 keystore.setKeyEntry( 108 "this entry is protected by " + algorithm, key, 109 ENTRY_PWD, null); 110 } else { 111 keystore.setEntry( 112 "this entry is protected by " + algorithm, 113 new KeyStore.SecretKeyEntry(key), 114 new KeyStore.PasswordProtection(ENTRY_PWD, algorithm, 115 null)); 116 } 117 count++; 118 119 } catch (KeyStoreException e) { 120 Throwable inner = e.getCause(); 121 if (inner instanceof UnrecoverableKeyException) { 122 Throwable inner2 = inner.getCause(); 123 if (inner2 instanceof InvalidAlgorithmParameterException) { 124 System.out.println("...re-trying due to: " + 125 inner2.getMessage()); 126 127 // Some PBE algorithms demand an 8-byte salt 128 keystore.setEntry( 129 "this entry is protected by " + algorithm, 130 new KeyStore.SecretKeyEntry(key), 131 new KeyStore.PasswordProtection(ENTRY_PWD, 132 algorithm, specWithEightByteSalt)); 133 count++; 134 135 } else if (inner2 instanceof InvalidKeyException) { 136 System.out.println("...skipping due to: " + 137 inner2.getMessage()); 138 // Unsupported crypto keysize 139 continue; 140 } 141 } else { 142 throw e; 143 } 144 } 145 } 146 147 // Store the PKCS#12 keystore 148 System.out.println("Storing PKCS#12 keystore to: " + KEYSTORE); 149 try (FileOutputStream out = new FileOutputStream(KEYSTORE)) { 150 keystore.store(out, KEYSTORE_PWD); 151 } 152 153 return count; 154 } 155 156 private static int recover() throws Exception { 157 int count = 0; 158 // Load the PKCS#12 keystore 159 KeyStore keystore = KeyStore.getInstance("PKCS12"); 160 System.out.println("\nLoading PKCS#12 keystore from: " + KEYSTORE); 161 try (FileInputStream in = new FileInputStream(KEYSTORE)) { 162 keystore.load(in, KEYSTORE_PWD); 163 } 164 165 SecretKey key; 166 SecretKeyFactory factory; 167 PBEKeySpec keySpec; 168 169 // Retrieve each user password from the keystore 170 for (String algorithm : PBE_ALGORITHMS) { 171 key = (SecretKey) keystore.getKey("this entry is protected by " + 172 algorithm, ENTRY_PWD); 173 174 if (key != null) { 175 count++; 176 factory = SecretKeyFactory.getInstance(key.getAlgorithm()); 177 keySpec = 178 (PBEKeySpec) factory.getKeySpec(key, PBEKeySpec.class); 179 char[] pwd = keySpec.getPassword(); 180 System.out.println("Recovered user password '" + 181 new String(pwd) + "' (protected by " + algorithm + ")"); 182 183 if (!Arrays.equals(USER_PWD, pwd)) { 184 throw new Exception("Failed to recover the user password " + 185 "protected by " + algorithm); 186 } 187 } 188 } 189 190 return count; 191 } 192} 193