/* This file is part of VoltDB.
* Copyright (C) 2008-2017 VoltDB Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with VoltDB. If not, see <http://www.gnu.org/licenses/>.
*/
package org.voltcore.utils.ssl;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.Set;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import com.google_voltpatches.common.collect.ImmutableSet;
/**
* Code common to ServerSSLEngineFactory and ClientSSLEngineFactory.
*/
public class SSLConfiguration {
public static final String KEYSTORE_CONFIG_PROP = "keyStore";
public static final String KEYSTORE_PASSWORD_CONFIG_PROP = "keyStorePassword";
public static final String TRUSTSTORE_CONFIG_PROP = "trustStore";
public static final String TRUSTSTORE_PASSWORD_CONFIG_PROP = "trustStorePassword";
public static Set<String> PREFERRED_CIPHERS = ImmutableSet.<String>builder()
.add("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256")
.add("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384")
.add("TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384")
.add("TLS_RSA_WITH_AES_256_GCM_SHA384")
.add("TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384")
.add("TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384")
.add("TLS_DHE_RSA_WITH_AES_256_GCM_SHA384")
.add("TLS_DHE_DSS_WITH_AES_256_GCM_SHA384")
.add("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256")
.add("TLS_RSA_WITH_AES_128_GCM_SHA256")
.add("TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256")
.add("TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256")
.add("TLS_DHE_RSA_WITH_AES_128_GCM_SHA256")
.add("TLS_DHE_DSS_WITH_AES_128_GCM_SHA256")
.add("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA")
.add("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA")
.add("TLS_RSA_WITH_AES_256_CBC_SHA")
.add("TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA")
.add("TLS_ECDH_RSA_WITH_AES_256_CBC_SHA")
.add("TLS_DHE_RSA_WITH_AES_256_CBC_SHA")
.add("TLS_DHE_DSS_WITH_AES_256_CBC_SHA")
.add("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA")
.add("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA")
.add("TLS_RSA_WITH_AES_128_CBC_SHA")
.add("TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA")
.add("TLS_ECDH_RSA_WITH_AES_128_CBC_SHA")
.add("TLS_DHE_RSA_WITH_AES_128_CBC_SHA")
.add("TLS_DHE_DSS_WITH_AES_128_CBC_SHA")
.build();
public static Set<String> GCM_CIPHERS = ImmutableSet.<String>builder()
.add("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256")
.add("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384")
.add("TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384")
.add("TLS_RSA_WITH_AES_256_GCM_SHA384")
.add("TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384")
.add("TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384")
.add("TLS_DHE_RSA_WITH_AES_256_GCM_SHA384")
.add("TLS_DHE_DSS_WITH_AES_256_GCM_SHA384")
.add("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256")
.add("TLS_RSA_WITH_AES_128_GCM_SHA256")
.add("TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256")
.add("TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256")
.add("TLS_DHE_RSA_WITH_AES_128_GCM_SHA256")
.add("TLS_DHE_DSS_WITH_AES_128_GCM_SHA256")
.build();
public static SSLContext createSslContext(SslConfig sslConfig) {
if (sslConfig == null) {
throw new IllegalArgumentException("sslConfig is null");
}
KeyManager[] keyManagers = null;
TrustManager[] trustManagers = null;
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
if (sslConfig.keyStorePath != null && sslConfig.keyStorePassword != null) {
keyManagers = createKeyManagers(sslConfig.keyStorePath, sslConfig.keyStorePassword, sslConfig.keyStorePassword);
}
if (sslConfig.trustStorePath != null && sslConfig.trustStorePassword != null) {
trustManagers = createTrustManagers(sslConfig.trustStorePath, sslConfig.trustStorePassword);
}
sslContext.init(keyManagers,trustManagers, new SecureRandom());
return sslContext;
} catch (IOException | NoSuchAlgorithmException | KeyStoreException | CertificateException | UnrecoverableKeyException | KeyManagementException ex) {
throw new IllegalArgumentException("Failed to initialize SSL using " + sslConfig, ex);
}
}
/**
* Creates the key managers required to initiate the {@link SSLContext}, using a JKS keystore as an input.
*
* @param filepath - the path to the JKS keystore.
* @param keystorePassword - the keystore's password.
* @param keyPassword - the key's passsword.
* @return {@link KeyManager} array that will be used to initiate the {@link SSLContext}.
* @throws Exception
*/
private static KeyManager[] createKeyManagers(String filepath, String keystorePassword, String keyPassword)
throws FileNotFoundException, KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException {
KeyStore keyStore = KeyStore.getInstance("JKS");
try (InputStream keyStoreIS = new FileInputStream(filepath)) {
keyStore.load(keyStoreIS, keystorePassword.toCharArray());
}
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, keyPassword.toCharArray());
return kmf.getKeyManagers();
}
/**
* Creates the trust managers required to initiate the {@link SSLContext}, using a JKS keystore as an input.
*
* @param filepath - the path to the JKS keystore.
* @param keystorePassword - the keystore's password.
* @return {@link TrustManager} array, that will be used to initiate the {@link SSLContext}.
* @throws Exception
*/
private static TrustManager[] createTrustManagers(String filepath, String keystorePassword)
throws KeyStoreException, FileNotFoundException,
IOException, NoSuchAlgorithmException, CertificateException {
KeyStore trustStore = KeyStore.getInstance("JKS");
try (InputStream trustStoreIS = new FileInputStream(filepath)) {
trustStore.load(trustStoreIS, keystorePassword.toCharArray());
}
TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustFactory.init(trustStore);
return trustFactory.getTrustManagers();
}
public static class SslConfig {
public final String keyStorePath;
public final String keyStorePassword;
public final String trustStorePath;
public final String trustStorePassword;
public SslConfig() {
this(null,null,null,null);
}
public SslConfig(String keyStorePath, String keyStorePassword, String trustStorePath, String trustStorePassword) {
String pval = System.getProperty("javax.net.ssl.keyStore");
if (pval != null) {
keyStorePath = pval;
}
pval = System.getProperty("javax.net.ssl.keyStorePassword");
if (pval != null) {
keyStorePassword = pval;
}
pval = System.getProperty("javax.net.ssl.trustStore");
if (pval != null) {
trustStorePath = pval;
}
pval = System.getProperty("javax.net.ssl.trustStorePassword");
if (pval != null) {
trustStorePassword = pval;
}
this.keyStorePath = keyStorePath;
this.keyStorePassword = keyStorePassword;
this.trustStorePath = trustStorePath;
this.trustStorePassword = trustStorePassword;
}
@Override
public String toString() {
return "SslConfig [keyStorePath=" + keyStorePath
+ ", trustStorePath=" + trustStorePath + "]";
}
}
}