I have ActiveMQ Artemis cluster, active-backup mode with shared store, version 2.17.0. SSL is used with this cluster.
I've taken keystore/truststore files from Kafka cluster that works totally fine with 2-way TLS. I re-use these files with Artemis and it also works perfectly fine. However, the issue comes when I enable needClientAuth=true
in URL string.
Here is what documentation says:
needClientAuth
This property is only for an
acceptor
. It tells a client connecting to this acceptor that 2-way SSL is required. Valid values aretrue
orfalse
. Default isfalse
.
So if I want to achieve 2-way TLS, I must use this option. Then both - the server and the client must have it's own keystore/truststore pair. In my case it's identical for both server and the client.
When I have needClientAuth=true
in acceptor URL string, I connect to Artemis cluster using Artemis CLI and that's what I get in CLI output:
Connection failed::Failed to create session factory
This is what Artemis instance says:
WARN [org.apache.activemq.artemis.core.server] AMQ222208: SSL handshake failed for client from /123.123.123.123:36788: javax.net.ssl.SSLHandshakeException: Empty client certificate chain.
As I said, mentioned keystore/truststore works fine with Kafka, no issues. However, it does not work with ActiveMQ Artemis. If I remove needClientAuth=true
from acceptor
URL string, everything works just fine.
Here is how I generate keystore/truststore:
# Generate private key and CSR
openssl req -new -newkey rsa:2048 -nodes -days 365 -subj '/CN=something.com/OU=XXX/O=Company/L=City/ST=City/C=XX' -keyout private.key -out mycsr.csr
# Upload CSR to magic website, get back CA and signed certificate.
# ...
# Since CA is chain, and you cannot import chain into keystore/truststore, split into multiple files: ca1.cer and ca2.cer.
# ...
# Create truststore.jks
keytool -importcert -noprompt -alias ca1 -file ca1.cer -keystore truststore.jks
keytool -importcert -noprompt -alias ca2 -file ca2.cer -keystore truststore.jks
# Because it's impossible to create JKS keystore out of private key, first generate PKCS12 keystore:
openssl pkcs12 -inkey private.key -in certificate.cer -export -out keystore.p12
# Now convert PKCS12 keystore to JKS keystore:
keytool -importkeystore -srckeystore keystore.p12 -srcstoretype pkcs12 -destkeystore keystore.jks -deststoretype jks
# Since above command added only a private key, we also need to add an issued certificate:
keytool -importcert -noprompt -alias certificate -file certificate.cer -keystore keystore.jks
# Some client's (in Kafka) does not work if CA is not added to keystore, so add it:
keytool -importcert -noprompt -alias ca1 -file ca1.cer -keystore keystore.jks
keytool -importcert -noprompt -alias ca2 -file ca2.cer -keystore keystore.jks
# End up with the following files:
keystore.jks
truststore.jks
---------
ca.cer
certificate.cer
private.key
Acceptor URL (with needClientAuth=true
):
tcp://server.com:1234?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;amqpMinLargeMessageSize=102400;protocols=CORE,AMQP,STOMP,HORNETQ,MQTT,OPENWIRE;useEpoll=true;amqpCredits=1000;amqpLowCredits=300;amqpDuplicateDetection=true;sslEnabled=true;needClientAuth=true;keyStorePath=/opt/ssl/keystore.jks;keyStorePassword=123;trustStorePath=/opt/ssl/truststore.jks;trustStorePassword=123
When connecting using ActiveMQ Artemis CLI, I pass this URL:
tcp://server.com:1234?sslEnabled=true;keyStorePath=/opt/ssl/truststore.jks;keyStorePassword=123;trustStorePath=/opt/ssl/truststore.jks;trustStorePassword=123
What am I missing?
Both the broker and the client need to have their own certificate. You appear to be creating just 1 certificate and using that on both the broker and the client.
For example, the broker needs to have its own certificate in its keystore which it presents to the client and that the client has in its truststore. Then the client needs to have its own certificate in its keystore which it presents to the broker and that the broker has is its truststore.
From what I can tell you're not doing this. Take a look at the ssl-enabled-dual-authentication
example that ships with ActiveMQ Artemis. It contains all the keytool
commands you need to get running with self-signed certificates. Once you get this up and running with simple self-signed certificates you can move on to certs signed by a certificate authority.
It's worth noting that if you're using certificates signed by a certificate authority you can just import the CA's root certificate into the JVM's truststore on the broker and the client and then you won't need to mess with any individual truststores. The client and the broker will implicitly trust certificates signed by your CA.
Lastly, the URL you're using in the ActiveMQ Artemis CLI looks incorrect since keyStorePath
is referencing the truststore /opt/ssl/truststore.jks
.