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 1.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 String secondaryType; // the secondary keystore's type 50 private Class<? extends KeyStoreSpi> primaryKeyStore; 51 // the primary keystore's class 52 private 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 compatModeEnabled = "true".equalsIgnoreCase( 66 AccessController.doPrivileged((PrivilegedAction<String>) () -> 67 Security.getProperty(KEYSTORE_TYPE_COMPAT))); 68 69 if (compatModeEnabled) { 70 this.primaryType = primaryType; 71 this.secondaryType = secondaryType; 72 this.primaryKeyStore = primaryKeyStore; 73 this.secondaryKeyStore = secondaryKeyStore; 74 } else { 75 this.primaryType = primaryType; 76 this.secondaryType = null; 77 this.primaryKeyStore = primaryKeyStore; 78 this.secondaryKeyStore = null; 79 80 if (debug != null) { 81 debug.println("WARNING: compatibility mode disabled for " + 82 primaryType + " and " + secondaryType + " 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 keystore = primaryKeyStore.newInstance(); 200 201 } catch (InstantiationException | IllegalAccessException e) { 202 // can safely ignore 203 } 204 type = primaryType; 205 206 if (debug != null) { 207 debug.println("Creating a new keystore in " + type + " format"); 208 } 209 keystore.engineLoad(stream, password); 210 211 } else { 212 // First try the primary keystore then try the secondary keystore 213 InputStream bufferedStream = new BufferedInputStream(stream); 214 bufferedStream.mark(Integer.MAX_VALUE); 215 216 try { 217 keystore = primaryKeyStore.newInstance(); 218 type = primaryType; 219 keystore.engineLoad(bufferedStream, password); 220 221 } catch (Exception e) { 222 223 // incorrect password 224 if (e instanceof IOException && 225 e.getCause() instanceof UnrecoverableKeyException) { 226 throw (IOException)e; 227 } 228 229 try { 230 // Ignore secondary keystore when no compatibility mode 231 if (!compatModeEnabled) { 232 throw e; 233 } 234 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 UnrecoverableKeyException) { 258 throw (IOException)e3; 259 } 260 // rethrow the outer exception 261 if (e instanceof IOException) { 262 throw (IOException)e; 263 } else if (e instanceof CertificateException) { 264 throw (CertificateException)e; 265 } else if (e instanceof NoSuchAlgorithmException) { 266 throw (NoSuchAlgorithmException)e; 267 } 268 } 269 } 270 271 if (debug != null) { 272 debug.println("Loaded a keystore in " + type + " format"); 273 } 274 } 275 } 276 277 /** 278 * Probe the first few bytes of the keystore data stream for a valid 279 * keystore encoding. Only the primary keystore implementation is probed. 280 */ 281 @Override 282 public boolean engineProbe(InputStream stream) throws IOException { 283 284 boolean result = false; 285 286 try { 287 keystore = primaryKeyStore.newInstance(); 288 type = primaryType; 289 result = keystore.engineProbe(stream); 290 291 } catch (Exception e) { 292 throw new IOException(e); 293 294 } finally { 295 // reset 296 if (result == false) { 297 type = null; 298 keystore = null; 299 } 300 } 301 302 return result; 303 } 304 }