ActiveMQ: Cant enable SSL over Stomp

1.1k Views Asked by At

I'm a new ActiveMQ user trying to enable SSL over Stomp with ActiveMQ. Previously I've enabled SSL over openwire for a CMS and I've tried to use both the certificate setup for the CMS and a new one for Stomp.

Here is my certificate setup

Creating the broker keystore:

keytool -genkeypair -alias broker -keyalg RSA -keysize 4096 -sigalg SHA256withRSA -validity 4383 -keystore AMQBroker.ks -storepass "password" -keypass "password" -dname "CN=localhost" -ext "SAN=DNS:localhost,DNS:%computername%.%userdomain%,IP:0.0.0.0,IP:127.0.0.1" -ext "BC:critical=ca:true" -ext "KU:critical=keyCertSign"

Creating the keystore for the CMS client:

keytool -genkey -alias client -keyalg RSA -keysize 4096 -sigalg SHA256withRSA -validity 4383 -keystore AMQClient.ks -storepass "password" -keypass "password" -dname "CN=localhost" -ext "SAN=DNS:localhost,DNS:%computername%.%userdomain%,IP:0.0.0.0,IP:127.0.0.1"

Creating truststores and importing certificates for the Broker and Client

keytool -export -alias broker -keystore AMQBroker.ks -storepass "password" -file AMQBroker.crt
keytool -export -alias client -keystore AMQClient.ks -storepass "password" -file AMQClient.crt
keytool -import -alias client -keystore AMQBroker.ts -storepass "password" -file AMQClient.crt -noprompt
keytool -import -alias broker -keystore AMQBroker.ts -storepass "password" -file AMQBroker.crt -noprompt (This was for the network connector)

Converting the broker keystore to p12 format to export as .pem format for the CMS client:

keytool -importkeystore -srckeystore AMQBroker.ks -destkeystore AMQBroker.p12 -srcstoretype jks -deststoretype pkcs12 -srcalias broker -deststorepass "password" -destkeypass "password" -srcstorepass "password"
openssl pkcs12 -in AMQBroker.p12 -out AMQClient-ts.pem -password pass:"password" -nokeys

Converting the client keystore to p12 format to export as .pem format for the CMS client. I also grabbed the .key file for testing with Stomp.py

keytool -importkeystore -srckeystore AMQClient.ks -destkeystore AMQClient.p12 -srcstoretype jks -deststoretype pkcs12 -srcalias client -deststorepass "password" -destkeypass "password" -srcstorepass "password"
openssl pkcs12 -in AMQClient.p12 -passin pass:"password" -out AMQClient.pem -passout pass:"password"
openssl pkcs12 -info -in AMQClient.p12 -passin pass:"password" -out AMQClient.key -nodes -nocerts

Here's some certificates generated trying to use Stomp specifically

openssl genrsa -out AMQStomp.key 4096
openssl req -sha256 -new -key AMQStomp.key -out AMQStomp.pem -subj "/CN=localhost" -addext "subjectAltName=DNS:localhost,DNS:%userdomain%,DNS:%computername%.%userdomain%,IP:0.0.0.0,IP:127.0.0.1"
keytool -import -alias stomp -keystore AMQBroker.ts -storepass "password" -file AMQStomp.pem -noprompt
keytool -exportcert -rfc -alias broker -keystore AMQBroker.ks -file AMQStomp-ts.pem -storepass "password"

Here's how I've setup my connection with Stomp.py

    brokerPort  = 61612
    stomp_key   = 'C:/path/to/AMQStomp.key'
    stomp_cert  = 'C:/path/to/AMQStomp.pem'
    ca_cert     = 'C:/path/to/AMQStomp-ts.pem'
    
    self.__conn = stomp.Connection(host_and_ports=[(brokerHost, brokerPort)],
            auto_content_length=False,
            use_ssl=True,
            ssl_key_file=stomp_key,
            ssl_cert_file=stomp_cert,
            ssl_ca_certs=ca_cert,
            ssl_version=ssl.PROTOCOL_TLSv1_2)
    self.__conn.set_ssl(
            for_hosts=[(brokerHost, brokerPort)],
            cert_file=stomp_cert,
            key_file=stomp_key,
            ca_certs=ca_cert,
            ssl_version=ssl.PROTOCOL_TLSv1_2)

I've also tried the CMS certificates.

    stomp_key   = 'C:/path/to/AMQClient.key'
    stomp_cert  = 'C:/path/to/AMQClient.pem'
    ca_cert     = 'C:/path/to/AMQClient-ts.pem'

