All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.hadoop.security.ssl.DelegatingSSLSocketFactory Maven / Gradle / Ivy

There is a newer version: 3.4.0
Show newest version
/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.hadoop.security.ssl;

import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.logging.Level;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

import org.apache.hadoop.thirdparty.com.google.common.annotations.VisibleForTesting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A {@link SSLSocketFactory} that can delegate to various SSL implementations.
 * Specifically, either OpenSSL or JSSE can be used. OpenSSL offers better
 * performance than JSSE and is made available via the
 * wildlfy-openssl
 * library.
 *
 * 

* The factory has several different modes of operation: *

    *
  • OpenSSL: Uses the wildly-openssl library to delegate to the * system installed OpenSSL. If the wildfly-openssl integration is not * properly setup, an exception is thrown.
  • *
  • Default: Attempts to use the OpenSSL mode, if it cannot load the * necessary libraries, it falls back to the Default_JSEE mode.
  • *
  • Default_JSSE: Delegates to the JSSE implementation of SSL, but * it disables the GCM cipher when running on Java 8.
  • *
  • Default_JSSE_with_GCM: Delegates to the JSSE implementation of * SSL with no modification to the list of enabled ciphers.
  • *
*

* * In order to load OpenSSL, applications must ensure the wildfly-openssl * artifact is on the classpath. Currently, only ABFS declares * wildfly-openssl as an explicit dependency. */ public final class DelegatingSSLSocketFactory extends SSLSocketFactory { /** * Default indicates Ordered, preferred OpenSSL, if failed to load then fall * back to Default_JSSE. * *

* Default_JSSE is not truly the the default JSSE implementation because * the GCM cipher is disabled when running on Java 8. However, the name * was not changed in order to preserve backwards compatibility. Instead, * a new mode called Default_JSSE_with_GCM delegates to the default JSSE * implementation with no changes to the list of enabled ciphers. *

*/ public enum SSLChannelMode { OpenSSL, Default, Default_JSSE, Default_JSSE_with_GCM } private static DelegatingSSLSocketFactory instance = null; private static final Logger LOG = LoggerFactory.getLogger( DelegatingSSLSocketFactory.class); private String providerName; private SSLContext ctx; private String[] ciphers; private SSLChannelMode channelMode; // This should only be modified within the #initializeDefaultFactory // method which is synchronized private boolean openSSLProviderRegistered; /** * Initialize a singleton SSL socket factory. * * @param preferredMode applicable only if the instance is not initialized. * @throws IOException if an error occurs. */ public static synchronized void initializeDefaultFactory( SSLChannelMode preferredMode) throws IOException { if (instance == null) { instance = new DelegatingSSLSocketFactory(preferredMode); } } /** * For testing only: reset the socket factory. */ @VisibleForTesting public static synchronized void resetDefaultFactory() { LOG.info("Resetting default SSL Socket Factory"); instance = null; } /** * Singleton instance of the SSLSocketFactory. * * SSLSocketFactory must be initialized with appropriate SSLChannelMode * using initializeDefaultFactory method. * * @return instance of the SSLSocketFactory, instance must be initialized by * initializeDefaultFactory. */ public static DelegatingSSLSocketFactory getDefaultFactory() { return instance; } private DelegatingSSLSocketFactory(SSLChannelMode preferredChannelMode) throws IOException { try { initializeSSLContext(preferredChannelMode); } catch (NoSuchAlgorithmException | KeyManagementException e) { throw new IOException(e); } // Get list of supported cipher suits from the SSL factory. SSLSocketFactory factory = ctx.getSocketFactory(); String[] defaultCiphers = factory.getSupportedCipherSuites(); String version = System.getProperty("java.version"); ciphers = (channelMode == SSLChannelMode.Default_JSSE && version.startsWith("1.8")) ? alterCipherList(defaultCiphers) : defaultCiphers; providerName = ctx.getProvider().getName() + "-" + ctx.getProvider().getVersion(); } private void initializeSSLContext(SSLChannelMode preferredChannelMode) throws NoSuchAlgorithmException, KeyManagementException, IOException { LOG.debug("Initializing SSL Context to channel mode {}", preferredChannelMode); switch (preferredChannelMode) { case Default: try { bindToOpenSSLProvider(); channelMode = SSLChannelMode.OpenSSL; } catch (LinkageError | NoSuchAlgorithmException | RuntimeException e) { LOG.debug("Failed to load OpenSSL. Falling back to the JSSE default.", e); ctx = SSLContext.getDefault(); channelMode = SSLChannelMode.Default_JSSE; } break; case OpenSSL: bindToOpenSSLProvider(); channelMode = SSLChannelMode.OpenSSL; break; case Default_JSSE: ctx = SSLContext.getDefault(); channelMode = SSLChannelMode.Default_JSSE; break; case Default_JSSE_with_GCM: ctx = SSLContext.getDefault(); channelMode = SSLChannelMode.Default_JSSE_with_GCM; break; default: throw new IOException("Unknown channel mode: " + preferredChannelMode); } } /** * Bind to the OpenSSL provider via wildfly. * This MUST be the only place where wildfly classes are referenced, * so ensuring that any linkage problems only surface here where they may * be caught by the initialization code. */ private void bindToOpenSSLProvider() throws NoSuchAlgorithmException, KeyManagementException { if (!openSSLProviderRegistered) { LOG.debug("Attempting to register OpenSSL provider"); org.wildfly.openssl.OpenSSLProvider.register(); openSSLProviderRegistered = true; } // Strong reference needs to be kept to logger until initialization of // SSLContext finished (see HADOOP-16174): java.util.logging.Logger logger = java.util.logging.Logger.getLogger( "org.wildfly.openssl.SSL"); Level originalLevel = logger.getLevel(); try { logger.setLevel(Level.WARNING); ctx = SSLContext.getInstance("openssl.TLS"); ctx.init(null, null, null); } finally { logger.setLevel(originalLevel); } } public String getProviderName() { return providerName; } @Override public String[] getDefaultCipherSuites() { return ciphers.clone(); } @Override public String[] getSupportedCipherSuites() { return ciphers.clone(); } /** * Get the channel mode of this instance. * @return a channel mode. */ public SSLChannelMode getChannelMode() { return channelMode; } public Socket createSocket() throws IOException { SSLSocketFactory factory = ctx.getSocketFactory(); return configureSocket(factory.createSocket()); } @Override public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { SSLSocketFactory factory = ctx.getSocketFactory(); return configureSocket( factory.createSocket(s, host, port, autoClose)); } @Override public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { SSLSocketFactory factory = ctx.getSocketFactory(); return configureSocket(factory .createSocket(address, port, localAddress, localPort)); } @Override public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException { SSLSocketFactory factory = ctx.getSocketFactory(); return configureSocket(factory .createSocket(host, port, localHost, localPort)); } @Override public Socket createSocket(InetAddress host, int port) throws IOException { SSLSocketFactory factory = ctx.getSocketFactory(); return configureSocket(factory.createSocket(host, port)); } @Override public Socket createSocket(String host, int port) throws IOException { SSLSocketFactory factory = ctx.getSocketFactory(); return configureSocket(factory.createSocket(host, port)); } private Socket configureSocket(Socket socket) { ((SSLSocket) socket).setEnabledCipherSuites(ciphers); return socket; } private String[] alterCipherList(String[] defaultCiphers) { ArrayList preferredSuites = new ArrayList<>(); // Remove GCM mode based ciphers from the supported list. for (int i = 0; i < defaultCiphers.length; i++) { if (defaultCiphers[i].contains("_GCM_")) { LOG.debug("Removed Cipher - {} from list of enabled SSLSocket ciphers", defaultCiphers[i]); } else { preferredSuites.add(defaultCiphers[i]); } } ciphers = preferredSuites.toArray(new String[0]); return ciphers; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy