Specifying the SSL protocol and cipher in Java client

3,218 views
Skip to first unread message

njgrplr2007

unread,
Aug 7, 2014, 10:08:57 PM8/7/14
to rabbitm...@googlegroups.com
After configuring RabbitMQ to support SSL, I was able to connect and send data from my Java client using the TLSv1.2 protocol. Packet traces confirm that the data is encrypted. However, I need to ensure that I am using a minimum of 128-bit encryption (AES preferred). Anybody know how to do this? Here's the code I am currently using:

     ConnectionFactory factory = new ConnectionFactory();


     factory.setHost(AppSettings.rabbitmq_host);

     factory.setPort(5671);

     factory.setUsername(AppSettings.rabbitmq_username);

     factory.setPassword(AppSettings.rabbitmq_password);

     factory.setVirtualHost(AppSettings.rabbitmq_virtual_host);


     try {


         factory.useSslProtocol("TLSv1.2");

         connection = factory.newConnection();

            log.info("Connected to RabbitMQ!");

         } catch (NoSuchAlgorithmException e) {

            log.error("Unsupported algorithm: " + e.getMessage());

            return false;

     } catch (Exception e) {

         log.error("Error establishing RabbitMQ connection: " + e.getMessage());

         return false;

     }


Thanks in advance,

Jim

Michael Klishin

unread,
Aug 7, 2014, 10:46:50 PM8/7/14
to njgrplr2007, rabbitm...@googlegroups.com
On 8 August 2014 at 06:09:06, njgrplr2007 (enfor...@gmail.com) wrote:
> > After configuring RabbitMQ to support SSL, I was able to connect
> and send data from my Java client using the TLSv1.2 protocol.
> Packet traces confirm that the data is encrypted. However, I
> need to ensure that I am using a minimum of 128-bit encryption
> (AES preferred). Anybody know how to do this?

This is a general Java question. If you want stronger encryption or use
a particular algorithm, then generate your keys accordingly and use the appropriate
JCA provider/extension/API.

RabbitMQ Java client lets you use any SSLContext you want, as documented
on http://www.rabbitmq.com/ssl.html.

Note that not all JDK releases support all cipher suites. Here's how you
can list the one yours support:
http://stackoverflow.com/questions/21289293/java-7-support-of-aes-gcm-in-ssl-tls 
--
MK

Staff Software Engineer, Pivotal/RabbitMQ

njgrplr2007

unread,
Aug 7, 2014, 11:22:16 PM8/7/14
to rabbitm...@googlegroups.com
Thank you for your response. I guess I figured the answer could be found in the correct use of the ConnectionFactory, which is part of the RabbitMQ client library. I have experimented with various SSLContext instances and setting/adjusting the list of cipher suites so that it only contains 128 bit ciphers. However, use of the modified SSLContexts [factory.useSslProtocol(sslContext)] always results in the same error:

sun.security.validator.ValidatorExceptionPKIX path building failed


From what I have read, it looks like the client does not like the cert I generated using my certificate authority. Unfortunately, I haven't discovered how to deal with this yet.


Jim

Michael Klishin

unread,
Aug 7, 2014, 11:29:13 PM8/7/14
to njgrplr2007, rabbitm...@googlegroups.com
On 8 August 2014 at 07:22:22, njgrplr2007 (enfor...@gmail.com) wrote:
> > From what I have read, it looks like the client does not like the
> cert I generated using my certificate authority. Unfortunately,
> I haven't discovered how to deal with this yet.

To clarify, RabbitMQ client makes no assumptions about your certificates,
or key store, or anything like that.

I cannot suggest anything without seeing how you generate your key and set up
your JDK key store. http://www.rabbitmq.com/ssl.html provides an example, though.

A couple of things I can suggest: PKIX here means "public key infrastructure"
and is quite often used as a synonym for "X.509".

If you want to quickly generate some self-signed CA/certificate/keys (just
for testing), tls-gen is a very convenient way to do it:
https://github.com/michaelklishin/tls-gen/ 

njgrplr2007

unread,
Aug 7, 2014, 11:53:36 PM8/7/14
to rabbitm...@googlegroups.com
I followed the instructions at https://www.rabbitmq.com/ssl.html to create the keys and certificates. The same page also provided the information I needed to enable SSL support in RabbitMQ. Upon startup, Rabbit gets the keys and certs from the local file system on my MacBook.

