jms

Apache ActiveMQ SSL Example

1. Introduction

Secure Sockets Layer (SSL) is a standard security protocol for establishing encrypted links between a web server and a browser in an online communication. SSL was originally developed for securing web browser and server communications by Netscape in 1994. Subsequently, the protocol was adopted by the Internet Engineering Task Force (IETF) and renamed to Transport Layer Security (TLS) under RFC 2246 in 1999. SSL/TLS addresses the following security considerations:

  • Authentication: During SSL Handshake process, the server and client make sure the computer speaking to is trusted
  • Confidentiality: The data that is passed between the client and server is encrypted
  • Integrity: The data that is passed between the client and server cannot be modified

 
Java Security Socket Extension (JSSE) is the Java implementation of SSL/TLS protocols. It includes functionality for data encryption, sever authentication, message integrity, and optional client-authentication.

Apache ActiveMQ (AMQ) is written in Java and implements JMS 1.1 specification from the Apache Software Foundation. ActiveMQ uses JSSE to support SSL.

Assuming you understand both AMQ and SSL. In this example, I will demonstrate how to configure an AMQ broker to support SSL and how to create a simple Java application which connects to it securely.

2. Technologies Used

The example code in this article was built and run using:

  • Java 1.8.101 (1.8.x will do fine)
  • Maven 3.3.9 (3.3.x will do fine)
  • Apache ActiveMQ 5.15.3 (others will do fine)
  • Eclipse Mars (Any Java IDE would work)

3. Configure ActiveMQ Server

In this step, we will configure an AMQ server to support the SSL transport in three steps:

  1. Install an AMQ server.
  2. Alter the configuration file to support SSL.
  3. Start the AMQ server and verify the SSL connector is started.

3.1 Activemq.xml

Install AMQ 5.15.3 at a Windows PC. Please check out my other article for more details. If you want to generate a new security key, please follow this article to do so.

In this step, we will use the security certificate that came with the installation and update activemq.xml to enable the SSL transport connector.

activemq.xml

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
<!--
    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
 
 
    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.
