How to select TLS cipher suites in Java

The Aerospike Knowledge Base has moved to https://support.aerospike.com. Content on https://discuss.aerospike.com is being migrated to either https://support.aerospike.com or https://docs.aerospike.com. Maintenance on articles stored in this repository ceased on December 31st 2022 and this article may be stale. If you have any questions, please do not hesitate to raise a case via https://support.aerospike.com.

How to select TLS cipher suites in Java

When using TLS encryption with Aerospike Server Enterprise it is recommended to explicitly specify the set of cipher suites that are allowed to be used during the TLS handshake. This will ensure that cipher suites are used which get the best performance while satisfying the organization’s security requirements.

This is especially important with JVM-based applications as the default cipher suite list can vary between JVM environments which may result in the application negotiating a cipher suite which has poor performance or weaker security.

Unlike public-facing servers like websites and web services, Aerospike Server is generally deployed in an environment where the TLS client (the application) and the TLS server (Aerospike Server) are both controlled by the same entity. Thus it is likely that the list of supported cipher suites can be much more specific.

For this article, let’s assume some hypothetical security requirements are based on the NIST Guidelines in SP 800-52 and result in the following prioritized list of cipher suites (in IANA notation):

  1. TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
  2. TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
  3. TLS_ECDHE_ECDSA_WITH_AES_128_CCM
  4. TLS_ECDHE_ECDSA_WITH_AES_256_CCM
  5. TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
  6. TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
  7. TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
  8. TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
  9. TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
  10. TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
  11. TLS_DHE_RSA_WITH_AES_128_CCM
  12. TLS_DHE_RSA_WITH_AES_256_CCM
  13. TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
  14. TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
  15. TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
  16. TLS_DHE_RSA_WITH_AES_256_CBC_SHA256

Since the cipher suites supported by the TLS client (the application) and the TLS server (Aerospike Server) are both controlled by the same entity, this list can be further reduced to the cipher suites which give better performance. This typically means selecting those using the Galois/Counter Mode (GCM) in the encryption algorithm and those using elliptic-curve diffie-hellman (ECDHE) for the key exchange:

  1. TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
  2. TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
  3. TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
  4. TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384

Now these 4 cipher suites become the prioritized list of cipher suites intended to be allowed.

There are two ways to ensure that this list is used and prioritized in the specified order:

  • Specify the allowed cipher suites in Aerospike configuration
  • Specify the allowed cipher suites in Java applications

These two approaches are not mutually exclusive. It is possible to use the Aerospike configuration to enforce the broadest set of acceptable ciphers and then further narrow the list direcly in applications that have specific requirements.

Note: It is recommended to always at least specify a cipher suite list in the Aerospike configuration to exclude cipher suites which do not meet security or performance requirements.

Specifying Cipher Suites in Aerospike Configuration

The list of allowed cipher suites can be configured in the Aerospike Server using the cipher-suite directive. However, since Aerospike Server uses OpenSSL, the cipher suite name must be mapped from the IANA notation that Java uses to the OpenSSL notation that Aerospike uses.

OpenSSL (Aerospike) IANA (Java)
ECDHE-ECDSA-AES128-GCM-SHA256 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
ECDHE-ECDSA-AES256-GCM-SHA384 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
ECDHE-RSA-AES128-GCM-SHA256 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
ECDHE-RSA-AES256-GCM-SHA384 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384

For simplicity, the example aerospike.conf configuration below shows only the stanzas and directives that are relevant for the cipher suite configuration:

network {
    tls example.server {
        cipher-suite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384
    }
}

Specifying Cipher Suites in Java Applications

The list of allowed cipher suites can also be specified in the Aerospike Java Client in the TlsPolicy attached to the ClientPolicy. For example, assume a new security policy requires all new applications to only use AES256 but the Aerospike Server still needs to accept AES128 ciphers from legacy applications.

New applications can further restrict the 4 cipher suites the Aerospike Server was configured to allow to just the AES256 variants:

ClientPolicy policy = new ClientPolicy();
policy.tlsPolicy = new TlsPolicy();

String[] ciphers = {
        // IANA (Java)                                OpenSSL (Aerospike Server)
        "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", // ECDHE-ECDSA-AES256-GCM-SHA384
        "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"    // ECDHE-RSA-AES256-GCM-SHA384
};
policy.tlsPolicy.ciphers = ciphers;

Troubleshooting Tips

Print Cipher Suites Available to Java

To find out which cipher suites the Java environment supports and in what order of priority, use the getSupportedCipherSuites() of the javax.net.ssl.SSLSocketFactory object for the default SSL context. If jrunscript is available on the client nodes, the following one-liner can be run from the command line:

jrunscript -e "java.util.Arrays.asList(javax.net.ssl.SSLContext.getDefault().getSocketFactory().getSupportedCipherSuites()).forEach(println)"

Alternatively, the following simple Java command-line application can be used:

package com.aerospike.examples;

import javax.net.ssl.SSLContext;
import java.security.NoSuchAlgorithmException;

public class Main {

    public static void main(String[] args) {

        System.out.println("Supported Cipher Suites:");

        try {
            String[] ciphers = SSLContext.getDefault().getSocketFactory().getSupportedCipherSuites();
            for (int i = 1; i < ciphers.length; i++) {
                System.out.println(" " + i + ". " + ciphers[i]);
            }
        } catch (NoSuchAlgorithmException e) {
            System.out.println("Failed to get default SSL context.");
        }
    }
}

Note: Many versions of the JDK prioritized CBC cipher suites over GCM ciphers. This is a common cause of poor performance. If the output of the above scripts show *_CBC_* cipher suites before the *_GCM_* cipher suites then it’s especially important to explicitly select the allowed cipher suites.

Check Aerospike Server Available Ciphers from Client Node

The nmap command can use the ssl-enum-ciphers script to probe an Aerospike Server node from a client application node to determine which cipher suites are actually being made available to the client.

The example used in this article configured the Aerospike Server to allow 4 ciphers, however, 2 of those ciphers were based on RSA-based certificates and the other 2 were based on ECDSA-based certificates. Therefore, which of the 4 cipher suites can actually be used are based on the type of certificate installed on the Aerospike Server nodes.

Assuming the Aerospike Server nodes have an ECDSA certificate, the output of the following command shows that only the 2 ECDSA-based certificates are actually being sent to the client as acceptable cipher suites:

$ nmap --script ssl-enum-ciphers -Pn -p 4000 192.168.105.20
Starting Nmap 7.70 ( https://nmap.org ) at 2020-04-06 22:52 UTC
Nmap scan report for 192.168.105.20
Host is up (0.00046s latency).

PORT     STATE SERVICE
4000/tcp open  remoteanything
| ssl-enum-ciphers: 
|   TLSv1.2: 
|     ciphers: 
|       TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (ecdh_x25519) - A
|       TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (ecdh_x25519) - A
|     compressors: 
|       NULL
|     cipher preference: server
|_  least strength: A

Nmap done: 1 IP address (1 host up) scanned in 0.22 seconds

Debug TLS Handshake in Java

If a Java application is not able to connect to an Aerospike cluster over TLS which is believed to be configured correctly, the javax.net.debug argument can be passed to the JVM when invoking the application to enable very verbose logging of each step of the TLS handshake. For example:

java -Djavax.net.debug=all -jar MyApplication.jar

Keywords

TLS CIPHER SUITE JAVA

Timestamp

April 2020