I run the Java client on the same laptop running RabbitMQ. It uses the example to connect without validating certificates:

        factory.useSslProtocol();
        // Tells the library to setup the default Key and Trust managers for you
        // which do not do any form of remote server trust verification

Again, this works without any key store. The problem is that I can't guarantee that the cipher that's used is >= 128-bit. 

Creating my own SSLContext fails:

    SSLContext sslContext = SSLContext.getInstance("TLSv1.2", "SunJSSE");

    sslContext.init(null, null, null);

    factory.useSslProtocol(sslContext);


Probably because it's doing the "remote server trust verification?"


Jim


On Thursday, August 7, 2014 10:08:57 PM UTC-4, njgrplr2007 wrote:

Michael Klishin

unread,
Aug 8, 2014, 12:06:20 AM8/8/14
to njgrplr2007, rabbitm...@googlegroups.com
On 8 August 2014 at 07:53:41, njgrplr2007 (enfor...@gmail.com) wrote:
> > Again, this works without any key store. The problem is that
> I can't guarantee that the cipher that's used is >= 128-bit.
>
> Creating my own SSLContext fails:
>
>
> SSLContext sslContext = SSLContext.getInstance("TLSv1.2",
> "SunJSSE");
> sslContext.init(null, null, null);
> factory.useSslProtocol(sslContext);
> Probably because it's doing the "remote server trust verification?"

"fails" is not specific. SSLContext *typically* assumes peer
verification is performed and for that you need a TrustManager, etc, but
that's not required by the protocol.

N.B. using strong encryption without peer verification will technically work
but you expose yourself to man-in-the-middle attacks by disabling it.

You can instantiate SSLContext using methods like
http://docs.oracle.com/javase/7/docs/api/javax/net/ssl/SSLContext.html#getInstance(java.lang.String,%20java.security.Provider)

list supported security providers for your system via
http://docs.oracle.com/javase/7/docs/api/java/security/Security.html#getProviders()

and obtain a reference to a specific provider by name with
http://docs.oracle.com/javase/7/docs/api/java/security/Security.html#getProvider(java.lang.String)

HTH. 

Michael Klishin

unread,
Aug 8, 2014, 12:09:21 AM8/8/14
to njgrplr2007, rabbitm...@googlegroups.com
 On 8 August 2014 at 07:53:41, njgrplr2007 (enfor...@gmail.com) wrote:
> >
> SSLContext sslContext = SSLContext.getInstance("TLSv1.2",
> "SunJSSE");
>
>
> sslContext.init(null, null, null);
>
>
> factory.useSslProtocol(sslContext);

Actually, JDK docs suggest you can init an SSLContext with all null values:
http://docs.oracle.com/javase/7/docs/api/javax/net/ssl/SSLContext.html#init(javax.net.ssl.KeyManager[],%20javax.net.ssl.TrustManager[],%20java.security.SecureRandom)

so please post a more specific error message than "fails". It can be that RabbitMQ
is configured to perform peer verification, and it is unhappy with a client
not providing anything (key manager is null).

njgrplr2007

unread,
Aug 8, 2014, 12:19:02 AM8/8/14
to rabbitm...@googlegroups.com, enfor...@gmail.com
This is the complete error message:

sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

njgrplr2007

unread,
Aug 8, 2014, 12:21:52 AM8/8/14
to rabbitm...@googlegroups.com, enfor...@gmail.com
I double checked that peer verification is disabled in the RabbitMQ config: {verify,verify_none}

Michael Klishin

unread,
Aug 8, 2014, 12:28:29 AM8/8/14
to njgrplr2007, rabbitm...@googlegroups.com, enfor...@gmail.com
On 8 August 2014 at 08:19:08, njgrplr2007 (enfor...@gmail.com) wrote:
> > sun.security.provider.certpath.SunCertPathBuilderException:
> unable to find valid certification path to requested target

Quick search suggests this indicates that the [server] CA is not trusted:

https://confluence.atlassian.com/display/JIRAKB/Unable+to+Connect+to+SSL+Services+due+to+PKIX+Path+Building+Failed+sun.security.provider.certpath.SunCertPathBuilderException
http://stackoverflow.com/questions/6908948/java-sun-security-provider-certpath-suncertpathbuilderexception-unable-to-find

I'd try using the peer verification example from 

http://www.rabbitmq.com/ssl.html

but replace

SSLContext c = SSLContext.getInstance("SSLv3");