-->
<!-- START SNIPPET: example -->
<beans
 
    <!-- Allows us to use system properties as variables in this configuration file -->
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <value>file:${activemq.conf}/credentials.properties</value>
        </property>
    </bean>
 
   <!-- Allows accessing the server log -->
    <bean id="logQuery" class="io.fabric8.insight.log.log4j.Log4jLogQuery"
          lazy-init="false" scope="singleton"
          init-method="start" destroy-method="stop">
    </bean>
 
    <!--
        The <broker> element is used to configure the ActiveMQ broker.
    -->
    <broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost" dataDirectory="${activemq.data}">
 
        <destinationPolicy>
            <policyMap>
              <policyEntries>
                <policyEntry topic=">" >
                    <!-- The constantPendingMessageLimitStrategy is used to prevent
                         slow topic consumers to block producers and affect other consumers
                         by limiting the number of messages that are retained
                         For more information, see:
 
                         http://activemq.apache.org/slow-consumer-handling.html
 
                    -->
                  <pendingMessageLimitStrategy>
                    <constantPendingMessageLimitStrategy limit="1000"/>
                  </pendingMessageLimitStrategy>
                </policyEntry>
              </policyEntries>
            </policyMap>
        </destinationPolicy>
 
 
        <!--
            The managementContext is used to configure how ActiveMQ is exposed in
            JMX. By default, ActiveMQ uses the MBean server that is started by
            the JVM. For more information, see:
 
            http://activemq.apache.org/jmx.html
        -->
        <managementContext>
            <managementContext createConnector="false"/>
        </managementContext>
        <sslContext>
            <sslContext keyStore="file:${activemq.base}/conf/broker.ks"
              keyStorePassword="password" trustStore="file:${activemq.base}/conf/broker.ts"
              trustStorePassword="password"/>
        </sslContext>
 
 
        <!--
            Configure message persistence for the broker. The default persistence
            mechanism is the KahaDB store (identified by the kahaDB tag).
            For more information, see:
 
        -->
        <persistenceAdapter>
            <kahaDB directory="${activemq.data}/kahadb"/>
        </persistenceAdapter>
 
 
          <!--
            The systemUsage controls the maximum amount of space the broker will
            use before disabling caching and/or slowing down producers. For more information, see:
          -->
          <systemUsage>
            <systemUsage>
                <memoryUsage>
                    <memoryUsage percentOfJvmHeap="70" />
                </memoryUsage>
                <storeUsage>
                    <storeUsage limit="100 gb"/>
                </storeUsage>
                <tempUsage>
                    <tempUsage limit="50 gb"/>
                </tempUsage>
            </systemUsage>
        </systemUsage>
 
        <!--
            The transport connectors expose ActiveMQ over a given protocol to
            clients and other brokers. For more information, see:
 
        -->
        <transportConnectors>
            <!-- DOS protection, limit concurrent connections to 1000 and frame size to 100MB
            
            <transportConnector name="amqp" uri="amqp://0.0.0.0:5672?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
            <transportConnector name="stomp" uri="stomp://0.0.0.0:61613?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
            <transportConnector name="mqtt" uri="mqtt://0.0.0.0:1883?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
            <transportConnector name="ws" uri="ws://0.0.0.0:61614?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
             -->
             
             <transportConnector name="openwire" uri="tcp://0.0.0.0:61616?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
             <transportConnector name="ssl" uri="ssl://0.0.0.0:61714?transport.enabledProtocols=TLSv1.2"/>
              
        </transportConnectors>
 
        <!-- destroy the spring context on shutdown to stop jetty -->
        <shutdownHooks>
            <bean xmlns="http://www.springframework.org/schema/beans" class="org.apache.activemq.hooks.SpringContextHook" />
        </shutdownHooks>
 
    </broker>
 
    <!--
        Enable web consoles, REST and Ajax APIs and demos
        The web consoles requires by default login, you can disable this in the jetty.xml file
 
        Take a look at ${ACTIVEMQ_HOME}/conf/jetty.xml for more details
    -->
    <import resource="jetty.xml"/>
 
</beans>
<!-- END SNIPPET: example -->
  • line 73-77: Specify the AMQ server keystore properties
  • line 126-127: Enable the SSL transport with TLS protocol

3.2 Server log

Start the AMQ server.

