Java tutorial
/************************************************************************* * Copyright 2009-2015 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. ************************************************************************/ package com.eucalyptus.auth.euare.identity.ws; import static java.lang.System.getProperty; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLException; import javax.net.ssl.SSLParameters; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier; import com.eucalyptus.auth.euare.common.identity.Identity; import com.eucalyptus.auth.euare.identity.region.RegionConfigurationManager; import com.eucalyptus.auth.euare.identity.region.RegionConfigurations; import com.eucalyptus.component.ComponentIds; import com.eucalyptus.component.annotation.ComponentPart; import com.eucalyptus.crypto.Crypto; import com.eucalyptus.crypto.util.DelegatingX509ExtendedTrustManager; import com.eucalyptus.crypto.util.SslUtils; import com.eucalyptus.crypto.util.X509ExtendedTrustManagerSupport; import com.eucalyptus.http.MappingHttpRequest; import com.eucalyptus.util.CollectionUtils; import com.eucalyptus.util.IO; import com.eucalyptus.util.Pair; import com.eucalyptus.ws.Handlers; import com.eucalyptus.ws.IoHandlers; import com.eucalyptus.ws.StackConfiguration; import com.eucalyptus.ws.client.MonitoredSocketChannelInitializer; import com.eucalyptus.ws.handlers.IoMessageWrapperHandler; import com.google.common.base.Predicates; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOutboundHandlerAdapter; import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPromise; import io.netty.channel.socket.SocketChannel; /** * */ @ComponentPart(Identity.class) public class IdentityClientChannelInitializer extends MonitoredSocketChannelInitializer { private static final String PROTOCOL = "TLS"; private static final String PROP_SSL_TRUSTSTORE_PASSWORD = "com.eucalyptus.auth.euare.identity.regionSslTrustStorePassword"; private static final String PROP_SSL_TRUSTSTORE_TYPE = "com.eucalyptus.auth.euare.identity.regionSslTrustStoreType"; private static final String PROP_SSL_TRUSTSTORE_PATH = "com.eucalyptus.auth.euare.identity.regionSslTrustStorePath"; private static final String DEFAULT_TRUSTSTORE_PASSWORD = "changeit"; private static final String DEFAULT_TRUSTSTORE_PATH = "lib/security/cacerts"; private static final AtomicReference<Pair<Long, KeyStore>> sslTrustStore = new AtomicReference<>(); private static final Supplier<Boolean> useDefaultCAs = new Supplier<Boolean>() { @Override public Boolean get() { return RegionConfigurations.isUseDefaultCAs(); } }; private static final Supplier<SSLContext> contextSupplier = Suppliers .memoizeWithExpiration(new Supplier<SSLContext>() { @Override public SSLContext get() { final String trustStorePath = getProperty(PROP_SSL_TRUSTSTORE_PATH, DEFAULT_TRUSTSTORE_PATH); try { final List<TrustManager> trustManagers = Lists.newArrayList(); trustManagers.add(regionTrustManager); final File trustStore = new File(System.getProperty("java.home"), trustStorePath); if (trustStore.isFile()) { trustManagers.add(buildDelegatingTrustManager( trustManagersForStore(getTrustStore(trustStore)), useDefaultCAs)); } final SSLContext clientContext = SSLContext.getInstance(PROTOCOL); clientContext.init(null, new TrustManager[] { buildDelegatingTrustManager(trustManagers, Suppliers.ofInstance(true)) }, Crypto.getSecureRandomSupplier().get()); return clientContext; } catch (Exception e) { throw new Error("Failed to initialize the client-side SSLContext", e); } } }, 15, TimeUnit.MINUTES); private static final X509TrustManager regionTrustManager = new RegionTrustManager(); private static TrustManager buildDelegatingTrustManager(final List<TrustManager> trustManagers, final Supplier<Boolean> enable) { return new DelegatingX509ExtendedTrustManager(Iterables.transform( Iterables.filter(trustManagers, Predicates.instanceOf(javax.net.ssl.X509ExtendedTrustManager.class)), CollectionUtils.cast(javax.net.ssl.X509ExtendedTrustManager.class)), enable); } @Override protected void initChannel(final SocketChannel socketChannel) throws Exception { super.initChannel(socketChannel); final ChannelPipeline pipeline = socketChannel.pipeline(); pipeline.addLast("decoder", IoHandlers.httpResponseDecoder()); pipeline.addLast("aggregator", IoHandlers.newHttpChunkAggregator()); pipeline.addLast("encoder", IoHandlers.httpRequestEncoder()); pipeline.addLast("wrapper", IoHandlers.ioMessageWrappingHandler()); pipeline.addLast("serializer", IoHandlers.soapMarshalling()); pipeline.addLast("wssec", IoHandlers.internalWsSecHandler()); pipeline.addLast("addressing", IoHandlers.addressingHandler()); pipeline.addLast("soap", IoHandlers.soapHandler()); pipeline.addLast("binding", IoHandlers.bindingHandler("www_eucalyptus_com_ns_identity_2016_10_01")); pipeline.addLast("ssl-detection-handler", new IoHandlers.ClientSslHandler("ssl-handler") { @Override protected SSLEngine createSSLEngine(final String peerHost, final int peerPort) { return getSSLEngine(peerHost, peerPort); } }); pipeline.addLast("remote", new RemotePathHandler()); } public static final class RemotePathHandler extends ChannelOutboundHandlerAdapter { @Override public void write(final io.netty.channel.ChannelHandlerContext ctx, final Object msg, final ChannelPromise promise) throws Exception { if (msg instanceof MappingHttpRequest) { final MappingHttpRequest httpMessage = (MappingHttpRequest) msg; httpMessage.setServicePath(ComponentIds.lookup(Identity.class).getServicePath()); String uri = URI.create(httpMessage.getUri()).resolve(httpMessage.getServicePath()).toString(); if (RegionConfigurations.isUseSsl()) { uri = uri.replace("http://", "https://"); } else { uri = uri.replace("https://", "http://"); } httpMessage.setUri(uri); } super.write(ctx, msg, promise); } } public static SSLContext getSSLContext() { return contextSupplier.get(); } public static SSLEngine getSSLEngine(final String peerHost, final int peerPort) { try { final SSLParameters sslParams = new SSLParameters(); if (RegionConfigurations.isVerifyHostnames()) { sslParams.setEndpointIdentificationAlgorithm("HTTPS"); } final SSLContext clientContext = getSSLContext(); final SSLEngine engine = clientContext.createSSLEngine(peerHost, peerPort); engine.setSSLParameters(sslParams); engine.setUseClientMode(true); engine.setEnabledProtocols(SslUtils.getEnabledProtocols(RegionConfigurations.getSslProtocols(), engine.getSupportedProtocols())); engine.setEnabledCipherSuites(SslUtils.getEnabledCipherSuites(RegionConfigurations.getSslCiphers(), engine.getSupportedCipherSuites())); return engine; } catch (Exception e) { throw new Error("Failed to initialize the client-side SSLContext", e); } } public static final class RegionTrustManager extends X509ExtendedTrustManagerSupport { private final RegionConfigurationManager regionConfigurationManager = new RegionConfigurationManager(); @Override public void checkServerTrusted(final X509Certificate[] x509Certificates, final String s, final SSLEngine sslEngine) throws CertificateException { final X509Certificate serverCertificate = x509Certificates[0]; final String hostname = sslEngine.getHandshakeSession().getPeerHost(); final SSLParameters sslParameters = sslEngine.getSSLParameters(); if (sslParameters != null && "HTTPS".equals(sslParameters.getEndpointIdentificationAlgorithm())) try { new BrowserCompatHostnameVerifier().verify(hostname, serverCertificate); } catch (SSLException e) { throw new CertificateException("Server cert not valid for host"); } if (!regionConfigurationManager.isRegionSSLCertificate(hostname, serverCertificate)) { throw new CertificateException("Server cert not trusted"); } } } 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_SSL_TRUSTSTORE_TYPE, KeyStore.getDefaultType()); final String trustStorePassword = getProperty(PROP_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()); } }