You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@synapse.apache.org by hi...@apache.org on 2009/07/31 10:47:29 UTC

svn commit: r799546 - in /synapse/trunk/java/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp: HttpCoreNIOSSLSender.java HttpCoreNIOSender.java SSLClientIOEventDispatch.java

Author: hiranya
Date: Fri Jul 31 08:47:29 2009
New Revision: 799546

URL: http://svn.apache.org/viewvc?rev=799546&view=rev
Log:
Implementing support for multiple SSL configurations at HTTPS transport sender. See SYNAPSE-563 for details. This patch introduces an optional transport sender parameter named "customSSLProfiles". A new IOEventDispatch is provided which supports manipulating multiple SSLContext instances. Still a single IOReactor and a threadpool is used by the sender.


Modified:
    synapse/trunk/java/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/HttpCoreNIOSSLSender.java
    synapse/trunk/java/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/HttpCoreNIOSender.java
    synapse/trunk/java/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/SSLClientIOEventDispatch.java

Modified: synapse/trunk/java/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/HttpCoreNIOSSLSender.java
URL: http://svn.apache.org/viewvc/synapse/trunk/java/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/HttpCoreNIOSSLSender.java?rev=799546&r1=799545&r2=799546&view=diff
==============================================================================
--- synapse/trunk/java/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/HttpCoreNIOSSLSender.java (original)
+++ synapse/trunk/java/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/HttpCoreNIOSSLSender.java Fri Jul 31 08:47:29 2009
@@ -33,43 +33,169 @@
 
 import javax.net.ssl.*;
 import javax.xml.namespace.QName;
-import java.security.GeneralSecurityException;
+import java.util.Map;
+import java.util.Iterator;
+import java.util.HashMap;
+import java.io.FileInputStream;
+import java.io.IOException;
 import java.security.KeyStore;
-import java.net.URL;
+import java.security.GeneralSecurityException;
 import java.net.SocketAddress;
 import java.net.InetSocketAddress;
