Commits

Christian Posta  committed 571d452

https://issues.apache.org/jira/browse/AMQ-4298 Add mutual authentication (needClientAuth) to https transport added tests as well

git-svn-id: https://svn.apache.org/repos/asf/activemq/trunk@144529913f79535-47bb-0310-9956-ffa450edef68

  • Participants
  • Parent commits cd5e1f3

Comments (0)

Files changed (9)

File activemq-http/src/main/java/org/apache/activemq/transport/SecureSocketConnectorFactory.java

     private String keyPassword = System.getProperty("javax.net.ssl.keyPassword");
     private String keyStorePassword = System.getProperty("javax.net.ssl.keyStorePassword");
     private String keyStore = System.getProperty("javax.net.ssl.keyStore");
+    private String trustStorePassword = System.getProperty("javax.net.ssl.trustStorePassword");
+    private String trustStore = System.getProperty("javax.net.ssl.trustStore");
+    private boolean needClientAuth;
+    private boolean wantClientAuth;
     private String keyStoreType;
     private String secureRandomCertficateAlgorithm;
     private String trustCertificateAlgorithm;
             if (protocol != null) {
                 factory.setProtocol(protocol);
             }
+            if (trustStore != null) {
+                factory.setTrustStore(trustStore);
+            }
+            if (trustStorePassword != null) {
+                factory.setTrustStorePassword(trustStorePassword);
+            }
+
         }
 
+        factory.setNeedClientAuth(needClientAuth);
+        factory.setWantClientAuth(wantClientAuth);
+
         return sslConnector;
     }
 
     public void setAuth(String auth) {
         this.auth = auth;
     }
+
+    public boolean isWantClientAuth() {
+        return wantClientAuth;
+    }
+
+    public void setWantClientAuth(boolean wantClientAuth) {
+        this.wantClientAuth = wantClientAuth;
+    }
+
+    public boolean isNeedClientAuth() {
+        return needClientAuth;
+    }
+
+    public void setNeedClientAuth(boolean needClientAuth) {
+        this.needClientAuth = needClientAuth;
+    }
+
+    public String getTrustStore() {
+        return trustStore;
+    }
+
+    public void setTrustStore(String trustStore) {
+        this.trustStore = trustStore;
+    }
+
+    public String getTrustStorePassword() {
+        return trustStorePassword;
+    }
+
+    public void setTrustStorePassword(String trustStorePassword) {
+        this.trustStorePassword = trustStorePassword;
+    }
 }

File activemq-http/src/main/java/org/apache/activemq/transport/http/HttpClientTransport.java

 import org.apache.http.client.methods.HttpHead;
 import org.apache.http.client.methods.HttpOptions;
 import org.apache.http.client.methods.HttpPost;
+import org.apache.http.conn.ClientConnectionManager;
 import org.apache.http.conn.params.ConnRoutePNames;
 import org.apache.http.entity.ByteArrayEntity;
 import org.apache.http.impl.client.BasicResponseHandler;
     private int soTimeout = MAX_CLIENT_TIMEOUT;
 
     private boolean useCompression = false;
