activemq ssl exchanges and handshake error messages
Fri, Jul 13, 2012
I decided to implement SSL in MatrixClient to let messaging clients connect securely to an ActiveMQ broker and I got a bit lost in the various SSL errors that can happen so I thought I’d summarise the flow, which is shown in the diagram.
It all hangs on each end of the connection having its own keystore and truststore. The keystore is used for signing the request and the truststore is used for verifying the signed request. So for it all to work the client’s certificate must be in the broker’s truststore and the broker’s certificate must be in the client’s truststore.
So first I created a keystore for the client:
keytool -genkey -alias client -keyalg RSA -keystore client-keystore.jks What is your first and last name?
then created a keystore for the broker:keytool -genkey -alias broker -keyalg RSA -keystore broker-keystore.jks What is your first and last name?
then I exported the client’s certificate from its keystore and imported it to the broker’s truststore, creating a new truststore in the process:keytool -export -alias client -keystore client-keystore.jks -file client_cert keytool -import -alias client -keystore broker-truststore.jks -file client_certthen I exported the broker’s certificate from its keystore and imported it to the client’s truststore, creating a new truststore in the process:keytool -export -alias broker -keystore broker-keystore.jks -file broker_cert keytool -import -alias broker -keystore client-truststore.jks -file broker_certI then wrote some unit tests to verify it all worked. MQClientSSLBrokerNotTrustedTest sets the client’s truststore to be its own keystore. So for this test to pass the broker would need to be using the client’s certificate as, due to the client’s truststore being its own keystore, the 0nly entity it will trust is something masquerading as itself. This should never happen and for the test to pass correctly I had to trap the exception:javax.jms.JMSException: Could not connect to broker URL: ssl: / / localhost:61617. Reason: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested targetSo this is what happens when the client does not trust the broker.
Next up was testing the broker not trusting the client. MQClientSSLClientNotTrustedTest sets the broker’s truststore to be its own keystore. So for this test to pass the client would need to be using the broker’s certificate as, due to the broker’s truststore being its own keystore, the 0nly entity it will trust is something masquerading as itself. This should never happen and for the test to pass correctly I had to trap the exception:javax.jms.JMSException: Could not connect to broker URL: ssl: / / localhost:61617. Reason: javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificateSo this is what happens when the broker does not trust the client.
One more thing I came across was this error:javax.net.ssl.SSLException: No available certificate or key corresponds to the SSL cipher suites which are enabledThis was casused by using ActiveMQConnectionFactory instead of ActiveMQSslConnectionFactory when connecting from the client.
I also came across this error in activemq.log:javax.net.ssl.SSLHandshakeException: Received fatal alert: certificate_unknownwhich initially I thought was caused by the client using its own keystore for its truststore so it wasn’t trusting the broker. Once I’d fixed that I still got the error and according to this page, you have to set:javax.net.ssl.trustStore javax.net.ssl.trustStorePasswordbefore starting the client. So in this case I added them to Tomcat’s catalina.sh and the secure connection worked. However, that doesn’t make any sense as I’m already setting the keystore and truststore in code. Turns out there’s an issue with ActiveMQSslConnectionFactory where the failover uri ‘masks out’ the ssl uri which causes it to delegate to ActiveMQConnectionFactory which knows nothing about SSL. So that’s why I have to use Java OPTS to force it to use the correct keystore and truststore.
Upshot is, ssl://localhost:61617 works but failover:ssl://localhost:61617 fails and you need to set the Java OPTS to get it to work due to the real keystore and truststore being masked out by the failover uri.
Reference: Setting up the Key and Trust Stores