--- 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;
}
}
-
}