November 22, 2024

ActiveMQ and the SSL Transport

In a previous article I’ve shown how to configure ActiveMQ for High Availability in a “shared storage master/slave” scenario. Now let’s see how you can communicate with ActiveMQ using the SSL transport. ActiveMQ uses the Java Secure Socket Extension (JSSE) to implement its SSL functionality so you must include SSL certificates for successful SSL communication. In this article, I will show an example of an SSL trusted client/server connection using either the Java SSL System Properties (directly in a Maven profile) or the Spring beans definition. Here the roadmap.

ROADMAP

1. Creating your own SSL certificates (keystore and truststore files)
2. Enabling the SSL transport for ActiveMQ with trusted clients
3. Setup clients using Java SSL System Properties in a Maven profile
4. Setup clients using Spring

1.  Creating your own SSL certificates (keystore and trustedstore files)

I have summed up the process of creating and sharing self-signed certificates using the command-line “keytool” distributed with Java.

## Create a keystore for the broker SERVER
$ keytool -genkey -alias amq-server -keyalg RSA -keysize 2048 -validity 90 -keystore amq-server.ks

## Export the broker SERVER certificate from the keystore
$ keytool -export -alias amq-server -keystore amq-server.ks -file amq-server_cert

## Create the CLIENT keystore
$ keytool -genkey -alias amq-client -keyalg RSA -keysize 2048 -validity 90 -keystore amq-client.ks

## Import the previous exported broker's certificate into a CLIENT truststore
$ keytool -import -alias amq-server -keystore amq-client.ts -file amq-server_cert

## If you want to make trusted also the client, you must export the client's certificate from the keystore
$ keytool -export -alias amq-client -keystore amq-client.ks -file amq-client_cert

## Import the client's exported certificate into a broker SERVER truststore
$ keytool -import -alias amq-client -keystore amq-server.ts -file amq-client_cert
## Other useful commands
$ openssl s_client -connect activemq-host:61617
$ keytool -list -keystore amq-server.ks
$ keytool -printcert -v -file amq-server_cert
$ keytool -storepasswd -keystore amq-server.ks

 

2. Enabling the SSL transport for ActiveMQ with trusted clients

To configure the ActiveMQ broker to use the SSL transport, the first thing to do is to replace the default keystore and truststore files in the conf directory with the ones that were previously created.

$ cp amq-server.ks ${ACTIVEMQ_HOME}/conf/
$ cp amq-server.ts ${ACTIVEMQ_HOME}/conf/

Now change the <transportConnectors> element in the ${ACTIVEMQ_HOME}/conf/activemq.xml file and specify the “needClientAuth=true” parameter which instructs the broker to check connecting client certificates and allow access only to those that are found in the truststore. Edit the file  ${ACTIVEMQ_HOME}/conf/activemq.xml as below.

<broker xmlns="http://activemq.apache.org/schema/core" brokerName="fnmamq01" dataDirectory="/mnt/amq_data/amq-5.9">
 <!-- blablablabla -->

 <transportConnectors>
     <transportConnector name="ssl" uri="ssl://0.0.0.0:61617?trace=true&needClientAuth=true" />
 </transportConnectors>

  <!-- SSL Configuration Context -->
  <sslContext>
     <sslContext keyStore="file:${activemq.base}/conf/amq-server.ks"
                 keyStorePassword="123456"
                 trustStore="file:${activemq.base}/conf/amq-server.ts"
                 trustStorePassword="123456" />
  </sslContext>

</broker>

If no error occurs, you can see the following message at the ActiveMQ bootstrap.

 activemq-and-ssl-transport-screen01

 3. Setup clients using Java SSL System Properties in a Maven profile

The typical URI syntax for the SSL protocol is ssl://hostname:port?key=value. If you have configured a Master/Slave ActiveMQ instance for High Availability you must use a failover connection string like this failover:(ssl://amq01:61617,ssl://amq02:61617)?randomize=false. In my example I have used a ResouceBundle to externalize some properties like the broker connection string.

– activemq-samples/ssl-transport/src/main/resources/configuration.properties