server.log

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
wrapper  | --> Wrapper Started as Console
wrapper  | Launching a JVM...
jvm 1    | Wrapper (Version 3.2.3) http://wrapper.tanukisoftware.org
jvm 1    |   Copyright 1999-2006 Tanuki Software, Inc.  All Rights Reserved.
jvm 1    |
jvm 1    | Java Runtime: Oracle Corporation 1.8.0_40 C:\Program Files\Java\jre1.8.0_40
jvm 1    |   Heap sizes: current=251392k  free=235655k  max=932352k
jvm 1    |     JVM args: -Dactivemq.home=../.. -Dactivemq.base=../.. -Djavax.net.ssl.keyStorePassword=password -javax.net.ssl.trustStorePassword=password -Djavax.net.ssl.keyStore=../../conf/broker.ks -Djavax.net.ssl.trustStore=../../conf/broker.ts -Dcom.sun.management.jmxremote -Dorg.apache.activemq.UseDedicatedTaskRunner=true -Djava.util.logging.config.file=logging.properties -Dactivemq.conf=../../conf -Dactivemq.data=../../data -Djava.security.auth.login.config=../../conf/login.config -Xmx1024m -Djava.library.path=../../bin/win64 -Dwrapper.key=V4xc5qXB92bkkPap -Dwrapper.port=32000 -Dwrapper.jvm.port.min=31000 -Dwrapper.jvm.port.max=31999 -Dwrapper.pid=19168 -Dwrapper.version=3.2.3 -Dwrapper.native_library=wrapper -Dwrapper.cpu.timeout=10 -Dwrapper.jvmid=1
jvm 1    | Extensions classpath:
jvm 1    |   [..\..\lib,..\..\lib\camel,..\..\lib\optional,..\..\lib\web,..\..\lib\extra]
jvm 1    | ACTIVEMQ_HOME: ..\..
jvm 1    | ACTIVEMQ_BASE: ..\..
jvm 1    | ACTIVEMQ_CONF: ..\..\conf
jvm 1    | ACTIVEMQ_DATA: ..\..\data
jvm 1    | Loading message broker from: xbean:activemq.xml
jvm 1    |  INFO | Refreshing org.apache.activemq.xbean.XBeanBrokerFactory$1@244a02d0: startup date [Mon May 14 19:42:09 CDT 2018]; root of context hierarchy
jvm 1    |  INFO | Using Persistence Adapter: KahaDBPersistenceAdapter[C:\MaryZheng\tools\apache-activemq-5.15.3\bin\win64\..\..\data\kahadb]
jvm 1    |  INFO | KahaDB is version 6
jvm 1    |  INFO | PListStore:[C:\MaryZheng\tools\apache-activemq-5.15.3\bin\win64\..\..\data\localhost\tmp_storage] started
jvm 1    |  INFO | Apache ActiveMQ 5.15.3 (localhost, ID:SL2LS431841-55107-1526344932236-0:1) is starting
jvm 1    |  INFO | Listening for connections at: tcp://SL2LS431841:61616?maximumConnections=1000&wireFormat.maxFrameSize=104857600
jvm 1    |  INFO | Connector openwire started
jvm 1    |  INFO | Listening for connections at: ssl://SL2LS431841:61714?transport.enabledProtocols=TLSv1.2
jvm 1    |  INFO | Connector ssl started
jvm 1    |  INFO | Apache ActiveMQ 5.15.3 (localhost, ID:SL2LS431841-55107-1526344932236-0:1) started
jvm 1    |  INFO | For help or more information please see: http://activemq.apache.org
jvm 1    |  WARN | Store limit is 102400 mb (current store usage is 3 mb). The data directory: C:\MaryZheng\tools\apache-activemq-5.15.3\bin\win64\..\..\data\kahadb only has 58614 mb of usable space. - resetting to maximum available disk space: 58614 mb
jvm 1    |  INFO | No Spring WebApplicationInitializer types detected on classpath
jvm 1    |  INFO | ActiveMQ WebConsole available at http://0.0.0.0:8161/
jvm 1    |  INFO | ActiveMQ Jolokia REST API available at http://0.0.0.0:8161/api/jolokia/
jvm 1    |  INFO | Initializing Spring FrameworkServlet 'dispatcher'
jvm 1    |  INFO | No Spring WebApplicationInitializer types detected on classpath
jvm 1    |  INFO | jolokia-agent: Using policy access restrictor classpath:/jolokia-access.xml

Note:

  • line 8: Pay attention to the JVM options for the security properties
  • line 21: AMQ server starts the tcp connector at port 61616
  • line 23-24: AMQ server starts the ssl connector at port 61714

3.3 AMQ Management Console

We can verify the AMQ server’s ssl connector via the AMQ management web console. Go to http://localhost:8161/admin/connections.jsp and confirm the ssl connector.

Figure 1, AMQ connection

4. Connect to ActiveMQ Server

In this step, we will build two Java applications:

  • QueueMessageConsumer – connects to an AMQ server at an open wire port and consumes the messages
  • QueueMessageProducer – connects to an AMQ server at a secured port and publishes the messages

4.1 Publish Messages via a Secured Port

Imagine a customer sends sensitive data to your AMQ server, we need to secure the data by enabling the SSL connection. In this step, we will build a QueueMessageProducer class to publish the messages into a queue via a secured connection.

QueueMessageProducer.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package jcg.demo.activemq.ssl;
 
import javax.jms.Connection;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
 
import org.apache.activemq.ActiveMQSslConnectionFactory;
 