with

http://docs.oracle.com/javase/7/docs/api/javax/net/ssl/SSLContext.html#getInstance(java.lang.String,%20java.lang.String)

and the name of the provider you want. So picking a provider from
what Security.getProviders() returns  is the key.

njgrplr2007

unread,
Aug 8, 2014, 12:36:45 AM8/8/14
to rabbitm...@googlegroups.com, enfor...@gmail.com
I have tried various combinations as you suggested:

SSLContext sslContext = SSLContext.getInstance("TLSv1.2", "SunJSSE");

- and -

SSLContext sslContext = SSLContext.getInstance("SSLv3""SunJSSE");

They yield the same error.

FYI, I wanted to avoid the peer verification example and the logistics of setting the key store on each client machine.

njgrplr2007

unread,
Aug 8, 2014, 3:06:57 PM8/8/14
to rabbitm...@googlegroups.com, enfor...@gmail.com
Michael,

First, thank you for your help last night. Your feedback prompted me to look more closely on the supporting Java classes. I think I found a way to ensure that the RabbitMQ SSL connection uses the TLS protocol and a AES 128-bit cipher. The following code successfully sends encrypted data to RabbitMQ. I confirmed that the modified SSL parameters are being used by attempting to specify unsupported protocols and ciphers. As expected, connections that use the unsupported parameters fail while the supported parameters work. The custom TrustManager was required to eliminate errors associated with my Certificate Authority not being trusted.

    ConnectionFactory factory = new ConnectionFactory();


     factory.setUsername(AppSettings.rabbitmq_username);

     factory.setPassword(AppSettings.rabbitmq_password);

     factory.setVirtualHost(AppSettings.rabbitmq_virtual_host);

     

                // Creates a Trust Manager that accepts any certificate

TrustManager[] trustAllCerts = new TrustManager[] {

new X509TrustManager() {

public java.security.cert.X509Certificate[] getAcceptedIssuers() {

return null;

}

public void checkClientTrusted(X509Certificate[] certs, String authType) {

}

public void checkServerTrusted(X509Certificate[] certs, String authType) {

}

}

};


     try {

             // Get the SSL Context and initialize it with our custom Trust Manager

          SSLContext sslContext = SSLContext.getInstance("TLSv1.2", "SunJSSE");

          sslContext.init(null, trustAllCerts, null);


          SSLParameters params = sslContext.getDefaultSSLParameters();

          ArrayList<String> protocols = new ArrayList<String>(Arrays.asList(params.getProtocols()));

         ArrayList<String> ciphers = new ArrayList<String>(Arrays.asList(params.getCipherSuites()));

         

         // Adjust the list of acceptable protocols

         protocols.remove("SSLv3");

         params.setProtocols(protocols.toArray(new String[protocols.size()]));

         

         //    Adjust the list of acceptable ciphers

         ciphers.retainAll(Arrays.asList("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",

"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",

"TLS_RSA_WITH_AES_128_CBC_SHA",

"TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA",

"TLS_ECDH_RSA_WITH_AES_128_CBC_SHA",

"TLS_DHE_RSA_WITH_AES_128_CBC_SHA",

"TLS_DHE_DSS_WITH_AES_128_CBC_SHA"));

         params.setCipherSuites(ciphers.toArray(new String[ciphers.size()]));

         

             //    Create the socket for the ConnectionFactory and set it to use the adjusted SSL Parameters

         SSLSocket socket = (SSLSocket) sslContext.getSocketFactory().createSocket(AppSettings.rabbitmq_host, 5671);

         socket.setTcpNoDelay(true);

         socket.setSSLParameters(params);

         socket.startHandshake();


          connection = factory.newConnection();

          log.info("Connected to RabbitMQ!");

     } catch (Exception e){

     log.error("Error establishing RabbitMQ connection: " + e.toString());

     return false;

Michael Klishin

unread,
Aug 9, 2014, 12:02:19 AM8/9/14
to njgrplr2007, rabbitm...@googlegroups.com
On 8 August 2014 at 23:07:06, njgrplr2007 (enfor...@gmail.com) wrote:
> > The following code successfully sends encrypted data to RabbitMQ.
> I confirmed that the modified SSL parameters are being used by
> attempting to specify unsupported protocols and ciphers.

Excellent! We may add one more example to our TLS guide. Good job!
Reply all
Reply to author
Forward
0 new messages