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