--- /dev/null Fri May 22 20:10:53 2015 +++ new/src/share/classes/sun/security/provider/KeyStoreDelegator.java Fri May 22 20:10:53 2015 @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import java.io.*; +import java.security.*; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.security.cert.CertificateException; +import java.util.*; + +import sun.security.util.Debug; + +/** + * This class delegates to a primary or secondary keystore implementation. + * + * @since 1.8 + */ + +class KeyStoreDelegator extends KeyStoreSpi { + + private static final String KEYSTORE_TYPE_COMPAT = "keystore.type.compat"; + private static final Debug debug = Debug.getInstance("keystore"); + + private final String primaryType; // the primary keystore's type + private final String secondaryType; // the secondary keystore's type + private final Class primaryKeyStore; + // the primary keystore's class + private final Class secondaryKeyStore; + // the secondary keystore's class + private String type; // the delegate's type + private KeyStoreSpi keystore; // the delegate + private boolean compatModeEnabled = true; + + public KeyStoreDelegator( + String primaryType, + Class primaryKeyStore, + String secondaryType, + Class secondaryKeyStore) { + + // Check whether compatibility mode has been disabled + // (Use inner-class instead of lambda to avoid init/ClassLoader problem) + compatModeEnabled = "true".equalsIgnoreCase( + AccessController.doPrivileged( + new PrivilegedAction() { + public String run() { + return Security.getProperty(KEYSTORE_TYPE_COMPAT); + } + } + )); + + if (compatModeEnabled) { + this.primaryType = primaryType; + this.secondaryType = secondaryType; + this.primaryKeyStore = primaryKeyStore; + this.secondaryKeyStore = secondaryKeyStore; + } else { + this.primaryType = primaryType; + this.secondaryType = null; + this.primaryKeyStore = primaryKeyStore; + this.secondaryKeyStore = null; + + if (debug != null) { + debug.println("WARNING: compatibility mode disabled for " + + primaryType + " and " + secondaryType + " keystore types"); + } + } + } + + @Override + public Key engineGetKey(String alias, char[] password) + throws NoSuchAlgorithmException, UnrecoverableKeyException { + return keystore.engineGetKey(alias, password); + } + + @Override + public Certificate[] engineGetCertificateChain(String alias) { + return keystore.engineGetCertificateChain(alias); + } + + @Override + public Certificate engineGetCertificate(String alias) { + return keystore.engineGetCertificate(alias); + } + + @Override + public Date engineGetCreationDate(String alias) { + return keystore.engineGetCreationDate(alias); + } + + @Override + public void engineSetKeyEntry(String alias, Key key, char[] password, + Certificate[] chain) throws KeyStoreException { + keystore.engineSetKeyEntry(alias, key, password, chain); + } + + @Override + public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain) + throws KeyStoreException { + keystore.engineSetKeyEntry(alias, key, chain); + } + + @Override + public void engineSetCertificateEntry(String alias, Certificate cert) + throws KeyStoreException { + keystore.engineSetCertificateEntry(alias, cert); + } + + @Override + public void engineDeleteEntry(String alias) throws KeyStoreException { + keystore.engineDeleteEntry(alias); + } + + @Override + public Enumeration engineAliases() { + return keystore.engineAliases(); + } + + @Override + public boolean engineContainsAlias(String alias) { + return keystore.engineContainsAlias(alias); + } + + @Override + public int engineSize() { + return keystore.engineSize(); + } + + @Override + public boolean engineIsKeyEntry(String alias) { + return keystore.engineIsKeyEntry(alias); + } + + @Override + public boolean engineIsCertificateEntry(String alias) { + return keystore.engineIsCertificateEntry(alias); + } + + @Override + public String engineGetCertificateAlias(Certificate cert) { + return keystore.engineGetCertificateAlias(cert); + } + + @Override + public KeyStore.Entry engineGetEntry(String alias, + KeyStore.ProtectionParameter protParam) + throws KeyStoreException, NoSuchAlgorithmException, + UnrecoverableEntryException { + return keystore.engineGetEntry(alias, protParam); + } + + @Override + public void engineSetEntry(String alias, KeyStore.Entry entry, + KeyStore.ProtectionParameter protParam) + throws KeyStoreException { + keystore.engineSetEntry(alias, entry, protParam); + } + + @Override + public boolean engineEntryInstanceOf(String alias, + Class entryClass) { + return keystore.engineEntryInstanceOf(alias, entryClass); + } + + @Override + public void engineStore(OutputStream stream, char[] password) + throws IOException, NoSuchAlgorithmException, CertificateException { + + if (debug != null) { + debug.println("Storing keystore in " + type + " format"); + } + keystore.engineStore(stream, password); + } + + @Override + public void engineLoad(InputStream stream, char[] password) + throws IOException, NoSuchAlgorithmException, CertificateException { + + // A new keystore is always created in the primary keystore format + if (stream == null || !compatModeEnabled) { + try { + keystore = primaryKeyStore.newInstance(); + + } catch (InstantiationException | IllegalAccessException e) { + // can safely ignore + } + type = primaryType; + + if (debug != null && stream == null) { + debug.println("Creating a new keystore in " + type + " format"); + } + keystore.engineLoad(stream, password); + + } else { + // First try the primary keystore then try the secondary keystore + try (InputStream bufferedStream = new BufferedInputStream(stream)) { + bufferedStream.mark(Integer.MAX_VALUE); + + try { + keystore = primaryKeyStore.newInstance(); + type = primaryType; + keystore.engineLoad(bufferedStream, password); + + } catch (Exception e) { + + // incorrect password + if (e instanceof IOException && + e.getCause() instanceof UnrecoverableKeyException) { + throw (IOException)e; + } + + try { + keystore = secondaryKeyStore.newInstance(); + type = secondaryType; + bufferedStream.reset(); + keystore.engineLoad(bufferedStream, password); + + if (debug != null) { + debug.println("WARNING: switching from " + + primaryType + " to " + secondaryType + + " keystore file format has altered the " + + "keystore security level"); + } + + } catch (InstantiationException | + IllegalAccessException e2) { + // can safely ignore + + } catch (IOException | + NoSuchAlgorithmException | + CertificateException e3) { + + // incorrect password + if (e3 instanceof IOException && + e3.getCause() instanceof + UnrecoverableKeyException) { + throw (IOException)e3; + } + // rethrow the outer exception + if (e instanceof IOException) { + throw (IOException)e; + } else if (e instanceof CertificateException) { + throw (CertificateException)e; + } else if (e instanceof NoSuchAlgorithmException) { + throw (NoSuchAlgorithmException)e; + } + } + } + } + + if (debug != null) { + debug.println("Loaded a keystore in " + type + " format"); + } + } + } +}