    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    }