001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017package org.apache.logging.log4j.core.net.ssl;
018
019import java.security.KeyStoreException;
020import java.security.NoSuchAlgorithmException;
021import java.security.UnrecoverableKeyException;
022import java.util.Arrays;
023import java.util.Objects;
024
025import javax.net.ssl.KeyManagerFactory;
026
027import org.apache.logging.log4j.core.Core;
028import org.apache.logging.log4j.core.config.plugins.Plugin;
029import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
030import org.apache.logging.log4j.core.config.plugins.PluginFactory;
031
032/**
033 * Configuration of the KeyStore
034 */
035@Plugin(name = "KeyStore", category = Core.CATEGORY_NAME, printObject = true)
036public class KeyStoreConfiguration extends AbstractKeyStoreConfiguration {
037
038    private final String keyManagerFactoryAlgorithm;
039
040    /**
041     *
042     * @throws StoreConfigurationException Thrown if this instance cannot load the KeyStore.
043     */
044    public KeyStoreConfiguration(final String location,
045                                 final PasswordProvider  passwordProvider,
046                                 final String keyStoreType,
047                                 final String keyManagerFactoryAlgorithm) throws StoreConfigurationException {
048        super(location, passwordProvider, keyStoreType);
049        this.keyManagerFactoryAlgorithm = keyManagerFactoryAlgorithm == null ? KeyManagerFactory.getDefaultAlgorithm()
050                : keyManagerFactoryAlgorithm;
051    }
052
053    /**
054     *
055     * @throws StoreConfigurationException Thrown if this instance cannot load the KeyStore.
056     * @deprecated use {@link #KeyStoreConfiguration(String, PasswordProvider, String, String)} instead
057     */
058    @Deprecated
059    public KeyStoreConfiguration(final String location,
060                                 final char[] password,
061                                 final String keyStoreType,
062                                 final String keyManagerFactoryAlgorithm) throws StoreConfigurationException {
063        this(location, new MemoryPasswordProvider(password), keyStoreType, keyManagerFactoryAlgorithm);
064        if (password != null) {
065            Arrays.fill(password, '\0');
066        }
067    }
068
069    /**
070     *
071     * @throws StoreConfigurationException Thrown if this instance cannot load the KeyStore.
072     * @deprecated Use {@link #KeyStoreConfiguration(String, PasswordProvider, String, String)} instead
073     */
074    @Deprecated
075    public KeyStoreConfiguration(final String location, final String password, final String keyStoreType,
076            final String keyManagerFactoryAlgorithm) throws StoreConfigurationException {
077        this(location, new MemoryPasswordProvider(password == null ? null : password.toCharArray()), keyStoreType,
078                keyManagerFactoryAlgorithm);
079    }
080
081    /**
082     * Creates a KeyStoreConfiguration.
083     *
084     * @param location
085     *        The location of the KeyStore, a file path, URL or resource.
086     * @param password
087     *        The password to access the KeyStore.
088     * @param keyStoreType
089     *        The KeyStore type, null defaults to {@code "JKS"}.
090     * @param keyManagerFactoryAlgorithm
091     *         The standard name of the requested algorithm. See the Java Secure Socket Extension Reference Guide for information about these names.
092     * @return a new KeyStoreConfiguration
093     * @throws StoreConfigurationException Thrown if this call cannot load the KeyStore.
094     */
095    @PluginFactory
096    public static KeyStoreConfiguration createKeyStoreConfiguration(
097            // @formatter:off
098            @PluginAttribute("location") final String location,
099            @PluginAttribute(value = "password", sensitive = true) final char[] password,
100            @PluginAttribute("passwordEnvironmentVariable") final String passwordEnvironmentVariable,
101            @PluginAttribute("passwordFile") final String passwordFile,
102            @PluginAttribute("type") final String keyStoreType,
103            @PluginAttribute("keyManagerFactoryAlgorithm") final String keyManagerFactoryAlgorithm) throws StoreConfigurationException {
104            // @formatter:on
105
106        if (password != null && passwordEnvironmentVariable != null && passwordFile != null) {
107            throw new StoreConfigurationException("You MUST set only one of 'password', 'passwordEnvironmentVariable' or 'passwordFile'.");
108        }
109        try {
110            // @formatter:off
111            final PasswordProvider provider = passwordFile != null
112                    ? new FilePasswordProvider(passwordFile)
113                    : passwordEnvironmentVariable != null
114                            ? new EnvironmentPasswordProvider(passwordEnvironmentVariable)
115                            // the default is memory char[] array, which may be null
116                            : new MemoryPasswordProvider(password);
117            // @formatter:on
118            if (password != null) {
119                Arrays.fill(password, '\0');
120            }
121            return new KeyStoreConfiguration(location, provider, keyStoreType, keyManagerFactoryAlgorithm);
122        } catch (final Exception ex) {
123            throw new StoreConfigurationException("Could not configure KeyStore", ex);
124        }
125    }
126
127    /**
128     * @deprecated use {@link #createKeyStoreConfiguration(String, char[], String, String, String, String)}
129     */
130    @Deprecated
131    public static KeyStoreConfiguration createKeyStoreConfiguration(
132            // @formatter:off
133            final String location,
134            final char[] password,
135            final String keyStoreType,
136            final String keyManagerFactoryAlgorithm) throws StoreConfigurationException {
137            // @formatter:on
138        return createKeyStoreConfiguration(location, password, null, null, keyStoreType, keyManagerFactoryAlgorithm);
139    }
140
141    /**
142     * Creates a KeyStoreConfiguration.
143     *
144     * @param location The location of the KeyStore, a file path, URL or resource.
145     * @param password The password to access the KeyStore.
146     * @param keyStoreType The KeyStore type, null defaults to {@code "JKS"}.
147     * @param keyManagerFactoryAlgorithm The standard name of the requested algorithm. See the Java Secure Socket
148     * Extension Reference Guide for information about these names.
149     * @return a new KeyStoreConfiguration
150     * @throws StoreConfigurationException Thrown if this call cannot load the KeyStore.
151     * @deprecated Use createKeyStoreConfiguration(String, char[], String, String)
152     */
153    @Deprecated
154    public static KeyStoreConfiguration createKeyStoreConfiguration(
155            // @formatter:off
156            final String location,
157            final String password,
158            final String keyStoreType,
159            final String keyManagerFactoryAlgorithm) throws StoreConfigurationException {
160            // @formatter:on
161        return createKeyStoreConfiguration(location,
162                (password == null ? null : password.toCharArray()),
163                keyStoreType,
164                keyManagerFactoryAlgorithm);
165    }
166
167    public KeyManagerFactory initKeyManagerFactory() throws NoSuchAlgorithmException, UnrecoverableKeyException,
168            KeyStoreException {
169        final KeyManagerFactory kmFactory = KeyManagerFactory.getInstance(this.keyManagerFactoryAlgorithm);
170        final char[] password = this.getPasswordAsCharArray();
171        try {
172            kmFactory.init(this.getKeyStore(), password);
173        } finally {
174            if (password != null) {
175                Arrays.fill(password, '\0');
176            }
177        }
178        return kmFactory;
179    }
180
181    @Override
182    public int hashCode() {
183        final int prime = 31;
184        int result = super.hashCode();
185        result = prime * result + ((keyManagerFactoryAlgorithm == null) ? 0 : keyManagerFactoryAlgorithm.hashCode());
186        return result;
187    }
188
189    @Override
190    public boolean equals(final Object obj) {
191        if (this == obj) {
192            return true;
193        }
194        if (!super.equals(obj)) {
195            return false;
196        }
197        if (getClass() != obj.getClass()) {
198            return false;
199        }
200        final KeyStoreConfiguration other = (KeyStoreConfiguration) obj;
201        if (!Objects.equals(keyManagerFactoryAlgorithm, other.keyManagerFactoryAlgorithm)) {
202            return false;
203        }
204        return true;
205    }
206
207    public String getKeyManagerFactoryAlgorithm() {
208        return keyManagerFactoryAlgorithm;
209    }
210}