1 /* 2 * Copyright (c) 2014, 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.util; 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 9 41 */ 42 43 public 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 String primaryType; // the primary keystore's type 49 private List<String> secondaryTypes; // the secondary keystore's type 50 private Class<? extends KeyStoreSpi> primaryKeyStore; 51 // the primary keystore's class 52 private List<Class<? extends KeyStoreSpi>> secondaryKeyStores; 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 List<String> secondaryTypes, 62 List<Class<? extends KeyStoreSpi>> secondaryKeyStores) { 63 64 // Check whether compatibility mode has been disabled 65 compatModeEnabled = "true".equalsIgnoreCase( 66 AccessController.doPrivileged((PrivilegedAction<String>) () -> 67 Security.getProperty(KEYSTORE_TYPE_COMPAT))); 68 69 if (compatModeEnabled) { 70 this.primaryType = primaryType; 71 this.secondaryTypes = secondaryTypes; 72 this.primaryKeyStore = primaryKeyStore; 73 this.secondaryKeyStores = secondaryKeyStores; 74 } else { 75 this.primaryType = primaryType; 76 this.secondaryTypes = Collections.emptyList(); 77 this.primaryKeyStore = primaryKeyStore; 78 this.secondaryKeyStores = Collections.emptyList(); 79 80 if (debug != null) { 81 debug.println("WARNING: compatibility mode disabled for " + 82 primaryType + " and " + secondaryTypes + " keystore types"); 83 } 84 } 85 } 86 87 @Override 88 public Key engineGetKey(String alias, char[] password) 89 throws NoSuchAlgorithmException, UnrecoverableKeyException { 90 return keystore.engineGetKey(alias, password); 91 } 92 93 @Override 94 public Certificate[] engineGetCertificateChain(String alias) { 95 return keystore.engineGetCertificateChain(alias); 96 } 97 98 @Override 99 public Certificate engineGetCertificate(String alias) { 100 return keystore.engineGetCertificate(alias); 101 } 102 103 @Override 104 public Date engineGetCreationDate(String alias) { 105 return keystore.engineGetCreationDate(alias); 106 } 107 108 @Override 109 public void engineSetKeyEntry(String alias, Key key, char[] password, 110 Certificate[] chain) throws KeyStoreException { 111 keystore.engineSetKeyEntry(alias, key, password, chain); 112 } 113 114 @Override 115 public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain) 116 throws KeyStoreException { 117 keystore.engineSetKeyEntry(alias, key, chain); 118 } 119 120 @Override 121 public void engineSetCertificateEntry(String alias, Certificate cert) 122 throws KeyStoreException { 123 keystore.engineSetCertificateEntry(alias, cert); 124 } 125 126 @Override 127 public void engineDeleteEntry(String alias) throws KeyStoreException { 128 keystore.engineDeleteEntry(alias); 129 } 130 131 @Override 132 public Enumeration<String> engineAliases() { 133 return keystore.engineAliases(); 134 } 135 136 @Override 137 public boolean engineContainsAlias(String alias) { 138 return keystore.engineContainsAlias(alias); 139 } 140 141 @Override 142 public int engineSize() { 143 return keystore.engineSize(); 144 } 145 146 @Override 147 public boolean engineIsKeyEntry(String alias) { 148 return keystore.engineIsKeyEntry(alias); 149 } 150 151 @Override 152 public boolean engineIsCertificateEntry(String alias) { 153 return keystore.engineIsCertificateEntry(alias); 154 } 155 156 @Override 157 public String engineGetCertificateAlias(Certificate cert) { 158 return keystore.engineGetCertificateAlias(cert); 159 } 160 161 @Override 162 public KeyStore.Entry engineGetEntry(String alias, 163 KeyStore.ProtectionParameter protParam) 164 throws KeyStoreException, NoSuchAlgorithmException, 165 UnrecoverableEntryException { 166 return keystore.engineGetEntry(alias, protParam); 167 } 168 169 @Override 170 public void engineSetEntry(String alias, KeyStore.Entry entry, 171 KeyStore.ProtectionParameter protParam) 172 throws KeyStoreException { 173 keystore.engineSetEntry(alias, entry, protParam); 174 } 175 176 @Override 177 public boolean engineEntryInstanceOf(String alias, 178 Class<? extends KeyStore.Entry> entryClass) { 179 return keystore.engineEntryInstanceOf(alias, entryClass); 180 } 181 182 @Override 183 public void engineStore(OutputStream stream, char[] password) 184 throws IOException, NoSuchAlgorithmException, CertificateException { 185 186 if (debug != null) { 187 debug.println("Storing keystore in " + type + " format"); 188 } 189 keystore.engineStore(stream, password); 190 } 191 192 @Override 193 public void engineLoad(InputStream stream, char[] password) 194 throws IOException, NoSuchAlgorithmException, CertificateException { 195 196 // A new keystore is always created in the primary keystore format 197 if (stream == null) { 198 try { 199 @SuppressWarnings("deprecation") 200 KeyStoreSpi tmp = primaryKeyStore.newInstance(); 201 keystore = tmp; 202 } catch (InstantiationException | IllegalAccessException e) { 203 // can safely ignore 204 } 205 type = primaryType; 206 207 if (debug != null) { 208 debug.println("Creating a new keystore in " + type + " format"); 209 } 210 keystore.engineLoad(stream, password); 211 212 } else { 213 // First try the primary keystore then try the secondary keystore 214 InputStream bufferedStream = new BufferedInputStream(stream); 215 bufferedStream.mark(Integer.MAX_VALUE); 216 217 try { 218 @SuppressWarnings("deprecation") 219 KeyStoreSpi tmp = primaryKeyStore.newInstance(); 220 keystore = tmp; 221 type = primaryType; 222 keystore.engineLoad(bufferedStream, password); 223 224 } catch (Exception e) { 225 226 // incorrect password 227 if (e instanceof IOException && 228 e.getCause() instanceof UnrecoverableKeyException) { 229 throw (IOException)e; 230 } 231 232 if (compatModeEnabled) { 233 for (int i = 0; i < secondaryTypes.size(); i++) { 234 try { 235 @SuppressWarnings("deprecation") 236 KeyStoreSpi tmp = secondaryKeyStores.get(i).newInstance(); 237 keystore = tmp; 238 type = secondaryTypes.get(i); 239 bufferedStream.reset(); 240 keystore.engineLoad(bufferedStream, password); 241 if (debug != null) { 242 debug.println("WARNING: switching from " + 243 primaryType + " to " + type + 244 " keystore file format has altered the " + 245 "keystore security level"); 246 } 247 e = null; 248 break; 249 } catch (InstantiationException | 250 IllegalAccessException e2) { 251 // can safely ignore 252 } catch (IOException e3) { 253 // incorrect password 254 if (e3.getCause() instanceof UnrecoverableKeyException) { 255 e = e3; 256 } 257 } catch (Exception e2) { 258 // continue; 259 } 260 } 261 } 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 } else if (e != null) { 271 throw new IOException(e); 272 } 273 } 274 275 if (debug != null) { 276 debug.println("Loaded a keystore in " + type + " format"); 277 } 278 } 279 } 280 281 /** 282 * Probe the first few bytes of the keystore data stream for a valid 283 * keystore encoding. Only the primary keystore implementation is probed. 284 */ 285 @Override 286 public boolean engineProbe(InputStream stream) throws IOException { 287 288 boolean result = false; 289 290 try { 291 @SuppressWarnings("deprecation") 292 KeyStoreSpi tmp = primaryKeyStore.newInstance(); 293 keystore = tmp; 294 type = primaryType; 295 result = keystore.engineProbe(stream); 296 297 } catch (Exception e) { 298 throw new IOException(e); 299 300 } finally { 301 // reset 302 if (result == false) { 303 type = null; 304 keystore = null; 305 } 306 } 307 308 return result; 309 } 310 }