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 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 94 public Key engineGetKey(String alias, char[] password) 95 throws NoSuchAlgorithmException, UnrecoverableKeyException { 96 return keystore.engineGetKey(alias, password); 97 } 98 99 @Override 100 public Certificate[] engineGetCertificateChain(String alias) { 101 return keystore.engineGetCertificateChain(alias); 102 } 103 104 @Override 105 public Certificate engineGetCertificate(String alias) { 106 return keystore.engineGetCertificate(alias); 107 } 108 109 @Override 110 public Date engineGetCreationDate(String alias) { 111 return keystore.engineGetCreationDate(alias); 112 } 113 114 @Override 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 121 public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain) 122 throws KeyStoreException { 123 keystore.engineSetKeyEntry(alias, key, chain); 124 } 125 126 @Override 127 public void engineSetCertificateEntry(String alias, Certificate cert) 128 throws KeyStoreException { 129 keystore.engineSetCertificateEntry(alias, cert); 130 } 131 132 @Override 133 public void engineDeleteEntry(String alias) throws KeyStoreException { 134 keystore.engineDeleteEntry(alias); 135 } 136 137 @Override 138 public Enumeration<String> engineAliases() { 139 return keystore.engineAliases(); 140 } 141 142 @Override 143 public boolean engineContainsAlias(String alias) { 144 return keystore.engineContainsAlias(alias); 145 } 146 147 @Override 148 public int engineSize() { 149 return keystore.engineSize(); 150 } 151 152 @Override 153 public boolean engineIsKeyEntry(String alias) { 154 return keystore.engineIsKeyEntry(alias); 155 } 156 157 @Override 158 public boolean engineIsCertificateEntry(String alias) { 159 return keystore.engineIsCertificateEntry(alias); 160 } 161 162 @Override 163 public String engineGetCertificateAlias(Certificate cert) { 164 return keystore.engineGetCertificateAlias(cert); 165 } 166 167 @Override 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 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 183 public boolean engineEntryInstanceOf(String alias, 184 Class<? extends KeyStore.Entry> entryClass) { 185 return keystore.engineEntryInstanceOf(alias, entryClass); 186 } 187 188 @Override 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 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 try (InputStream bufferedStream = new BufferedInputStream(stream)) { 220 bufferedStream.mark(Integer.MAX_VALUE); 221 222 try { 223 keystore = primaryKeyStore.newInstance(); 224 type = primaryType; 225 keystore.engineLoad(bufferedStream, password); 226 227 } catch (Exception e) { 228 229 // incorrect password 230 if (e instanceof IOException && 231 e.getCause() instanceof UnrecoverableKeyException) { 232 throw (IOException)e; 233 } 234 235 try { 236 keystore = secondaryKeyStore.newInstance(); 237 type = secondaryType; 238 bufferedStream.reset(); 239 keystore.engineLoad(bufferedStream, password); 240 241 if (debug != null) { 242 debug.println("WARNING: switching from " + 243 primaryType + " to " + secondaryType + 244 " keystore file format has altered the " + 245 "keystore security level"); 246 } 247 248 } catch (InstantiationException | 249 IllegalAccessException e2) { 250 // can safely ignore 251 252 } catch (IOException | 253 NoSuchAlgorithmException | 254 CertificateException e3) { 255 256 // incorrect password 257 if (e3 instanceof IOException && 258 e3.getCause() instanceof 259 UnrecoverableKeyException) { 260 throw (IOException)e3; 261 } 262 // rethrow the outer exception 263 if (e instanceof IOException) { 264 throw (IOException)e; 265 } else if (e instanceof CertificateException) { 266 throw (CertificateException)e; 267 } else if (e instanceof NoSuchAlgorithmException) { 268 throw (NoSuchAlgorithmException)e; 269 } 270 } 271 } 272 } 273 274 if (debug != null) { 275 debug.println("Loaded a keystore in " + type + " format"); 276 } 277 } 278 } 279 }