-import java.io.IOException;
-import java.io.FileInputStream;
 
 public class HttpCoreNIOSSLSender extends HttpCoreNIOSender{
 
     private static final Log log = LogFactory.getLog(HttpCoreNIOSSLSender.class);
 
-    protected IOEventDispatch getEventDispatch(
-        NHttpClientHandler handler, SSLContext sslContext,
-        SSLIOSessionHandler sslIOSessionHandler, HttpParams params) {
-        return new SSLClientIOEventDispatch(handler, sslContext, sslIOSessionHandler, params);
+    protected IOEventDispatch getEventDispatch(NHttpClientHandler handler, SSLContext sslContext,
+        SSLIOSessionHandler sslIOSessionHandler, HttpParams params,
+        TransportOutDescription transportOut) throws AxisFault {
+
+        SSLClientIOEventDispatch dispatch = new SSLClientIOEventDispatch(handler, sslContext,
+                sslIOSessionHandler, params);
+        dispatch.setContextMap(getCustomSSLContexts(transportOut));
+        return dispatch;
     }
 
     /**
-     * Create the SSLContext to be used by this listener
+     * Create the SSLContext to be used by this sender
      * @param transportOut the Axis2 transport configuration
      * @return the SSLContext to be used
      */
     protected SSLContext getSSLContext(TransportOutDescription transportOut) throws AxisFault {
 
-        KeyManager[] keymanagers  = null;
-        TrustManager[] trustManagers = null;
-
         Parameter keyParam    = transportOut.getParameter("keystore");
         Parameter trustParam  = transportOut.getParameter("truststore");
 
+        OMElement ksEle = null;
+        OMElement tsEle = null;
+
         if (keyParam != null) {
-            OMElement ksEle      = keyParam.getParameterElement().getFirstElement();
-            String location      = ksEle.getFirstChildWithName(new QName("Location")).getText();
-            String type          = ksEle.getFirstChildWithName(new QName("Type")).getText();
-            String storePassword = ksEle.getFirstChildWithName(new QName("Password")).getText();
-            String keyPassword   = ksEle.getFirstChildWithName(new QName("KeyPassword")).getText();
+            ksEle = keyParam.getParameterElement().getFirstElement();
+        }
+
+        boolean novalidatecert = ParamUtils.getOptionalParamBoolean(transportOut,
+                "novalidatecert", false);
+
+        if (trustParam != null) {
+            if (novalidatecert) {
+                log.warn("Ignoring novalidatecert parameter since a truststore has been specified");
+            }
+            tsEle = trustParam.getParameterElement().getFirstElement();
+        }
+
+        return createSSLContext(ksEle, tsEle, novalidatecert);
+    }
+
+    /**
+     * Create the SSLIOSessionHandler to initialize the host name verification at the following
+     * levels, through an Axis2 transport configuration parameter as follows:
+     * HostnameVerifier - Default, DefaultAndLocalhost, Strict, AllowAll
+     *
+     * @param transportOut the Axis2 transport configuration
+     * @return the SSLIOSessionHandler to be used
+     * @throws AxisFault if a configuration error occurs
+     */
+    protected SSLIOSessionHandler getSSLIOSessionHandler(TransportOutDescription transportOut)
+            throws AxisFault {
+
+        final Parameter hostnameVerifier = transportOut.getParameter("HostnameVerifier");
+        if (hostnameVerifier != null) {
+            return createSSLIOSessionHandler(hostnameVerifier.getValue().toString());
+        } else {
+            return createSSLIOSessionHandler(null);
+        }        
+    }
+
+    /**
+     * Looks for a transport parameter named customSSLProfiles and initializes zero or more
+     * custom SSLContext instances. The syntax for defining custom SSL profiles is as follows.
+     *
+     * <parameter name="customSSLProfiles>
+     *      <profile>
+     *          <servers>www.test.org:80, www.test2.com:9763</servers>
+     *          <KeyStore>
+     *              <Location>/path/to/identity/store</Location>
+     *              <Type>JKS</Type>
+     *              <Password>password</Password>
+     *              <KeyPassword>password</KeyPassword>
+     *          </KeyStore>
+     *          <TrustStore>
+     *              <Location>path/tp/trust/store</Location>
+     *              <Type>JKS</Type>
+     *              <Password>password</Password>
+     *          </TrustStore>
+     *      </profile>
+     * </parameter>
+     *
+     * Any number of profiles can be defined under the customSSLProfiles parameter.
+     *
+     * @param transportOut transport out description
+     * @return a map of server addresses and SSL contexts
+     * @throws AxisFault if at least on SSL profile is not properly configured
+     */
+    private Map<String, SSLContext> getCustomSSLContexts(TransportOutDescription transportOut)
+            throws AxisFault {
+
+        if (log.isDebugEnabled()) {
+            log.info("Loading custom SSL profiles for the HTTPS sender");
+        }
+
+        Parameter customProfilesParam = transportOut.getParameter("customSSLProfiles");
+        if (customProfilesParam == null) {
+            return null;
+        }
+
+        OMElement customProfilesElt = customProfilesParam.getParameterElement();
+        Iterator profiles = customProfilesElt.getChildrenWithName(new QName("profile"));
+        Map<String, SSLContext> contextMap = new HashMap<String, SSLContext>();
+        while (profiles.hasNext()) {
+            OMElement profile = (OMElement) profiles.next();
+            OMElement serversElt = profile.getFirstChildWithName(new QName("servers"));
+            if (serversElt == null || serversElt.getText() == null) {
+                String msg = "Each custom SSL profile must define at least one host:port " +
+                        "pair under the servers element";
+                log.error(msg);
+                throw new AxisFault(msg);
+            }
+
+            String[] servers = serversElt.getText().split(",");
+            OMElement ksElt = profile.getFirstChildWithName(new QName("KeyStore"));
+            OMElement trElt = profile.getFirstChildWithName(new QName("TrustStore"));
+            String noValCert = profile.getAttributeValue(new QName("novalidatecert"));
+            boolean novalidatecert = "true".equals(noValCert);
+            SSLContext sslContext = createSSLContext(ksElt, trElt, novalidatecert);
+
+            for (String server : servers) {
+                server = server.trim();                
+                if (!contextMap.containsKey(server)) {
+                    contextMap.put(server, sslContext);
+                } else {
+                    log.warn("Multiple SSL profiles were found for the server : " + server + ". " +
+                            "Ignoring the excessive profiles.");
+                }
+            }
+        }
+
+        if (contextMap.size() > 0) {
+            log.info("Custom SSL profiles initialized for " + contextMap.size() + " servers");
+            return contextMap;
+        }
+        return null;
+    }
+
+    private SSLContext createSSLContext(OMElement keyStoreElt, OMElement trustStoreElt,
+                                        boolean novalidatecert) throws AxisFault {
+
+        KeyManager[] keymanagers  = null;
+        TrustManager[] trustManagers = null;
+
+
+        if (keyStoreElt != null) {
+            String location      = keyStoreElt.getFirstChildWithName(new QName("Location")).getText();
+            String type          = keyStoreElt.getFirstChildWithName(new QName("Type")).getText();
+            String storePassword = keyStoreElt.getFirstChildWithName(new QName("Password")).getText();
+            String keyPassword   = keyStoreElt.getFirstChildWithName(new QName("KeyPassword")).getText();
 
             FileInputStream fis = null;
             try {
@@ -98,17 +224,14 @@
             }
         }
 
-        boolean novalidatecert = ParamUtils.getOptionalParamBoolean(transportOut, "novalidatecert", false);
-
-        if (trustParam != null) {
+        if (trustStoreElt != null) {
             if (novalidatecert) {
                 log.warn("Ignoring novalidatecert parameter since a truststore has been specified");
             }
-            
-            OMElement tsEle      = trustParam.getParameterElement().getFirstElement();
-            String location      = tsEle.getFirstChildWithName(new QName("Location")).getText();
-            String type          = tsEle.getFirstChildWithName(new QName("Type")).getText();
-            String storePassword = tsEle.getFirstChildWithName(new QName("Password")).getText();
+
+            String location      = trustStoreElt.getFirstChildWithName(new QName("Location")).getText();
+            String type          = trustStoreElt.getFirstChildWithName(new QName("Type")).getText();
+            String storePassword = trustStoreElt.getFirstChildWithName(new QName("Password")).getText();
 
             FileInputStream fis = null;
             try {
@@ -136,7 +259,8 @@
                 }
             }
         } else if (novalidatecert) {
-            log.warn("Server certificate validation (trust) has been disabled. DO NOT USE IN PRODUCTION!");
+            log.warn("Server certificate validation (trust) has been disabled. " +
+                    "DO NOT USE IN PRODUCTION!");
             trustManagers = new TrustManager[] { new NoValidateCertTrustManager() };
         }
 
@@ -144,25 +268,15 @@
             SSLContext sslcontext = SSLContext.getInstance("TLS");
             sslcontext.init(keymanagers, trustManagers, null);
             return sslcontext;
-            
+
         } catch (GeneralSecurityException gse) {
             log.error("Unable to create SSL context with the given configuration", gse);
             throw new AxisFault("Unable to create SSL context with the given configuration", gse);
         }
     }
 
-    /**
-     * Create the SSLIOSessionHandler to initialize the host name verification at the following
-     * levels, through an Axis2 transport configuration parameter as follows:
-     * HostnameVerifier - Default, DefaultAndLocalhost, Strict, AllowAll
-     *
-     * @param transportOut the Axis2 transport configuration
-     * @return the SSLIOSessionHandler to be used
-     * @throws AxisFault if a configuration error occurs
-     */
-    protected SSLIOSessionHandler getSSLIOSessionHandler(TransportOutDescription transportOut) throws AxisFault {
-
-        final Parameter hostnameVerifier = transportOut.getParameter("HostnameVerifier");
+    private SSLIOSessionHandler createSSLIOSessionHandler(final String hostnameVerifier)
+            throws AxisFault {
 
         return new SSLIOSessionHandler() {
 
@@ -172,7 +286,7 @@
             public void verify(SocketAddress remoteAddress, SSLSession session)
                 throws SSLException {
 
-                String address = null;
+                String address;
                 if (remoteAddress instanceof InetSocketAddress) {
                     address = ((InetSocketAddress) remoteAddress).getHostName();
                 } else {
@@ -181,11 +295,11 @@
 
                 boolean valid = false;
                 if (hostnameVerifier != null) {
-                    if ("Strict".equals(hostnameVerifier.getValue())) {
+                    if ("Strict".equals(hostnameVerifier)) {
                         valid = HostnameVerifier.STRICT.verify(address, session);
-                    } else if ("AllowAll".equals(hostnameVerifier.getValue())) {
+                    } else if ("AllowAll".equals(hostnameVerifier)) {
                         valid = HostnameVerifier.ALLOW_ALL.verify(address, session);
-                    } else if ("DefaultAndLocalhost".equals(hostnameVerifier.getValue())) {
+                    } else if ("DefaultAndLocalhost".equals(hostnameVerifier)) {
                         valid = HostnameVerifier.DEFAULT_AND_LOCALHOST.verify(address, session);
                     }
                 } else {
@@ -193,7 +307,7 @@
                 }
 
                 if (!valid) {
-                    throw new SSLException("Host name verification failed for host : " + address);    
+                    throw new SSLException("Host name verification failed for host : " + address);
                 }
             }
         };

Modified: synapse/trunk/java/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/HttpCoreNIOSender.java
URL: http://svn.apache.org/viewvc/synapse/trunk/java/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/HttpCoreNIOSender.java?rev=799546&r1=799545&r2=799546&view=diff
==============================================================================
--- synapse/trunk/java/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/HttpCoreNIOSender.java (original)
+++ synapse/trunk/java/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/HttpCoreNIOSender.java Fri Jul 31 08:47:29 2009
@@ -175,7 +175,7 @@
 
         handler = new ClientHandler(cfgCtx, params, metrics);
         final IOEventDispatch ioEventDispatch = getEventDispatch(
-            handler, sslContext, sslIOSessionHandler, params);
+            handler, sslContext, sslIOSessionHandler, params, transportOut);
 
         // start the Sender in a new seperate thread
         Thread t = new Thread(new Runnable() {
@@ -209,9 +209,10 @@
      * @param params
      * @return
      */
-    protected IOEventDispatch getEventDispatch(
-        NHttpClientHandler handler, SSLContext sslContext,
-        SSLIOSessionHandler sslIOSessionHandler, HttpParams params) {
+    protected IOEventDispatch getEventDispatch(NHttpClientHandler handler, SSLContext sslContext,
+        SSLIOSessionHandler sslIOSessionHandler, HttpParams params,
+        TransportOutDescription trpOut) throws AxisFault {
+
         return new PlainClientIOEventDispatch(handler, params);
     }
 

Modified: synapse/trunk/java/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/SSLClientIOEventDispatch.java
URL: http://svn.apache.org/viewvc/synapse/trunk/java/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/SSLClientIOEventDispatch.java?rev=799546&r1=799545&r2=799546&view=diff
==============================================================================
--- synapse/trunk/java/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/SSLClientIOEventDispatch.java (original)
+++ synapse/trunk/java/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/SSLClientIOEventDispatch.java Fri Jul 31 08:47:29 2009
@@ -21,14 +21,25 @@
 import javax.net.ssl.SSLContext;
 
 import org.apache.http.impl.nio.reactor.SSLIOSessionHandler;
+import org.apache.http.impl.nio.reactor.SSLIOSession;
 import org.apache.http.nio.NHttpClientHandler;
 import org.apache.http.nio.NHttpClientIOTarget;
 import org.apache.http.nio.reactor.IOSession;
 import org.apache.http.params.HttpParams;
 
+import java.net.InetSocketAddress;
+import java.util.Map;
+
+/**
+ * This custom SSLClientIOEventDispatch can keep a map of SSLContexts and use the correct
+ * SSLContext when connecting to different servers. If a SSLContext cannot be found for a
+ * particular server from the specified map it uses the default SSLContext.
+ */
 public class SSLClientIOEventDispatch 
     extends org.apache.http.impl.nio.SSLClientIOEventDispatch {
 
+    private Map<String, SSLContext> contextMap;
+
     public SSLClientIOEventDispatch(
             final NHttpClientHandler handler,
             final SSLContext sslcontext,
@@ -36,15 +47,29 @@
             final HttpParams params) {
         super(LoggingUtils.decorate(handler), sslcontext, sslHandler, params);
     }
-    
-    public SSLClientIOEventDispatch(
-            final NHttpClientHandler handler,
-            final SSLContext sslcontext,
-            final HttpParams params) {
-        this(handler, sslcontext, null, params);
+
+    public void setContextMap(Map<String,SSLContext> contextMap) {
+        this.contextMap = contextMap;
+    }
+
+    protected SSLIOSession createSSLIOSession(IOSession ioSession, SSLContext sslContext,
+                                              SSLIOSessionHandler sslioSessionHandler) {
+
+        InetSocketAddress address = (InetSocketAddress) ioSession.getRemoteAddress();
+        String host = address.getHostName() + ":" + address.getPort();
+        SSLContext customContext = null;
+        if (contextMap != null) {
+            // See if there's a custom SSL profile configured for this server
+            customContext = contextMap.get(host);
+        }
+
+        if (customContext == null) {
+            customContext = sslContext;
+        }
+        
+        return super.createSSLIOSession(ioSession, customContext, sslioSessionHandler);
     }
-    
-    @Override
+
     protected NHttpClientIOTarget createConnection(IOSession session) {
         return LoggingUtils.decorate(
                 super.createConnection(LoggingUtils.decorate(session, "sslclient")));