I'm trying to set up ActiveMQ for mutual authentication, that the client will need a certificate in order to pass messages to the broker. I created a keystore and a truststore on the broker and exported a certificate that was copied to the client. On the client side I did the same, although I am using NMS so I am only using the exported certificate, which I added to the broker's truststore. I also added the certificate's to the other's Local Computer Trusted Root Certificates.
The broker's configuration has this:
<transportConnectors>
<transportConnector name="ssl" uri="ssl://0.0.0.0:61616"/>
</transportConnectors>
<sslContext>
<sslContext keyStore="file:${activemq.base}/conf/keystore.jks"
keyStorePassword="ksPass"
trustStore="file:${activemq.base}/conf/shared.ks"
trustStorePassword="ksPass"/>
</sslContext>
<plugins>
<jaasCertificateAuthenticationPlugin configuration="CertLogin" />
</plugins>
amq service wrapper.conf has
wrapper.java.additional.8=-Djava.security.auth.login.config="%ACTIVEMQ_CONF%/login.config"
${activemq.base}/conf/login.config
CertLogin {
org.apache.activemq.jaas.TextFileCertificateLoginModule required
debug=true
org.apache.activemq.jaas.textfiledn.user="users.properties"
org.apache.activemq.jaas.textfiledn.group="groups.properties";
};
${activemq.base}/conf/users.properties has
user=CN=nms.client.170,\ OU=IT,\ O=MyOrg,\ L=Oslo,\ S=Oslo,\ C=NO
${activemq.base}/conf/groups.properties has
admins=system
users=system,user
In the NMS client's appSettings I use this to connect:
< add key="jms.uri" value="ssl://brokeraddress.in.hosts:61616?needClientAuth=true&wantClientAuth=true&transport.clientCertSubject=nms.client.170&transport.clientCertPassword=ksClientPw&transport.clientCertFilename=C:\TestClient\client170.crt" />
If I don't have the jaasCertificateAuthenticationPlugin in the broker I can connect via ssl, but with it (which is what I had in mind) I get an error, it fails in Apache.NMS.ActiveMQ.Connection
// Send the connection and see if an ack/nak is returned.
Response response = transport.Request(this.info, this.RequestTimeout);
Gets an ExeptionResponse: "java.lang.SecurityException: Unable to authenticate transport without SSL certificate."
The trace shows:
10:19:16,479 INFO Client.MyTrace - BrokerUri set = ssl://brokeraddress.in.hosts:61616?transport.clientcertpassword=ksClient&transport.clientcertsubject=nms.client.170&needclientauth=true&wantclientauth=true&transport.clientcertfilename=C:\TestClient\client170.crt
10:19:16,492 DEBUG Client.MyTrace - SetProperties called with target: ConnectionFactory, and prefix: connection.
10:19:16,492 DEBUG Client.MyTrace - SetProperties called with target: ConnectionFactory, and prefix: nms.
10:19:16,495 INFO Client.MyTrace - Connecting to: ssl://brokeraddress.in.hosts:61616/?transport.clientcertpassword=ksClient&transport.clientcertsubject=nms.client.170&needclientauth=true&wantclientauth=true&transport.clientcertfilename=C:\TestClient\client170.crt
10:19:16,497 DEBUG Client.MyTrace - Searching Assembly: Apache.NMS.ActiveMQ for factory of the id: ssl
10:19:16,549 DEBUG Client.MyTrace - Found the Factory of type Apache.NMS.ActiveMQ.Transport.Tcp.SslTransportFactory for id: ssl
10:19:16,552 DEBUG Client.MyTrace - Opening socket to: brokeraddress.in.hosts on port: 61616
10:19:16,554 DEBUG Client.MyTrace - Connected to brokeraddress.in.hosts:61616 using InterNetwork protocol.
10:19:16,562 DEBUG Client.MyTrace - Creating new instance of the SSL Transport.
10:19:16,564 DEBUG Client.MyTrace - Creating Inactivity Monitor: 1
10:19:16,677 DEBUG Client.MyTrace - Authorizing as Client for Server: brokeraddress.in.hosts
10:19:16,679 DEBUG Client.MyTrace - Attempting to load Client Certificate from file := C:\TestClient\client170.crt
10:19:16,682 DEBUG Client.MyTrace - Loaded Client Certificate := [Subject] CN=nms.client.170, OU=IT, O=MyOrg, L=Oslo, S=Oslo, C=NO [Issuer] CN=nms.client.170, OU=IT, O=MyOrg, L=Oslo, S=Oslo, C=NO
10:19:16,684 DEBUG Client.MyTrace - Client is selecting a local certificate from 1 possibilities.
10:19:16,684 DEBUG Client.MyTrace - Client has selected certificate with Subject = CN=nms.client.170, OU=IT, O=MyOrg, L=Oslo, S=Oslo, C=NO
10:19:16,969 DEBUG Client.MyTrace - ValidateServerCertificate: Issued By CN=brokeraddress.in.hosts, OU=DataCom, O=MyOrg, L=Oslo, S=Oslo, C=NO
10:19:16,969 DEBUG Client.MyTrace - Server is Authenticated = True
10:19:16,970 DEBUG Client.MyTrace - Server is Encrypted = True
10:19:16,978 DEBUG Client.MyTrace - InactivityMonitor[1]: Read Check time interval: 30000
10:19:16,978 DEBUG Client.MyTrace - InactivityMonitor[1]: Initial Delay time interval: 10000
10:19:16,985 DEBUG Client.MyTrace - InactivityMonitor[1]: Write Check time interval: 10000
10:19:19,017 DEBUG Client.MyTrace - Exception received in the Inactivity Monitor: Unable to read beyond the end of the stream.
10:19:19,019 DEBUG Client.MyTrace - InactivityMonitor[1].Runner: Task Runner Shut Down
10:19:19,019 DEBUG Client.MyTrace - InactivityMonitor[1]: Stopped Monitor Threads.
10:19:19,032 DEBUG Client.MyTrace - Connection[ID:EJPB-56409-635193299565662525-1:0]: Async exception with no exception listener: System.IO.EndOfStreamException: Unable to read beyond the end of the stream.
System.IO.BinaryRe.FillBuffer(Int32 numBytes)
System.IO.BinaryRe.ReadInt32()
Apache.NMS.Util.EnBinaryReader.ReadInt32() in c:\dev\NMS\src\main\csharp\Util\EndianBinaryReader.cs:line 135
Apache.NMS.ActiveMenWire.OpenWireFormat.Unmarshal(BinaryReader dis) in c:\dev\NMS.ActiveMQ\src\main\csharp\OpenWire\OpenWireFormat.cs:line 228
Apache.NMS.ActiveMansport.Tcp.TcpTransport.ReadLoop() in c:\dev\NMS.ActiveMQ\src\main\csharp\Transport\Tcp\TcpTransport.cs:line 295
10:19:19,035 DEBUG Client.MyTrace - TransportFilter disposing of next Transport: MutexTransport
10:19:19,035 DEBUG Client.MyTrace - TransportFilter disposing of next Transport: WireFormatNegotiator
10:19:19,036 DEBUG Client.MyTrace - TransportFilter disposing of next Transport: InactivityMonitor
10:19:19,036 DEBUG Client.MyTrace - InactivityMonitor[1]: Stopped Monitor Threads.
10:19:19,037 DEBUG Client.MyTrace - TransportFilter disposing of next Transport: SslTransport
10:19:19,071 INFO Client.MyTrace - Connection[ID:SUSSDEV2-56409-635193299565662525-1:0]: Closing Connection Now.
10:19:19,073 DEBUG Client.MyTrace - Connection[ID:SUSSDEV2-56409-635193299565662525-1:0]: Disposing of the Transport.
10:19:19,073 DEBUG Client.MyTrace - InactivityMonitor[1]: Stopped Monitor Threads.
and in the broker it says:
INFO | jvm 1 | 10:18:20 | WARN | Failed to add Connection ID:EJPB-56409-635193299565662525-1:0, reason: java.lang.SecurityException: Unable to authenticate transport without SSL certificate. INFO | jvm 1 | 10:18:22 | INFO | Stopping tcp://192.168.5.170:56408 because Failed with SecurityException: Unable to authenticate transport without SSL certificate.
Ok, what am I missing? It says "transport without SSL certificate", but it selects it during the client's connection and it is in both the broker's truststore and Root Certs.
Using NMS 1.6.0 and activeMQ 5.8.0.
I also tried a simple client in java with the same result.
Exception in thread "main" javax.jms.JMSException: Unable to authenticate transport without SSL certificate. at org.apache.activemq.util.JMSExceptionSupport.create(JMSExceptionSupport.java:49) at org.apache.activemq.ActiveMQConnection.syncSendPacket (ActiveMQConnection.java:1295) at org.apache.activemq.ActiveMQConnection.ensureConnectionInfoSent (ActiveMQConnection.java:1392) at org.apache.activemq.ActiveMQConnection.start(ActiveMQConnection.java:504) at com.atest.jms.Client.main(Client.java:69) Caused by: java.lang.SecurityException: Unable to authenticate transport without SSL certificate. at org.apache.activemq.security.JaasCertificateAuthenticationBroker.addConnection(JaasCertificateAuthenticationBroker.java:74) at org.apache.activemq.broker.MutableBrokerFilter.addConnection(MutableBrokerFilter.java:91) at org.apache.activemq.broker.TransportConnection.processAddConnection(TransportConnection.java:766) at org.apache.activemq.broker.jmx.ManagedTransportConnection.processAddConnection(ManagedTransportConnection.java:79) at org.apache.activemq.command.ConnectionInfo.visit(ConnectionInfo.java:139) at org.apache.activemq.broker.TransportConnection.service(TransportConnection.java:329) at org.apache.activemq.broker.TransportConnection$1.onCommand(TransportConnection.java:184) at org.apache.activemq.transport.MutexTransport.onCommand(MutexTransport.java:50) at org.apache.activemq.transport.WireFormatNegotiator.onCommand(WireFormatNegotiator.java:113) at org.apache.activemq.transport.AbstractInactivityMonitor.onCommand(AbstractInactivityMonitor.java:288) at org.apache.activemq.transport.TransportSupport.doConsume(TransportSupport.java:83) at org.apache.activemq.transport.tcp.SslTransport.doConsume(SslTransport.java:91) at org.apache.activemq.transport.tcp.TcpTransport.doRun(TcpTransport.java:214) at org.apache.activemq.transport.tcp.TcpTransport.run(TcpTransport.java:196) at java.lang.Thread.run(Unknown Source)
Anyone has any ideas? Thanks.
Ok, I tried again. I see I hadn't added to the broker's transportConnector ssl:
<transportConnector name="ssl" uri="ssl://0.0.0.0:61616?needClientAuth=true"/>
I tried the java client and it could send, but I'm still getting error for the NMS client (same config):
15:28:14,044 ERROR Test_DataCom.MyTrace - Exception: A call to SSPI failed, see inner exception.
15:28:14,045 ERROR Test_DataCom.MyTrace - Inner exception: An unknown error occurred while processing the certificate
15:28:14,045 ERROR Test_DataCom.MyTrace - Authentication failed - closing the connection.
and at the broker I get null cert chain
INFO | jvm 1 | 15:28:13 | ERROR | Could not accept connection from tcp://192.168.50.170:61978: javax.net.ssl.SSLHandshakeException: null cert chain
I have the certs in each other's truststore and Local Computer Trusted Root CA... what more is needed? Where should it find the cert chain for self signed certs if not there?
OK, I seem to have it now. After re-reading this comment I exported the client cert and key to a PKCS file and imported it to Current User Certification Store in Windows.
C:\> keytool -v -importkeystore -srckeystore client-keystore.jks -srcalias client -destkeystore client.p12 -deststoretype PKCS12
Then I used this url-encoded connection:
<add key="jms.uri" value="ssl://brokeraddress.in.hosts:61616?needClientAuth=true&wantClientAuth=true&transport.clientCertSubject=CN%3Dnms.client.170%2C+OU%3DIT%2C+O%3DMyOrg%2C+L%3DOslo%2C+S%3DOslo%2C+C%3DNO" />
I also had
${activemq.base}/conf/users.properties: user=CN=nms.client.170,\ OU=IT,\ O=MyOrg,\ L=Oslo,\ S=Oslo,\ C=NO
, trying to escape the spaces, I removed those and corrected S to ST, just as keytool reports the Owner line. I checked, and that gives error otherwise.
Corrected ${activemq.base}/conf/users.properties:
user=CN=nms.client.170, OU=IT, O=MyOrg, L=Oslo, ST=Oslo, C=NO