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