### TCP over SSL Access to a Master/Slave  ActiveMQ istance
amq.url=failover:(ssl://amq01:61617,ssl://amq02:61617)?randomize=false

– activemq-samples/ssl-transport/src/main/java/activemq/samples/ssltransport/Producer.java

public class Producer {

    ResourceBundle settings = ResourceBundle.getBundle("configuration");
    private String brokerURL = settings.getString("amq.url");

    private static transient ConnectionFactory factory;
    private transient Connection connection;
    private transient Session session;
    private transient MessageProducer producer;

    private static int count = 10;
    private static int total;
    private static int id = 1000000;

    private String jobs[] = new String[]{"coda1", "coda2"};

    public Producer() throws JMSException {
    	factory = new ActiveMQConnectionFactory(brokerURL);
    	connection = factory.createConnection();
        connection.start();
        session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        producer = session.createProducer(null);
    }    
// Something other here ...
}

When using JSSE, in order to successfully connect to the broker via SSL, you must provide  the keystore, the keystore password, and the truststore as system properties. You can set the JAVA_OPTS enviorment variable using the following java system properties.

JAVA_OPTS="-Djavax.net.ssl.keyStore=${ACTIVEMQ_HOME}/conf/amq-client.ks
-Djavax.net.ssl.keyStorePassword=123456
-Djavax.net.ssl.trustStore=${ACTIVEMQ_HOME}/conf/amq-client.ts
-Djavax.net.ssl.trustStorePassword=123456"

To run clients with the Java SSL System Properties, I have used in this example the “exec-maven-plugin” in a dedicated Maven profile. Here is an example for the Producer.

– activemq-samples/ssl-transport/pom.xml

<properties>
		<sslsample.keystore>${basedir}/src/main/resources/amq-client.ks</sslsample.keystore>
		<sslsample.keypasswd>123456</sslsample.keypasswd>
		<sslsample.truststore>${basedir}/src/main/resources/amq-client.ts</sslsample.truststore>
		<sslsample.truststorepasswd>123456</sslsample.truststorepasswd>
	</properties>

	<profiles>
		<profile>
			<id>producer</id>
			<build>
				<plugins>
					<plugin>
						<groupId>org.codehaus.mojo</groupId>
						<artifactId>exec-maven-plugin</artifactId>
						<version>1.2.1</version>
						<executions>
							<execution>
								<goals>
									<goal>java</goal>
								</goals>
							</execution>
						</executions>
						<configuration>
							<mainClass>activemq.samples.ssltransport.Producer</mainClass>
							<systemProperties>
								<systemProperty>
									<key>javax.net.ssl.keyStore</key>
									<value>${sslsample.keystore}</value>
								</systemProperty>
								<systemProperty>
									<key>javax.net.ssl.keyStorePassword</key>
									<value>${sslsample.keypasswd}</value>
								</systemProperty>
								<systemProperty>
									<key>javax.net.ssl.trustStore</key>
									<value>${sslsample.truststore}</value>
								</systemProperty>
								<systemProperty>
									<key>javax.net.ssl.trustStorePassword</key>
									<value>${sslsample.truststorepasswd}</value>
								</systemProperty>
							</systemProperties>
						</configuration>
					</plugin>
				</plugins>
			</build>
		</profile>			
	</profiles>

Compile the project source and run the maven “exec:java” goal

$ cd activemq-samples/ssl-transport
$ mvn clean install
$ mvn exec:java -Pproducer

If no errors occur, you can see the SSL connection established message in the output console

activemq-and-ssl-transport-screen02

 4. Setup clients using Spring

If you want to use Spring for your clients, you can define your own JMS SSL Connection Factory using the org.apache.activemq.ActiveMQSslConnectionFactory object.

–  activemq-samples/ssl-transport/src/main/resources/SpringBeans-Producer.xml 

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

	<bean id="propertyPlaceholderConfigurer"
		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations">
			<list>
				<value>classpath:configuration.properties</value>
			</list>
		</property>
	</bean>

	<!-- SSL Connection Factory -->
	<bean id="jmsSslConnectionFactory" class="org.apache.activemq.ActiveMQSslConnectionFactory">
		<property name="brokerURL" value="${amq.url}" />
		<property name="keyStore" value="amq-client.ks" />
		<property name="keyStorePassword" value="123456" />
		<property name="trustStore" value="amq-client.ts" />
		<property name="trustStorePassword" value="123456" />
    	</bean>

	<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
		<property name="connectionFactory" ref="jmsSslConnectionFactory" />
	</bean>

	<bean id="producer" class="activemq.samples.ssltransport.Producer" scope="prototype">
		<property name="template" ref="jmsTemplate" />
	</bean>

</beans>

Related posts

21 Comments

  1. mailk

    Hi,

    I followed the step 1 and 2 to configure ssl in activemq. I can see the ssl started. While trying to access the broker with admin port like https://host:8161/admin getting error:

    Unable to make a secure connection to the server. This may be a problem with the server or it may be requiring a client authentication certificate that you don’t have.
    Error code: ERR_SSL_PROTOCOL_ERROR

    Sametime I can see below error in the log

    Transport Connection to: tcp://172.18.108.153:55482 failed: javax.net.ssl.SSLException: Connection has been shutdown: javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake | org.apache.activemq.broker.TransportConnection.Transport | ActiveMQ Transport: ssl:///172.18.108.153:55482
    2014-05-01 01:45:14,510 | ERROR | Could not accept connection from tcp://172.18.108.153:55482: javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake | org.apache.activemq.broker.TransportConnector | ActiveMQ BrokerService[localhost] Task-1
    2014-05-01 01:45:14,535 | ERROR | Could not accept connection from tcp://172.18.108.153:55483: javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake | org.apache.activemq.broker.TransportConnector | ActiveMQ BrokerService[localhost] Task-2
    2014-05-01 01:45:14,941 | WARN | Transport Connection to: tcp://172.18.108.153:55484 failed: javax.net.ssl.SSLException: Connection has been shutdown: javax.net.ssl.SSLHandshakeException: null cert chain | org.apache.activemq.broker.TransportConnection.Transport | ActiveMQ Transport: ssl:///172.18.108.153:55484
    2014-05-01 01:45:14,943 | ERROR | Could not accept connection from tcp://172.18.108.153:55484: javax.net.ssl.SSLHandshakeException: null cert chain | org.apache.activemq.broker.TransportConnector | ActiveMQ BrokerService[localhost]

    Reply
    1. Giuseppe Urso

      Hi mailk,

      thanks for your comment.By default, the admin web panel of Activemq should be running on HTTP (not https), you can access it using a url like this:
      http://your_host:8161/admin

      The configuration I exposed in this article enables the ssl protocol for the ActiveMQConnectionFactory so the Broker can manage messages using a secure ssl transport connector.
      About the errors in the log, it seems there is a problem with the java SSLHandshake when a client tries to connect to the broker. You can deeply investigate the ssl handshake adding the following option when you run the client:

      -Djavax.net.debug=ssl,handshake,record

      Giuseppe

  2. Giuseppe Urso

    Thanks Thomas!
    In my work experience I’ve been involved with IBM WebSphere Business Integration 5.0 and the MQ-Series. But I’ve to admit that ActiveMQ represents a valid and reliable open-source alternative.

    Reply
  3. Cihat Imamoglu

    Thanks for the great article!

    -Djavax.net.ssl.trustStorePassword=${ACTIVEMQ_HOME}/conf/123456

    I believe you meant -Djavax.net.ssl.trustStorePassword=123456

    Reply
    1. Giuseppe Urso

      Thank you!

      You say right, it was a typo! The javax.net.ssl.trustStorePassword property must be set with the truststore’s password.

      Thanks for reporting I updated the post 😉

      Thank again Cihat
      Giuseppe

  4. Zach Jacobs

    I’m not familiar with Java so I’m not sure where you’re getting your examples starting in Step 3. Is this a project that you have cloned from somewhere?

    ie activemq-samples/ssl-transport/src/main/java/activemq/samples/ssltransport/Producer.java

    Reply
    1. Giuseppe Urso

      Hi Zach,

      thanks for you comment. This is because this post comes from my collection of sample projects now hosted on my github repositories, you can check it here:
      https://github.com/giuseppeurso-eu/activemq

      In this article I use a conventional name “activemq-samples” for the base directory in order to not confuse the reader. The focus of this post should be the ActiveMQ SSL configuration and the metodology to quick setup a maven-based client project. Starting from the Step 3, I provide an example of naming of source files in order to show a possible maven project layout.
      Giuseppe

  5. Renukaradhya

    Hi,
    I tried below steps and after starting activeMQ,its failing to start:
    1. Creating your own SSL certificates (keystore and trustedstore files)
    2. Enabling the SSL transport for ActiveMQ with trusted clients – i copied two files manually to conf folder of activeMQ

    My confihurations:


    | Loading message broker from: xbean:activemq.xml
    jvm 1 | INFO | Refreshing org.apache.activemq.xbean.XBeanBrokerFactory$1@53bfbef7: startup date [Sat May 21 20:33:30 GMT+05:30 2016]; root of context hierarchy
    jvm 1 | WARN | Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.apache.activemq.xbean.XBeanBrokerService#0' defined in class path resource [activemq.xml]: Cannot create inner bean '(inner bean)#1d45d7cf' of type [org.apache.activemq.spring.SpringSslContext] while setting bean property 'sslContext'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#1d45d7cf' defined in class path resource [activemq.xml]: Invocation of init method failed; nested exception is java.security.UnrecoverableKeyException: Cannot recover key
    jvm 1 | ERROR | Failed to load: class path resource [activemq.xml], reason: Error creating bean with name 'org.apache.activemq.xbean.XBeanBrokerService#0' defined in class path resource [activemq.xml]: Cannot create inner bean '(inner bean)#1d45d7cf' of type [org.apache.activemq.spring.SpringSslContext] while setting bean property 'sslContext'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#1d45d7cf' defined in class path resource [activemq.xml]: Invocation of init method failed; nested exception is java.security.UnrecoverableKeyException: Cannot recover key
    jvm 1 |

    Reply
    1. Giuseppe Urso

      Hi Renukaradhya,

      it seems something in your keystores is incorrect. Pay attention to the log snippet:

      "... nested exception is java.security.UnrecoverableKeyException: Cannot recover key..."

      This could mean something like this:
      – you had a bad password;
      – your keystore is corrupted somehow.
      Try to check the integrity of your keystore and password with keytool. Take a look at the other useful commands I’ve posted in the article and verify the key password is correct.

      Giuseppe

  6. Jay

    Hi Giuseppe,
    I have a JMS client which used to connect to a broker which did not have SSL configured and everything was working.
    Recently SSL has been enabled on the broker.
    When I asked the administrator for the certificate, he said that
    “we’re not doing mutual client authentication, so the client doesn’t need to have a certificate, it just has to know how to do an SSL handshake”
    Do you know if it is possible to connect to the broker over SSL without a certificate ?

    Thanks
    Jay

    Reply
    1. Giuseppe

      Hi Jay,

      it seems your administrator has not enabled the SSL/TLS two-way authentication (mutual authentication), this means the client only needs to import the broker certificate as a Trusted Certificate.
      First of all, make sure the broker’s SSL/TLS certificate is imported into the trust store of your CLIENT java virtual machine (you can download the broker certificate by using a browser or openssl command):

      ## Copy and paste the BEGIN/END part into a file broker.cert
      > openssl s_client -connect Broker_Host:SSL_Port
      ## Import broker cert into a trust store
      > keytool -import -alias broker-server -keystore client.ts -file broker.cert

      After importing the broker certificate into the truststore, you only need to run your JMS client specifying the truststore property (take a look at the Step 3 in the article):

      -Djavax.net.ssl.trustStore=your_path/client.ts

      Giuseppe

Leave a Reply

Your email address will not be published.