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.config.plugins.Plugin;
033import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
034import org.apache.logging.log4j.core.config.plugins.PluginElement;
035import org.apache.logging.log4j.core.config.plugins.PluginFactory;
036import org.apache.logging.log4j.status.StatusLogger;
037
038/**
039 *  SSL Configuration
040 */
041@Plugin(name = "Ssl", category = "Core", printObject = true)
042public class SslConfiguration {
043    private static final StatusLogger LOGGER = StatusLogger.getLogger();
044    private final KeyStoreConfiguration keyStoreConfig;
045    private final TrustStoreConfiguration trustStoreConfig;
046    private final SSLContext sslContext;
047    private final String protocol;
048
049    private SslConfiguration(final String protocol, final KeyStoreConfiguration keyStoreConfig,
050            final TrustStoreConfiguration trustStoreConfig) {
051        this.keyStoreConfig = keyStoreConfig;
052        this.trustStoreConfig = trustStoreConfig;
053        this.protocol = protocol == null ? SslConfigurationDefaults.PROTOCOL : protocol;
054        this.sslContext = this.createSslContext();
055    }
056
057    public SSLSocketFactory getSslSocketFactory() {
058        return sslContext.getSocketFactory();
059    }
060
061    public SSLServerSocketFactory getSslServerSocketFactory() {
062        return sslContext.getServerSocketFactory();
063    }
064
065    private SSLContext createSslContext() {
066        SSLContext context = null;
067
068        try {
069            context = createSslContextBasedOnConfiguration();
070            LOGGER.debug("Creating SSLContext with the given parameters");
071        }
072        catch (final TrustStoreConfigurationException e) {
073            context = createSslContextWithTrustStoreFailure();
074        }
075        catch (final KeyStoreConfigurationException e) {
076            context = createSslContextWithKeyStoreFailure();
077        }
078        return context;
079    }
080
081    private SSLContext createSslContextWithTrustStoreFailure() {
082        SSLContext context;
083
084        try {
085            context = createSslContextWithDefaultTrustManagerFactory();
086            LOGGER.debug("Creating SSLContext with default truststore");
087        }
088        catch (final KeyStoreConfigurationException e) {
089            context = createDefaultSslContext();
090            LOGGER.debug("Creating SSLContext with default configuration");
091        }
092        return context;
093    }
094
095    private SSLContext createSslContextWithKeyStoreFailure() {
096        SSLContext context;
097
098        try {
099            context = createSslContextWithDefaultKeyManagerFactory();
100            LOGGER.debug("Creating SSLContext with default keystore");
101        }
102        catch (final TrustStoreConfigurationException e) {
103            context = createDefaultSslContext();
104            LOGGER.debug("Creating SSLContext with default configuration");
105        }
106        return context;
107    }
108
109    private SSLContext createSslContextBasedOnConfiguration() throws KeyStoreConfigurationException, TrustStoreConfigurationException {
110        return createSslContext(false, false);
111    }
112
113    private SSLContext createSslContextWithDefaultKeyManagerFactory() throws TrustStoreConfigurationException {
114        try {
115            return createSslContext(true, false);
116        } catch (final KeyStoreConfigurationException dummy) {
117             LOGGER.debug("Exception occured while using default keystore. This should be a BUG");
118             return null;
119        }
120    }
121
122    private SSLContext createSslContextWithDefaultTrustManagerFactory() throws KeyStoreConfigurationException {
123        try {
124            return createSslContext(false, true);
125        }
126        catch (final TrustStoreConfigurationException dummy) {
127            LOGGER.debug("Exception occured while using default truststore. This should be a BUG");
128            return null;
129        }
130    }
131
132    private SSLContext createDefaultSslContext() {
133        try {
134            return SSLContext.getDefault();
135        } catch (final NoSuchAlgorithmException e) {
136            LOGGER.error("Failed to create an SSLContext with default configuration");
137            return null;
138        }
139    }
140
141    private SSLContext createSslContext(final boolean loadDefaultKeyManagerFactory, final boolean loadDefaultTrustManagerFactory)
142            throws KeyStoreConfigurationException, TrustStoreConfigurationException {
143        try {
144            KeyManager[] kManagers = null;
145            TrustManager[] tManagers = null;
146
147            final SSLContext newSslContext = SSLContext.getInstance(this.protocol);
148            if (!loadDefaultKeyManagerFactory) {
149                final KeyManagerFactory kmFactory = loadKeyManagerFactory();
150                kManagers = kmFactory.getKeyManagers();
151            }
152            if (!loadDefaultTrustManagerFactory) {
153                final TrustManagerFactory tmFactory = loadTrustManagerFactory();
154                tManagers = tmFactory.getTrustManagers();
155            }
156
157            newSslContext.init(kManagers, tManagers, null);
158            return newSslContext;
159        }
160        catch (final NoSuchAlgorithmException e) {
161            LOGGER.error("No Provider supports a TrustManagerFactorySpi implementation for the specified protocol");
162            throw new TrustStoreConfigurationException(e);
163        }
164        catch (final KeyManagementException e) {
165            LOGGER.error("Failed to initialize the SSLContext");
166            throw new KeyStoreConfigurationException(e);
167        }
168    }
169
170    private TrustManagerFactory loadTrustManagerFactory() throws TrustStoreConfigurationException {
171        if (trustStoreConfig == null) {
172            throw new TrustStoreConfigurationException(new Exception("The trustStoreConfiguration is null"));
173        }
174
175        try {
176            return trustStoreConfig.initTrustManagerFactory();
177        }
178        catch (final NoSuchAlgorithmException e) {
179            LOGGER.error("The specified algorithm is not available from the specified provider");
180            throw new TrustStoreConfigurationException(e);
181        } catch (final KeyStoreException e) {
182            LOGGER.error("Failed to initialize the TrustManagerFactory");
183            throw new TrustStoreConfigurationException(e);
184        }
185    }
186
187    private KeyManagerFactory loadKeyManagerFactory() throws KeyStoreConfigurationException {
188        if (keyStoreConfig == null) {
189            throw new KeyStoreConfigurationException(new Exception("The keyStoreConfiguration is null"));
190        }
191
192        try {
193            return keyStoreConfig.initKeyManagerFactory();
194        }
195        catch (final NoSuchAlgorithmException e) {
196            LOGGER.error("The specified algorithm is not available from the specified provider");
197            throw new KeyStoreConfigurationException(e);
198        } catch (final KeyStoreException e) {
199            LOGGER.error("Failed to initialize the TrustManagerFactory");
200            throw new KeyStoreConfigurationException(e);
201        } catch (final UnrecoverableKeyException e) {
202            LOGGER.error("The key cannot be recovered (e.g. the given password is wrong)");
203            throw new KeyStoreConfigurationException(e);
204        }
205    }
206
207    public boolean equals(final SslConfiguration config) {
208        if (config == null) {
209            return false;
210        }
211
212        boolean keyStoreEquals = false;
213        boolean trustStoreEquals = false;
214
215        if (keyStoreConfig != null) {
216            keyStoreEquals = keyStoreConfig.equals(config.keyStoreConfig);
217        } else {
218            keyStoreEquals = keyStoreConfig == config.keyStoreConfig;
219        }
220
221        if (trustStoreConfig != null) {
222            trustStoreEquals = trustStoreConfig.equals(config.trustStoreConfig);
223        } else {
224            trustStoreEquals = trustStoreConfig == config.trustStoreConfig;
225        }
226
227        return keyStoreEquals && trustStoreEquals;
228    }
229
230    /**
231     * Creates an SslConfiguration from a KeyStoreConfiguration and a TrustStoreConfiguration.
232     * @param protocol The protocol, see http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#SSLContext
233     * @param keyStoreConfig The KeyStoreConfiguration.
234     * @param trustStoreConfig The TrustStoreConfiguration.
235     * @return a new SslConfiguration
236     */
237    @PluginFactory
238    public static SslConfiguration createSSLConfiguration(
239            // @formatter:off
240            @PluginAttribute("protocol") final String protocol,
241            @PluginElement("KeyStore") final KeyStoreConfiguration keyStoreConfig, 
242            @PluginElement("TrustStore") final TrustStoreConfiguration trustStoreConfig) {
243            // @formatter:on
244        return new SslConfiguration(protocol, keyStoreConfig, trustStoreConfig);
245    }
246}