In case the RabbitMQ broker and client communicate through the Internet, it sounds reasonable that only authorized clients can connect to the broker.
This is the scope of typical user password authentication, but by using, in addition, client-side certificates, the security of the distributed application is highly improved. It also avoids the possibility of MITM attack.
This recipe is the extension/prosecution of the previous one. So, we assume that we already have the CA set up and the server configured, as shown previously.
Perform the following steps for the client to be able to connect to the RabbitMQ server:
Chapter03/Recipe04/certificates
directory:cp –rp ../Recipe03/certificates/testca . cp –rp ../Recipe03/certificates/server .
Chapter03/Recipe04/certificates/04_create_client_certificates.sh
:openssl genrsa -out key.pem 2048
openssl req -new -key key.pem -out req.pem -outform PEM -subj /CN=$(hostname)/O=client/ -nodes
openssl ca -config openssl.cnf -in ../client/req.pem -out ../client/cert.pem -notext -batch -extensions client_ca_extensions
PKCS#12
store containing the client certificate and the key protected by a password:openssl pkcs12 -export -out keycert.p12 -in cert.pem -in keykey.pem -passout pass:client1234passwd
Chapter03/Recipe04/certificates/05_create_keystore.sh
:keytool -importcert -alias server001 -file server/cert.pem -keystore keystore/rabbit.jks -keypass passwd1234
rabbitmq.config
option fail_if_no_peer_cert
to true
:{fail_if_no_peer_cert,true}
rabbitmqctl stop rabbitmq-server –detached
char[] keyPassphrase = "client1234passwd".toCharArray(); KeyStoreks = KeyStore.getInstance("PKCS12"); ks.load(newFileInputStream("certificates/client/keycert.p12"), keyPassphrase); KeyManagerFactorykmf = KeyManagerFactory.getInstance("SunX509"); kmf.init(ks, keyPassphrase); char[] trustPassphrase = "passwd1234".toCharArray(); KeyStoretks = KeyStore.getInstance("JKS"); tks.load(newFileInputStream("certificates/keystore/rabbit.jks"), trustPassphrase); TrustManagerFactorytmf = TrustManagerFactory.getInstance("SunX509"); tmf.init(tks); SSLContext c = SSLContext.getInstance("SSLv3"); c.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); ConnectionFactory factory = newConnectionFactory(); factory.setHost(hostname); factory.setPort(5671); factory.useSslProtocol(c); Connection connection = factory.newConnection();
For the client certificate to work, it must be signed with the same CA that has been used to sign the server. Once the certificate is prepared, it is very useful to save it in a keystore, a PKCS#12
store as shown in step 5.
The client needs the server certificate too—it contains the server public key—and so we have prepared a keystore for this one too using a Java keystore with the Java keytool command this time.
Then we reconfigured RabbitMQ (steps 6-7). In this way, the server will deny access if the client has not presented any certificate. You can easily check this by running the previous example now.
Once the client certificates are ready, the RabbitMQ client (step 8) must use them to set up SSLContext. Unlike the previous example, we pass the following call:
factory.useSslProtocol(c);
Only a client following this setup can connect to the RabbitMQ server. The client presenting these certificates cannot connect to a server that has a different server private key since the traffic is being encrypted using the server public key stored in the server certificate.