<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.
-->

## SSL

### Quick overview

Secure the traffic between the driver and Cassandra.

* `advanced.ssl-engine-factory` in the configuration; defaults to none, also available:
  config-based, or write your own.
* or programmatically:
  [CqlSession.builder().withSslEngineFactory()][SessionBuilder.withSslEngineFactory] or
  [CqlSession.builder().withSslContext()][SessionBuilder.withSslContext].

-----

There are two aspects to SSL:

* **client-to-node encryption**, where the traffic is encrypted, and the client verifies the
  identity of the Cassandra nodes it connects to;
* optionally, **client certificate authentication**, where Cassandra nodes also verify the identity
  of the client.

This section describes the driver-side configuration; it assumes that you've already configured SSL
in Cassandra:

* [the Cassandra documentation][dsClientToNode] covers a basic approach with self-signed
  certificates, which is fine for development and tests.
* [this blog post][pickle] details a more advanced solution based on a Certificate Authority (CA).

### Preparing the certificates

#### Client truststore

This is required for client-to-node encryption.

If you're using self-signed certificates, you need to export the public part of each node's
certificate from that node's keystore:

```
keytool -export -alias cassandra -file cassandranode0.cer -keystore .keystore
```

Then add all public certificates to the client truststore:

```
keytool -import -v -trustcacerts -alias <cassandra_node0> -file cassandranode0.cer -keystore client.truststore
keytool -import -v -trustcacerts -alias <cassandra_node1> -file cassandranode1.cer -keystore client.truststore
...
```

If you're using a Certificate Authority, the client truststore only needs to contain the CA's
certificate:

```
keytool -import -v -trustcacerts -alias CARoot -file ca.cer -keystore client.truststore
```

#### Client keystore

If you also intend to use client certificate authentication, generate the public and private key
pair for the client:

```
keytool -genkey -keyalg RSA -alias client -keystore client.keystore
```

If you're using self-signed certificates, extract the public part of the client certificate, and
import it in the truststore of each Cassandra node:

```
keytool -export -alias client -file client.cer -keystore client.keystore
keytool -import -v -trustcacerts -alias client -file client.cer -keystore server.truststore
```

If you're using a CA, sign the client certificate with it (see the blog post linked at the top of
this page). Then the nodes' truststores only need to contain the CA's certificate (which should
already be the case if you've followed the steps for inter-node encryption).

`DefaultSslEngineFactory` supports client keystore reloading; see property
`advanced.ssl-engine-factory.keystore-reload-interval`.

### Driver configuration

By default, the driver's SSL support is based on the JDK's built-in implementation: JSSE (Java
Secure Socket Extension).

To enable it, you need to define an engine factory in the [configuration](../configuration/README.md).

#### JSSE, property-based

```
datastax-java-driver {
  advanced.ssl-engine-factory {
    class = DefaultSslEngineFactory
    
    # This property is optional. If it is not present, the driver won't explicitly enable cipher
    # suites on the engine, which according to the JDK documentations results in "a minimum quality
    # of service".
    // cipher-suites = [ "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_RSA_WITH_AES_256_CBC_SHA" ]

    # Whether or not to require validation that the hostname of the server certificate's common
    # name matches the hostname of the server being connected to. If not set, defaults to true.
    // hostname-validation = true

    # The locations and passwords used to access truststore and keystore contents.
    # These properties are optional. If either truststore-path or keystore-path are specified,
    # the driver builds an SSLContext from these files.  If neither option is specified, the
    # default SSLContext is used, which is based on system property configuration.
    // truststore-path = /path/to/client.truststore
    // truststore-password = password123
    // keystore-path = /path/to/client.keystore
    // keystore-password = password123

    # The duration between attempts to reload the keystore from the contents of the file specified
    # by `keystore-path`. This is mainly relevant in environments where certificates have short
    # lifetimes and applications are restarted infrequently, since an expired client certificate
    # will prevent new connections from being established until the application is restarted.
    // keystore-reload-interval = 30 minutes
  }
}
```

Alternatively to storing keystore and truststore information in your configuration, you can instead
use [JSSE system properties]:

```
-Djavax.net.ssl.trustStore=/path/to/client.truststore
-Djavax.net.ssl.trustStorePassword=password123
# If you're using client authentication:
-Djavax.net.ssl.keyStore=/path/to/client.keystore
-Djavax.net.ssl.keyStorePassword=password123
```

#### JSSE, custom factory

If you need more control than what system properties allow, you need to write your own engine
factory. If you just need specific configuration on the `SSLEngine`, you can extend the default
factory and override `newSslEngine`. For example, here is how you would configure custom
`AlgorithmConstraints`:

```java
public class CustomSslEngineFactory extends DefaultSslEngineFactory {

  public CustomSslEngineFactory(DriverContext context) {
    super(context);
  }

  @Override
  public SSLEngine newSslEngine(SocketAddress remoteEndpoint) {
    SSLEngine engine = super.newSslEngine(remoteEndpoint);
    SSLParameters parameters = engine.getSSLParameters();
    parameters.setAlgorithmConstraints(...);
    engine.setSSLParameters(parameters);
    return engine;
  }
}
```

Then declare your custom implementation in the configuration:

```
datastax-java-driver {
  advanced.ssl-engine-factory {
    class = com.mycompany.CustomSslEngineFactory
  }
}
```

#### JSSE, programmatic

You can also provide a factory instance programmatically. This will take precedence over the
configuration:

```java
SslEngineFactory yourFactory = ...
CqlSession session = CqlSession.builder()
    .withSslEngineFactory(yourFactory)
    .build();
```

If you are reusing code that configures SSL programmatically, you can use
[ProgrammaticSslEngineFactory] as an easy way to wrap that into a factory instance:

```java
SSLContext sslContext = ...
String[] cipherSuites = ...
boolean requireHostNameValidation = ...
CqlSession session =
    CqlSession.builder()
        .withSslEngineFactory(
            new ProgrammaticSslEngineFactory(
                sslContext, cipherSuites, requireHostNameValidation))
        .build();
```

Finally, there is a convenient shortcut on the session builder if you just need to pass an
`SSLContext`:

```java
SSLContext sslContext = ...
CqlSession session = CqlSession.builder()
    .withSslContext(sslContext)
    .build();
```

#### Netty-tcnative

Netty supports native integration with OpenSSL / boringssl. The driver does not provide this out of
the box, but with a bit of custom development it is fairly easy to add. See
[SslHandlerFactory](../../developer/netty_pipeline/README.md#sslhandlerfactory) in the developer docs.


[dsClientToNode]: https://docs.datastax.com/en/cassandra/3.0/cassandra/configuration/secureSSLClientToNode.html
[pickle]: http://thelastpickle.com/blog/2015/09/30/hardening-cassandra-step-by-step-part-1-server-to-server.html
[JSSE system properties]: http://docs.oracle.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html#Customization
[SessionBuilder.withSslEngineFactory]: https://docs.datastax.com/en/drivers/java/4.17/com/datastax/oss/driver/api/core/session/SessionBuilder.html#withSslEngineFactory-com.datastax.oss.driver.api.core.ssl.SslEngineFactory-
[SessionBuilder.withSslContext]: https://docs.datastax.com/en/drivers/java/4.17/com/datastax/oss/driver/api/core/session/SessionBuilder.html#withSslContext-javax.net.ssl.SSLContext-
[ProgrammaticSslEngineFactory]: https://docs.datastax.com/en/drivers/java/4.17/com/datastax/oss/driver/api/core/ssl/ProgrammaticSslEngineFactory.html