/**
 * A simple message producer which sends the message to ActiveMQ Broker
 *
 * @author Mary.Zheng
 *
 */
public class QueueMessageProducer {
 
    private String activeMqBrokerUri;
    private String username;
    private String password;
 
    public static void main(String[] args) {
        QueueMessageProducer queProducer = new QueueMessageProducer("ssl://localhost:61714", "admin", "admin");
        queProducer.sendDummyMessages("test.queue");
 
    }
 
    public QueueMessageProducer(String activeMqBrokerUri, String username, String password) {
        super();
        this.activeMqBrokerUri = activeMqBrokerUri;
        this.username = username;
        this.password = password;
    }
 
    public void sendDummyMessages(String queueName) {
        System.out.println("QueueMessageProducer started " + this.activeMqBrokerUri);
        ActiveMQSslConnectionFactory connFactory = null;
        Connection connection = null;
        Session session = null;
        MessageProducer msgProducer = null;
        try {
            connFactory = new ActiveMQSslConnectionFactory(activeMqBrokerUri);
            connFactory.setUserName(username);
            connFactory.setPassword(password);
            connection = connFactory.createConnection();
 
            connection.start();
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            msgProducer = session.createProducer(session.createQueue(queueName));
 
            for (int i = 0; i < 10; i++) {
                TextMessage textMessage = session.createTextMessage(buildDummyMessage(i));
                msgProducer.send(textMessage);
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                }
            }
            System.out.println("QueueMessageProducer completed");
        } catch (JMSException e) {
            e.printStackTrace();
            System.out.println("Caught exception: " + e.getMessage());
        } catch (Exception e1) {
            System.out.println("Caught exception: " + e1.getMessage());
        }
        try {
            if (msgProducer != null) {
                msgProducer.close();
            }
            if (session != null) {
                session.close();
            }
            if (connection != null) {
                connection.close();
            }
        } catch (Throwable ignore) {
        }
    }
 
    private String buildDummyMessage(int value) {
        return "dummy message " + value;
    }
}
  • line 24: Pay attention to the ssl connection

4.2 Consume Messages via a Non-Secured Port

Once the data is in the AMQ server, internal processes, which are protected by the IT firewall, can consume these messages via a non-secured connector for better performance. In this step, we will build a QueueMessageConsumer class to consume the messages from a queue via a non-secured connection.

Want to be an ActiveMQ Master ?
Subscribe to our newsletter and download the Apache ActiveMQ Cookbook right now!
In order to help you master Apache ActiveMQ JMS, we have compiled a kick-ass guide with all the major ActiveMQ features and use cases! Besides studying them online you may download the eBook in PDF format!

QueueMessageConsumer.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
package jcg.demo.activemq.ssl;
 
import javax.jms.Connection;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.Session;
import javax.jms.TextMessage;
 
import org.apache.activemq.ActiveMQConnectionFactory;
 
/**
 * A simple message consumer which consumes the message from ActiveMQ Broker
 *
 * @author Mary.Zheng
 *
 */
public class QueueMessageConsumer implements MessageListener {
 
    private String activeMqBrokerUri;
    private String username;
    private String password;
    private String destinationName;
 
