/*
* Copyright (C) 2012-2015 DataStax Inc.
*
* Licensed 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.
*/
package com.datastax.driver.core;
import io.netty.handler.ssl.SslContextBuilder;
import org.testng.annotations.DataProvider;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import java.security.KeyStore;
import java.security.SecureRandom;
import static com.datastax.driver.core.SSLTestBase.SslImplementation.JDK;
import static com.datastax.driver.core.SSLTestBase.SslImplementation.NETTY_OPENSSL;
import static io.netty.handler.ssl.SslProvider.OPENSSL;
import static org.assertj.core.api.Assertions.fail;
@CCMConfig(ssl = true, createCluster = false)
public abstract class SSLTestBase extends CCMTestsSupport {
@DataProvider(name = "sslImplementation")
public static Object[][] sslImplementation() {
// Bypass Netty SSL if on JDK 1.6 since it only works on 1.7+.
String javaVersion = System.getProperty("java.version");
if (javaVersion.startsWith("1.6")) {
return new Object[][]{{JDK}};
} else {
return new Object[][]{{JDK}, {NETTY_OPENSSL}};
}
}
/**
* <p>
* Attempts to connect to a cassandra cluster with the given SSLOptions and then closes the
* created {@link Cluster} instance.
* </p>
*
* @param sslOptions SSLOptions to use.
* @throws Exception A {@link com.datastax.driver.core.exceptions.NoHostAvailableException} will be
* raised here if connection cannot be established.
*/
protected void connectWithSSLOptions(SSLOptions sslOptions) throws Exception {
Cluster cluster = register(Cluster.builder()
.addContactPoints(getContactPoints())
.withPort(ccm().getBinaryPort())
.withSSL(sslOptions)
.build());
cluster.connect();
}
/**
* <p>
* Attempts to connect to a cassandra cluster with using {@link Cluster.Builder#withSSL} with no
* provided {@link SSLOptions} and then closes the created {@link Cluster} instance.
* </p>
*
* @throws Exception A {@link com.datastax.driver.core.exceptions.NoHostAvailableException} will be
* raised here if connection cannot be established.
*/
protected void connectWithSSL() throws Exception {
Cluster cluster = register(Cluster.builder()
.addContactPoints(getContactPoints())
.withPort(ccm().getBinaryPort())
.withSSL()
.build());
cluster.connect();
}
enum SslImplementation {JDK, NETTY_OPENSSL}
/**
* @param sslImplementation the SSL implementation to use
* @param clientAuth whether the client should authenticate
* @param trustingServer whether the client should trust the server's certificate
* @return {@link com.datastax.driver.core.SSLOptions} with the given configuration for
* server certificate validation and client certificate authentication.
*/
public SSLOptions getSSLOptions(SslImplementation sslImplementation, boolean clientAuth, boolean trustingServer) throws Exception {
TrustManagerFactory tmf = null;
if (trustingServer) {
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(
this.getClass().getResourceAsStream(CCMBridge.DEFAULT_CLIENT_TRUSTSTORE_PATH),
CCMBridge.DEFAULT_CLIENT_TRUSTSTORE_PASSWORD.toCharArray());
tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
}
switch (sslImplementation) {
case JDK:
KeyManagerFactory kmf = null;
if (clientAuth) {
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(
this.getClass().getResourceAsStream(CCMBridge.DEFAULT_CLIENT_KEYSTORE_PATH),
CCMBridge.DEFAULT_CLIENT_KEYSTORE_PASSWORD.toCharArray());
kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, CCMBridge.DEFAULT_CLIENT_KEYSTORE_PASSWORD.toCharArray());
}
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf != null ? kmf.getKeyManagers() : null, tmf != null ? tmf.getTrustManagers() : null, new SecureRandom());
return RemoteEndpointAwareJdkSSLOptions.builder().withSSLContext(sslContext).build();
case NETTY_OPENSSL:
SslContextBuilder builder = SslContextBuilder
.forClient()
.sslProvider(OPENSSL)
.trustManager(tmf);
if (clientAuth) {
builder.keyManager(CCMBridge.DEFAULT_CLIENT_CERT_CHAIN_FILE, CCMBridge.DEFAULT_CLIENT_PRIVATE_KEY_FILE);
}
return new RemoteEndpointAwareNettySSLOptions(builder.build());
default:
fail("Unsupported SSL implementation: " + sslImplementation);
return null;
}
}
}