/* * 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 libcore.io.IoUtils; import org.apache.harmony.security.fortress.Engine; /** * {@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"; // Used to access common engine functionality private static final Engine ENGINE = new Engine(SERVICE); // Store KeyStore property name private static final String PROPERTYNAME = "keystore.type"; // Store default KeyStore type private static final String DEFAULT_KEYSTORE_TYPE = "jks"; // 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; } /** * Throws the standard "keystore not initialized" exception. */ private static void throwNotInitialized() throws KeyStoreException { throw new KeyStoreException("KeyStore was not initialized"); } /** * 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 == null} * @see #getDefaultType */ public static KeyStore getInstance(String type) throws KeyStoreException { if (type == null) { throw new NullPointerException(); } try { Engine.SpiAndProvider sap = ENGINE.getInstance(type, null); return new KeyStore((KeyStoreSpi) sap.spi, sap.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 == null || provider.isEmpty()} * @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.isEmpty()) { throw new IllegalArgumentException(); } 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 == 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(); } if (type == null) { throw new NullPointerException(); } // return KeyStore instance try { Object spi = ENGINE.getInstance(type, provider, null); return new KeyStore((KeyStoreSpi) 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 (key != null && key instanceof PrivateKey && (chain == null || chain.length == 0)) {
throw new IllegalArgumentException("Certificate chain is not defined for Private key");
}
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.
*/
public final void deleteEntry(String alias) throws KeyStoreException {
if (!isInit) {
// BEGIN android-changed
throwNotInitialized();
// END android-changed
}
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("alias == null");
}
if (entry == null) {
throw new NullPointerException("entry == null");
}
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("alias == null");
}
if (entryClass == null) {
throw new NullPointerException("entryClass == null");
}
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("keyStore == null");
}
if (protectionParameter == null) {
throw new NullPointerException("protectionParameter == null");
}
if (!keyStore.isInit) {
throw new IllegalArgumentException("KeyStore was not initialized");
}
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("type == null");
}
if (protectionParameter == null) {
throw new NullPointerException("protectionParameter == null");
}
if (file == null) {
throw new NullPointerException("file == null");
}
// protection parameter should be PasswordProtection or
// CallbackHandlerProtection
if (!(protectionParameter instanceof PasswordProtection)
&& !(protectionParameter instanceof CallbackHandlerProtection)) {
throw new IllegalArgumentException("protectionParameter is neither "
+ "PasswordProtection nor CallbackHandlerProtection instance");
}
// check file parameter
if (!file.exists()) {
throw new IllegalArgumentException("File does not exist: " + file.getName());
}
if (!file.isFile()) {
throw new IllegalArgumentException("Not a regular file: " + file.getName());
}
// 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("type == null");
}
if (protectionParameter == null) {
throw new NullPointerException("protectionParameter == null");
}
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("protectionParameter is neither "
+ "PasswordProtection nor CallbackHandlerProtection instance");
}
// load KeyStore from file
AccessController.doPrivileged(
new PrivilegedExceptionAction