    public static void main(String[] args) {
 
        QueueMessageConsumer queueMsgListener = new QueueMessageConsumer("tcp://localhost:61616", "admin", "admin");
        queueMsgListener.setDestinationName("test.queue");
 
        try {
            queueMsgListener.run();
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
 
    public QueueMessageConsumer(String activeMqBrokerUri, String username, String password) {
        super();
        this.activeMqBrokerUri = activeMqBrokerUri;
        this.username = username;
        this.password = password;
    }
 
    public void run() throws JMSException {
        ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(username, password, activeMqBrokerUri);
        Connection connection = factory.createConnection();
        connection.setClientID("MaryClient");
        connection.start();
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        Destination destination = session.createQueue(destinationName);
        MessageConsumer consumer = session.createConsumer(destination);
        consumer.setMessageListener(this);
 
        System.out.println(String.format("QueueMessageConsumer Waiting for messages at %s %s", destinationName,
                this.activeMqBrokerUri));
    }
 
    @Override
    public void onMessage(Message message) {
        String msg;
        try {
            msg = String.format("QueueMessageConsumer Received message [ %s ]", ((TextMessage) message).getText());
            Thread.sleep(10000);// sleep for 10 seconds
            System.out.println(msg);
        } catch (JMSException | InterruptedException e) {
            e.printStackTrace();
        }
    }
 
    public String getDestinationName() {
        return destinationName;
    }
 
    public void setDestinationName(String destinationName) {
        this.destinationName = destinationName;
    }
}
  • line 29: Pay attention to the open wire connection

4.3 Install Security Certificate

The client needs to install the security certificate to establish the secured connection. There are several ways to get the security certificate installed for a Java program. As a Java developer, I use the InstallCert class to do so. The source code is credited to Andreas Sterbenz.

InstallCert.java

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/*
 * Copyright 2006 Sun Microsystems, Inc.  All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Sun Microsystems nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * Originally from:
 * Use:
 * java InstallCert hostname
 * Example:
 *% java InstallCert ecc.fedora.redhat.com
 */
 
package jcg.demo.activemq.ssl.util;
 
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
 
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
 
public class InstallCert {
 
    public static void main(final String[] args) {
        InstallCert installCert = new InstallCert();
 
        try {
            installCert.generateCert("localhost", 61714);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
 
    public void generateCert(String host, int port) throws Exception {
 
        File file = getJsSecCertsFile();
 
        System.out.println("Loading KeyStore " + file + "...");
        final InputStream in = new FileInputStream(file);
        final KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
        ks.load(in, passphrase);
        in.close();
 
        final SSLContext context = SSLContext.getInstance("TLS");
        final TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(ks);
        final X509TrustManager defaultTrustManager = (X509TrustManager) tmf.getTrustManagers()[0];
        final SavingTrustManager tm = new SavingTrustManager(defaultTrustManager);
        context.init(null, new TrustManager[] { tm }, null);
        final SSLSocketFactory factory = context.getSocketFactory();
 
        System.out.println("Opening connection to " + host + ":" + port + "...");
        final SSLSocket socket = (SSLSocket) factory.createSocket(host, port);
        socket.setSoTimeout(10000);
        try {
            System.out.println("Starting SSL handshake...");
            socket.startHandshake();
            socket.close();
            System.out.println();
            System.out.println("No errors, certificate is already trusted");
        } catch (final SSLException e) {
            System.out.println();
            e.printStackTrace(System.out);
        }
 
        final X509Certificate[] chain = tm.chain;
        if (chain == null) {
            System.out.println("Could not obtain server certificate chain");
            return;
        }
 
        final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
 
        System.out.println();
        System.out.println("Server sent " + chain.length + " certificate(s):");
        System.out.println();
        final MessageDigest sha1 = MessageDigest.getInstance("SHA1");
        final MessageDigest md5 = MessageDigest.getInstance("MD5");
        for (int i = 0; i > 4]);
            sb.append(HEXDIGITS[b & 15]);
            sb.append(' ');
        }
        return sb.toString();
    }
 
    private static class SavingTrustManager implements X509TrustManager {
 
        private final X509TrustManager tm;
        private X509Certificate[] chain;
 
        SavingTrustManager(final X509TrustManager tm) {
            this.tm = tm;
        }
 
        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[0];
        }
 
        @Override
        public void checkClientTrusted(final X509Certificate[] chain, final String authType)
                throws CertificateException {
            throw new UnsupportedOperationException();
        }
 
        @Override
        public void checkServerTrusted(final X509Certificate[] chain, final String authType)
                throws CertificateException {
            this.chain = chain;
            this.tm.checkServerTrusted(chain, authType);
        }
    }
}
  • line 68: AMQ starts the SSL connector at port 61714. Client installs its certificate.

5. Demo

First, execute the InstallCert to install the certificate into Java keystore. You can try running the program twice to confirm that the certificate is installed correctly.

InstallCert output

01
02
03
04
05
06
07
08
09
10
11
12
13
14
Loading KeyStore C:\MaryZheng\tools\java\jdk1.8.0_31\jre\lib\security\cacerts...
Opening connection to localhost:61714...
Starting SSL handshake...
 
No errors, certificate is already trusted
 
Server sent 1 certificate(s):
 