-    private boolean canSendCompressed = false;
+    protected boolean canSendCompressed = false;
     private int minSendAsCompressedSize = 0;
 
     public HttpClientTransport(TextWireFormat wireFormat, URI remoteUrl) {
     }
 
     protected HttpClient createHttpClient() {
-        DefaultHttpClient client = new DefaultHttpClient(new PoolingClientConnectionManager());
+        DefaultHttpClient client = new DefaultHttpClient(createClientConnectionManager());
         if (useCompression) {
             client.addRequestInterceptor( new HttpRequestInterceptor() {
                 @Override
         return client;
     }
 
+    protected ClientConnectionManager createClientConnectionManager() {
+        return new PoolingClientConnectionManager();
+    }
+
     protected void configureMethod(AbstractHttpMessage method) {
         method.setHeader("clientID", clientID);
     }

File activemq-http/src/main/java/org/apache/activemq/transport/https/HttpsClientTransport.java

 */
 package org.apache.activemq.transport.https;
 
-import java.net.URI;
-
 import org.apache.activemq.transport.http.HttpClientTransport;
 import org.apache.activemq.transport.util.TextWireFormat;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.impl.conn.PoolingClientConnectionManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.net.URI;
+import java.security.KeyStore;
 
 public class HttpsClientTransport extends HttpClientTransport {
-  
+
   public HttpsClientTransport(TextWireFormat wireFormat, URI remoteUrl) {
     super(wireFormat, remoteUrl);
   }
+
+    @Override
+    protected ClientConnectionManager createClientConnectionManager() {
+        PoolingClientConnectionManager connectionManager = new PoolingClientConnectionManager(createSchemeRegistry());
+        return connectionManager;
+    }
+
+    private SchemeRegistry createSchemeRegistry() {
+
+        SchemeRegistry schemeRegistry = new SchemeRegistry();
+        try {
+            // register the default socket factory so that it looks at the javax.net.ssl.keyStore,
+            // javax.net.ssl.trustStore, etc, properties by default
+            SSLSocketFactory sslSocketFactory =
+                    new SSLSocketFactory((javax.net.ssl.SSLSocketFactory) javax.net.ssl.SSLSocketFactory.getDefault(),
+                    SSLSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
+            schemeRegistry.register(new Scheme("https", getRemoteUrl().getPort(), sslSocketFactory));
+            return schemeRegistry;
+        } catch (Exception e) {
+            throw new IllegalStateException("Failure trying to create scheme registry", e);
+        }
+    }
 }

File activemq-http/src/test/java/org/apache/activemq/transport/https/HttpsNeedClientAuthBrokerTest.java

+/**
+ * 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.activemq.transport.https;
+
+import junit.framework.Test;
+import junit.textui.TestRunner;
+import org.apache.activemq.broker.BrokerFactory;
+import org.apache.activemq.broker.BrokerService;
+import org.apache.activemq.transport.http.HttpTransportBrokerTest;
+
+import java.net.URI;
+
+/**
+ * @author <a href="http://www.christianposta.com/blog">Christian Posta</a>
+ */
+public class HttpsNeedClientAuthBrokerTest extends HttpTransportBrokerTest {
+
+    private static final String HTTPS_TRANSPORT_URL = "https://localhost:8161?transport.needClientAuth=true";
+
+    protected String getBindLocation() {
+        return "https://localhost:8161";
+    }
+
+    protected void setUp() throws Exception {
+        System.setProperty("javax.net.ssl.trustStore", "src/test/resources/client.keystore");
+        System.setProperty("javax.net.ssl.trustStorePassword", "password");
+        System.setProperty("javax.net.ssl.trustStoreType", "jks");
+        System.setProperty("javax.net.ssl.keyStore", "src/test/resources/server.keystore");
+        System.setProperty("javax.net.ssl.keyStorePassword", "password");
+        System.setProperty("javax.net.ssl.keyStoreType", "jks");
+        super.setUp();
+    }
+
+    @Override
+    protected BrokerService createBroker() throws Exception {
+        BrokerService broker = BrokerFactory.createBroker(new URI("broker:()/localhost?persistent=false&useJmx=false"));
+        connector = broker.addConnector(HTTPS_TRANSPORT_URL);
+        return broker;
+    }
+
+    public static Test suite() {
+        return suite(HttpsNeedClientAuthBrokerTest.class);
+    }
+
+    public static void main(String[] args) {
+        TestRunner.run(suite());
+    }
+}

File activemq-http/src/test/java/org/apache/activemq/transport/https/HttpsNeedClientAuthSendAndReceiveTest.java

+/**
+ * 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.activemq.transport.https;
+
+import org.apache.activemq.ActiveMQConnectionFactory;
+import org.apache.activemq.JmsTopicSendReceiveTest;
+import org.apache.activemq.broker.BrokerFactory;
+import org.apache.activemq.broker.BrokerService;
+
+/**
+ * @author <a href="http://www.christianposta.com/blog">Christian Posta</a>
+ */
+public class HttpsNeedClientAuthSendAndReceiveTest extends JmsTopicSendReceiveTest {
+
+    public static final String KEYSTORE_TYPE = "jks";
+    public static final String PASSWORD = "password";
+    public static final String TRUST_KEYSTORE = "src/test/resources/client.keystore";
+    public static final String KEYSTORE = "src/test/resources/server.keystore";
+
+
+    private BrokerService broker;
+
+
+
+    // set up broker with https first...
+    @Override
+    protected void setUp() throws Exception {
+        broker = BrokerFactory.createBroker("xbean:activemq-https-need-client-auth.xml");
+        broker.setPersistent(false);
+        broker.start();
+        broker.waitUntilStarted();
+
+        // these are used for the client side... for the server side, the SSL context
+        // will be configured through the <sslContext> spring beans
+        System.setProperty("javax.net.ssl.trustStore", TRUST_KEYSTORE);
+        System.setProperty("javax.net.ssl.trustStorePassword", PASSWORD);
+        System.setProperty("javax.net.ssl.trustStoreType", KEYSTORE_TYPE);
+        System.setProperty("javax.net.ssl.keyStore", KEYSTORE);
+        System.setProperty("javax.net.ssl.keyStorePassword", PASSWORD);
+        System.setProperty("javax.net.ssl.keyStoreType", KEYSTORE_TYPE);
+        super.setUp();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        if (broker != null) {
+            broker.stop();
+        }
+    }
+
+    @Override
+    protected ActiveMQConnectionFactory createConnectionFactory() throws Exception {
+        return new ActiveMQConnectionFactory("https://localhost:8161");
+    }
+}

File activemq-http/src/test/java/org/apache/activemq/transport/https/HttpsWantClientAuthSendAndReceiveTest.java

+/**
+ * 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.activemq.transport.https;
+
+import org.apache.activemq.ActiveMQConnectionFactory;
+import org.apache.activemq.JmsTopicSendReceiveTest;
+import org.apache.activemq.broker.BrokerFactory;
+import org.apache.activemq.broker.BrokerService;
+
+/**
+ * @author <a href="http://www.christianposta.com/blog">Christian Posta</a>
+ */
+public class HttpsWantClientAuthSendAndReceiveTest extends JmsTopicSendReceiveTest {
+
+    public static final String KEYSTORE_TYPE = "jks";
+    public static final String PASSWORD = "password";
+    public static final String TRUST_KEYSTORE = "src/test/resources/client.keystore";
+    public static final String KEYSTORE = "src/test/resources/server.keystore";
+
+
+    private BrokerService broker;
+
+
+
+    // set up broker with https first...
+    @Override
+    protected void setUp() throws Exception {
+        broker = BrokerFactory.createBroker("xbean:activemq-https-want-client-auth.xml");
+        broker.setPersistent(false);
+        broker.start();
+        broker.waitUntilStarted();
+
+        // these are used for the client side... for the server side, the SSL context
+        // will be configured through the <sslContext> spring beans
+        System.setProperty("javax.net.ssl.trustStore", TRUST_KEYSTORE);
+        System.setProperty("javax.net.ssl.trustStorePassword", PASSWORD);
+        System.setProperty("javax.net.ssl.trustStoreType", KEYSTORE_TYPE);
+//        System.setProperty("javax.net.ssl.keyStore", KEYSTORE);
+//        System.setProperty("javax.net.ssl.keyStorePassword", PASSWORD);
+//        System.setProperty("javax.net.ssl.keyStoreType", KEYSTORE_TYPE);
+        super.setUp();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        if (broker != null) {
+            broker.stop();
+        }
+    }
+
+    @Override
+    protected ActiveMQConnectionFactory createConnectionFactory() throws Exception {
+        return new ActiveMQConnectionFactory("https://localhost:8161");
+    }
+}

File activemq-http/src/test/resources/activemq-https-need-client-auth.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+
+<beans xmlns="http://www.springframework.org/schema/beans" 
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
+	xmlns:amq="http://activemq.apache.org/schema/core" 
+	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd ">
+
+	<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" />
+
+	<amq:broker brokerName="localhost" useJmx="false">
+
+		<amq:sslContext>
+			<amq:sslContext keyStorePassword="password"
+				keyStore="src/test/resources/server.keystore"
+				trustStore="src/test/resources/client.keystore"
+				trustStorePassword="password" />
+		</amq:sslContext>
+
+		<amq:transportConnectors>
+			<amq:transportConnector name="https"
+				uri="https://localhost:8161?transport.needClientAuth=true" />
+		</amq:transportConnectors>
+	</amq:broker>
+
+</beans>

File activemq-http/src/test/resources/activemq-https-want-client-auth.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:amq="http://activemq.apache.org/schema/core"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd ">
+
+    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" />
+
+    <amq:broker brokerName="localhost" useJmx="false">
+
+        <amq:sslContext>
+            <amq:sslContext keyStorePassword="password"
+                            keyStore="src/test/resources/server.keystore"
+                            trustStore="src/test/resources/client.keystore"
+                            trustStorePassword="password" />
+        </amq:sslContext>
+
+        <amq:transportConnectors>
+            <amq:transportConnector name="https"
+                                    uri="https://localhost:8161?transport.wantClientAuth=true" />
+        </amq:transportConnectors>
+    </amq:broker>
+
+</beans>

File activemq-unit-tests/src/test/java/org/apache/activemq/transport/tcp/SslBrokerServiceTest.java

         return service;
     }
 
-    public void testNeedClientAuth() throws Exception {   
+    public void testNeedClientAuthReject() throws Exception {
         SSLContext context = SSLContext.getInstance("TLS");    
         // no client cert
         context.init(null, getTrustManager(), null);
         // should work with regular connector
         makeSSLConnection(context, null, connector);
     }
+
+    public void testNeedClientAuthSucceed() throws Exception {
+        SSLContext context = SSLContext.getInstance("TLS");
+        context.init(getKeyManager(), getTrustManager(), null);
+        makeSSLConnection(context, null, needClientAuthConnector);
+    }
     
     public void testCipherSuitesDisabled() throws Exception {
         SSLContext context = SSLContext.getInstance("TLS");