/*************************************************************************
* Copyright 2009-2014 Eucalyptus Systems, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*
* Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta
* CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need
* additional information or have any questions.
*
* This file may incorporate work covered under the following copyright
* and permission notice:
*
* Software License Agreement (BSD License)
*
* Copyright (c) 2008, Regents of the University of California
* All rights reserved.
*
* Redistribution and use of this software in source and binary forms,
* with or without modification, are permitted provided that the
* following conditions are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE. USERS OF THIS SOFTWARE ACKNOWLEDGE
* THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE LICENSED MATERIAL,
* COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS SOFTWARE,
* AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING
* IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA,
* SANTA BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY,
* WHICH IN THE REGENTS' DISCRETION MAY INCLUDE, WITHOUT LIMITATION,
* REPLACEMENT OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO
* IDENTIFIED, OR WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT
* NEEDED TO COMPLY WITH ANY SUCH LICENSES OR RIGHTS.
************************************************************************/
package com.eucalyptus.crypto.util;
import static java.lang.System.getProperty;
import static com.eucalyptus.crypto.util.SslUtils.getEnabledCipherSuites;
import static com.google.common.base.Throwables.propagate;
import static com.google.common.collect.Iterables.toArray;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.net.URLConnection;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactorySpi;
import javax.net.ssl.ManagerFactoryParameters;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.TrustManagerFactorySpi;
import javax.net.ssl.X509ExtendedKeyManager;
import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier;
import org.apache.log4j.Logger;
import com.eucalyptus.component.ComponentIds;
import com.eucalyptus.component.auth.SystemCredentials;
import com.eucalyptus.component.id.Eucalyptus;
import com.eucalyptus.configurable.ConfigurableClass;
import com.eucalyptus.configurable.ConfigurableField;
import com.eucalyptus.configurable.ConfigurableFieldType;
import com.eucalyptus.configurable.ConfigurableProperty;
import com.eucalyptus.configurable.ConfigurablePropertyException;
import com.eucalyptus.configurable.PropertyChangeListener;
import com.eucalyptus.configurable.PropertyChangeListeners;
import com.eucalyptus.crypto.Crypto;
import com.eucalyptus.system.SubDirectory;
import com.eucalyptus.util.CollectionUtils;
import com.eucalyptus.util.IO;
import com.eucalyptus.util.Pair;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.base.Predicates;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.ObjectArrays;
import com.sun.net.ssl.internal.ssl.X509ExtendedTrustManager;
@ConfigurableClass( root = "bootstrap.webservices.ssl",
description = "Parameters controlling the SSL configuration for the web services endpoint." )
public class SslSetup {
private static final Logger LOG = Logger.getLogger( SslSetup.class );
private static final String PROTOCOL = "TLS";
private static final String DEFAULT_SERVER_CIPHERS =
"RSA:DSS:ECDSA:+3DES:TLS_EMPTY_RENEGOTIATION_INFO_SCSV:!NULL:!EXPORT:!EXPORT1024:!MD5:!DES:!RC4:!ECDHE";
private static final String DEFAULT_SERVER_PROTOCOLS = "SSLv2Hello,TLSv1,TLSv1.1,TLSv1.2";
private static final String DEFAULT_USER_CIPHERS = DEFAULT_SERVER_CIPHERS;
private static final String DEFAULT_USER_PROTOCOLS = DEFAULT_SERVER_PROTOCOLS;
private static SSLContext SERVER_CONTEXT = null;
private static String[] SERVER_SUPPORTED_CIPHERS = null;
private static SSLContext CLIENT_CONTEXT = null;
@ConfigurableField( description = "Alias of the certificate entry in euca.p12 to use for SSL for webservices.",
changeListener = SslCertChangeListener.class,
initial = "eucalyptus" )
public static String SERVER_ALIAS = ComponentIds.lookup( Eucalyptus.class ).name( );
@ConfigurableField( description = "Password of the private key corresponding to the specified certificate for SSL for webservices.",
type = ConfigurableFieldType.KEYVALUEHIDDEN,
changeListener = SslPasswordChangeListener.class )
public static String SERVER_PASSWORD = ComponentIds.lookup( Eucalyptus.class ).name( );
@ConfigurableField( description = "SSL ciphers for webservices.", initial = DEFAULT_SERVER_CIPHERS )
public static String SERVER_SSL_CIPHERS = DEFAULT_SERVER_CIPHERS;
@ConfigurableField( description = "SSL protocols for webservices.", initial = DEFAULT_SERVER_PROTOCOLS )
public static String SERVER_SSL_PROTOCOLS = DEFAULT_SERVER_PROTOCOLS;
@ConfigurableField( description = "Client HTTPS enabled", initial = "true" )
public static Boolean CLIENT_HTTPS_ENABLED = true;
@ConfigurableField( description = "Client HTTPS verify server certificate enabled", initial = "true" )
public static Boolean CLIENT_HTTPS_SERVER_CERT_VERIFY = true;
@ConfigurableField( description = "Client HTTPS ciphers for internal use", initial = "RSA+AES:+SHA:!EXPORT:!EXPORT1025:!MD5:!ECDHE" )
public static String CLIENT_SSL_CIPHERS = "RSA+AES:+SHA:!EXPORT:!EXPORT1025:!MD5:!ECDHE";
@ConfigurableField( description = "Client HTTPS protocols for internal use", initial = "TLSv1.2" )
public static String CLIENT_SSL_PROTOCOLS = "TLSv1.2";
@ConfigurableField( description = "Use default CAs with SSL for external use.",
changeListener = PropertyChangeListeners.IsBoolean.class,
initial = "true" )
public static Boolean USER_SSL_DEFAULT_CAS = true;
@ConfigurableField( description = "SSL ciphers for external use.", initial = DEFAULT_USER_CIPHERS )
public static String USER_SSL_CIPHERS = DEFAULT_USER_CIPHERS;
@ConfigurableField( description = "SSL protocols for external use.", initial = DEFAULT_USER_PROTOCOLS )
public static String USER_SSL_PROTOCOLS = DEFAULT_USER_PROTOCOLS;
@ConfigurableField( description = "SSL hostname validation for external use.",
changeListener = PropertyChangeListeners.IsBoolean.class,
initial = "true" )
public static Boolean USER_SSL_ENABLE_HOSTNAME_VERIFICATION = true;
public static class SslCertChangeListener implements PropertyChangeListener<String> {
@Override
public void fireChange( ConfigurableProperty t, String newValue ) throws ConfigurablePropertyException {
if ( SERVER_ALIAS != null && !SERVER_ALIAS.equals( newValue ) ) {
try {
String oldValue = SERVER_ALIAS;
SSLContext newContext = createServerContext( );
SERVER_ALIAS = newValue;
SERVER_CONTEXT = newContext;
SERVER_SUPPORTED_CIPHERS = SERVER_CONTEXT.createSSLEngine( ).getSupportedCipherSuites( );
} catch ( Exception ex ) {
throw new ConfigurablePropertyException( ex );
}
}
}
}
public static class SslPasswordChangeListener implements PropertyChangeListener<String> {
@Override
public void fireChange( ConfigurableProperty t, String newValue ) throws ConfigurablePropertyException {
if ( SERVER_PASSWORD != null && !SERVER_PASSWORD.equals( newValue ) ) {
try {
String oldValue = SERVER_PASSWORD;
SSLContext newContext = createServerContext( );
SERVER_PASSWORD = newValue;
SERVER_CONTEXT = newContext;
SERVER_SUPPORTED_CIPHERS = SERVER_CONTEXT.createSSLEngine( ).getSupportedCipherSuites( );
} catch ( Exception ex ) {
throw new ConfigurablePropertyException( ex );
}
}
}
}
static {
BCSslSetup.initBouncyCastleDHParams();
SSLContext serverContext;
SSLContext clientContext;
System.setProperty( "javax.net.ssl.trustStore", SubDirectory.KEYS.toString( ) + File.separator + "euca.p12" );
System.setProperty( "javax.net.ssl.keyStore", SubDirectory.KEYS.toString( ) + File.separator + "euca.p12" );
System.setProperty( "javax.net.ssl.trustStoreType", "PKCS12" );
System.setProperty( "javax.net.ssl.keyStoreType", "PKCS12" );
System.setProperty( "javax.net.ssl.trustStorePassword", ComponentIds.lookup( Eucalyptus.class ).name( ) );
System.setProperty( "javax.net.ssl.keyStorePassword", ComponentIds.lookup( Eucalyptus.class ).name( ) );
// System.setProperty( "javax.net.debug", "ssl" );//set this to "ssl" for debugging.
try {
serverContext = createServerContext( );
} catch ( Exception e ) {
LOG.debug( e, e );
throw new Error( "Failed to initialize the server-side SSLContext", e );
}
try {
clientContext = SSLContext.getInstance( PROTOCOL );
clientContext.init( SslSetup.ClientKeyManager.getKeyManagers( ), SslSetup.ClientTrustManager.getTrustManagers( ), null );
} catch ( Exception e ) {
LOG.debug( e, e );
throw new Error( "Failed to initialize the client-side SSLContext", e );
}
SERVER_CONTEXT = serverContext;
SERVER_SUPPORTED_CIPHERS = SERVER_CONTEXT.createSSLEngine( ).getSupportedCipherSuites( );
CLIENT_CONTEXT = clientContext;
}
private static SSLContext createServerContext( ) throws NoSuchAlgorithmException, KeyManagementException {
SSLContext serverContext;
serverContext = SSLContext.getInstance( PROTOCOL );
serverContext.init( SslSetup.ServerKeyManager.getKeyManagers( ), SslSetup.ServerTrustManager.getTrustManagers( ), null );
return serverContext;
}
public static SSLContext getServerContext( ) {
return SERVER_CONTEXT;
}
public static SSLEngine getServerEngine( ) {//TODO:GRZE: @Configurability
final SSLEngine engine = getServerContext( ).createSSLEngine( );
engine.setUseClientMode( false );
engine.setWantClientAuth( false );
engine.setNeedClientAuth( false );
engine.setEnabledProtocols( SslUtils.getEnabledProtocols( SERVER_SSL_PROTOCOLS, engine.getSupportedProtocols( ) ) );
engine.setEnabledCipherSuites( SslUtils.getEnabledCipherSuites( SERVER_SSL_CIPHERS, SERVER_SUPPORTED_CIPHERS ) );
return engine;
}
/**
* Configure an unconnected HttpsURLConnection for trust.
*/
public static URLConnection configureHttpsUrlConnection( final URLConnection urlConnection ) {
if ( urlConnection instanceof HttpsURLConnection ) {
final HttpsURLConnection httpsURLConnection = (HttpsURLConnection) urlConnection;
httpsURLConnection.setHostnameVerifier(
Objects.firstNonNull( USER_SSL_ENABLE_HOSTNAME_VERIFICATION, Boolean.TRUE ) ?
new BrowserCompatHostnameVerifier( ) :
new AllowAllHostnameVerifier( ) );
httpsURLConnection.setSSLSocketFactory( UserSSLSocketFactory.get( ) );
}
return urlConnection;
}
public static SSLContext getClientContext( ) {
return CLIENT_CONTEXT;
}
public static SSLEngine getClientEngine( ) {
final SSLEngine engine = getClientContext( ).createSSLEngine( );
engine.setUseClientMode( true );
engine.setEnabledProtocols( SslUtils.getEnabledProtocols( CLIENT_SSL_PROTOCOLS, engine.getSupportedProtocols( ) ) );
engine.setEnabledCipherSuites( SslUtils.getEnabledCipherSuites( CLIENT_SSL_CIPHERS, SERVER_SUPPORTED_CIPHERS ) );
return engine;
}
static class ClientKeyManager extends KeyManagerFactorySpi {
private static KeyManager singleton = new ClientPKCS12KeyManager( );
public static KeyManager[] getKeyManagers( ) {
return new KeyManager[] { singleton };
}
@Override
protected KeyManager[] engineGetKeyManagers( ) {
return new KeyManager[] { singleton };
}
@Override
protected void engineInit( ManagerFactoryParameters spec ) throws InvalidAlgorithmParameterException {}
@Override
protected void engineInit( KeyStore ks, char[] password ) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {}
static class ClientPKCS12KeyManager extends X509ExtendedKeyManager {
@Override
public String chooseClientAlias( String[] arg0, Principal[] arg1, Socket arg2 ) {
return ComponentIds.lookup( Eucalyptus.class ).name( );
}
@Override
public String chooseServerAlias( String arg0, Principal[] arg1, Socket arg2 ) {
return ComponentIds.lookup( Eucalyptus.class ).name( );
}
@Override
public X509Certificate[] getCertificateChain( String arg0 ) {
if ( ComponentIds.lookup( Eucalyptus.class ).name( ).equals( arg0 ) ) {
return trustedCerts;
} else {
return null;
}
}
@Override
public String[] getClientAliases( String arg0, Principal[] arg1 ) {
return new String[] { ComponentIds.lookup( Eucalyptus.class ).name( ) };
}
@Override
public PrivateKey getPrivateKey( String arg0 ) {
if ( ComponentIds.lookup( Eucalyptus.class ).name( ).equals( arg0 ) ) {
return trustedKey;
} else {
return null;
}
}
@Override
public String[] getServerAliases( String arg0, Principal[] arg1 ) {
return new String[] { ComponentIds.lookup( Eucalyptus.class ).name( ) };
}
@Override
public String chooseEngineClientAlias( String[] keyType, Principal[] issuers, SSLEngine engine ) {
return ComponentIds.lookup( Eucalyptus.class ).name( );
}
@Override
public String chooseEngineServerAlias( String keyType, Principal[] issuers, SSLEngine engine ) {
return ComponentIds.lookup( Eucalyptus.class ).name( );
}
}
}
static class ServerKeyManager extends KeyManagerFactorySpi {
private static KeyManager singleton = new ServerPKCS12KeyManager( );
public static KeyManager[] getKeyManagers( ) {
return new KeyManager[] { singleton };
}
@Override
protected KeyManager[] engineGetKeyManagers( ) {
return new KeyManager[] { singleton };
}
@Override
protected void engineInit( ManagerFactoryParameters spec ) throws InvalidAlgorithmParameterException {}
@Override
protected void engineInit( KeyStore ks, char[] password ) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {}
static class ServerPKCS12KeyManager extends X509ExtendedKeyManager {
@Override
public String chooseClientAlias( String[] arg0, Principal[] arg1, Socket arg2 ) {
return SslSetup.SERVER_ALIAS;
}
@Override
public String chooseServerAlias( String arg0, Principal[] arg1, Socket arg2 ) {
return SslSetup.SERVER_ALIAS;
}
@Override
public X509Certificate[] getCertificateChain( String arg0 ) {
if ( SslSetup.SERVER_ALIAS.equals( arg0 ) ) {
return memoizedServerCertSupplier.get( );
} else {
return null;
}
}
@Override
public String[] getClientAliases( String arg0, Principal[] arg1 ) {
return new String[] { SslSetup.SERVER_ALIAS };
}
@Override
public PrivateKey getPrivateKey( String arg0 ) {
if ( SslSetup.SERVER_ALIAS.equals( arg0 ) ) {
return memoizedPrivateKeySupplier.get( );
} else {
return null;
}
}
@Override
public String[] getServerAliases( String arg0, Principal[] arg1 ) {
return new String[] { SslSetup.SERVER_ALIAS };
}
@Override
public String chooseEngineClientAlias( String[] keyType, Principal[] issuers, SSLEngine engine ) {
return SslSetup.SERVER_ALIAS;
}
@Override
public String chooseEngineServerAlias( String keyType, Principal[] issuers, SSLEngine engine ) {
return SslSetup.SERVER_ALIAS;
}
}
}
private static final Supplier<PrivateKey> serverPrivateKeySupplier = new Supplier<PrivateKey>( ) {
@Override
public PrivateKey get( ) {
try {
return SystemCredentials.getKeyStore( ).getKeyPair(
SslSetup.SERVER_ALIAS,
SslSetup.SERVER_ALIAS ).getPrivate( );
} catch ( GeneralSecurityException ex ) {
LOG.error( ex, ex );
return null;
}
}
};
private static final Supplier<X509Certificate[]> serverCertSupplier = new Supplier<X509Certificate[]>( ) {
@Override
public X509Certificate[] get( ) {
try {
return SystemCredentials.getKeyStore( ).getCertificateChain(
SslSetup.SERVER_ALIAS ).toArray( new X509Certificate[0] );
} catch ( GeneralSecurityException ex ) {
LOG.error( ex, ex );
// Return an empty array
return ObjectArrays.newArray( X509Certificate.class, 1);
}
}
};
private static Supplier<PrivateKey> memoizedPrivateKeySupplier = Suppliers.memoizeWithExpiration( serverPrivateKeySupplier, 15l, TimeUnit.SECONDS );
private static Supplier<X509Certificate[]> memoizedServerCertSupplier = Suppliers.memoizeWithExpiration( serverCertSupplier, 15l, TimeUnit.SECONDS );
private static PrivateKey trustedKey = getTrustedKey( );
private static X509Certificate[] trustedCerts = getTrustedCertificates( );
private static X509Certificate[] getTrustedCertificates( ) {
try {
synchronized ( SslSetup.class ) {
if ( trustedCerts == null ) {
trustedCerts = SystemCredentials.getKeyStore( ).getAllCertificates( ).toArray( new X509Certificate[0] );
}
return trustedCerts;
}
} catch ( Exception e ) {
LOG.error( e, e );
throw new RuntimeException( e );
}
}
private static PrivateKey getTrustedKey( ) {
try {
synchronized ( SslSetup.class ) {
if ( trustedKey == null ) {
trustedKey = SystemCredentials.getKeyStore( ).getKeyPair(
ComponentIds.lookup( Eucalyptus.class ).name( ),
ComponentIds.lookup( Eucalyptus.class ).name( ) ).getPrivate( );
}
return trustedKey;
}
} catch ( Exception e ) {
LOG.error( e, e );
throw new RuntimeException( e );
}
}
public static class ClientTrustManager extends TrustManagerFactorySpi {
private static final TrustManager singleton = new SimpleX509TrustManager( );
public static TrustManager[] getTrustManagers( ) {
return new TrustManager[] { singleton };
}
@Override
protected TrustManager[] engineGetTrustManagers( ) {
return getTrustManagers( );
}
@Override
protected void engineInit( KeyStore keystore ) throws KeyStoreException {}
@Override
protected void engineInit( ManagerFactoryParameters managerFactoryParameters ) throws InvalidAlgorithmParameterException {}
public static class SimpleX509TrustManager extends X509ExtendedTrustManager {
private static void verifyServerCert( final X509Certificate[] chain ) throws CertificateException {
final X509Certificate[ ] expected = memoizedServerCertSupplier.get( );
if ( MoreObjects.firstNonNull( CLIENT_HTTPS_SERVER_CERT_VERIFY, true ) &&
( chain.length < 1 || expected.length < 1 || !expected[0].equals( chain[0] ) ) ) {
throw new CertificateException( "Server certificate not trusted" );
}
}
@Override
public void checkClientTrusted( X509Certificate[] arg0, String arg1 ) throws CertificateException {
throw new CertificateException("Client certificate not trusted");
}
@Override
public void checkServerTrusted( X509Certificate[] chain, String authType ) throws CertificateException {
verifyServerCert( chain );
}
@Override
public X509Certificate[] getAcceptedIssuers( ) {
return new X509Certificate[0];
}
@Override
public void checkClientTrusted( X509Certificate[] arg0, String arg1, String arg2, String arg3 ) throws CertificateException {
throw new CertificateException("Client certificate not trusted");
}
@Override
public void checkServerTrusted( X509Certificate[] chain, String arg1, String arg2, String arg3 ) throws CertificateException {
verifyServerCert( chain );
}
}
}
static class ServerTrustManager extends TrustManagerFactorySpi {
private static final TrustManager singleton = new SimpleX509TrustManager( );
public static TrustManager[] getTrustManagers( ) {
return new TrustManager[] { singleton };
}
@Override
protected TrustManager[] engineGetTrustManagers( ) {
return getTrustManagers( );
}
@Override
protected void engineInit( KeyStore keystore ) throws KeyStoreException {}
@Override
protected void engineInit( ManagerFactoryParameters managerFactoryParameters ) throws InvalidAlgorithmParameterException {}
public static class SimpleX509TrustManager extends X509ExtendedTrustManager {
@Override
public void checkClientTrusted( X509Certificate[] arg0, String arg1 ) throws CertificateException {}
@Override
public void checkServerTrusted( X509Certificate[] chain, String authType ) throws CertificateException {}
@Override
public X509Certificate[] getAcceptedIssuers( ) {
return serverCertSupplier.get( );
}
@Override
public void checkClientTrusted( X509Certificate[] arg0, String arg1, String arg2, String arg3 ) throws CertificateException {}
@Override
public void checkServerTrusted( X509Certificate[] arg0, String arg1, String arg2, String arg3 ) throws CertificateException {}
}
}
public static class UserSSLSocketFactory extends SSLSocketFactoryWrapper {
private static final String PROP_USER_SSL_PROVIDER = "com.eucalyptus.crypto.util.userSslProvider";
private static final String PROP_USER_SSL_PROTOCOL = "com.eucalyptus.crypto.util.userSslProtocol";
private static final String PROP_USER_SSL_TRUSTSTORE_PASSWORD = "com.eucalyptus.crypto.util.userSslTrustStorePassword";
private static final String PROP_USER_SSL_TRUSTSTORE_TYPE = "com.eucalyptus.crypto.util.userSslTrustStoreType";
private static final String PROP_USER_SSL_TRUSTSTORE_PATH = "com.eucalyptus.crypto.util.userSslTrustStorePath";
private static final String DEFAULT_PROTOCOL = "TLS";
private static final String DEFAULT_TRUSTSTORE_PASSWORD = "changeit";
private static final String DEFAULT_TRUSTSTORE_PATH = "lib/security/cacerts";
private final List<String> cipherSuites;
private final static AtomicReference<Pair<Long,KeyStore>> sslTrustStore = new AtomicReference<>( );
private static final Supplier<UserSSLSocketFactory> supplier = new Supplier<UserSSLSocketFactory>( ){
@Override
public UserSSLSocketFactory get( ) {
return new UserSSLSocketFactory( );
}
};
private static final Supplier<UserSSLSocketFactory> memoizedSupplier =
Suppliers.memoizeWithExpiration( supplier, 60l, TimeUnit.SECONDS );
public static UserSSLSocketFactory get( ) {
return memoizedSupplier.get( );
}
public UserSSLSocketFactory( ) {
super( Suppliers.ofInstance( buildDelegate( ) ) );
this.cipherSuites =
ImmutableList.copyOf( getEnabledCipherSuites( USER_SSL_CIPHERS, getSupportedCipherSuites( ) ) );
}
@Override
protected Socket notifyCreated(final Socket socket) {
if ( socket instanceof SSLSocket ) {
final SSLSocket sslSocket = (SSLSocket) socket;
sslSocket.setEnabledCipherSuites( toArray( cipherSuites, String.class ) );
sslSocket.setEnabledProtocols( SslUtils.getEnabledProtocols( USER_SSL_PROTOCOLS, sslSocket.getSupportedProtocols( ) ) );
}
return socket;
}
private static KeyStore getTrustStore( final File trustStore ) throws GeneralSecurityException, IOException {
final Pair<Long,KeyStore> currentTrustStore = sslTrustStore.get( );
InputStream trustStoreIn = null;
if ( currentTrustStore != null && currentTrustStore.getLeft( ) == trustStore.lastModified( ) ) {
return currentTrustStore.getRight( );
} else try {
final String trustStoreType = getProperty( PROP_USER_SSL_TRUSTSTORE_TYPE, KeyStore.getDefaultType( ) );
final String trustStorePassword = getProperty( PROP_USER_SSL_TRUSTSTORE_PASSWORD, DEFAULT_TRUSTSTORE_PASSWORD );
final KeyStore userTrustStore = KeyStore.getInstance( trustStoreType );
userTrustStore.load( trustStoreIn = new FileInputStream( trustStore ), trustStorePassword.toCharArray() );
sslTrustStore.set( Pair.pair( trustStore.lastModified( ), userTrustStore ) );
return userTrustStore;
} finally {
IO.close( trustStoreIn );
}
}
private static List<TrustManager> trustManagersForStore( final KeyStore trustStore ) throws GeneralSecurityException {
final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance( "PKIX", "SunJSSE" );
trustManagerFactory.init( trustStore );
return Arrays.asList( trustManagerFactory.getTrustManagers( ) );
}
private static SSLSocketFactory buildDelegate( ) {
final String provider = getProperty( PROP_USER_SSL_PROVIDER );
final String protocol = getProperty( PROP_USER_SSL_PROTOCOL, DEFAULT_PROTOCOL );
final String trustStorePath = getProperty( PROP_USER_SSL_TRUSTSTORE_PATH, DEFAULT_TRUSTSTORE_PATH );
final SSLContext sslContext;
try {
sslContext = provider!=null ?
SSLContext.getInstance( protocol, provider ) :
SSLContext.getInstance( protocol );
final List<TrustManager> trustManagers = Lists.newArrayList( );
trustManagers.addAll( trustManagersForStore( ((AbstractKeyStore)SystemCredentials.getKeyStore( )).getKeyStore( ) ) );
final File trustStore = new File( System.getProperty( "java.home" ), trustStorePath );
if ( Objects.firstNonNull( USER_SSL_DEFAULT_CAS, false ) && trustStore.isFile( ) ) {
trustManagers.addAll( trustManagersForStore( getTrustStore( trustStore ) ) );
}
sslContext.init(
null,
new TrustManager[]{ new DelegatingX509ExtendedTrustManager(
Iterables.transform(
Iterables.filter(
trustManagers,
Predicates.instanceOf( javax.net.ssl.X509ExtendedTrustManager.class ) ),
CollectionUtils.cast( javax.net.ssl.X509ExtendedTrustManager.class ) ) ) },
Crypto.getSecureRandomSupplier( ).get( ) );
return sslContext.getSocketFactory( );
} catch ( final GeneralSecurityException | IOException e ) {
throw propagate(e);
}
}
}
}