diff -r 0cbef7c46996 -r 47080f9ae750 jdk/src/java.base/share/classes/java/security/KeyStore.java --- a/jdk/src/java.base/share/classes/java/security/KeyStore.java Tue Dec 23 15:10:15 2014 +0000 +++ b/jdk/src/java.base/share/classes/java/security/KeyStore.java Tue Dec 23 16:30:57 2014 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2014, 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 @@ -92,9 +92,23 @@ * be used (in a variety of formats). * *

Typical ways to request a KeyStore object include + * specifying an existing keystore file, * relying on the default type and providing a specific keystore type. * *

* *

Before a keystore can be accessed, it must be - * {@link #load(java.io.InputStream, char[]) loaded}. + * {@link #load(java.io.InputStream, char[]) loaded} + * (unless it was already loaded during instantiation). *

  *    KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
  *
@@ -179,6 +194,7 @@
 
 public class KeyStore {
 
+    private static final Debug kdebug = Debug.getInstance("keystore");
     private static final Debug pdebug =
                         Debug.getInstance("provider", "Provider");
     private static final boolean skipDebug =
@@ -1594,6 +1610,188 @@
     }
 
     /**
+     * Returns a loaded keystore object of the appropriate keystore type.
+     * First the keystore type is determined by probing the specified file.
+     * Then a keystore object is instantiated and loaded using the data from
+     * that file.
+     * A password may be supplied to unlock the keystore data or perform an
+     * integrity check.
+     *
+     * 

+ * This method traverses the list of registered security {@link Providers}, + * starting with the most preferred Provider. + * For each {@link KeyStoreSpi} implementation supported by a Provider, + * it invokes the {@link engineProbe} method to determine if it supports + * the specified keystore. + * A new KeyStore object is returned that encapsulates the KeyStoreSpi + * implementation from the first Provider that supports the specified file. + * + *

Note that the list of registered providers may be retrieved via + * the {@link Security#getProviders() Security.getProviders()} method. + * + * @param file the keystore file + * @param password the keystore password, which may be {@code null} + * + * @return a keystore object loaded with keystore data + * + * @throws KeyStoreException if no Provider supports a KeyStoreSpi + * implementation for the specified keystore file. + * @throws IOException if there is an I/O or format problem with the + * keystore data, if a password is required but not given, + * or if the given password was incorrect. If the error is + * due to a wrong password, the {@link Throwable#getCause cause} + * of the {@code IOException} should be an + * {@code UnrecoverableKeyException}. + * @throws NoSuchAlgorithmException if the algorithm used to check the + * integrity of the keystore cannot be found. + * @throws CertificateException if any of the certificates in the + * keystore could not be loaded. + * @throws IllegalArgumentException if file does not exist or does not + * refer to a normal file. + * @throws NullPointerException if file is {@code null}. + * @throws SecurityException if a security manager exists and its + * {@link java.lang.SecurityManager#checkRead} method denies + * read access to the specified file. + * + * @see Provider + * + * @since 1.9 + */ + public static final KeyStore getInstance(File file, char[] password) + throws KeyStoreException, IOException, NoSuchAlgorithmException, + CertificateException { + return getInstance(file, password, null, true); + } + + /** + * Returns a loaded keystore object of the appropriate keystore type. + * First the keystore type is determined by probing the specified file. + * Then a keystore object is instantiated and loaded using the data from + * that file. + * A {@code LoadStoreParameter} may be supplied which specifies how to + * unlock the keystore data or perform an integrity check. + * + *

+ * This method traverses the list of registered security {@link Providers}, + * starting with the most preferred Provider. + * For each {@link KeyStoreSpi} implementation supported by a Provider, + * it invokes the {@link engineProbe} method to determine if it supports + * the specified keystore. + * A new KeyStore object is returned that encapsulates the KeyStoreSpi + * implementation from the first Provider that supports the specified file. + * + *

Note that the list of registered providers may be retrieved via + * the {@link Security#getProviders() Security.getProviders()} method. + * + * @param file the keystore file + * @param param the {@code LoadStoreParameter} that specifies how to load + * the keystore, which may be {@code null} + * + * @return a keystore object loaded with keystore data + * + * @throws KeyStoreException if no Provider supports a KeyStoreSpi + * implementation for the specified keystore file. + * @throws IOException if there is an I/O or format problem with the + * keystore data. If the error is due to an incorrect + * {@code ProtectionParameter} (e.g. wrong password) + * the {@link Throwable#getCause cause} of the + * {@code IOException} should be an + * {@code UnrecoverableKeyException}. + * @throws NoSuchAlgorithmException if the algorithm used to check the + * integrity of the keystore cannot be found. + * @throws CertificateException if any of the certificates in the + * keystore could not be loaded. + * @throws IllegalArgumentException if file does not exist or does not + * refer to a normal file, or if param is not recognized. + * @throws NullPointerException if file is {@code null}. + * @throws SecurityException if a security manager exists and its + * {@link java.lang.SecurityManager#checkRead} method denies + * read access to the specified file. + * + * @see Provider + * + * @since 1.9 + */ + public static final KeyStore getInstance(File file, + LoadStoreParameter param) throws KeyStoreException, IOException, + NoSuchAlgorithmException, CertificateException { + return getInstance(file, null, param, false); + } + + // Used by getInstance(File, char[]) & getInstance(File, LoadStoreParameter) + private static final KeyStore getInstance(File file, char[] password, + LoadStoreParameter param, boolean hasPassword) + throws KeyStoreException, IOException, NoSuchAlgorithmException, + CertificateException { + + if (file == null) { + throw new NullPointerException(); + } + + if (file.isFile() == false) { + throw new IllegalArgumentException( + "File does not exist or it does not refer to a normal file: " + + file); + } + + KeyStore keystore = null; + + try (DataInputStream dataStream = + new DataInputStream( + new BufferedInputStream( + new FileInputStream(file)))) { + + dataStream.mark(Integer.MAX_VALUE); + + // Detect the keystore type + for (String type : Security.getAlgorithms("KeyStore")) { + Object[] objs = null; + + try { + objs = Security.getImpl(type, "KeyStore", (String)null); + + KeyStoreSpi impl = (KeyStoreSpi)objs[0]; + if (impl.engineProbe(dataStream)) { + + if (kdebug != null) { + kdebug.println(type + " keystore detected: " + + file); + } + + keystore = new KeyStore(impl, (Provider)objs[1], type); + break; + } + } catch (NoSuchAlgorithmException | NoSuchProviderException e) { + // ignore + if (kdebug != null) { + kdebug.println(type + " not found - " + e); + } + } catch (IOException e) { + // ignore + if (kdebug != null) { + kdebug.println("I/O error in " + file + " - " + e); + } + } + dataStream.reset(); // prepare the stream for the next probe + } + + // Load the keystore data + if (keystore != null) { + if (hasPassword) { + dataStream.reset(); // prepare the stream for loading + keystore.load(dataStream, password); + } else { + keystore.load(param); + } + return keystore; + } + } + + throw new KeyStoreException("Unrecognized keystore format: " + + keystore); + } + + /** * A description of a to-be-instantiated KeyStore object. * *

An instance of this class encapsulates the information needed to @@ -1713,7 +1911,7 @@ * by invoking the CallbackHandler. * *

Subsequent calls to {@link #getKeyStore} return the same object - * as the initial call. If the initial call to failed with a + * as the initial call. If the initial call failed with a * KeyStoreException, subsequent calls also throw a * KeyStoreException. * @@ -1760,6 +1958,50 @@ AccessController.getContext()); } + /** + * Returns a new Builder object. + * + *

The first call to the {@link #getKeyStore} method on the returned + * builder will create a KeyStore using {@code file} to detect the + * keystore type and then call its {@link KeyStore#load load()} method. + * It uses the same algorithm to determine the keystore type as + * described in {@link KeyStore#getInstance(File, LoadStoreParameter)}. + * The {@code inputStream} argument is constructed from {@code file}. + * If {@code protection} is a {@code PasswordProtection}, the password + * is obtained by calling the {@code getPassword} method. + * Otherwise, if {@code protection} is a + * {@code CallbackHandlerProtection}, + * the password is obtained by invoking the CallbackHandler. + * + *

Subsequent calls to {@link #getKeyStore} return the same object + * as the initial call. If the initial call failed with a + * KeyStoreException, subsequent calls also throw a KeyStoreException. + * + *

Calls to {@link #getProtectionParameter getProtectionParameter()} + * will return a {@link KeyStore.PasswordProtection PasswordProtection} + * object encapsulating the password that was used to invoke the + * {@code load} method. + * + *

Note that the {@link #getKeyStore} method is executed + * within the {@link AccessControlContext} of the code invoking this + * method. + * + * @return a new Builder object + * @param file the File that contains the KeyStore data + * @param protection the ProtectionParameter securing the KeyStore data + * @throws NullPointerException if file or protection is null + * @throws IllegalArgumentException if protection is not an instance + * of either PasswordProtection or CallbackHandlerProtection; or + * if file does not exist or does not refer to a normal file + * + * @since 1.9 + */ + public static Builder newInstance(File file, + ProtectionParameter protection) { + + return newInstance("", null, file, protection); + } + private static final class FileBuilder extends Builder { private final String type; @@ -1817,42 +2059,46 @@ } public KeyStore run0() throws Exception { KeyStore ks; - if (provider == null) { - ks = KeyStore.getInstance(type); + char[] password = null; + + // Acquire keystore password + if (protection instanceof PasswordProtection) { + password = + ((PasswordProtection)protection).getPassword(); + keyProtection = protection; } else { - ks = KeyStore.getInstance(type, provider); - } - InputStream in = null; - char[] password = null; - try { - in = new FileInputStream(file); - if (protection instanceof PasswordProtection) { - password = - ((PasswordProtection)protection).getPassword(); - keyProtection = protection; - } else { - CallbackHandler handler = - ((CallbackHandlerProtection)protection) + CallbackHandler handler = + ((CallbackHandlerProtection)protection) .getCallbackHandler(); - PasswordCallback callback = new PasswordCallback - ("Password for keystore " + file.getName(), + PasswordCallback callback = new PasswordCallback + ("Password for keystore " + file.getName(), false); - handler.handle(new Callback[] {callback}); - password = callback.getPassword(); - if (password == null) { - throw new KeyStoreException("No password" + - " provided"); - } - callback.clearPassword(); - keyProtection = new PasswordProtection(password); + handler.handle(new Callback[] {callback}); + password = callback.getPassword(); + if (password == null) { + throw new KeyStoreException("No password" + + " provided"); } - ks.load(in, password); - return ks; - } finally { - if (in != null) { - in.close(); + callback.clearPassword(); + keyProtection = new PasswordProtection(password); + } + + if (type.isEmpty()) { + // Instantiate keystore and load keystore data + ks = KeyStore.getInstance(file, password); + } else { + // Instantiate keystore + if (provider == null) { + ks = KeyStore.getInstance(type); + } else { + ks = KeyStore.getInstance(type, provider); + } + // Load keystore data + try (InputStream in = new FileInputStream(file)) { + ks.load(in, password); } } + return ks; } }; try { @@ -1998,5 +2244,4 @@ return protection; } } - }