Path: blob/master/src/java.base/share/classes/sun/security/provider/DomainKeyStore.java
41159 views
/*1* Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.provider;2627import java.io.*;28import java.net.*;29import java.security.*;30import java.security.cert.Certificate;31import java.security.cert.CertificateFactory;32import java.security.cert.CertificateException;33import java.util.*;3435import static java.nio.charset.StandardCharsets.UTF_8;3637import sun.security.pkcs.EncryptedPrivateKeyInfo;38import sun.security.util.PolicyUtil;3940/**41* This class provides the domain keystore type identified as "DKS".42* DKS presents a collection of separate keystores as a single logical keystore.43* The collection of keystores is specified in a domain configuration file which44* is passed to DKS in a {@link DomainLoadStoreParameter}.45* <p>46* The following properties are supported:47* <dl>48* <dt> {@code keystoreType="<type>"} </dt>49* <dd> The keystore type. </dd>50* <dt> {@code keystoreURI="<url>"} </dt>51* <dd> The keystore location. </dd>52* <dt> {@code keystoreProviderName="<name>"} </dt>53* <dd> The name of the keystore's JCE provider. </dd>54* <dt> {@code keystorePasswordEnv="<environment-variable>"} </dt>55* <dd> The environment variable that stores a keystore password.56* <dt> {@code entryNameSeparator="<separator>"} </dt>57* <dd> The separator between a keystore name prefix and an entry name.58* When specified, it applies to all the entries in a domain.59* Its default value is a space. </dd>60* </dl>61*62* @since 1.863*/6465abstract class DomainKeyStore extends KeyStoreSpi {6667// regular DKS68public static final class DKS extends DomainKeyStore {69String convertAlias(String alias) {70return alias.toLowerCase(Locale.ENGLISH);71}72}7374// DKS property names75private static final String ENTRY_NAME_SEPARATOR = "entrynameseparator";76private static final String KEYSTORE_PROVIDER_NAME = "keystoreprovidername";77private static final String KEYSTORE_TYPE = "keystoretype";78private static final String KEYSTORE_URI = "keystoreuri";79private static final String KEYSTORE_PASSWORD_ENV = "keystorepasswordenv";8081// RegEx meta characters82private static final String REGEX_META = ".$|()[{^?*+\\";8384// Default prefix for keystores loaded-by-stream85private static final String DEFAULT_STREAM_PREFIX = "iostream";86private int streamCounter = 1;87private String entryNameSeparator = " ";88private String entryNameSeparatorRegEx = " ";8990// Default keystore type91private static final String DEFAULT_KEYSTORE_TYPE =92KeyStore.getDefaultType();9394// Domain keystores95private final Map<String, KeyStore> keystores = new HashMap<>();9697DomainKeyStore() {98}99100// convert an alias to internal form, overridden in subclasses:101// lower case for regular DKS102abstract String convertAlias(String alias);103104/**105* Returns the key associated with the given alias, using the given106* password to recover it.107*108* @param alias the alias name109* @param password the password for recovering the key110*111* @return the requested key, or null if the given alias does not exist112* or does not identify a <i>key entry</i>.113*114* @exception NoSuchAlgorithmException if the algorithm for recovering the115* key cannot be found116* @exception UnrecoverableKeyException if the key cannot be recovered117* (e.g., the given password is wrong).118*/119public Key engineGetKey(String alias, char[] password)120throws NoSuchAlgorithmException, UnrecoverableKeyException121{122AbstractMap.SimpleEntry<String, Collection<KeyStore>> pair =123getKeystoresForReading(alias);124Key key = null;125126try {127String entryAlias = pair.getKey();128for (KeyStore keystore : pair.getValue()) {129key = keystore.getKey(entryAlias, password);130if (key != null) {131break;132}133}134} catch (KeyStoreException e) {135throw new IllegalStateException(e);136}137138return key;139}140141/**142* Returns the certificate chain associated with the given alias.143*144* @param alias the alias name145*146* @return the certificate chain (ordered with the user's certificate first147* and the root certificate authority last), or null if the given alias148* does not exist or does not contain a certificate chain (i.e., the given149* alias identifies either a <i>trusted certificate entry</i> or a150* <i>key entry</i> without a certificate chain).151*/152public Certificate[] engineGetCertificateChain(String alias) {153154AbstractMap.SimpleEntry<String, Collection<KeyStore>> pair =155getKeystoresForReading(alias);156Certificate[] chain = null;157158try {159String entryAlias = pair.getKey();160for (KeyStore keystore : pair.getValue()) {161chain = keystore.getCertificateChain(entryAlias);162if (chain != null) {163break;164}165}166} catch (KeyStoreException e) {167throw new IllegalStateException(e);168}169170return chain;171}172173/**174* Returns the certificate associated with the given alias.175*176* <p>If the given alias name identifies a177* <i>trusted certificate entry</i>, the certificate associated with that178* entry is returned. If the given alias name identifies a179* <i>key entry</i>, the first element of the certificate chain of that180* entry is returned, or null if that entry does not have a certificate181* chain.182*183* @param alias the alias name184*185* @return the certificate, or null if the given alias does not exist or186* does not contain a certificate.187*/188public Certificate engineGetCertificate(String alias) {189190AbstractMap.SimpleEntry<String, Collection<KeyStore>> pair =191getKeystoresForReading(alias);192Certificate cert = null;193194try {195String entryAlias = pair.getKey();196for (KeyStore keystore : pair.getValue()) {197cert = keystore.getCertificate(entryAlias);198if (cert != null) {199break;200}201}202} catch (KeyStoreException e) {203throw new IllegalStateException(e);204}205206return cert;207}208209/**210* Returns the creation date of the entry identified by the given alias.211*212* @param alias the alias name213*214* @return the creation date of this entry, or null if the given alias does215* not exist216*/217public Date engineGetCreationDate(String alias) {218219AbstractMap.SimpleEntry<String, Collection<KeyStore>> pair =220getKeystoresForReading(alias);221Date date = null;222223try {224String entryAlias = pair.getKey();225for (KeyStore keystore : pair.getValue()) {226date = keystore.getCreationDate(entryAlias);227if (date != null) {228break;229}230}231} catch (KeyStoreException e) {232throw new IllegalStateException(e);233}234235return date;236}237238/**239* Assigns the given private key to the given alias, protecting240* it with the given password as defined in PKCS8.241*242* <p>The given java.security.PrivateKey <code>key</code> must243* be accompanied by a certificate chain certifying the244* corresponding public key.245*246* <p>If the given alias already exists, the keystore information247* associated with it is overridden by the given key and certificate248* chain.249*250* @param alias the alias name251* @param key the private key to be associated with the alias252* @param password the password to protect the key253* @param chain the certificate chain for the corresponding public254* key (only required if the given key is of type255* <code>java.security.PrivateKey</code>).256*257* @exception KeyStoreException if the given key is not a private key,258* cannot be protected, or this operation fails for some other reason259*/260public void engineSetKeyEntry(String alias, Key key, char[] password,261Certificate[] chain)262throws KeyStoreException263{264AbstractMap.SimpleEntry<String,265AbstractMap.SimpleEntry<String, KeyStore>> pair =266getKeystoreForWriting(alias);267268if (pair == null) {269throw new KeyStoreException("Error setting key entry for '" +270alias + "'");271}272String entryAlias = pair.getKey();273Map.Entry<String, KeyStore> keystore = pair.getValue();274keystore.getValue().setKeyEntry(entryAlias, key, password, chain);275}276277/**278* Assigns the given key (that has already been protected) to the given279* alias.280*281* <p>If the protected key is of type282* <code>java.security.PrivateKey</code>, it must be accompanied by a283* certificate chain certifying the corresponding public key. If the284* underlying keystore implementation is of type <code>jks</code>,285* <code>key</code> must be encoded as an286* <code>EncryptedPrivateKeyInfo</code> as defined in the PKCS #8 standard.287*288* <p>If the given alias already exists, the keystore information289* associated with it is overridden by the given key (and possibly290* certificate chain).291*292* @param alias the alias name293* @param key the key (in protected format) to be associated with the alias294* @param chain the certificate chain for the corresponding public295* key (only useful if the protected key is of type296* <code>java.security.PrivateKey</code>).297*298* @exception KeyStoreException if this operation fails.299*/300public void engineSetKeyEntry(String alias, byte[] key,301Certificate[] chain)302throws KeyStoreException303{304AbstractMap.SimpleEntry<String,305AbstractMap.SimpleEntry<String, KeyStore>> pair =306getKeystoreForWriting(alias);307308if (pair == null) {309throw new KeyStoreException(310"Error setting protected key entry for '" + alias + "'");311}312String entryAlias = pair.getKey();313Map.Entry<String, KeyStore> keystore = pair.getValue();314keystore.getValue().setKeyEntry(entryAlias, key, chain);315}316317/**318* Assigns the given certificate to the given alias.319*320* <p>If the given alias already exists in this keystore and identifies a321* <i>trusted certificate entry</i>, the certificate associated with it is322* overridden by the given certificate.323*324* @param alias the alias name325* @param cert the certificate326*327* @exception KeyStoreException if the given alias already exists and does328* not identify a <i>trusted certificate entry</i>, or this operation329* fails for some other reason.330*/331public void engineSetCertificateEntry(String alias, Certificate cert)332throws KeyStoreException333{334AbstractMap.SimpleEntry<String,335AbstractMap.SimpleEntry<String, KeyStore>> pair =336getKeystoreForWriting(alias);337338if (pair == null) {339throw new KeyStoreException("Error setting certificate entry for '"340+ alias + "'");341}342String entryAlias = pair.getKey();343Map.Entry<String, KeyStore> keystore = pair.getValue();344keystore.getValue().setCertificateEntry(entryAlias, cert);345}346347/**348* Deletes the entry identified by the given alias from this keystore.349*350* @param alias the alias name351*352* @exception KeyStoreException if the entry cannot be removed.353*/354public void engineDeleteEntry(String alias) throws KeyStoreException355{356AbstractMap.SimpleEntry<String,357AbstractMap.SimpleEntry<String, KeyStore>> pair =358getKeystoreForWriting(alias);359360if (pair == null) {361throw new KeyStoreException("Error deleting entry for '" + alias +362"'");363}364String entryAlias = pair.getKey();365Map.Entry<String, KeyStore> keystore = pair.getValue();366keystore.getValue().deleteEntry(entryAlias);367}368369/**370* Lists all the alias names of this keystore.371*372* @return enumeration of the alias names373*/374public Enumeration<String> engineAliases() {375final Iterator<Map.Entry<String, KeyStore>> iterator =376keystores.entrySet().iterator();377378return new Enumeration<String>() {379private int index = 0;380private Map.Entry<String, KeyStore> keystoresEntry = null;381private String prefix = null;382private Enumeration<String> aliases = null;383384public boolean hasMoreElements() {385try {386if (aliases == null) {387if (iterator.hasNext()) {388keystoresEntry = iterator.next();389prefix = keystoresEntry.getKey() +390entryNameSeparator;391aliases = keystoresEntry.getValue().aliases();392} else {393return false;394}395}396if (aliases.hasMoreElements()) {397return true;398} else {399if (iterator.hasNext()) {400keystoresEntry = iterator.next();401prefix = keystoresEntry.getKey() +402entryNameSeparator;403aliases = keystoresEntry.getValue().aliases();404} else {405return false;406}407}408} catch (KeyStoreException e) {409return false;410}411412return aliases.hasMoreElements();413}414415public String nextElement() {416if (hasMoreElements()) {417return prefix + aliases.nextElement();418}419throw new NoSuchElementException();420}421};422}423424/**425* Checks if the given alias exists in this keystore.426*427* @param alias the alias name428*429* @return true if the alias exists, false otherwise430*/431public boolean engineContainsAlias(String alias) {432433AbstractMap.SimpleEntry<String, Collection<KeyStore>> pair =434getKeystoresForReading(alias);435436try {437String entryAlias = pair.getKey();438for (KeyStore keystore : pair.getValue()) {439if (keystore.containsAlias(entryAlias)) {440return true;441}442}443} catch (KeyStoreException e) {444throw new IllegalStateException(e);445}446447return false;448}449450/**451* Retrieves the number of entries in this keystore.452*453* @return the number of entries in this keystore454*/455public int engineSize() {456457int size = 0;458try {459for (KeyStore keystore : keystores.values()) {460size += keystore.size();461}462} catch (KeyStoreException e) {463throw new IllegalStateException(e);464}465466return size;467}468469/**470* Returns true if the entry identified by the given alias is a471* <i>key entry</i>, and false otherwise.472*473* @return true if the entry identified by the given alias is a474* <i>key entry</i>, false otherwise.475*/476public boolean engineIsKeyEntry(String alias) {477478AbstractMap.SimpleEntry<String, Collection<KeyStore>> pair =479getKeystoresForReading(alias);480481try {482String entryAlias = pair.getKey();483for (KeyStore keystore : pair.getValue()) {484if (keystore.isKeyEntry(entryAlias)) {485return true;486}487}488} catch (KeyStoreException e) {489throw new IllegalStateException(e);490}491492return false;493}494495/**496* Returns true if the entry identified by the given alias is a497* <i>trusted certificate entry</i>, and false otherwise.498*499* @return true if the entry identified by the given alias is a500* <i>trusted certificate entry</i>, false otherwise.501*/502public boolean engineIsCertificateEntry(String alias) {503504AbstractMap.SimpleEntry<String, Collection<KeyStore>> pair =505getKeystoresForReading(alias);506507try {508String entryAlias = pair.getKey();509for (KeyStore keystore : pair.getValue()) {510if (keystore.isCertificateEntry(entryAlias)) {511return true;512}513}514} catch (KeyStoreException e) {515throw new IllegalStateException(e);516}517518return false;519}520521/*522* Returns a keystore entry alias and a list of target keystores.523* When the supplied alias prefix identifies a keystore then that single524* keystore is returned. When no alias prefix is supplied then all the525* keystores are returned.526*/527private AbstractMap.SimpleEntry<String, Collection<KeyStore>>528getKeystoresForReading(String alias) {529530String[] splits = alias.split(this.entryNameSeparatorRegEx, 2);531if (splits.length == 2) { // prefixed alias532KeyStore keystore = keystores.get(splits[0]);533if (keystore != null) {534return new AbstractMap.SimpleEntry<>(splits[1],535(Collection<KeyStore>) Collections.singleton(keystore));536}537} else if (splits.length == 1) { // unprefixed alias538// Check all keystores for the first occurrence of the alias539return new AbstractMap.SimpleEntry<>(alias, keystores.values());540}541return new AbstractMap.SimpleEntry<>("",542(Collection<KeyStore>) Collections.<KeyStore>emptyList());543}544545/*546* Returns a keystore entry alias and a single target keystore.547* An alias prefix must be supplied.548*/549private550AbstractMap.SimpleEntry<String, AbstractMap.SimpleEntry<String, KeyStore>>551getKeystoreForWriting(String alias) {552553String[] splits = alias.split(this.entryNameSeparator, 2);554if (splits.length == 2) { // prefixed alias555KeyStore keystore = keystores.get(splits[0]);556if (keystore != null) {557return new AbstractMap.SimpleEntry<>(splits[1],558new AbstractMap.SimpleEntry<>(splits[0], keystore));559}560}561return null;562}563564/**565* Returns the (alias) name of the first keystore entry whose certificate566* matches the given certificate.567*568* <p>This method attempts to match the given certificate with each569* keystore entry. If the entry being considered570* is a <i>trusted certificate entry</i>, the given certificate is571* compared to that entry's certificate. If the entry being considered is572* a <i>key entry</i>, the given certificate is compared to the first573* element of that entry's certificate chain (if a chain exists).574*575* @param cert the certificate to match with.576*577* @return the (alias) name of the first entry with matching certificate,578* or null if no such entry exists in this keystore.579*/580public String engineGetCertificateAlias(Certificate cert) {581582try {583584String alias = null;585for (KeyStore keystore : keystores.values()) {586if ((alias = keystore.getCertificateAlias(cert)) != null) {587break;588}589}590return alias;591592} catch (KeyStoreException e) {593throw new IllegalStateException(e);594}595}596597/**598* Stores this keystore to the given output stream, and protects its599* integrity with the given password.600*601* @param stream the output stream to which this keystore is written.602* @param password the password to generate the keystore integrity check603*604* @exception IOException if there was an I/O problem with data605* @exception NoSuchAlgorithmException if the appropriate data integrity606* algorithm could not be found607* @exception CertificateException if any of the certificates included in608* the keystore data could not be stored609*/610public void engineStore(OutputStream stream, char[] password)611throws IOException, NoSuchAlgorithmException, CertificateException612{613// Support storing to a stream only when a single keystore has been614// configured615try {616if (keystores.size() == 1) {617keystores.values().iterator().next().store(stream, password);618return;619}620} catch (KeyStoreException e) {621throw new IllegalStateException(e);622}623624throw new UnsupportedOperationException(625"This keystore must be stored using a DomainLoadStoreParameter");626}627628@Override629public void engineStore(KeyStore.LoadStoreParameter param)630throws IOException, NoSuchAlgorithmException, CertificateException631{632if (param instanceof DomainLoadStoreParameter) {633DomainLoadStoreParameter domainParameter =634(DomainLoadStoreParameter) param;635List<KeyStoreBuilderComponents> builders = getBuilders(636domainParameter.getConfiguration(),637domainParameter.getProtectionParams());638639for (KeyStoreBuilderComponents builder : builders) {640641try {642643KeyStore.ProtectionParameter pp = builder.protection;644if (!(pp instanceof KeyStore.PasswordProtection)) {645throw new KeyStoreException(646new IllegalArgumentException("ProtectionParameter" +647" must be a KeyStore.PasswordProtection"));648}649char[] password =650((KeyStore.PasswordProtection) builder.protection)651.getPassword();652653// Store the keystores654KeyStore keystore = keystores.get(builder.name);655656try (FileOutputStream stream =657new FileOutputStream(builder.file)) {658659keystore.store(stream, password);660}661} catch (KeyStoreException e) {662throw new IOException(e);663}664}665} else {666throw new UnsupportedOperationException(667"This keystore must be stored using a " +668"DomainLoadStoreParameter");669}670}671672/**673* Loads the keystore from the given input stream.674*675* <p>If a password is given, it is used to check the integrity of the676* keystore data. Otherwise, the integrity of the keystore is not checked.677*678* @param stream the input stream from which the keystore is loaded679* @param password the (optional) password used to check the integrity of680* the keystore.681*682* @exception IOException if there is an I/O or format problem with the683* keystore data684* @exception NoSuchAlgorithmException if the algorithm used to check685* the integrity of the keystore cannot be found686* @exception CertificateException if any of the certificates in the687* keystore could not be loaded688*/689public void engineLoad(InputStream stream, char[] password)690throws IOException, NoSuchAlgorithmException, CertificateException691{692// Support loading from a stream only for a JKS or default type keystore693try {694KeyStore keystore = null;695696try {697keystore = KeyStore.getInstance("JKS");698keystore.load(stream, password);699700} catch (Exception e) {701// Retry702if (!"JKS".equalsIgnoreCase(DEFAULT_KEYSTORE_TYPE)) {703keystore = KeyStore.getInstance(DEFAULT_KEYSTORE_TYPE);704keystore.load(stream, password);705} else {706throw e;707}708}709String keystoreName = DEFAULT_STREAM_PREFIX + streamCounter++;710keystores.put(keystoreName, keystore);711712} catch (Exception e) {713throw new UnsupportedOperationException(714"This keystore must be loaded using a " +715"DomainLoadStoreParameter");716}717}718719@Override720public void engineLoad(KeyStore.LoadStoreParameter param)721throws IOException, NoSuchAlgorithmException, CertificateException722{723if (param instanceof DomainLoadStoreParameter) {724DomainLoadStoreParameter domainParameter =725(DomainLoadStoreParameter) param;726List<KeyStoreBuilderComponents> builders = getBuilders(727domainParameter.getConfiguration(),728domainParameter.getProtectionParams());729730for (KeyStoreBuilderComponents builder : builders) {731732try {733// Load the keystores (file-based and non-file-based)734if (builder.file != null) {735keystores.put(builder.name,736KeyStore.Builder.newInstance(builder.type,737builder.provider, builder.file,738builder.protection)739.getKeyStore());740} else {741keystores.put(builder.name,742KeyStore.Builder.newInstance(builder.type,743builder.provider, builder.protection)744.getKeyStore());745}746} catch (KeyStoreException e) {747throw new IOException(e);748}749}750} else {751throw new UnsupportedOperationException(752"This keystore must be loaded using a " +753"DomainLoadStoreParameter");754}755}756757/*758* Parse a keystore domain configuration file and associated collection759* of keystore passwords to create a collection of KeyStore.Builder.760*/761private List<KeyStoreBuilderComponents> getBuilders(URI configuration,762Map<String, KeyStore.ProtectionParameter> passwords)763throws IOException {764765PolicyParser parser = new PolicyParser(true); // expand properties766Collection<PolicyParser.DomainEntry> domains = null;767List<KeyStoreBuilderComponents> builders = new ArrayList<>();768String uriDomain = configuration.getFragment();769770try (InputStreamReader configurationReader =771new InputStreamReader(772PolicyUtil.getInputStream(configuration.toURL()), UTF_8)) {773parser.read(configurationReader);774domains = parser.getDomainEntries();775776} catch (MalformedURLException mue) {777throw new IOException(mue);778779} catch (PolicyParser.ParsingException pe) {780throw new IOException(pe);781}782783for (PolicyParser.DomainEntry domain : domains) {784Map<String, String> domainProperties = domain.getProperties();785786if (uriDomain != null &&787(!uriDomain.equalsIgnoreCase(domain.getName()))) {788continue; // skip this domain789}790791if (domainProperties.containsKey(ENTRY_NAME_SEPARATOR)) {792this.entryNameSeparator =793domainProperties.get(ENTRY_NAME_SEPARATOR);794// escape any regex meta characters795char ch = 0;796StringBuilder s = new StringBuilder();797for (int i = 0; i < this.entryNameSeparator.length(); i++) {798ch = this.entryNameSeparator.charAt(i);799if (REGEX_META.indexOf(ch) != -1) {800s.append('\\');801}802s.append(ch);803}804this.entryNameSeparatorRegEx = s.toString();805}806807Collection<PolicyParser.KeyStoreEntry> keystores =808domain.getEntries();809for (PolicyParser.KeyStoreEntry keystore : keystores) {810String keystoreName = keystore.getName();811Map<String, String> properties =812new HashMap<>(domainProperties);813properties.putAll(keystore.getProperties());814815String keystoreType = DEFAULT_KEYSTORE_TYPE;816if (properties.containsKey(KEYSTORE_TYPE)) {817keystoreType = properties.get(KEYSTORE_TYPE);818}819820Provider keystoreProvider = null;821if (properties.containsKey(KEYSTORE_PROVIDER_NAME)) {822String keystoreProviderName =823properties.get(KEYSTORE_PROVIDER_NAME);824keystoreProvider =825Security.getProvider(keystoreProviderName);826if (keystoreProvider == null) {827throw new IOException("Error locating JCE provider: " +828keystoreProviderName);829}830}831832File keystoreFile = null;833if (properties.containsKey(KEYSTORE_URI)) {834String uri = properties.get(KEYSTORE_URI);835836try {837if (uri.startsWith("file://")) {838keystoreFile = new File(new URI(uri));839} else {840keystoreFile = new File(uri);841}842843} catch (URISyntaxException | IllegalArgumentException e) {844throw new IOException(845"Error processing keystore property: " +846"keystoreURI=\"" + uri + "\"", e);847}848}849850KeyStore.ProtectionParameter keystoreProtection = null;851if (passwords.containsKey(keystoreName)) {852keystoreProtection = passwords.get(keystoreName);853854} else if (properties.containsKey(KEYSTORE_PASSWORD_ENV)) {855String env = properties.get(KEYSTORE_PASSWORD_ENV);856String pwd = System.getenv(env);857if (pwd != null) {858keystoreProtection =859new KeyStore.PasswordProtection(pwd.toCharArray());860} else {861throw new IOException(862"Error processing keystore property: " +863"keystorePasswordEnv=\"" + env + "\"");864}865} else {866keystoreProtection = new KeyStore.PasswordProtection(null);867}868869builders.add(new KeyStoreBuilderComponents(keystoreName,870keystoreType, keystoreProvider, keystoreFile,871keystoreProtection));872}873break; // skip other domains874}875if (builders.isEmpty()) {876throw new IOException("Error locating domain configuration data " +877"for: " + configuration);878}879880return builders;881}882883/*884* Utility class that holds the components used to construct a KeyStore.Builder885*/886static class KeyStoreBuilderComponents {887String name;888String type;889Provider provider;890File file;891KeyStore.ProtectionParameter protection;892893KeyStoreBuilderComponents(String name, String type, Provider provider,894File file, KeyStore.ProtectionParameter protection) {895this.name = name;896this.type = type;897this.provider = provider;898this.file = file;899this.protection = protection;900}901}902}903904905