/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package java.security; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.Date; import java.util.Enumeration; import javax.crypto.SecretKey; import javax.security.auth.DestroyFailedException; import javax.security.auth.Destroyable; import javax.security.auth.callback.CallbackHandler; import org.apache.harmony.security.fortress.Engine; import org.apache.harmony.security.internal.nls.Messages; /** * {@code KeyStore} is responsible for maintaining cryptographic keys and their * owners. *
* The type of the system key store can be changed by setting the {@code * 'keystore.type'} property in the file named {@code * JAVA_HOME/lib/security/java.security}. * * @see Certificate * @see PrivateKey */ public class KeyStore { // Store KeyStore SERVICE name private static final String SERVICE = "KeyStore"; //$NON-NLS-1$ // Used to access common engine functionality private static Engine engine = new Engine(SERVICE); // Store KeyStore property name private static final String PROPERTYNAME = "keystore.type"; //$NON-NLS-1$ // Store default KeyStore type private static final String DEFAULT_KEYSTORE_TYPE = "jks"; //$NON-NLS-1$ // Message to report about non-initialized key store object // BEGIN android-changed private static String NOTINITKEYSTORE; // END android-changed // Store KeyStore state (initialized or not) private boolean isInit; // Store used KeyStoreSpi private final KeyStoreSpi implSpi; // Store used provider private final Provider provider; // Store used type private final String type; /** * Constructs a new instance of {@code KeyStore} with the given arguments. * * @param keyStoreSpi * the concrete key store. * @param provider * the provider. * @param type * the type of the {@code KeyStore} to be constructed. */ protected KeyStore(KeyStoreSpi keyStoreSpi, Provider provider, String type) { this.type = type; this.provider = provider; this.implSpi = keyStoreSpi; isInit = false; } // BEGIN android-added /** * Throws the standard "keystore not initialized" exception. */ private static void throwNotInitialized() throws KeyStoreException { if (NOTINITKEYSTORE == null) { NOTINITKEYSTORE = Messages.getString("security.4F"); //$NON-NLS-1$ } throw new KeyStoreException(NOTINITKEYSTORE); } // END android-added /** * Returns a new instance of {@code KeyStore} with the specified type. * * @param type * the type of the returned {@code KeyStore}. * @return a new instance of {@code KeyStore} with the specified type. * @throws KeyStoreException * if an error occurred during the creation of the new {@code * KeyStore}. * @throws NullPointerException * if {@code type} is {@code null}. * @see #getDefaultType */ public static KeyStore getInstance(String type) throws KeyStoreException { if (type == null) { throw new NullPointerException(Messages.getString("security.07")); //$NON-NLS-1$ } synchronized (engine) { try { engine.getInstance(type, null); return new KeyStore((KeyStoreSpi) engine.spi, engine.provider, type); } catch (NoSuchAlgorithmException e) { throw new KeyStoreException(e.getMessage()); } } } /** * Returns a new instance of {@code KeyStore} from the specified provider * with the given type. * * @param type * the type of the returned {@code KeyStore}. * @param provider * name of the provider of the {@code KeyStore}. * @return a new instance of {@code KeyStore} from the specified provider * with the given type. * @throws KeyStoreException * if an error occurred during the creation of the new {@code * KeyStore}. * @throws NoSuchProviderException * if the specified provider is not available. * @throws IllegalArgumentException * if {@code provider} is {@code null} or the empty string. * @throws NullPointerException * if {@code type} is {@code null} (instead of * NoSuchAlgorithmException) as in 1.4 release * @see #getDefaultType */ public static KeyStore getInstance(String type, String provider) throws KeyStoreException, NoSuchProviderException { if ((provider == null) || (provider.length() == 0)) { throw new IllegalArgumentException(Messages.getString("security.02")); //$NON-NLS-1$ } Provider impProvider = Security.getProvider(provider); if (impProvider == null) { throw new NoSuchProviderException(provider); } try { return getInstance(type, impProvider); } catch (Exception e) { throw new KeyStoreException(e.getMessage(), e); } } /** * Returns a new instance of {@code KeyStore} from the specified provider * with the given type. * * @param type * the type of the returned {@code KeyStore}. * @param provider * the provider of the {@code KeyStore}. * @return a new instance of {@code KeyStore} from the specified provider * with the given type. * @throws KeyStoreException * if an error occurred during the creation of the new {@code * KeyStore}. * @throws IllegalArgumentException * if {@code provider} is {@code null} or the empty string. * @throws NullPointerException * if {@code type} is {@code null} (instead of * NoSuchAlgorithmException) as in 1.4 release * @see #getDefaultType */ public static KeyStore getInstance(String type, Provider provider) throws KeyStoreException { // check parameters if (provider == null) { throw new IllegalArgumentException(Messages.getString("security.04")); //$NON-NLS-1$ } if (type == null) { throw new NullPointerException(Messages.getString("security.07")); //$NON-NLS-1$ } // return KeyStore instance synchronized (engine) { try { engine.getInstance(type, provider, null); return new KeyStore((KeyStoreSpi) engine.spi, provider, type); } catch (Exception e) { // override exception throw new KeyStoreException(e.getMessage()); } } } /** * Returns the default type for {@code KeyStore} instances. *
* The default is specified in the {@code 'keystore.type'} property in the
* file named {@code JAVA_HOME/lib/security/java.security}. If this property
* is not set, {@code "jks"} will be used.
*
* @return the default type for {@code KeyStore} instances
*/
public static final String getDefaultType() {
String dt = AccessController.doPrivileged(
new PrivilegedAction
* If the specified alias already exists, it will be reassigned.
*
* @param alias
* the alias for the key.
* @param key
* the key.
* @param password
* the password.
* @param chain
* the certificate chain.
* @throws KeyStoreException
* if this {@code KeyStore} is not initialized.
* @throws IllegalArgumentException
* if {@code key} is a {@code PrivateKey} and {@code chain} does
* not contain any certificates.
* @throws NullPointerException
* if {@code alias} is {@code null}.
*/
public final void setKeyEntry(String alias, Key key, char[] password,
Certificate[] chain) throws KeyStoreException {
if (!isInit) {
// BEGIN android-changed
throwNotInitialized();
// END android-changed
}
// Certificate chain is required for PrivateKey
if (null != key && key instanceof PrivateKey
&& (chain == null || chain.length == 0)) {
throw new IllegalArgumentException(Messages
.getString("security.52")); //$NON-NLS-1$
}
implSpi.engineSetKeyEntry(alias, key, password, chain);
}
/**
* Associates the given alias with a key and a certificate chain.
*
* If the specified alias already exists, it will be reassigned.
*
* If this {@code KeyStore} is of type {@code "jks"}, {@code key} must be
* encoded conform to the PKS#8 standard as an
* {@link javax.crypto.EncryptedPrivateKeyInfo}.
*
* @param alias
* the alias for the key.
* @param key
* the key in an encoded format.
* @param chain
* the certificate chain.
* @throws KeyStoreException
* if this {@code KeyStore} is not initialized or if {@code key}
* is null.
* @throws IllegalArgumentException
* if {@code key} is a {@code PrivateKey} and {@code chain}
* does.
* @throws NullPointerException
* if {@code alias} is {@code null}.
*/
public final void setKeyEntry(String alias, byte[] key, Certificate[] chain)
throws KeyStoreException {
if (!isInit) {
// BEGIN android-changed
throwNotInitialized();
// END android-changed
}
implSpi.engineSetKeyEntry(alias, key, chain);
}
/**
* Associates the given alias with a certificate.
*
* If the specified alias already exists, it will be reassigned.
*
* @param alias
* the alias for the certificate.
* @param cert
* the certificate.
* @throws KeyStoreException
* if this {@code KeyStore} is not initialized, or an existing
* alias is not associated to an entry containing a trusted
* certificate, or this method fails for any other reason.
* @throws NullPointerException
* if {@code alias} is {@code null}.
*/
public final void setCertificateEntry(String alias, Certificate cert)
throws KeyStoreException {
if (!isInit) {
// BEGIN android-changed
throwNotInitialized();
// END android-changed
}
implSpi.engineSetCertificateEntry(alias, cert);
}
/**
* Deletes the entry identified with the given alias from this {@code
* KeyStore}.
*
* @param alias
* the alias for the entry.
* @throws KeyStoreException
* if this {@code KeyStore} is not initialized, or if the entry
* can not be deleted.
* @throws NullPointerException
* if {@code alias} is {@code null}.
*/
public final void deleteEntry(String alias) throws KeyStoreException {
if (!isInit) {
// BEGIN android-changed
throwNotInitialized();
// END android-changed
}
if (alias == null) {
throw new NullPointerException(Messages.getString("security.3F")); //$NON-NLS-1$
}
implSpi.engineDeleteEntry(alias);
}
/**
* Returns an {@code Enumeration} over all alias names stored in this
* {@code KeyStore}.
*
* @return an {@code Enumeration} over all alias names stored in this
* {@code KeyStore}.
* @throws KeyStoreException
* if this {@code KeyStore} is not initialized.
*/
public final Enumeration
* If the specified alias already exists, it will be reassigned.
*
* @param alias
* the alias for the entry.
* @param entry
* the entry to store.
* @param param
* the {@code ProtectionParameter} to protect the entry.
* @throws KeyStoreException
* if this {@code KeyStore} is not initialized.
* @throws NullPointerException
* if {@code alias} is {@code null} or {@code entry} is {@code
* null}.
*/
public final void setEntry(String alias, Entry entry,
ProtectionParameter param) throws KeyStoreException {
if (!isInit) {
// BEGIN android-changed
throwNotInitialized();
// END android-changed
}
if (alias == null) {
throw new NullPointerException(Messages.getString("security.3F")); //$NON-NLS-1$
}
if (entry == null) {
throw new NullPointerException(Messages.getString("security.39")); //$NON-NLS-1$
}
implSpi.engineSetEntry(alias, entry, param);
}
/**
* Indicates whether the entry for the given alias is assignable to the
* provided {@code Class}.
*
* @param alias
* the alias for the entry.
* @param entryClass
* the type of the entry.
* @return {@code true} if the {@code Entry} for the alias is assignable to
* the specified {@code entryClass}.
* @throws KeyStoreException
* if this {@code KeyStore} is not initialized.
*/
public final boolean entryInstanceOf(String alias,
Class extends KeyStore.Entry> entryClass)
throws KeyStoreException {
if (alias == null) {
throw new NullPointerException(Messages.getString("security.3F")); //$NON-NLS-1$
}
if (entryClass == null) {
throw new NullPointerException(Messages.getString("security.40")); //$NON-NLS-1$
}
if (!isInit) {
// BEGIN android-changed
throwNotInitialized();
// END android-changed
}
return implSpi.engineEntryInstanceOf(alias, entryClass);
}
/**
* {@code Builder} is used to construct new instances of {@code KeyStore}.
*/
public abstract static class Builder {
/**
* Constructs a new instance of {@code Builder}.
*/
protected Builder() {
}
/**
* Returns the {@code KeyStore} created by this {@code Builder}.
*
* @return the {@code KeyStore} created by this {@code Builder}.
* @throws KeyStoreException
* if an error occurred during construction.
*/
public abstract KeyStore getKeyStore() throws KeyStoreException;
/**
* Returns the {@code ProtectionParameter} to be used when a {@code
* Entry} with the specified alias is requested. Before this method is
* invoked, {@link #getKeyStore()} must be called.
*
* @param alias
* the alias for the entry.
* @return the {@code ProtectionParameter} to be used when a {@code
* Entry} with the specified alias is requested.
* @throws KeyStoreException
* if an error occurred during the lookup for the protection
* parameter.
* @throws IllegalStateException
* if {@link #getKeyStore()} is not called prior the
* invocation of this method.
* @throws NullPointerException
* if {@code alias} is {@code null}.
*/
public abstract ProtectionParameter getProtectionParameter(String alias)
throws KeyStoreException;
/**
* Returns a new {@code Builder} that holds the given {@code KeyStore}
* and the given {@code ProtectionParameter}.
*
* @param keyStore
* the {@code KeyStore} to be held.
* @param protectionParameter
* the {@code ProtectionParameter} to be held.
* @return a new instance of {@code Builder} that holds the specified
* {@code KeyStore} and the specified {@code
* ProtectionParameter}.
* @throws NullPointerException
* if {@code keyStore} or {@code protectionParameter} is
* {@code null}.
* @throws IllegalArgumentException
* if the given {@code KeyStore} is not initialized.
*/
public static Builder newInstance(KeyStore keyStore,
ProtectionParameter protectionParameter) {
if (keyStore == null) {
throw new NullPointerException(Messages.getString("security.41")); //$NON-NLS-1$
}
if (protectionParameter == null) {
throw new NullPointerException(Messages.getString("security.42")); //$NON-NLS-1$
}
if (!keyStore.isInit) {
throw new IllegalArgumentException(NOTINITKEYSTORE);
}
return new BuilderImpl(keyStore, protectionParameter,
null, null, null, null);
}
/**
* Returns a new {@code Builder} that creates a new {@code KeyStore}
* based on the provided arguments.
*
* If {@code provider} is {@code null}, all installed providers are
* searched, otherwise the key store from the specified provider is
* used.
*
* @param type
* the type of the {@code KeyStore} to be constructed.
* @param provider
* the provider of the {@code KeyStore} to be constructed,
* maybe {@code null}.
* @param file
* the {@code File} that contains the data for the {@code
* KeyStore}.
* @param protectionParameter
* the {@code ProtectionParameter} used to protect the stored
* keys.
* @return a new {@code Builder} that creates a new {@code KeyStore}
* based on the provided arguments.
* @throws NullPointerException
* if {@code type, protectionParameter} or {@code file} is
* {@code null}.
* @throws IllegalArgumentException
* {@code protectionParameter} not an instance of either
* {@code PasswordProtection} or {@code
* CallbackHandlerProtection}, {@code file} is not a file or
* does not exist at all.
*/
public static Builder newInstance(String type, Provider provider,
File file, ProtectionParameter protectionParameter) {
// check null parameters
if (type == null) {
throw new NullPointerException(Messages.getString("security.07")); //$NON-NLS-1$
}
if (protectionParameter == null) {
throw new NullPointerException(Messages.getString("security.42")); //$NON-NLS-1$
}
if (file == null) {
throw new NullPointerException(Messages.getString("security.43")); //$NON-NLS-1$
}
// protection parameter should be PasswordProtection or
// CallbackHandlerProtection
if (!(protectionParameter instanceof PasswordProtection)
&& !(protectionParameter instanceof CallbackHandlerProtection)) {
throw new IllegalArgumentException(Messages.getString("security.35")); //$NON-NLS-1$
}
// check file parameter
if (!file.exists()) {
throw new IllegalArgumentException(Messages.getString("security.44", file.getName())); //$NON-NLS-1$
}
if (!file.isFile()) {
throw new IllegalArgumentException(Messages.getString("security.45", file.getName())); //$NON-NLS-1$
}
// create new instance
return new BuilderImpl(null, protectionParameter, file,
type, provider, AccessController.getContext());
}
/**
* Returns a new {@code Builder} that creates a new {@code KeyStore}
* based on the provided arguments.
*
* If {@code provider} is {@code null}, all installed providers are
* searched, otherwise the key store from the specified provider is
* used.
*
* @param type
* the type of the {@code KeyStore} to be constructed.
* @param provider
* the provider of the {@code KeyStore} to be constructed,
* maybe {@code null}.
* @param protectionParameter
* the {@code ProtectionParameter} used to protect the stored
* keys.
* @return a new {@code Builder} that creates a new {@code KeyStore}
* based on the provided arguments.
* @throws NullPointerException
* if {@code type} or {@code protectionParameter} is {@code
* null}.
* @throws IllegalArgumentException
* {@code protectionParameter} not an instance of either
* {@code PasswordProtection} or {@code
* CallbackHandlerProtection}, {@code file} is not a file or
* does not exist at all.
*/
public static Builder newInstance(String type, Provider provider,
ProtectionParameter protectionParameter) {
if (type == null) {
throw new NullPointerException(Messages.getString("security.07")); //$NON-NLS-1$
}
if (protectionParameter == null) {
throw new NullPointerException(Messages.getString("security.42")); //$NON-NLS-1$
}
return new BuilderImpl(null, protectionParameter, null,
type, provider, AccessController.getContext());
}
/*
* This class is implementation of abstract class KeyStore.Builder
*
* @author Vera Petrashkova
*
*/
private static class BuilderImpl extends Builder {
// Store used KeyStore
private KeyStore keyStore;
// Store used ProtectionParameter
private ProtectionParameter protParameter;
// Store used KeyStore type
private final String typeForKeyStore;
// Store used KeyStore provider
private final Provider providerForKeyStore;
// Store used file for KeyStore loading
private final File fileForLoad;
// Store getKeyStore method was invoked or not for KeyStoreBuilder
private boolean isGetKeyStore = false;
// Store last Exception in getKeyStore()
private KeyStoreException lastException;
// Store AccessControlContext which is used in getKeyStore() method
private final AccessControlContext accControlContext;
//
// Constructor BuilderImpl initializes private fields: keyStore,
// protParameter, typeForKeyStore providerForKeyStore fileForLoad,
// isGetKeyStore
//
BuilderImpl(KeyStore ks, ProtectionParameter pp, File file,
String type, Provider provider, AccessControlContext context) {
super();
keyStore = ks;
protParameter = pp;
fileForLoad = file;
typeForKeyStore = type;
providerForKeyStore = provider;
isGetKeyStore = false;
lastException = null;
accControlContext = context;
}
//
// Implementation of abstract getKeyStore() method If
// KeyStoreBuilder encapsulates KeyStore object then this object is
// returned
//
// If KeyStoreBuilder encapsulates KeyStore type and provider then
// KeyStore is created using these parameters. If KeyStoreBuilder
// encapsulates file and ProtectionParameter then KeyStore data are
// loaded from FileInputStream that is created on file. If file is
// not defined then KeyStore object is initialized with null
// InputStream and null password.
//
// Result KeyStore object is returned.
//
@Override
public synchronized KeyStore getKeyStore() throws KeyStoreException {
// If KeyStore was created but in final block some exception was
// thrown
// then it was stored in lastException variable and will be
// thrown
// all subsequent calls of this method.
if (lastException != null) {
throw lastException;
}
if (keyStore != null) {
isGetKeyStore = true;
return keyStore;
}
try {
final KeyStore ks;
final char[] passwd;
// get KeyStore instance using type or type and provider
ks = (providerForKeyStore == null ? KeyStore
.getInstance(typeForKeyStore) : KeyStore
.getInstance(typeForKeyStore, providerForKeyStore));
// protection parameter should be PasswordProtection
// or CallbackHandlerProtection
if (protParameter instanceof PasswordProtection) {
passwd = ((PasswordProtection) protParameter)
.getPassword();
} else if (protParameter instanceof CallbackHandlerProtection) {
passwd = KeyStoreSpi
.getPasswordFromCallBack(protParameter);
} else {
throw new KeyStoreException(Messages.getString("security.35")); //$NON-NLS-1$
}
// load KeyStore from file
AccessController.doPrivileged(
new PrivilegedExceptionAction