Here's what's currently configured with my activemq.xml

    <sslContext>
        <sslContext 
        keyStore="C:/path/to/AMQBroker.ks"
        keyStorePassword="password"
        trustStore="C:/path/to/AMQBroker.ts"
        trustStorePassword="password" />
    </sslContext>
    
    <transportConnectors>
            <transportConnector name="openwire+ssl" uri="ssl://0.0.0.0:61617?needClientAuth=true&amp;maximumConnections=1000&amp;transport.enabledProtocols=TLSv1.2&amp;wireformat.maxFrameSize=104857600&amp;wireFormat.maxInactivityDuration=-1"/>
            <transportConnector name="stomp+ssl"  uri="stomp+nio+ssl://0.0.0.0:61612?maximumConnections=1000&amp;transport.enabledProtocols=TLSv1.2&amp;needClientAuth=true"/>
    </transportConnectors>

ACTIVEMQ_SSL_OPTS is set as:

-Djavax.net.ssl.keyStore=C:\path\to\AMQBroker.ks -Djavax.net.ssl.trustStore=C:\path\to\AMQBroker.ts -Djavax.net.ssl.keyStorePassword=password -Djavax.net.ssl.trustStorePassword=password -Djavax.net.debug=ssl

Here's the error I'm getting when Trying to connect with Stomp

2021-01-18 19:35:40,184 | ERROR | Could not accept connection from null : {} | org.apache.activemq.broker.TransportConnector | ActiveMQ BrokerService[infrastructure] Task-10
java.io.IOException: javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection?
    at org.apache.activemq.transport.nio.NIOSSLTransport.initializeStreams(NIOSSLTransport.java:196)[activemq-client-5.15.9.jar:5.15.9]
    at org.apache.activemq.transport.stomp.StompNIOSSLTransport.initializeStreams(StompNIOSSLTransport.java:57)[activemq-stomp-5.15.9.jar:5.15.9]
    at org.apache.activemq.transport.tcp.TcpTransport.connect(TcpTransport.java:543)[activemq-client-5.15.9.jar:5.15.9]
    at org.apache.activemq.transport.nio.NIOTransport.doStart(NIOTransport.java:174)[activemq-client-5.15.9.jar:5.15.9]
    at org.apache.activemq.transport.nio.NIOSSLTransport.doStart(NIOSSLTransport.java:470)[activemq-client-5.15.9.jar:5.15.9]
    at org.apache.activemq.util.ServiceSupport.start(ServiceSupport.java:55)[activemq-client-5.15.9.jar:5.15.9]
    at org.apache.activemq.transport.TransportFilter.start(TransportFilter.java:64)[activemq-client-5.15.9.jar:5.15.9]
    at org.apache.activemq.transport.stomp.StompTransportFilter.start(StompTransportFilter.java:65)[activemq-stomp-5.15.9.jar:5.15.9]
    at org.apache.activemq.transport.AbstractInactivityMonitor.start(AbstractInactivityMonitor.java:169)[activemq-client-5.15.9.jar:5.15.9]
    at org.apache.activemq.transport.TransportFilter.start(TransportFilter.java:64)[activemq-client-5.15.9.jar:5.15.9]
    at org.apache.activemq.broker.TransportConnection.start(TransportConnection.java:1072)[activemq-broker-5.15.9.jar:5.15.9]
    at org.apache.activemq.broker.TransportConnector$1$1.run(TransportConnector.java:218)[activemq-broker-5.15.9.jar:5.15.9]
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)[:]
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)[:]
    at java.base/java.lang.Thread.run(Unknown Source)[:]
