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