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     */
017    package org.apache.logging.log4j.core.net.ssl;
018    
019    import java.security.KeyManagementException;
020    import java.security.KeyStoreException;
021    import java.security.NoSuchAlgorithmException;
022    import java.security.UnrecoverableKeyException;
023    
024    import javax.net.ssl.KeyManager;
025    import javax.net.ssl.KeyManagerFactory;
026    import javax.net.ssl.SSLContext;
027    import javax.net.ssl.SSLServerSocketFactory;
028    import javax.net.ssl.SSLSocketFactory;
029    import javax.net.ssl.TrustManager;
030    import javax.net.ssl.TrustManagerFactory;
031    
032    import org.apache.logging.log4j.core.config.plugins.Plugin;
033    import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
034    import org.apache.logging.log4j.core.config.plugins.PluginElement;
035    import org.apache.logging.log4j.core.config.plugins.PluginFactory;
036    import org.apache.logging.log4j.status.StatusLogger;
037    
038    /**
039     *  SSL Configuration
040     */
041    @Plugin(name = "Ssl", category = "Core", printObject = true)
042    public 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    }