jdk/src/java.base/share/classes/java/security/KeyStore.java
changeset 28243 47080f9ae750
parent 27771 360714d431ab
child 28853 29df6c77d2b7
--- 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).
  *
  * <p> Typical ways to request a KeyStore object include
+ * specifying an existing keystore file,
  * relying on the default type and providing a specific keystore type.
  *
  * <ul>
+ * <li>To specify an existing keystore file:
+ * <pre>
+ *    // get keystore password
+ *    char[] password = getPassword();
+ *
+ *    // probe the keystore file and load the keystore entries
+ *    KeyStore ks = KeyStore.getInstance(new File("keyStoreName"), password);
+ *</pre>
+ * The system will probe the specified file to determine its keystore type
+ * and return a keystore implementation with its entries already loaded.
+ * When this approach is used there is no need to call the keystore's
+ * {@link #load(java.io.InputStream, char[]) load} method.
+ *
  * <li>To rely on the default type:
  * <pre>
  *    KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
@@ -110,7 +124,8 @@
  * </ul>
  *
  * <p> 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).
  * <pre>
  *    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.
+     *
+     * <p>
+     * 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.
+     *
+     * <p> 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.
+     *
+     * <p>
+     * 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.
+     *
+     * <p> 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.
      *
      * <p>An instance of this class encapsulates the information needed to
@@ -1713,7 +1911,7 @@
          * by invoking the CallbackHandler.
          *
          * <p>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.
+         *
+         * <p>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.
+         *
+         * <p>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.
+         *
+         * <p>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.
+         *
+         * <p><em>Note</em> 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;
         }
     }
-
 }