TLS/SSL
You cannot use TLS/SSL when Hazelcast Encryption is enabled. |
You can use the SSL (Secure Sockets Layer) protocol to establish an encrypted communication across your Hazelcast cluster with key stores and trust stores. Note that, if you are developing applications using Java 8, you will be using its successor TLS (Transport Layer Security).
It is NOT recommended to reuse the key stores and trust stores for external applications. |
TLS/SSL for Hazelcast Members
Hazelcast allows you to encrypt socket level communication between
Hazelcast members and between Hazelcast clients and members, for end
to end encryption. To use it, you need to implement
com.hazelcast.nio.ssl.SSLContextFactory
and configure the SSL section
in the network configuration.
The following is the implementation code snippet:
public class MySSLContextFactory implements SSLContextFactory {
public void init( Properties properties ) throws Exception {
}
public SSLContext getSSLContext() {
...
SSLContext sslCtx = SSLContext.getInstance( "the protocol to be used" );
return sslCtx;
}
}
The following is the base declarative configuration for the
implemented SSLContextFactory
:
<hazelcast>
...
<network>
<ssl enabled="true">
<factory-class-name>
com.hazelcast.examples.MySSLContextFactory
</factory-class-name>
<properties>
<property name="foo">bar</property>
</properties>
</ssl>
</network>
...
</hazelcast>
hazelcast:
network:
ssl:
enabled: true
factory-class-name: com.hazelcast.examples.MySSLContextFactory
properties:
foo: bar
Hazelcast provides a default SSLContextFactory,
com.hazelcast.nio.ssl.BasicSSLContextFactory
, which uses the configured
keystore to initialize SSLContext
; see the following example configuration
for TLS/SSL.
<hazelcast>
...
<network>
<ssl enabled="true">
<factory-class-name>
com.hazelcast.nio.ssl.BasicSSLContextFactory
</factory-class-name>
<properties>
<property name="protocol">TLSv1.2</property>
<property name="mutualAuthentication">REQUIRED</property>
<property name="keyStore">/opt/hazelcast-keystore.p12</property>
<property name="keyStorePassword">secret.123</property>
<property name="keyStoreType">PKCS12</property>
<property name="trustStore">/opt/hazelcast-truststore.p12</property>
<property name="trustStorePassword">changeit</property>
<property name="trustStoreType">PKCS12</property>
</properties>
</ssl>
</network>
...
</hazelcast>
hazelcast:
network:
ssl:
enabled: true
factory-class-name: com.hazelcast.nio.ssl.BasicSSLContextFactory
properties:
protocol: TLSv1.2
mutualAuthentication: REQUIRED
keyStore: /opt/hazelcast-keystore.p12
keyStorePassword: secret.123
keyStoreType: PKCS12
trustStore: /opt/hazelcast-truststore.p12
trustStorePassword: changeit
trustStoreType: PKCS12
The following are the descriptions of the properties:
-
keyStore
: Path of your keystore file. -
keyStorePassword
: Password to access the key from your keystore file. -
keyManagerAlgorithm
: Name of the algorithm based on which the authentication keys are provided. -
keyStoreType
: Type of the keystore. Its default value isJKS
. Another commonly used type is thePKCS12
. Available keystore/truststore types depend on your Operating system and the Java runtime. -
trustStore
: Path of your truststore file. The file truststore is a keystore file that contains a collection of certificates trusted by your application. -
trustStorePassword
: Password to unlock the truststore file. -
trustManagerAlgorithm
: Name of the algorithm based on which the trust managers are provided. -
trustStoreType
: Type of the truststore. Its default value isJKS
. Another commonly used type is thePKCS12
. Available keystore/truststore types depend on your Operating system and the Java runtime. -
mutualAuthentication
: Mutual authentication configuration. It’s empty by default which means the client side of connection is not authenticated. Available values are:-
REQUIRED
- server forces usage of a trusted client certificate -
OPTIONAL
- server asks for a client certificate, but it doesn’t require itSee the Mutual Authentication section.
-
-
ciphersuites
: Comma-separated list of cipher suite names allowed to be used. Its default value are all supported suites in your Java runtime. -
protocol
: Name of the algorithm which is used in your TLS/SSL. Its default value isTLS
. Available values are:-
TLS
-
TLSv1
-
TLSv1.1
-
TLSv1.2
-
TLSv1.3
For the
protocol
property, we recommend you to provide TLS with its version information, e.g.,TLSv1.2
. Note that if you write onlyTLS
, your application chooses the TLS version according to your Java version.
-
Other Property Configuration Options
You can set all the properties presented in this section as system properties using the javax.net.ssl
prefix, e.g., javax.net.ssl.keyStore
and javax.net.ssl.keyStorePassword
.
See below equivalent examples:
System.setProperty("javax.net.ssl.trustStore", "/user/home/hazelcast.ts");
Or,
-Djavax.net.ssl.trustStore=/user/home/hazelcast.ts
This way of TLS/SSL configuration is then system wide in your Java runtime.
TLS/SSL for Hazelcast Clients
The TLS configuration in Hazelcast clients is very similar to member configuration.
<hazelcast-client>
...
<network>
<ssl enabled="true">
<factory-class-name>
com.hazelcast.nio.ssl.BasicSSLContextFactory
</factory-class-name>
<properties>
<property name="keyStore">/opt/hazelcast-client.keystore</property>
<property name="keyStorePassword">clientsSecret</property>
<property name="trustStore">/opt/hazelcast-client.truststore</property>
<property name="trustStorePassword">changeit</property>
<property name="protocol">TLSv1.2</property>
</properties>
</ssl>
</network>
...
</hazelcast-client>
The same BasicSSLContextFactory
properties used for members are available on clients.
Clients don’t need to set mutualAuthentication
property as it’s used in configuring the server side of TLS connections.
Mutual Authentication
TLS connections have two sides: the one opening the connection (TLS client) and the one accepting the connection (TLS server). By default only the TLS server proves its identity by presenting a certificate to the TLS client. The mutual authentication means that also the TLS clients prove their identity to the TLS servers.
Hazelcast members can be on both sides of TLS connection - TLS servers and TLS clients. Hazelcast clients are always on the client side of a TLS connection.
By default Hazelcast members have keyStore used to identify themselves to the clients and other members. Both Hazelcast members and Hazelcast clients have trustStore used to define which members they can trust.
When the mutual authentication feature is enabled, Hazelcast clients need to provide keyStore. A client proves its identity by providing its certificate to the Hazelcast member it’s connecting to. The member only accepts the connection if the client’s certificate is present in the member’s trustStore.
To enable the mutual authentication, set the mutualAuthentication
property
value to REQUIRED
on the member side, as shown below:
Config cfg = new Config();
Properties props = new Properties();
props.setProperty("mutualAuthentication", "REQUIRED");
props.setProperty("keyStore", "/opt/hazelcast.keystore");
props.setProperty("keyStorePassword", "123456");
props.setProperty("trustStore", "/opt/hazelcast.truststore");
props.setProperty("trustStorePassword", "123456");
cfg.getNetworkConfig().setSSLConfig(new SSLConfig().setEnabled(true).setProperties(props));
Hazelcast.newHazelcastInstance(cfg);
And on the client side, you need to set client identity by providing the keystore:
clientSslProps.setProperty("keyStore", "/opt/client.keystore");
clientSslProps.setProperty("keyStorePassword", "123456");
The property mutualAuthentication
has the following options:
-
REQUIRED
: Server asks for client certificate. If the client does not provide a keystore or the provided keystore is not verified against member’s truststore, the client is not authenticated. -
OPTIONAL
: Server asks for client certificate, but client is not required to provide any valid certificate.
When a new client is introduced with a new keystore, the truststore on the member side should be updated accordingly to include new clients' information to be able to accept it. |
See the below example snippet to see the full configuration on the client side:
ClientConfig config = new ClientConfig();
Properties clientSslProps = new Properties();
clientSslProps.setProperty("keyStore", "/opt/client.keystore");
clientSslProps.setProperty("keyStorePassword", "123456");
clientSslProps.setProperty("trustStore", "/opt/client.truststore");
clientSslProps.setProperty("trustStorePassword", "123456");
config.getNetworkConfig().setSSLConfig(new SSLConfig().setEnabled(true).setProperties(clientSslProps));
HazelcastClient.newHazelcastClient(config);
If the mutual authentication is not required, the Hazelcast members accept all incoming TLS connections without verifying if the connecting side is trusted. Therefore it’s recommended to require the mutual authentication in Hazelcast members configuration.
TLS/SSL Performance Improvements for Java
TLS/SSL can have a significant impact on performance. There are a few ways to increase the performance.
The first thing that can be done is making sure that AES intrinsics are used.
Modern CPUs (2010 or newer Westmere) have hardware support for AES encryption/decryption
and if a Java 8 or newer JVM is
used, the JIT automatically makes use of these AES intrinsics. They can also be
explicitly enabled using -XX:+UseAES -XX:+UseAESIntrinsics
,
or disabled using -XX:-UseAES -XX:-UseAESIntrinsics
.
A lot of encryption algorithms make use of padding because they encrypt/decrypt in
fixed sized blocks. If there is no enough data
for a block, the algorithm relies on random number generation to pad. Under Linux,
the JVM automatically makes use of /dev/random
for
the generation of random numbers. /dev/random
relies on entropy to be able to
generate random numbers. However, if this entropy is
insufficient to keep up with the rate requiring random numbers, it can slow down
the encryption/decryption since /dev/random
will
block; it could block for minutes waiting for sufficient entropy . This can be fixed
by setting the -Djava.security.egd=file:/dev/./urandom
system property.
For a more permanent solution, modify the
<JAVA_HOME>/jre/lib/security/java.security
file, look for the
securerandom.source=/dev/urandom
and change it
to securerandom.source=file:/dev/./urandom
. Switching to /dev/urandom
could
be controversial because /dev/urandom
will not
block if there is a shortage of entropy and the returned random values could
theoretically be vulnerable to a cryptographic attack.
If this is a concern in your application, use /dev/random
instead.
Hazelcast’s Java smart client automatically makes use of extra I/O threads
for encryption/decryption and this have a significant impact on the performance.
This can be changed using the hazelcast.client.io.input.thread.count
and
hazelcast.client.io.output.thread.count
client system properties.
By default it is 1 input thread and 1 output thread. If TLS/SSL is enabled,
it defaults to 3 input threads and 3 output threads.
Having more client I/O threads than members in the cluster does not lead to
an increased performance. So with a 2-member cluster,
2 in and 2 out threads give the best performance.