 1 Subject CN=localhost, OU=broker, O=Unknown, L=Unknown, ST=Unknown, C=Unknown
   Issuer  CN=localhost, OU=broker, O=Unknown, L=Unknown, ST=Unknown, C=Unknown
   sha1    f0 79 0d 04 38 5a 46 ce 86 e1 8a 20 1f 7b ab 3a 46 e4 34 5c
   md5     3f 6c 0c 89 a8 80 29 cc f5 2d da 5c d7 3f ab 37
 
Enter certificate to add to trusted keystore or 'q' to quit: [1]

5.1 Execute both Applications

Start QueueMessageProducer as a Java application and capture the output:

QueueMessageProducer output

1
2
QueueMessageProducer started ssl://localhost:61714
QueueMessageProducer completed

Start QueueMessageConsumer as a Java application and capture the output:

QueueMessageConsumeroutput

01
02
03
04
05
06
07
08
09
10
11
QueueMessageConsumer Waiting for messages at test.queue tcp://localhost:61616
QueueMessageConsumer Received message [ dummy message 0 ]
QueueMessageConsumer Received message [ dummy message 1 ]
QueueMessageConsumer Received message [ dummy message 2 ]
QueueMessageConsumer Received message [ dummy message 3 ]
QueueMessageConsumer Received message [ dummy message 4 ]
QueueMessageConsumer Received message [ dummy message 5 ]
QueueMessageConsumer Received message [ dummy message 6 ]
QueueMessageConsumer Received message [ dummy message 7 ]
QueueMessageConsumer Received message [ dummy message 8 ]
QueueMessageConsumer Received message [ dummy message 9 ]

Note: The QueueMessageProducer starts with an ssl connector.
Verify the ssl connector is enabled via the management web console.

Figure 2, AMQ connection SSL

6. Summary

In this tutorial, we outlined the steps to configure an AMQ server to enable the SSL/TLS transport. SSL must be enabled for Payment Card Industry (PCI) applications. Please check out this article for pro and cons about SSL for other type of applications.

7. Download the Source Code

This example builds two java applications to send and receive messages via the AMQ broker. One via secured SSL, the other not.

Download
You can download the full source code of this example here: Apache ActiveMQ SSL Example
Do you want to know how to develop your skillset to become a Java Rockstar?
Subscribe to our newsletter to start Rocking right now!
To get you started we give you our best selling eBooks for FREE!
1. JPA Mini Book
2. JVM Troubleshooting Guide
3. JUnit Tutorial for Unit Testing
4. Java Annotations Tutorial
5. Java Interview Questions
6. Spring Interview Questions
7. Android UI Design
and many more ....
I agree to the Terms and Privacy Policy

Mary Zheng

Mary has graduated from Mechanical Engineering department at ShangHai JiaoTong University. She also holds a Master degree in Computer Science from Webster University. During her studies she has been involved with a large number of projects ranging from programming and software engineering. She works as a senior Software Engineer in the telecommunications sector where she acts as a leader and works with others to design, implement, and monitor the software solution.
Subscribe
Notify of
guest


This site uses Akismet to reduce spam. Learn how your comment data is processed.

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Richard Yan
Richard Yan
4 years ago

Thank you very much for the detailed article! When I ran the SSL code, it gave some error messages (InstallCert gives “PKIX path building failed”, QueueMessageProducer gives “could not connect to broker URL: ssl:….” and “PKIX path building failed”. I would like to get some advice as to what caused them. Thank you in advance for your time!

Back to top button