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.KeyManagementException;
020import java.security.KeyStoreException;
021import java.security.NoSuchAlgorithmException;
022import java.security.UnrecoverableKeyException;
023
024import javax.net.ssl.KeyManager;
025import javax.net.ssl.KeyManagerFactory;
026import javax.net.ssl.SSLContext;
027import javax.net.ssl.SSLServerSocketFactory;
028import javax.net.ssl.SSLSocketFactory;
029import javax.net.ssl.TrustManager;
030import javax.net.ssl.TrustManagerFactory;
031
032import org.apache.logging.log4j.core.Core;
033import org.apache.logging.log4j.core.config.plugins.Plugin;
034import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
035import org.apache.logging.log4j.core.config.plugins.PluginElement;
036import org.apache.logging.log4j.core.config.plugins.PluginFactory;
037import org.apache.logging.log4j.status.StatusLogger;
038
039/**
040 *  SSL Configuration
041 */
042@Plugin(name = "Ssl", category = Core.CATEGORY_NAME, printObject = true)
043public class SslConfiguration {
044    private static final StatusLogger LOGGER = StatusLogger.getLogger();
045    private final KeyStoreConfiguration keyStoreConfig;
046    private final TrustStoreConfiguration trustStoreConfig;
047    private final SSLContext sslContext;
048    private final String protocol;
049    private final boolean verifyHostName;
050
051    private SslConfiguration(final String protocol, final KeyStoreConfiguration keyStoreConfig,
052            final TrustStoreConfiguration trustStoreConfig, boolean verifyHostName) {
053        this.keyStoreConfig = keyStoreConfig;
054        this.trustStoreConfig = trustStoreConfig;
055        this.protocol = protocol == null ? SslConfigurationDefaults.PROTOCOL : protocol;
056        this.sslContext = this.createSslContext();
057        this.verifyHostName = verifyHostName;
058    }
059
060    /**
061     * Clears the secret fields in this object but still allow it to operate normally.
062     */
063    public void clearSecrets() {
064        if (this.keyStoreConfig != null) {
065            this.keyStoreConfig.clearSecrets();
066        }
067        if (this.trustStoreConfig != null) {
068            this.trustStoreConfig.clearSecrets();
069        }
070    }
071
072    public SSLSocketFactory getSslSocketFactory() {
073        return sslContext.getSocketFactory();
074    }
075
076    public SSLServerSocketFactory getSslServerSocketFactory() {
077        return sslContext.getServerSocketFactory();
078    }
079
080    private SSLContext createSslContext() {
081        SSLContext context = null;
082
083        try {
084            context = createSslContextBasedOnConfiguration();
085            LOGGER.debug("Creating SSLContext with the given parameters");
086        }
087        catch (final TrustStoreConfigurationException e) {
088            context = createSslContextWithTrustStoreFailure();
089        }
090        catch (final KeyStoreConfigurationException e) {
091            context = createSslContextWithKeyStoreFailure();
092        }
093        return context;
094    }
095
096    private SSLContext createSslContextWithTrustStoreFailure() {
097        SSLContext context;
098
099        try {
100            context = createSslContextWithDefaultTrustManagerFactory();
101            LOGGER.debug("Creating SSLContext with default truststore");
102        }
103        catch (final KeyStoreConfigurationException e) {
104            context = createDefaultSslContext();
105            LOGGER.debug("Creating SSLContext with default configuration");
106        }
107        return context;
108    }
109
110    private SSLContext createSslContextWithKeyStoreFailure() {
111        SSLContext context;
112
113        try {
114            context = createSslContextWithDefaultKeyManagerFactory();
115            LOGGER.debug("Creating SSLContext with default keystore");
116        }
117        catch (final TrustStoreConfigurationException e) {
118            context = createDefaultSslContext();
119            LOGGER.debug("Creating SSLContext with default configuration");
120        }
121        return context;
122    }
123
124    private SSLContext createSslContextBasedOnConfiguration() throws KeyStoreConfigurationException, TrustStoreConfigurationException {
125        return createSslContext(false, false);
126    }
127
128    private SSLContext createSslContextWithDefaultKeyManagerFactory() throws TrustStoreConfigurationException {
129        try {
130            return createSslContext(true, false);
131        } catch (final KeyStoreConfigurationException dummy) {
132             LOGGER.debug("Exception occured while using default keystore. This should be a BUG");
133             return null;
134        }
135    }
136
137    private SSLContext createSslContextWithDefaultTrustManagerFactory() throws KeyStoreConfigurationException {
138        try {
139            return createSslContext(false, true);
140        }
141        catch (final TrustStoreConfigurationException dummy) {
142            LOGGER.debug("Exception occured while using default truststore. This should be a BUG");
143            return null;
144        }
145    }
146
147    private SSLContext createDefaultSslContext() {
148        try {
149            return SSLContext.getDefault();
150        } catch (final NoSuchAlgorithmException e) {
151            LOGGER.error("Failed to create an SSLContext with default configuration", e);
152            return null;
153        }
154    }
155
156    private SSLContext createSslContext(final boolean loadDefaultKeyManagerFactory, final boolean loadDefaultTrustManagerFactory)
157            throws KeyStoreConfigurationException, TrustStoreConfigurationException {
158        try {
159            KeyManager[] kManagers = null;
160            TrustManager[] tManagers = null;
161
162            final SSLContext newSslContext = SSLContext.getInstance(this.protocol);
163            if (!loadDefaultKeyManagerFactory) {
164                final KeyManagerFactory kmFactory = loadKeyManagerFactory();
165                kManagers = kmFactory.getKeyManagers();
166            }
167            if (!loadDefaultTrustManagerFactory) {
168                final TrustManagerFactory tmFactory = loadTrustManagerFactory();
169                tManagers = tmFactory.getTrustManagers();
170            }
171
172            newSslContext.init(kManagers, tManagers, null);
173            return newSslContext;
174        }
175        catch (final NoSuchAlgorithmException e) {
176            LOGGER.error("No Provider supports a TrustManagerFactorySpi implementation for the specified protocol", e);
177            throw new TrustStoreConfigurationException(e);
178        }
179        catch (final KeyManagementException e) {
180            LOGGER.error("Failed to initialize the SSLContext", e);
181            throw new KeyStoreConfigurationException(e);
182        }
183    }
184
185    private TrustManagerFactory loadTrustManagerFactory() throws TrustStoreConfigurationException {
186        if (trustStoreConfig == null) {
187            throw new TrustStoreConfigurationException(new Exception("The trustStoreConfiguration is null"));
188        }
189
190        try {
191            return trustStoreConfig.initTrustManagerFactory();
192        }
193        catch (final NoSuchAlgorithmException e) {
194            LOGGER.error("The specified algorithm is not available from the specified provider", e);
195            throw new TrustStoreConfigurationException(e);
196        } catch (final KeyStoreException e) {
197            LOGGER.error("Failed to initialize the TrustManagerFactory", e);
198            throw new TrustStoreConfigurationException(e);
199        }
200    }
201
202    private KeyManagerFactory loadKeyManagerFactory() throws KeyStoreConfigurationException {
203        if (keyStoreConfig == null) {
204            throw new KeyStoreConfigurationException(new Exception("The keyStoreConfiguration is null"));
205        }
206
207        try {
208            return keyStoreConfig.initKeyManagerFactory();
209        }
210        catch (final NoSuchAlgorithmException e) {
211            LOGGER.error("The specified algorithm is not available from the specified provider", e);
212            throw new KeyStoreConfigurationException(e);
213        } catch (final KeyStoreException e) {
214            LOGGER.error("Failed to initialize the TrustManagerFactory", e);
215            throw new KeyStoreConfigurationException(e);
216        } catch (final UnrecoverableKeyException e) {
217            LOGGER.error("The key cannot be recovered (e.g. the given password is wrong)", e);
218            throw new KeyStoreConfigurationException(e);
219        }
220    }
221
222    /**
223     * Creates an SslConfiguration from a KeyStoreConfiguration and a TrustStoreConfiguration.
224     *
225     * @param protocol The protocol, see http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#SSLContext
226     * @param keyStoreConfig The KeyStoreConfiguration.
227     * @param trustStoreConfig The TrustStoreConfiguration.
228     * @return a new SslConfiguration
229     */
230    @PluginFactory
231    public static SslConfiguration createSSLConfiguration(
232            // @formatter:off
233            @PluginAttribute("protocol") final String protocol,
234            @PluginElement("KeyStore") final KeyStoreConfiguration keyStoreConfig,
235            @PluginElement("TrustStore") final TrustStoreConfiguration trustStoreConfig) {
236            // @formatter:on
237        return new SslConfiguration(protocol, keyStoreConfig, trustStoreConfig, false);
238    }
239
240    /**
241     * Creates an SslConfiguration from a KeyStoreConfiguration and a TrustStoreConfiguration.
242     *
243     * @param protocol The protocol, see http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#SSLContext
244     * @param keyStoreConfig The KeyStoreConfiguration.
245     * @param trustStoreConfig The TrustStoreConfiguration.
246     * @return a new SslConfiguration
247     * @since 2.12
248     */
249    public static SslConfiguration createSSLConfiguration(
250        // @formatter:off
251        @PluginAttribute("protocol") final String protocol,
252        @PluginElement("KeyStore") final KeyStoreConfiguration keyStoreConfig,
253        @PluginElement("TrustStore") final TrustStoreConfiguration trustStoreConfig,
254        @PluginElement("verifyHostName") final boolean verifyHostName) {
255        // @formatter:on
256        return new SslConfiguration(protocol, keyStoreConfig, trustStoreConfig, verifyHostName);
257    }
258
259    @Override
260    public int hashCode() {
261        final int prime = 31;
262        int result = 1;
263        result = prime * result + ((keyStoreConfig == null) ? 0 : keyStoreConfig.hashCode());
264        result = prime * result + ((protocol == null) ? 0 : protocol.hashCode());
265        result = prime * result + ((sslContext == null) ? 0 : sslContext.hashCode());
266        result = prime * result + ((trustStoreConfig == null) ? 0 : trustStoreConfig.hashCode());
267        return result;
268    }
269
270    @Override
271    public boolean equals(final Object obj) {
272        if (this == obj) {
273            return true;
274        }
275        if (obj == null) {
276            return false;
277        }
278        if (getClass() != obj.getClass()) {
279            return false;
280        }
281        final SslConfiguration other = (SslConfiguration) obj;
282        if (keyStoreConfig == null) {
283            if (other.keyStoreConfig != null) {
284                return false;
285            }
286        } else if (!keyStoreConfig.equals(other.keyStoreConfig)) {
287            return false;
288        }
289        if (protocol == null) {
290            if (other.protocol != null) {
291                return false;
292            }
293        } else if (!protocol.equals(other.protocol)) {
294            return false;
295        }
296        if (sslContext == null) {
297            if (other.sslContext != null) {
298                return false;
299            }
300        } else if (!sslContext.equals(other.sslContext)) {
301            return false;
302        }
303        if (trustStoreConfig == null) {
304            if (other.trustStoreConfig != null) {
305                return false;
306            }
307        } else if (!trustStoreConfig.equals(other.trustStoreConfig)) {
308            return false;
309        }
310        return true;
311    }
312
313    public KeyStoreConfiguration getKeyStoreConfig() {
314        return keyStoreConfig;
315    }
316
317    public TrustStoreConfiguration getTrustStoreConfig() {
318        return trustStoreConfig;
319    }
320
321    public SSLContext getSslContext() {
322        return sslContext;
323    }
324
325    public String getProtocol() {
326        return protocol;
327    }
328
329    public boolean isVerifyHostName() {
330        return verifyHostName;
331    }
332}