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