/*
* Copyright (c) 2013, 2019, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.provider;
import java.io.*;
import java.net.*;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateException;
import java.util.*;
import static java.nio.charset.StandardCharsets.UTF_8;
import sun.security.pkcs.EncryptedPrivateKeyInfo;
import sun.security.util.PolicyUtil;
This class provides the domain keystore type identified as "DKS". DKS presents a collection of separate keystores as a single logical keystore. The collection of keystores is specified in a domain configuration file which is passed to DKS in a DomainLoadStoreParameter
.
The following properties are supported:
-
keystoreType="<type>"
- The keystore type.
-
keystoreURI="<url>"
- The keystore location.
-
keystoreProviderName="<name>"
- The name of the keystore's JCE provider.
-
keystorePasswordEnv="<environment-variable>"
- The environment variable that stores a keystore password.
-
entryNameSeparator="<separator>"
- The separator between a keystore name prefix and an entry name.
When specified, it applies to all the entries in a domain.
Its default value is a space.
Since: 1.8
/**
* This class provides the domain keystore type identified as "DKS".
* DKS presents a collection of separate keystores as a single logical keystore.
* The collection of keystores is specified in a domain configuration file which
* is passed to DKS in a {@link DomainLoadStoreParameter}.
* <p>
* The following properties are supported:
* <dl>
* <dt> {@code keystoreType="<type>"} </dt>
* <dd> The keystore type. </dd>
* <dt> {@code keystoreURI="<url>"} </dt>
* <dd> The keystore location. </dd>
* <dt> {@code keystoreProviderName="<name>"} </dt>
* <dd> The name of the keystore's JCE provider. </dd>
* <dt> {@code keystorePasswordEnv="<environment-variable>"} </dt>
* <dd> The environment variable that stores a keystore password.
* <dt> {@code entryNameSeparator="<separator>"} </dt>
* <dd> The separator between a keystore name prefix and an entry name.
* When specified, it applies to all the entries in a domain.
* Its default value is a space. </dd>
* </dl>
*
* @since 1.8
*/
abstract class DomainKeyStore extends KeyStoreSpi {
// regular DKS
public static final class DKS extends DomainKeyStore {
String convertAlias(String alias) {
return alias.toLowerCase(Locale.ENGLISH);
}
}
// DKS property names
private static final String ENTRY_NAME_SEPARATOR = "entrynameseparator";
private static final String KEYSTORE_PROVIDER_NAME = "keystoreprovidername";
private static final String KEYSTORE_TYPE = "keystoretype";
private static final String KEYSTORE_URI = "keystoreuri";
private static final String KEYSTORE_PASSWORD_ENV = "keystorepasswordenv";
// RegEx meta characters
private static final String REGEX_META = ".$|()[{^?*+\\";
// Default prefix for keystores loaded-by-stream
private static final String DEFAULT_STREAM_PREFIX = "iostream";
private int streamCounter = 1;
private String entryNameSeparator = " ";
private String entryNameSeparatorRegEx = " ";
// Default keystore type
private static final String DEFAULT_KEYSTORE_TYPE =
KeyStore.getDefaultType();
// Domain keystores
private final Map<String, KeyStore> keystores = new HashMap<>();
DomainKeyStore() {
}
// convert an alias to internal form, overridden in subclasses:
// lower case for regular DKS
abstract String convertAlias(String alias);
Returns the key associated with the given alias, using the given
password to recover it.
Params: - alias – the alias name
- password – the password for recovering the key
Throws: - NoSuchAlgorithmException – if the algorithm for recovering the
key cannot be found
- UnrecoverableKeyException – if the key cannot be recovered
(e.g., the given password is wrong).
Returns: the requested key, or null if the given alias does not exist
or does not identify a key entry.
/**
* Returns the key associated with the given alias, using the given
* password to recover it.
*
* @param alias the alias name
* @param password the password for recovering the key
*
* @return the requested key, or null if the given alias does not exist
* or does not identify a <i>key entry</i>.
*
* @exception NoSuchAlgorithmException if the algorithm for recovering the
* key cannot be found
* @exception UnrecoverableKeyException if the key cannot be recovered
* (e.g., the given password is wrong).
*/
public Key engineGetKey(String alias, char[] password)
throws NoSuchAlgorithmException, UnrecoverableKeyException
{
AbstractMap.SimpleEntry<String, Collection<KeyStore>> pair =
getKeystoresForReading(alias);
Key key = null;
try {
String entryAlias = pair.getKey();
for (KeyStore keystore : pair.getValue()) {
key = keystore.getKey(entryAlias, password);
if (key != null) {
break;
}
}
} catch (KeyStoreException e) {
throw new IllegalStateException(e);
}
return key;
}
Returns the certificate chain associated with the given alias.
Params: - alias – the alias name
Returns: the certificate chain (ordered with the user's certificate first
and the root certificate authority last), or null if the given alias
does not exist or does not contain a certificate chain (i.e., the given
alias identifies either a trusted certificate entry or a
key entry without a certificate chain).
/**
* Returns the certificate chain associated with the given alias.
*
* @param alias the alias name
*
* @return the certificate chain (ordered with the user's certificate first
* and the root certificate authority last), or null if the given alias
* does not exist or does not contain a certificate chain (i.e., the given
* alias identifies either a <i>trusted certificate entry</i> or a
* <i>key entry</i> without a certificate chain).
*/
public Certificate[] engineGetCertificateChain(String alias) {
AbstractMap.SimpleEntry<String, Collection<KeyStore>> pair =
getKeystoresForReading(alias);
Certificate[] chain = null;
try {
String entryAlias = pair.getKey();
for (KeyStore keystore : pair.getValue()) {
chain = keystore.getCertificateChain(entryAlias);
if (chain != null) {
break;
}
}
} catch (KeyStoreException e) {
throw new IllegalStateException(e);
}
return chain;
}
Returns the certificate associated with the given alias.
If the given alias name identifies a
trusted certificate entry, the certificate associated with that
entry is returned. If the given alias name identifies a
key entry, the first element of the certificate chain of that
entry is returned, or null if that entry does not have a certificate
chain.
Params: - alias – the alias name
Returns: the certificate, or null if the given alias does not exist or
does not contain a certificate.
/**
* Returns the certificate associated with the given alias.
*
* <p>If the given alias name identifies a
* <i>trusted certificate entry</i>, the certificate associated with that
* entry is returned. If the given alias name identifies a
* <i>key entry</i>, the first element of the certificate chain of that
* entry is returned, or null if that entry does not have a certificate
* chain.
*
* @param alias the alias name
*
* @return the certificate, or null if the given alias does not exist or
* does not contain a certificate.
*/
public Certificate engineGetCertificate(String alias) {
AbstractMap.SimpleEntry<String, Collection<KeyStore>> pair =
getKeystoresForReading(alias);
Certificate cert = null;
try {
String entryAlias = pair.getKey();
for (KeyStore keystore : pair.getValue()) {
cert = keystore.getCertificate(entryAlias);
if (cert != null) {
break;
}
}
} catch (KeyStoreException e) {
throw new IllegalStateException(e);
}
return cert;
}
Returns the creation date of the entry identified by the given alias.
Params: - alias – the alias name
Returns: the creation date of this entry, or null if the given alias does
not exist
/**
* Returns the creation date of the entry identified by the given alias.
*
* @param alias the alias name
*
* @return the creation date of this entry, or null if the given alias does
* not exist
*/
public Date engineGetCreationDate(String alias) {
AbstractMap.SimpleEntry<String, Collection<KeyStore>> pair =
getKeystoresForReading(alias);
Date date = null;
try {
String entryAlias = pair.getKey();
for (KeyStore keystore : pair.getValue()) {
date = keystore.getCreationDate(entryAlias);
if (date != null) {
break;
}
}
} catch (KeyStoreException e) {
throw new IllegalStateException(e);
}
return date;
}
Assigns the given private key to the given alias, protecting
it with the given password as defined in PKCS8.
The given java.security.PrivateKey key
must
be accompanied by a certificate chain certifying the
corresponding public key.
If the given alias already exists, the keystore information
associated with it is overridden by the given key and certificate
chain.
Params: - alias – the alias name
- key – the private key to be associated with the alias
- password – the password to protect the key
- chain – the certificate chain for the corresponding public
key (only required if the given key is of type
java.security.PrivateKey
).
Throws: - KeyStoreException – if the given key is not a private key,
cannot be protected, or this operation fails for some other reason
/**
* Assigns the given private key to the given alias, protecting
* it with the given password as defined in PKCS8.
*
* <p>The given java.security.PrivateKey <code>key</code> must
* be accompanied by a certificate chain certifying the
* corresponding public key.
*
* <p>If the given alias already exists, the keystore information
* associated with it is overridden by the given key and certificate
* chain.
*
* @param alias the alias name
* @param key the private key to be associated with the alias
* @param password the password to protect the key
* @param chain the certificate chain for the corresponding public
* key (only required if the given key is of type
* <code>java.security.PrivateKey</code>).
*
* @exception KeyStoreException if the given key is not a private key,
* cannot be protected, or this operation fails for some other reason
*/
public void engineSetKeyEntry(String alias, Key key, char[] password,
Certificate[] chain)
throws KeyStoreException
{
AbstractMap.SimpleEntry<String,
AbstractMap.SimpleEntry<String, KeyStore>> pair =
getKeystoreForWriting(alias);
if (pair == null) {
throw new KeyStoreException("Error setting key entry for '" +
alias + "'");
}
String entryAlias = pair.getKey();
Map.Entry<String, KeyStore> keystore = pair.getValue();
keystore.getValue().setKeyEntry(entryAlias, key, password, chain);
}
Assigns the given key (that has already been protected) to the given
alias.
If the protected key is of type
java.security.PrivateKey
, it must be accompanied by a
certificate chain certifying the corresponding public key. If the
underlying keystore implementation is of type jks
,
key
must be encoded as an
EncryptedPrivateKeyInfo
as defined in the PKCS #8 standard.
If the given alias already exists, the keystore information
associated with it is overridden by the given key (and possibly
certificate chain).
Params: - alias – the alias name
- key – the key (in protected format) to be associated with the alias
- chain – the certificate chain for the corresponding public
key (only useful if the protected key is of type
java.security.PrivateKey
).
Throws: - KeyStoreException – if this operation fails.
/**
* Assigns the given key (that has already been protected) to the given
* alias.
*
* <p>If the protected key is of type
* <code>java.security.PrivateKey</code>, it must be accompanied by a
* certificate chain certifying the corresponding public key. If the
* underlying keystore implementation is of type <code>jks</code>,
* <code>key</code> must be encoded as an
* <code>EncryptedPrivateKeyInfo</code> as defined in the PKCS #8 standard.
*
* <p>If the given alias already exists, the keystore information
* associated with it is overridden by the given key (and possibly
* certificate chain).
*
* @param alias the alias name
* @param key the key (in protected format) to be associated with the alias
* @param chain the certificate chain for the corresponding public
* key (only useful if the protected key is of type
* <code>java.security.PrivateKey</code>).
*
* @exception KeyStoreException if this operation fails.
*/
public void engineSetKeyEntry(String alias, byte[] key,
Certificate[] chain)
throws KeyStoreException
{
AbstractMap.SimpleEntry<String,
AbstractMap.SimpleEntry<String, KeyStore>> pair =
getKeystoreForWriting(alias);
if (pair == null) {
throw new KeyStoreException(
"Error setting protected key entry for '" + alias + "'");
}
String entryAlias = pair.getKey();
Map.Entry<String, KeyStore> keystore = pair.getValue();
keystore.getValue().setKeyEntry(entryAlias, key, chain);
}
Assigns the given certificate to the given alias.
If the given alias already exists in this keystore and identifies a
trusted certificate entry, the certificate associated with it is
overridden by the given certificate.
Params: - alias – the alias name
- cert – the certificate
Throws: - KeyStoreException – if the given alias already exists and does
not identify a trusted certificate entry, or this operation
fails for some other reason.
/**
* Assigns the given certificate to the given alias.
*
* <p>If the given alias already exists in this keystore and identifies a
* <i>trusted certificate entry</i>, the certificate associated with it is
* overridden by the given certificate.
*
* @param alias the alias name
* @param cert the certificate
*
* @exception KeyStoreException if the given alias already exists and does
* not identify a <i>trusted certificate entry</i>, or this operation
* fails for some other reason.
*/
public void engineSetCertificateEntry(String alias, Certificate cert)
throws KeyStoreException
{
AbstractMap.SimpleEntry<String,
AbstractMap.SimpleEntry<String, KeyStore>> pair =
getKeystoreForWriting(alias);
if (pair == null) {
throw new KeyStoreException("Error setting certificate entry for '"
+ alias + "'");
}
String entryAlias = pair.getKey();
Map.Entry<String, KeyStore> keystore = pair.getValue();
keystore.getValue().setCertificateEntry(entryAlias, cert);
}
Deletes the entry identified by the given alias from this keystore.
Params: - alias – the alias name
Throws: - KeyStoreException – if the entry cannot be removed.
/**
* Deletes the entry identified by the given alias from this keystore.
*
* @param alias the alias name
*
* @exception KeyStoreException if the entry cannot be removed.
*/
public void engineDeleteEntry(String alias) throws KeyStoreException
{
AbstractMap.SimpleEntry<String,
AbstractMap.SimpleEntry<String, KeyStore>> pair =
getKeystoreForWriting(alias);
if (pair == null) {
throw new KeyStoreException("Error deleting entry for '" + alias +
"'");
}
String entryAlias = pair.getKey();
Map.Entry<String, KeyStore> keystore = pair.getValue();
keystore.getValue().deleteEntry(entryAlias);
}
Lists all the alias names of this keystore.
Returns: enumeration of the alias names
/**
* Lists all the alias names of this keystore.
*
* @return enumeration of the alias names
*/
public Enumeration<String> engineAliases() {
final Iterator<Map.Entry<String, KeyStore>> iterator =
keystores.entrySet().iterator();
return new Enumeration<String>() {
private int index = 0;
private Map.Entry<String, KeyStore> keystoresEntry = null;
private String prefix = null;
private Enumeration<String> aliases = null;
public boolean hasMoreElements() {
try {
if (aliases == null) {
if (iterator.hasNext()) {
keystoresEntry = iterator.next();
prefix = keystoresEntry.getKey() +
entryNameSeparator;
aliases = keystoresEntry.getValue().aliases();
} else {
return false;
}
}
if (aliases.hasMoreElements()) {
return true;
} else {
if (iterator.hasNext()) {
keystoresEntry = iterator.next();
prefix = keystoresEntry.getKey() +
entryNameSeparator;
aliases = keystoresEntry.getValue().aliases();
} else {
return false;
}
}
} catch (KeyStoreException e) {
return false;
}
return aliases.hasMoreElements();
}
public String nextElement() {
if (hasMoreElements()) {
return prefix + aliases.nextElement();
}
throw new NoSuchElementException();
}
};
}
Checks if the given alias exists in this keystore.
Params: - alias – the alias name
Returns: true if the alias exists, false otherwise
/**
* Checks if the given alias exists in this keystore.
*
* @param alias the alias name
*
* @return true if the alias exists, false otherwise
*/
public boolean engineContainsAlias(String alias) {
AbstractMap.SimpleEntry<String, Collection<KeyStore>> pair =
getKeystoresForReading(alias);
try {
String entryAlias = pair.getKey();
for (KeyStore keystore : pair.getValue()) {
if (keystore.containsAlias(entryAlias)) {
return true;
}
}
} catch (KeyStoreException e) {
throw new IllegalStateException(e);
}
return false;
}
Retrieves the number of entries in this keystore.
Returns: the number of entries in this keystore
/**
* Retrieves the number of entries in this keystore.
*
* @return the number of entries in this keystore
*/
public int engineSize() {
int size = 0;
try {
for (KeyStore keystore : keystores.values()) {
size += keystore.size();
}
} catch (KeyStoreException e) {
throw new IllegalStateException(e);
}
return size;
}
Returns true if the entry identified by the given alias is a
key entry, and false otherwise.
Returns: true if the entry identified by the given alias is a
key entry, false otherwise.
/**
* Returns true if the entry identified by the given alias is a
* <i>key entry</i>, and false otherwise.
*
* @return true if the entry identified by the given alias is a
* <i>key entry</i>, false otherwise.
*/
public boolean engineIsKeyEntry(String alias) {
AbstractMap.SimpleEntry<String, Collection<KeyStore>> pair =
getKeystoresForReading(alias);
try {
String entryAlias = pair.getKey();
for (KeyStore keystore : pair.getValue()) {
if (keystore.isKeyEntry(entryAlias)) {
return true;
}
}
} catch (KeyStoreException e) {
throw new IllegalStateException(e);
}
return false;
}
Returns true if the entry identified by the given alias is a
trusted certificate entry, and false otherwise.
Returns: true if the entry identified by the given alias is a
trusted certificate entry, false otherwise.
/**
* Returns true if the entry identified by the given alias is a
* <i>trusted certificate entry</i>, and false otherwise.
*
* @return true if the entry identified by the given alias is a
* <i>trusted certificate entry</i>, false otherwise.
*/
public boolean engineIsCertificateEntry(String alias) {
AbstractMap.SimpleEntry<String, Collection<KeyStore>> pair =
getKeystoresForReading(alias);
try {
String entryAlias = pair.getKey();
for (KeyStore keystore : pair.getValue()) {
if (keystore.isCertificateEntry(entryAlias)) {
return true;
}
}
} catch (KeyStoreException e) {
throw new IllegalStateException(e);
}
return false;
}
/*
* Returns a keystore entry alias and a list of target keystores.
* When the supplied alias prefix identifies a keystore then that single
* keystore is returned. When no alias prefix is supplied then all the
* keystores are returned.
*/
private AbstractMap.SimpleEntry<String, Collection<KeyStore>>
getKeystoresForReading(String alias) {
String[] splits = alias.split(this.entryNameSeparatorRegEx, 2);
if (splits.length == 2) { // prefixed alias
KeyStore keystore = keystores.get(splits[0]);
if (keystore != null) {
return new AbstractMap.SimpleEntry<>(splits[1],
(Collection<KeyStore>) Collections.singleton(keystore));
}
} else if (splits.length == 1) { // unprefixed alias
// Check all keystores for the first occurrence of the alias
return new AbstractMap.SimpleEntry<>(alias, keystores.values());
}
return new AbstractMap.SimpleEntry<>("",
(Collection<KeyStore>) Collections.<KeyStore>emptyList());
}
/*
* Returns a keystore entry alias and a single target keystore.
* An alias prefix must be supplied.
*/
private
AbstractMap.SimpleEntry<String, AbstractMap.SimpleEntry<String, KeyStore>>
getKeystoreForWriting(String alias) {
String[] splits = alias.split(this.entryNameSeparator, 2);
if (splits.length == 2) { // prefixed alias
KeyStore keystore = keystores.get(splits[0]);
if (keystore != null) {
return new AbstractMap.SimpleEntry<>(splits[1],
new AbstractMap.SimpleEntry<>(splits[0], keystore));
}
}
return null;
}
Returns the (alias) name of the first keystore entry whose certificate
matches the given certificate.
This method attempts to match the given certificate with each
keystore entry. If the entry being considered
is a trusted certificate entry, the given certificate is
compared to that entry's certificate. If the entry being considered is
a key entry, the given certificate is compared to the first
element of that entry's certificate chain (if a chain exists).
Params: - cert – the certificate to match with.
Returns: the (alias) name of the first entry with matching certificate,
or null if no such entry exists in this keystore.
/**
* Returns the (alias) name of the first keystore entry whose certificate
* matches the given certificate.
*
* <p>This method attempts to match the given certificate with each
* keystore entry. If the entry being considered
* is a <i>trusted certificate entry</i>, the given certificate is
* compared to that entry's certificate. If the entry being considered is
* a <i>key entry</i>, the given certificate is compared to the first
* element of that entry's certificate chain (if a chain exists).
*
* @param cert the certificate to match with.
*
* @return the (alias) name of the first entry with matching certificate,
* or null if no such entry exists in this keystore.
*/
public String engineGetCertificateAlias(Certificate cert) {
try {
String alias = null;
for (KeyStore keystore : keystores.values()) {
if ((alias = keystore.getCertificateAlias(cert)) != null) {
break;
}
}
return alias;
} catch (KeyStoreException e) {
throw new IllegalStateException(e);
}
}
Stores this keystore to the given output stream, and protects its
integrity with the given password.
Params: - stream – the output stream to which this keystore is written.
- password – the password to generate the keystore integrity check
Throws: - IOException – if there was an I/O problem with data
- NoSuchAlgorithmException – if the appropriate data integrity
algorithm could not be found
- CertificateException – if any of the certificates included in
the keystore data could not be stored
/**
* Stores this keystore to the given output stream, and protects its
* integrity with the given password.
*
* @param stream the output stream to which this keystore is written.
* @param password the password to generate the keystore integrity check
*
* @exception IOException if there was an I/O problem with data
* @exception NoSuchAlgorithmException if the appropriate data integrity
* algorithm could not be found
* @exception CertificateException if any of the certificates included in
* the keystore data could not be stored
*/
public void engineStore(OutputStream stream, char[] password)
throws IOException, NoSuchAlgorithmException, CertificateException
{
// Support storing to a stream only when a single keystore has been
// configured
try {
if (keystores.size() == 1) {
keystores.values().iterator().next().store(stream, password);
return;
}
} catch (KeyStoreException e) {
throw new IllegalStateException(e);
}
throw new UnsupportedOperationException(
"This keystore must be stored using a DomainLoadStoreParameter");
}
@Override
public void engineStore(KeyStore.LoadStoreParameter param)
throws IOException, NoSuchAlgorithmException, CertificateException
{
if (param instanceof DomainLoadStoreParameter) {
DomainLoadStoreParameter domainParameter =
(DomainLoadStoreParameter) param;
List<KeyStoreBuilderComponents> builders = getBuilders(
domainParameter.getConfiguration(),
domainParameter.getProtectionParams());
for (KeyStoreBuilderComponents builder : builders) {
try {
KeyStore.ProtectionParameter pp = builder.protection;
if (!(pp instanceof KeyStore.PasswordProtection)) {
throw new KeyStoreException(
new IllegalArgumentException("ProtectionParameter" +
" must be a KeyStore.PasswordProtection"));
}
char[] password =
((KeyStore.PasswordProtection) builder.protection)
.getPassword();
// Store the keystores
KeyStore keystore = keystores.get(builder.name);
try (FileOutputStream stream =
new FileOutputStream(builder.file)) {
keystore.store(stream, password);
}
} catch (KeyStoreException e) {
throw new IOException(e);
}
}
} else {
throw new UnsupportedOperationException(
"This keystore must be stored using a " +
"DomainLoadStoreParameter");
}
}
Loads the keystore from the given input stream.
If a password is given, it is used to check the integrity of the
keystore data. Otherwise, the integrity of the keystore is not checked.
Params: - stream – the input stream from which the keystore is loaded
- password – the (optional) password used to check the integrity of
the keystore.
Throws: - IOException – if there is an I/O or format problem with the
keystore data
- NoSuchAlgorithmException – if the algorithm used to check
the integrity of the keystore cannot be found
- CertificateException – if any of the certificates in the
keystore could not be loaded
/**
* Loads the keystore from the given input stream.
*
* <p>If a password is given, it is used to check the integrity of the
* keystore data. Otherwise, the integrity of the keystore is not checked.
*
* @param stream the input stream from which the keystore is loaded
* @param password the (optional) password used to check the integrity of
* the keystore.
*
* @exception IOException if there is an I/O or format problem with the
* keystore data
* @exception NoSuchAlgorithmException if the algorithm used to check
* the integrity of the keystore cannot be found
* @exception CertificateException if any of the certificates in the
* keystore could not be loaded
*/
public void engineLoad(InputStream stream, char[] password)
throws IOException, NoSuchAlgorithmException, CertificateException
{
// Support loading from a stream only for a JKS or default type keystore
try {
KeyStore keystore = null;
try {
keystore = KeyStore.getInstance("JKS");
keystore.load(stream, password);
} catch (Exception e) {
// Retry
if (!"JKS".equalsIgnoreCase(DEFAULT_KEYSTORE_TYPE)) {
keystore = KeyStore.getInstance(DEFAULT_KEYSTORE_TYPE);
keystore.load(stream, password);
} else {
throw e;
}
}
String keystoreName = DEFAULT_STREAM_PREFIX + streamCounter++;
keystores.put(keystoreName, keystore);
} catch (Exception e) {
throw new UnsupportedOperationException(
"This keystore must be loaded using a " +
"DomainLoadStoreParameter");
}
}
@Override
public void engineLoad(KeyStore.LoadStoreParameter param)
throws IOException, NoSuchAlgorithmException, CertificateException
{
if (param instanceof DomainLoadStoreParameter) {
DomainLoadStoreParameter domainParameter =
(DomainLoadStoreParameter) param;
List<KeyStoreBuilderComponents> builders = getBuilders(
domainParameter.getConfiguration(),
domainParameter.getProtectionParams());
for (KeyStoreBuilderComponents builder : builders) {
try {
// Load the keystores (file-based and non-file-based)
if (builder.file != null) {
keystores.put(builder.name,
KeyStore.Builder.newInstance(builder.type,
builder.provider, builder.file,
builder.protection)
.getKeyStore());
} else {
keystores.put(builder.name,
KeyStore.Builder.newInstance(builder.type,
builder.provider, builder.protection)
.getKeyStore());
}
} catch (KeyStoreException e) {
throw new IOException(e);
}
}
} else {
throw new UnsupportedOperationException(
"This keystore must be loaded using a " +
"DomainLoadStoreParameter");
}
}
/*
* Parse a keystore domain configuration file and associated collection
* of keystore passwords to create a collection of KeyStore.Builder.
*/
private List<KeyStoreBuilderComponents> getBuilders(URI configuration,
Map<String, KeyStore.ProtectionParameter> passwords)
throws IOException {
PolicyParser parser = new PolicyParser(true); // expand properties
Collection<PolicyParser.DomainEntry> domains = null;
List<KeyStoreBuilderComponents> builders = new ArrayList<>();
String uriDomain = configuration.getFragment();
try (InputStreamReader configurationReader =
new InputStreamReader(
PolicyUtil.getInputStream(configuration.toURL()), UTF_8)) {
parser.read(configurationReader);
domains = parser.getDomainEntries();
} catch (MalformedURLException mue) {
throw new IOException(mue);
} catch (PolicyParser.ParsingException pe) {
throw new IOException(pe);
}
for (PolicyParser.DomainEntry domain : domains) {
Map<String, String> domainProperties = domain.getProperties();
if (uriDomain != null &&
(!uriDomain.equalsIgnoreCase(domain.getName()))) {
continue; // skip this domain
}
if (domainProperties.containsKey(ENTRY_NAME_SEPARATOR)) {
this.entryNameSeparator =
domainProperties.get(ENTRY_NAME_SEPARATOR);
// escape any regex meta characters
char ch = 0;
StringBuilder s = new StringBuilder();
for (int i = 0; i < this.entryNameSeparator.length(); i++) {
ch = this.entryNameSeparator.charAt(i);
if (REGEX_META.indexOf(ch) != -1) {
s.append('\\');
}
s.append(ch);
}
this.entryNameSeparatorRegEx = s.toString();
}
Collection<PolicyParser.KeyStoreEntry> keystores =
domain.getEntries();
for (PolicyParser.KeyStoreEntry keystore : keystores) {
String keystoreName = keystore.getName();
Map<String, String> properties =
new HashMap<>(domainProperties);
properties.putAll(keystore.getProperties());
String keystoreType = DEFAULT_KEYSTORE_TYPE;
if (properties.containsKey(KEYSTORE_TYPE)) {
keystoreType = properties.get(KEYSTORE_TYPE);
}
Provider keystoreProvider = null;
if (properties.containsKey(KEYSTORE_PROVIDER_NAME)) {
String keystoreProviderName =
properties.get(KEYSTORE_PROVIDER_NAME);
keystoreProvider =
Security.getProvider(keystoreProviderName);
if (keystoreProvider == null) {
throw new IOException("Error locating JCE provider: " +
keystoreProviderName);
}
}
File keystoreFile = null;
if (properties.containsKey(KEYSTORE_URI)) {
String uri = properties.get(KEYSTORE_URI);
try {
if (uri.startsWith("file://")) {
keystoreFile = new File(new URI(uri));
} else {
keystoreFile = new File(uri);
}
} catch (URISyntaxException | IllegalArgumentException e) {
throw new IOException(
"Error processing keystore property: " +
"keystoreURI=\"" + uri + "\"", e);
}
}
KeyStore.ProtectionParameter keystoreProtection = null;
if (passwords.containsKey(keystoreName)) {
keystoreProtection = passwords.get(keystoreName);
} else if (properties.containsKey(KEYSTORE_PASSWORD_ENV)) {
String env = properties.get(KEYSTORE_PASSWORD_ENV);
String pwd = System.getenv(env);
if (pwd != null) {
keystoreProtection =
new KeyStore.PasswordProtection(pwd.toCharArray());
} else {
throw new IOException(
"Error processing keystore property: " +
"keystorePasswordEnv=\"" + env + "\"");
}
} else {
keystoreProtection = new KeyStore.PasswordProtection(null);
}
builders.add(new KeyStoreBuilderComponents(keystoreName,
keystoreType, keystoreProvider, keystoreFile,
keystoreProtection));
}
break; // skip other domains
}
if (builders.isEmpty()) {
throw new IOException("Error locating domain configuration data " +
"for: " + configuration);
}
return builders;
}
/*
* Utility class that holds the components used to construct a KeyStore.Builder
*/
class KeyStoreBuilderComponents {
String name;
String type;
Provider provider;
File file;
KeyStore.ProtectionParameter protection;
KeyStoreBuilderComponents(String name, String type, Provider provider,
File file, KeyStore.ProtectionParameter protection) {
this.name = name;
this.type = type;
this.provider = provider;
this.file = file;
this.protection = protection;
}
}
}