1 /*
2  * Copyright (c) 2015, 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.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package sun.security.provider;
27 
28 import java.io.*;
29 import java.security.*;
30 import java.security.cert.Certificate;
31 import java.security.cert.CertificateFactory;
32 import java.security.cert.CertificateException;
33 import java.util.*;
34 
35 import sun.security.util.Debug;
36 
37 /**
38  * This class delegates to a primary or secondary keystore implementation.
39  *
40  * @since 1.8
41  */
42 
43 class KeyStoreDelegator extends KeyStoreSpi {
44 
45     private static final String KEYSTORE_TYPE_COMPAT = "keystore.type.compat";
46     private static final Debug debug = Debug.getInstance("keystore");
47 
48     private final String primaryType;   // the primary keystore's type
49     private final String secondaryType; // the secondary keystore's type
50     private final Class<? extends KeyStoreSpi> primaryKeyStore;
51                                         // the primary keystore's class
52     private final Class<? extends KeyStoreSpi> secondaryKeyStore;
53                                         // the secondary keystore's class
54     private String type; // the delegate's type
55     private KeyStoreSpi keystore; // the delegate
56     private boolean compatModeEnabled = true;
57 
KeyStoreDelegator( String primaryType, Class<? extends KeyStoreSpi> primaryKeyStore, String secondaryType, Class<? extends KeyStoreSpi> secondaryKeyStore)58     public KeyStoreDelegator(
59         String primaryType,
60         Class<? extends KeyStoreSpi> primaryKeyStore,
61         String secondaryType,
62         Class<? extends KeyStoreSpi> secondaryKeyStore) {
63 
64         // Check whether compatibility mode has been disabled
65         // (Use inner-class instead of lambda to avoid init/ClassLoader problem)
66         compatModeEnabled = "true".equalsIgnoreCase(
67             AccessController.doPrivileged(
68                 new PrivilegedAction<String>() {
69                     public String run() {
70                         return Security.getProperty(KEYSTORE_TYPE_COMPAT);
71                     }
72                 }
73             ));
74 
75         if (compatModeEnabled) {
76             this.primaryType = primaryType;
77             this.secondaryType = secondaryType;
78             this.primaryKeyStore = primaryKeyStore;
79             this.secondaryKeyStore = secondaryKeyStore;
80         } else {
81             this.primaryType = primaryType;
82             this.secondaryType = null;
83             this.primaryKeyStore = primaryKeyStore;
84             this.secondaryKeyStore = null;
85 
86             if (debug != null) {
87                 debug.println("WARNING: compatibility mode disabled for " +
88                     primaryType + " and " + secondaryType + " keystore types");
89             }
90         }
91     }
92 
93     @Override
engineGetKey(String alias, char[] password)94     public Key engineGetKey(String alias, char[] password)
95         throws NoSuchAlgorithmException, UnrecoverableKeyException {
96         return keystore.engineGetKey(alias, password);
97     }
98 
99     @Override
engineGetCertificateChain(String alias)100     public Certificate[] engineGetCertificateChain(String alias) {
101         return keystore.engineGetCertificateChain(alias);
102     }
103 
104     @Override
engineGetCertificate(String alias)105     public Certificate engineGetCertificate(String alias) {
106         return keystore.engineGetCertificate(alias);
107     }
108 
109     @Override
engineGetCreationDate(String alias)110     public Date engineGetCreationDate(String alias) {
111         return keystore.engineGetCreationDate(alias);
112     }
113 
114     @Override
engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain)115     public void engineSetKeyEntry(String alias, Key key, char[] password,
116         Certificate[] chain) throws KeyStoreException {
117         keystore.engineSetKeyEntry(alias, key, password, chain);
118     }
119 
120     @Override
engineSetKeyEntry(String alias, byte[] key, Certificate[] chain)121     public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain)
122         throws KeyStoreException {
123         keystore.engineSetKeyEntry(alias, key, chain);
124     }
125 
126     @Override
engineSetCertificateEntry(String alias, Certificate cert)127     public void engineSetCertificateEntry(String alias, Certificate cert)
128         throws KeyStoreException {
129         keystore.engineSetCertificateEntry(alias, cert);
130     }
131 
132     @Override
engineDeleteEntry(String alias)133     public void engineDeleteEntry(String alias) throws KeyStoreException {
134         keystore.engineDeleteEntry(alias);
135     }
136 
137     @Override
engineAliases()138     public Enumeration<String> engineAliases() {
139         return keystore.engineAliases();
140     }
141 
142     @Override
engineContainsAlias(String alias)143     public boolean engineContainsAlias(String alias) {
144         return keystore.engineContainsAlias(alias);
145     }
146 
147     @Override
engineSize()148     public int engineSize() {
149         return keystore.engineSize();
150     }
151 
152     @Override
engineIsKeyEntry(String alias)153     public boolean engineIsKeyEntry(String alias) {
154         return keystore.engineIsKeyEntry(alias);
155     }
156 
157     @Override
engineIsCertificateEntry(String alias)158     public boolean engineIsCertificateEntry(String alias) {
159         return keystore.engineIsCertificateEntry(alias);
160     }
161 
162     @Override
engineGetCertificateAlias(Certificate cert)163     public String engineGetCertificateAlias(Certificate cert) {
164         return keystore.engineGetCertificateAlias(cert);
165     }
166 
167     @Override
engineGetEntry(String alias, KeyStore.ProtectionParameter protParam)168     public KeyStore.Entry engineGetEntry(String alias,
169         KeyStore.ProtectionParameter protParam)
170             throws KeyStoreException, NoSuchAlgorithmException,
171                 UnrecoverableEntryException {
172         return keystore.engineGetEntry(alias, protParam);
173     }
174 
175     @Override
engineSetEntry(String alias, KeyStore.Entry entry, KeyStore.ProtectionParameter protParam)176     public void engineSetEntry(String alias, KeyStore.Entry entry,
177         KeyStore.ProtectionParameter protParam)
178             throws KeyStoreException {
179         keystore.engineSetEntry(alias, entry, protParam);
180     }
181 
182     @Override
engineEntryInstanceOf(String alias, Class<? extends KeyStore.Entry> entryClass)183     public boolean engineEntryInstanceOf(String alias,
184         Class<? extends KeyStore.Entry> entryClass) {
185         return keystore.engineEntryInstanceOf(alias, entryClass);
186     }
187 
188     @Override
engineStore(OutputStream stream, char[] password)189     public void engineStore(OutputStream stream, char[] password)
190         throws IOException, NoSuchAlgorithmException, CertificateException {
191 
192         if (debug != null) {
193             debug.println("Storing keystore in " + type + " format");
194         }
195         keystore.engineStore(stream, password);
196     }
197 
198     @Override
engineLoad(InputStream stream, char[] password)199     public void engineLoad(InputStream stream, char[] password)
200         throws IOException, NoSuchAlgorithmException, CertificateException {
201 
202         // A new keystore is always created in the primary keystore format
203         if (stream == null || !compatModeEnabled) {
204             try {
205                 keystore = primaryKeyStore.newInstance();
206 
207             } catch (InstantiationException | IllegalAccessException e) {
208                 // can safely ignore
209             }
210             type = primaryType;
211 
212             if (debug != null && stream == null) {
213                 debug.println("Creating a new keystore in " + type + " format");
214             }
215             keystore.engineLoad(stream, password);
216 
217         } else {
218             // First try the primary keystore then try the secondary keystore
219             InputStream bufferedStream = new BufferedInputStream(stream);
220             bufferedStream.mark(Integer.MAX_VALUE);
221             try {
222                 keystore = primaryKeyStore.newInstance();
223                 type = primaryType;
224                 keystore.engineLoad(bufferedStream, password);
225 
226             } catch (Exception e) {
227 
228                 // incorrect password
229                 if (e instanceof IOException &&
230                     e.getCause() instanceof UnrecoverableKeyException) {
231                     throw (IOException)e;
232                 }
233 
234                 try {
235                     keystore = secondaryKeyStore.newInstance();
236                     type = secondaryType;
237                     bufferedStream.reset();
238                     keystore.engineLoad(bufferedStream, password);
239 
240                     if (debug != null) {
241                         debug.println("WARNING: switching from " +
242                           primaryType + " to " + secondaryType +
243                           " keystore file format has altered the " +
244                           "keystore security level");
245                     }
246 
247                 } catch (InstantiationException |
248                     IllegalAccessException e2) {
249                     // can safely ignore
250 
251                 } catch (IOException |
252                     NoSuchAlgorithmException |
253                     CertificateException e3) {
254 
255                     // incorrect password
256                     if (e3 instanceof IOException &&
257                         e3.getCause() instanceof
258                             UnrecoverableKeyException) {
259                         throw (IOException)e3;
260                     }
261                     // rethrow the outer exception
262                     if (e instanceof IOException) {
263                         throw (IOException)e;
264                     } else if (e instanceof CertificateException) {
265                         throw (CertificateException)e;
266                     } else if (e instanceof NoSuchAlgorithmException) {
267                         throw (NoSuchAlgorithmException)e;
268                     } else if (e instanceof RuntimeException){
269                         throw (RuntimeException)e;
270                     }
271                 }
272             }
273 
274             if (debug != null) {
275                 debug.println("Loaded a keystore in " + type + " format");
276             }
277         }
278     }
279 }
280