2021-01-18 19:35:40,184 | DEBUG | Reason: java.io.IOException: javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection? | org.apache.activemq.broker.TransportConnector | ActiveMQ BrokerService[infrastructure] Task-10
java.io.IOException: javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection?
    at org.apache.activemq.transport.nio.NIOSSLTransport.initializeStreams(NIOSSLTransport.java:196)[activemq-client-5.15.9.jar:5.15.9]
    at org.apache.activemq.transport.stomp.StompNIOSSLTransport.initializeStreams(StompNIOSSLTransport.java:57)[activemq-stomp-5.15.9.jar:5.15.9]
    at org.apache.activemq.transport.tcp.TcpTransport.connect(TcpTransport.java:543)[activemq-client-5.15.9.jar:5.15.9]
    at org.apache.activemq.transport.nio.NIOTransport.doStart(NIOTransport.java:174)[activemq-client-5.15.9.jar:5.15.9]
    at org.apache.activemq.transport.nio.NIOSSLTransport.doStart(NIOSSLTransport.java:470)[activemq-client-5.15.9.jar:5.15.9]
    at org.apache.activemq.util.ServiceSupport.start(ServiceSupport.java:55)[activemq-client-5.15.9.jar:5.15.9]
    at org.apache.activemq.transport.TransportFilter.start(TransportFilter.java:64)[activemq-client-5.15.9.jar:5.15.9]
    at org.apache.activemq.transport.stomp.StompTransportFilter.start(StompTransportFilter.java:65)[activemq-stomp-5.15.9.jar:5.15.9]
    at org.apache.activemq.transport.AbstractInactivityMonitor.start(AbstractInactivityMonitor.java:169)[activemq-client-5.15.9.jar:5.15.9]
    at org.apache.activemq.transport.TransportFilter.start(TransportFilter.java:64)[activemq-client-5.15.9.jar:5.15.9]
    at org.apache.activemq.broker.TransportConnection.start(TransportConnection.java:1072)[activemq-broker-5.15.9.jar:5.15.9]
    at org.apache.activemq.broker.TransportConnector$1$1.run(TransportConnector.java:218)[activemq-broker-5.15.9.jar:5.15.9]
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)[:]
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)[:]
    at java.base/java.lang.Thread.run(Unknown Source)[:]
2

There are 2 best solutions below

1
On

It would be quite hard to know from the data given what is going on between the client and broker over the wire which is where you are going to need to look in order to understand what is going wrong. The Broker STOMP module has a number of tests that run using an SSL configuration which you can see here:

One means of debugging is to set the JVM option to enable debug for SSL:

-Djavax.net.debug=ssl

Review the handshake information to see where the negotiation goes wrong.

0
On

I did get this running from a smaller test file

the following code snippet was modified from https://developers.redhat.com/blog/2018/06/14/stomp-with-activemq-artemis-python/

stomp_test.py

import time
import sys
import stomp
import ssl

class MyListener(stomp.ConnectionListener):
    def on_error(self, headers, message):
        print('received an error "%s"' % message)
    def on_message(self, headers, message):
        print('received a message "%s"' % message)
    
hosts = [('localhost', 61613)]
stomp_key   = 'D:/FOSS/ActiveMQ/conf/AMQClient.key'
stomp_cert  = 'D:/FOSS/ActiveMQ/conf/AMQClient.pem'
stomp_ca    = 'D:/FOSS/ActiveMQ/conf/AMQClient-ts.pem'
conn = stomp.Connection(host_and_ports=hosts,
    use_ssl=True,
    ssl_key_file=stomp_key,
    ssl_cert_file=stomp_cert,
    ssl_ca_certs=stomp_ca,
    ssl_version=ssl.PROTOCOL_TLSv1_2)
conn.set_ssl(for_hosts=hosts,
    cert_file=stomp_cert,
    key_file=stomp_key,
    ca_certs=stomp_ca,
    ssl_version=ssl.PROTOCOL_TLSv1_2,
    password='password')
conn.set_listener('', MyListener())
conn.connect('admin', 'admin', wait=True,headers = {'client-id': 'clientname'} )
conn.subscribe(destination='A.B.C.D', id=1, ack='auto',headers = {'subscription-type': 'MULTICAST','durable-subscription-name':'someValue'})

conn.send(body=' '.join(sys.argv[1:]), destination='A.B.C.D')

time.sleep(2)
conn.disconnect()

I also had to add a few more extensions to my broker's keystore (I went a little overkill)

-ext KeyUsage=digitalSignature,keyEncipherment,keyCertSign -ext ExtendedKeyUsage=serverAuth,clientAuth -ext BasicConstraints=ca:true

keytool -genkeypair -alias broker -keyalg RSA -keysize 4096 -sigalg SHA256withRSA -validity 4383 -keystore AMQBroker.ks -storepass "password" -keypass "password" -dname "CN=localhost" -ext "SAN=DNS:localhost,IP:0.0.0.0,IP:127.0.0.1" -ext KeyUsage=digitalSignature,keyEncipherment,keyCertSign -ext ExtendedKeyUsage=serverAuth,clientAuth -ext BasicConstraints=ca:true

I also removed nio from my stomp transportConnector in the activemq.xml

<transportConnector name="stomp+ssl"  uri="stomp+ssl://0.0.0.0:61613?maximumConnections=1000&amp;transport.enabledProtocols=TLSv1.2&amp;needClientAuth=true"/>

I hope this helps anyone with a similar issue.