You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2013/07/11 18:09:25 UTC

[3/3] git commit: CAMEL-6540: Added option to enrich Camel message with client certificate information for SSL consumers.

CAMEL-6540: Added option to enrich Camel message with client certificate information for SSL consumers.


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/dc281f36
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/dc281f36
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/dc281f36

Branch: refs/heads/master
Commit: dc281f360e1e5548057409a7bb9120c70e59f085
Parents: 6406914
Author: Claus Ibsen <da...@apache.org>
Authored: Thu Jul 11 18:04:18 2013 +0200
Committer: Claus Ibsen <da...@apache.org>
Committed: Thu Jul 11 18:04:39 2013 +0200

----------------------------------------------------------------------
 .../camel/component/netty/NettyConstants.java   |  5 ++
 .../camel/component/netty/NettyEndpoint.java    | 48 ++++++++++++-
 .../NettyServerBootstrapConfiguration.java      | 10 ++-
 .../netty/NettySSLClientCertHeadersTest.java    | 74 ++++++++++++++++++++
 4 files changed, 134 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/dc281f36/components/camel-netty/src/main/java/org/apache/camel/component/netty/NettyConstants.java
----------------------------------------------------------------------
diff --git a/components/camel-netty/src/main/java/org/apache/camel/component/netty/NettyConstants.java b/components/camel-netty/src/main/java/org/apache/camel/component/netty/NettyConstants.java
index 1ea260c..4082c7d 100644
--- a/components/camel-netty/src/main/java/org/apache/camel/component/netty/NettyConstants.java
+++ b/components/camel-netty/src/main/java/org/apache/camel/component/netty/NettyConstants.java
@@ -29,6 +29,11 @@ public final class NettyConstants {
     public static final String NETTY_REMOTE_ADDRESS = "CamelNettyRemoteAddress";
     public static final String NETTY_LOCAL_ADDRESS = "CamelNettyLocalAddress";
     public static final String NETTY_SSL_SESSION = "CamelNettySSLSession";
+    public static final String NETTY_SSL_CLIENT_CERT_SUBJECT_NAME = "CamelNettySSLClientCertSubjectName";
+    public static final String NETTY_SSL_CLIENT_CERT_ISSUER_NAME = "CamelNettySSLClientCertIssuerName";
+    public static final String NETTY_SSL_CLIENT_CERT_SERIAL_NO = "CamelNettySSLClientCertSerialNumber";
+    public static final String NETTY_SSL_CLIENT_CERT_NOT_BEFORE = "CamelNettySSLClientCertNotBefore";
+    public static final String NETTY_SSL_CLIENT_CERT_NOT_AFTER = "CamelNettySSLClientCertNotAfter";
 
     private NettyConstants() {
         // Utility class

http://git-wip-us.apache.org/repos/asf/camel/blob/dc281f36/components/camel-netty/src/main/java/org/apache/camel/component/netty/NettyEndpoint.java
----------------------------------------------------------------------
diff --git a/components/camel-netty/src/main/java/org/apache/camel/component/netty/NettyEndpoint.java b/components/camel-netty/src/main/java/org/apache/camel/component/netty/NettyEndpoint.java
index a712b7e..7b464fa 100644
--- a/components/camel-netty/src/main/java/org/apache/camel/component/netty/NettyEndpoint.java
+++ b/components/camel-netty/src/main/java/org/apache/camel/component/netty/NettyEndpoint.java
@@ -16,7 +16,11 @@
  */
 package org.apache.camel.component.netty;
 
+import java.math.BigInteger;
+import java.security.Principal;
+import javax.net.ssl.SSLPeerUnverifiedException;
 import javax.net.ssl.SSLSession;
+import javax.security.cert.X509Certificate;
 
 import org.apache.camel.Consumer;
 import org.apache.camel.Exchange;
@@ -106,7 +110,7 @@ public class NettyEndpoint extends DefaultEndpoint {
         } 
         return sslSession;
     }
-    
+
     protected void updateMessageHeader(Message in, ChannelHandlerContext ctx, MessageEvent messageEvent) {
         in.setHeader(NettyConstants.NETTY_CHANNEL_HANDLER_CONTEXT, ctx);
         in.setHeader(NettyConstants.NETTY_MESSAGE_EVENT, messageEvent);
@@ -115,7 +119,47 @@ public class NettyEndpoint extends DefaultEndpoint {
 
         if (configuration.isSsl()) {
             // setup the SslSession header
-            in.setHeader(NettyConstants.NETTY_SSL_SESSION, getSSLSession(ctx));
+            SSLSession sslSession = getSSLSession(ctx);
+            in.setHeader(NettyConstants.NETTY_SSL_SESSION, sslSession);
+
+            // enrich headers with details from the client certificate if option is enabled
+            if (configuration.isSslClientCertHeaders()) {
+                enrichWithClientCertInformation(sslSession, in);
+            }
+        }
+    }
+
+    /**
+     * Enriches the message with client certificate details such as subject name, serial number etc.
+     * <p/>
+     * If the certificate is unverified then the headers is not enriched.
+     *
+     * @param sslSession  the SSL session
+     * @param message     the message to enrich
+     */
+    protected void enrichWithClientCertInformation(SSLSession sslSession, Message message) {
+        try {
+            X509Certificate[] certificates = sslSession.getPeerCertificateChain();
+            if (certificates != null && certificates.length > 0) {
+                X509Certificate cert = certificates[0];
+
+                Principal subject = cert.getSubjectDN();
+                if (subject != null) {
+                    message.setHeader(NettyConstants.NETTY_SSL_CLIENT_CERT_SUBJECT_NAME, subject.getName());
+                }
+                Principal issuer = cert.getIssuerDN();
+                if (issuer != null) {
+                    message.setHeader(NettyConstants.NETTY_SSL_CLIENT_CERT_ISSUER_NAME, issuer.getName());
+                }
+                BigInteger serial = cert.getSerialNumber();
+                if (serial != null) {
+                    message.setHeader(NettyConstants.NETTY_SSL_CLIENT_CERT_SERIAL_NO, serial.toString());
+                }
+                message.setHeader(NettyConstants.NETTY_SSL_CLIENT_CERT_NOT_BEFORE, cert.getNotBefore());
+                message.setHeader(NettyConstants.NETTY_SSL_CLIENT_CERT_NOT_AFTER, cert.getNotAfter());
+            }
+        } catch (SSLPeerUnverifiedException e) {
+            // ignore
         }
     }
 

http://git-wip-us.apache.org/repos/asf/camel/blob/dc281f36/components/camel-netty/src/main/java/org/apache/camel/component/netty/NettyServerBootstrapConfiguration.java
----------------------------------------------------------------------
diff --git a/components/camel-netty/src/main/java/org/apache/camel/component/netty/NettyServerBootstrapConfiguration.java b/components/camel-netty/src/main/java/org/apache/camel/component/netty/NettyServerBootstrapConfiguration.java
index 7df49b0..e7972fb 100644
--- a/components/camel-netty/src/main/java/org/apache/camel/component/netty/NettyServerBootstrapConfiguration.java
+++ b/components/camel-netty/src/main/java/org/apache/camel/component/netty/NettyServerBootstrapConfiguration.java
@@ -18,7 +18,6 @@ package org.apache.camel.component.netty;
 
 import java.io.File;
 import java.util.Map;
-import java.util.concurrent.ExecutorService;
 
 import org.apache.camel.util.jsse.SSLContextParameters;
 import org.jboss.netty.channel.socket.nio.BossPool;
@@ -46,6 +45,7 @@ public class NettyServerBootstrapConfiguration implements Cloneable {
     protected Map<String, Object> options;
     // SSL options is also part of the server bootstrap as the server listener on port X is either plain or SSL
     protected boolean ssl;
+    protected boolean sslClientCertHeaders;
     protected SslHandler sslHandler;
     protected SSLContextParameters sslContextParameters;
     protected boolean needClientAuth;
@@ -187,6 +187,14 @@ public class NettyServerBootstrapConfiguration implements Cloneable {
         this.ssl = ssl;
     }
 
+    public boolean isSslClientCertHeaders() {
+        return sslClientCertHeaders;
+    }
+
+    public void setSslClientCertHeaders(boolean sslClientCertHeaders) {
+        this.sslClientCertHeaders = sslClientCertHeaders;
+    }
+
     public SslHandler getSslHandler() {
         return sslHandler;
     }

http://git-wip-us.apache.org/repos/asf/camel/blob/dc281f36/components/camel-netty/src/test/java/org/apache/camel/component/netty/NettySSLClientCertHeadersTest.java
----------------------------------------------------------------------
diff --git a/components/camel-netty/src/test/java/org/apache/camel/component/netty/NettySSLClientCertHeadersTest.java b/components/camel-netty/src/test/java/org/apache/camel/component/netty/NettySSLClientCertHeadersTest.java
new file mode 100644
index 0000000..7469d1c
--- /dev/null
+++ b/components/camel-netty/src/test/java/org/apache/camel/component/netty/NettySSLClientCertHeadersTest.java
@@ -0,0 +1,74 @@
+/**
+ * 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.camel.component.netty;
+
+import java.io.File;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.impl.JndiRegistry;
+import org.junit.Test;
+
+public class NettySSLClientCertHeadersTest extends BaseNettyTest {
+
+    @Override
+    protected JndiRegistry createRegistry() throws Exception {
+        JndiRegistry registry = super.createRegistry();
+        registry.bind("ksf", new File("src/test/resources/keystore.jks"));
+        registry.bind("tsf", new File("src/test/resources/keystore.jks"));
+        return registry;
+    }
+    
+    @Override
+    public boolean isUseRouteBuilder() {
+        return false;
+    }
+
+    @Test
+    public void testSSLInOutWithNettyConsumer() throws Exception {
+        // ibm jdks dont have sun security algorithms
+        if (isJavaVendor("ibm")) {
+            return;
+        }
+
+        getMockEndpoint("mock:input").expectedMessageCount(1);
+
+        getMockEndpoint("mock:input").expectedHeaderReceived(NettyConstants.NETTY_SSL_CLIENT_CERT_SUBJECT_NAME,
+                "CN=arlu15, OU=Sun Java System Application Server, O=Sun Microsystems, L=Santa Clara, ST=California, C=US");
+        getMockEndpoint("mock:input").expectedHeaderReceived(NettyConstants.NETTY_SSL_CLIENT_CERT_ISSUER_NAME,
+                "CN=arlu15, OU=Sun Java System Application Server, O=Sun Microsystems, L=Santa Clara, ST=California, C=US");
+        getMockEndpoint("mock:input").expectedHeaderReceived(NettyConstants.NETTY_SSL_CLIENT_CERT_SERIAL_NO, "1210701502");
+
+        context.addRoutes(new RouteBuilder() {
+            public void configure() {
+                // needClientAuth=true so we can get the client certificate details
+                from("netty:tcp://localhost:{{port}}?sync=true&ssl=true&passphrase=changeit&keyStoreFile=#ksf&trustStoreFile=#tsf"
+                        + "&needClientAuth=true&sslClientCertHeaders=true")
+                    .to("mock:input")
+                    .transform().constant("Bye World");
+            }
+        });
+        context.start();
+
+        String response = template.requestBody(
+                "netty:tcp://localhost:{{port}}?sync=true&ssl=true&passphrase=changeit&keyStoreFile=#ksf&trustStoreFile=#tsf",
+                "Hello World", String.class);
+        assertEquals("Bye World", response);
+
+        assertMockEndpointsSatisfied();
+    }
+
+}