diff --git a/adapter-cqld4/src/main/resources/ssl.md b/adapter-cqld4/src/main/resources/ssl.md index b6b4ece26..e38e046c4 100644 --- a/adapter-cqld4/src/main/resources/ssl.md +++ b/adapter-cqld4/src/main/resources/ssl.md @@ -54,3 +54,8 @@ For `openssl` type, the following options are available: Examples: - `keyFilePath=file.key` +- **sslValidation** - optional boolean flag to enable/disable SSLValidation. + + Examples: + - `sslValidation=true` (the default) + - `sslValidation=false` \ No newline at end of file diff --git a/engine-api/src/test/java/io/nosqlbench/engine/api/util/SSLKsFactoryTest.java b/engine-api/src/test/java/io/nosqlbench/engine/api/util/SSLKsFactoryTest.java index d7d0c4570..7e2ce43a8 100644 --- a/engine-api/src/test/java/io/nosqlbench/engine/api/util/SSLKsFactoryTest.java +++ b/engine-api/src/test/java/io/nosqlbench/engine/api/util/SSLKsFactoryTest.java @@ -178,8 +178,8 @@ public class SSLKsFactoryTest { NBConfiguration sslCfg = SSLKsFactory.get().getConfigModel().extractConfig(activityDef.getParams()); assertThatExceptionOfType(RuntimeException.class) - .isThrownBy(() -> SSLKsFactory.get().getContext(sslCfg)) - .withMessageMatching("Unable to init KeyManagerFactory. Please check.*"); + .isThrownBy(() -> SSLKsFactory.get().getContext(sslCfg)) + .withMessageMatching("Unable to init KeyManagerFactory. Please check.*"); } @Test @@ -196,6 +196,62 @@ public class SSLKsFactoryTest { .withMessageMatching("Unable to load the truststore: .*"); } + @Test + void testSSLValidationActive() { + { + final String[] params1 = { + "ssl=openssl", + "certFilePath=src/test/resources/ssl/client_cert.pem", + "keyFilePath=src/test/resources/ssl/client.key", + "sslValidation=true" + }; + ActivityDef activityDef = ActivityDef.parseActivityDef(String.join(";", params1)); + NBConfiguration sslCfg1 = SSLKsFactory.get().getConfigModel().extractConfig(activityDef.getParams()); + assertThat(SSLKsFactory.get().getContext(sslCfg1)).isNotNull(); + } + + { + final String[] params2 = { + "ssl=jdk", + "keystore=src/test/resources/ssl/client_diff_password.p12", + "kspass=nosqlbench_client", + "keyPassword=nosqlbench", + "sslValidation=true" + }; + ActivityDef activityDef2 = ActivityDef.parseActivityDef(String.join(";", params2)); + NBConfiguration sslCfg2 = SSLKsFactory.get().getConfigModel().extractConfig(activityDef2.getParams()); + assertThat(SSLKsFactory.get().getContext(sslCfg2)).isNotNull(); + } + } + + @Test + void testSSLValidationNotActive() { + { + final String[] params1 = { + "ssl=openssl", + "certFilePath=src/test/resources/ssl/client_cert.pem", + "keyFilePath=src/test/resources/ssl/client.key", + "sslValidation=false" + }; + ActivityDef activityDef = ActivityDef.parseActivityDef(String.join(";", params1)); + NBConfiguration sslCfg1 = SSLKsFactory.get().getConfigModel().extractConfig(activityDef.getParams()); + assertThat(SSLKsFactory.get().getContext(sslCfg1)).isNotNull(); + } + + { + final String[] params2 = { + "ssl=jdk", + "keystore=src/test/resources/ssl/client_diff_password.p12", + "kspass=nosqlbench_client", + "keyPassword=nosqlbench", + "sslValidation=false" + }; + ActivityDef activityDef2 = ActivityDef.parseActivityDef(String.join(";", params2)); + NBConfiguration sslCfg2 = SSLKsFactory.get().getConfigModel().extractConfig(activityDef2.getParams()); + assertThat(SSLKsFactory.get().getContext(sslCfg2)).isNotNull(); + } + } + @Test public void testOpenSSLGetContextWithCaCertError() { String[] params = { @@ -241,16 +297,16 @@ public class SSLKsFactoryTest { @Test public void testOpenSSLGetContextWithMissingCertError() { String[] params = { - "ssl=openssl", - "caCertFilePath=src/test/resources/ssl/cacert.crt", - "keyFilePath=src/test/resources/ssl/client.key" + "ssl=openssl", + "caCertFilePath=src/test/resources/ssl/cacert.crt", + "keyFilePath=src/test/resources/ssl/client.key" }; ActivityDef activityDef = ActivityDef.parseActivityDef(String.join(";", params)); NBConfiguration sslCfg = SSLKsFactory.get().getConfigModel().extractConfig(activityDef.getParams()); assertThatExceptionOfType(RuntimeException.class) - .isThrownBy(() -> SSLKsFactory.get().getContext(sslCfg)) - .withMessageContaining("Unable to load key from") - .withCauseInstanceOf(IllegalArgumentException.class); + .isThrownBy(() -> SSLKsFactory.get().getContext(sslCfg)) + .withMessageContaining("Unable to load key from") + .withCauseInstanceOf(IllegalArgumentException.class); } } diff --git a/nb-api/src/main/java/io/nosqlbench/api/engine/util/SSLKsFactory.java b/nb-api/src/main/java/io/nosqlbench/api/engine/util/SSLKsFactory.java index fa9919068..d79c51209 100644 --- a/nb-api/src/main/java/io/nosqlbench/api/engine/util/SSLKsFactory.java +++ b/nb-api/src/main/java/io/nosqlbench/api/engine/util/SSLKsFactory.java @@ -22,9 +22,7 @@ import org.apache.logging.log4j.Logger; import javax.net.ServerSocketFactory; import javax.net.SocketFactory; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.*; import java.io.*; import java.nio.charset.StandardCharsets; import java.security.KeyFactory; @@ -45,11 +43,31 @@ public class SSLKsFactory implements NBMapConfigurable { private static final SSLKsFactory instance = new SSLKsFactory(); - private static final Pattern CERT_PATTERN = Pattern.compile("-+BEGIN\\s+.*CERTIFICATE[^-]*-+(?:\\s|\\r|\\n){1,10}([a-z0-9+/=\\r\\n]+)-+END\\s+.*CERTIFICATE[^-]*-+", Pattern.CASE_INSENSITIVE); - private static final Pattern KEY_PATTERN = Pattern.compile("-+BEGIN\\s+.*PRIVATE\\s+KEY[^-]*-+(?:\\s|\\r|\\n){1,10}([a-z0-9+/=\\r\\n]+)-+END\\s+.*PRIVATE\\s+KEY[^-]*-+", Pattern.CASE_INSENSITIVE); + private static final Pattern CERT_PATTERN = Pattern.compile("-+BEGIN\\s+.*CERTIFICATE[^-]*-+(?:\\s|\\r|\\n)" + + "{1,10}([a-z0-9+/=\\r\\n]+)-+END\\s+.*CERTIFICATE[^-]*-+", Pattern.CASE_INSENSITIVE); + private static final Pattern KEY_PATTERN = Pattern.compile("-+BEGIN\\s+.*PRIVATE\\s+KEY[^-]*-+(?:\\s|\\r|\\n)" + + "{1,10}([a-z0-9+/=\\r\\n]+)-+END\\s+.*PRIVATE\\s+KEY[^-]*-+", Pattern.CASE_INSENSITIVE); + public static final String SSL = "ssl"; public static final String DEFAULT_TLSVERSION = "TLSv1.2"; + private static final TrustManager[] trustAllCerts = new TrustManager[]{ + new X509TrustManager() { + @Override + public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) { + } + + @Override + public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) { + } + + @Override + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return new java.security.cert.X509Certificate[]{}; + } + } + }; + /** * Consider: https://gist.github.com/artem-smotrakov/bd14e4bde4d7238f7e5ab12c697a86a3 */ @@ -70,6 +88,7 @@ public class SSLKsFactory implements NBMapConfigurable { public SocketFactory createSocketFactory(NBConfiguration cfg) { SSLContext context = getContext(cfg); + if (context == null) { throw new IllegalArgumentException("SSL is not enabled."); } @@ -81,18 +100,26 @@ public class SSLKsFactory implements NBMapConfigurable { if (sslParam.isPresent()) { String tlsVersion = cfg.getOptional("tlsversion").orElse(DEFAULT_TLSVERSION); - KeyStore keyStore; + KeyStore keyStore = null; char[] keyPassword = null; - KeyStore trustStore; + KeyStore trustStore = null; + + boolean activeSSLValidation = true; + if (cfg.getOptional("sslValidation").isPresent() && cfg.getOptional("sslValidation") + .get().equals("false")) { + logger.warn("sslValidation=false, skipping SSL validation. " + + "THIS OPTION SHOULD ONLY BE TESTING AND NON-PROD ENVIRONMENTS"); + activeSSLValidation = false; + } if (sslParam.get().equals("jdk")) { final char[] keyStorePassword = cfg.getOptional("kspass") - .map(String::toCharArray) - .orElse(null); + .map(String::toCharArray) + .orElse(null); keyPassword = cfg.getOptional("keyPassword", "keypassword") - .map(String::toCharArray) - .orElse(keyStorePassword); + .map(String::toCharArray) + .orElse(keyStorePassword); keyStore = cfg.getOptional("keystore").map(ksPath -> { try { @@ -105,9 +132,9 @@ public class SSLKsFactory implements NBMapConfigurable { trustStore = cfg.getOptional("truststore").map(tsPath -> { try { return KeyStore.getInstance(new File(tsPath), - cfg.getOptional("tspass") - .map(String::toCharArray) - .orElse(null)); + cfg.getOptional("tspass") + .map(String::toCharArray) + .orElse(null)); } catch (Exception e) { throw new RuntimeException("Unable to load the truststore: " + e, e); } @@ -115,8 +142,8 @@ public class SSLKsFactory implements NBMapConfigurable { } else if (sslParam.get().equals("openssl")) { try { - CertificateFactory cf = CertificateFactory.getInstance("X.509"); + CertificateFactory cf = CertificateFactory.getInstance("X.509"); keyStore = KeyStore.getInstance("JKS"); keyStore.load(null, null); @@ -125,9 +152,8 @@ public class SSLKsFactory implements NBMapConfigurable { return cf.generateCertificate(is); } catch (Exception e) { throw new RuntimeException( - String.format("Unable to load cert from %s: " + e, certFilePath), - e - ); + String.format("Unable to load cert from %s: due to:%s ", certFilePath, + e.getMessage()), e); } }).orElse(null); @@ -135,37 +161,33 @@ public class SSLKsFactory implements NBMapConfigurable { keyStore.setCertificateEntry("certFile", cert); File keyFile = cfg.getOptional("keyfilepath").map(File::new) - .orElse(null); + .orElse(null); if (keyFile != null) { try { keyPassword = cfg.getOptional("keyPassword", "keypassword") - .map(String::toCharArray) - .orElse("temp_key_password".toCharArray()); + .map(String::toCharArray) + .orElse("temp_key_password".toCharArray()); KeyFactory kf = KeyFactory.getInstance("RSA"); PrivateKey key = kf.generatePrivate(new PKCS8EncodedKeySpec(loadKeyFromPem(keyFile))); keyStore.setKeyEntry("key", key, keyPassword, - cert != null ? new Certificate[]{cert} : null); + cert != null ? new Certificate[]{cert} : null); } catch (Exception e) { - throw new RuntimeException(String.format("Unable to load key from %s: " + e, - keyFile), - e); + throw new RuntimeException(String.format("Unable to load key from: %s due to: %s ", + keyFile, e.getMessage()), e); } } trustStore = cfg.getOptional("caCertFilePath", "cacertfilepath").map(caCertFilePath -> { - try (InputStream is = new FileInputStream(new File(caCertFilePath))) { + try (InputStream is = new FileInputStream(caCertFilePath)) { KeyStore ts = KeyStore.getInstance("JKS"); ts.load(null, null); - - Certificate caCert = cf.generateCertificate(is); - ts.setCertificateEntry("caCertFile", caCert); + ts.setCertificateEntry("caCertFile", cf.generateCertificate(is)); return ts; } catch (Exception e) { - throw new RuntimeException(String.format("Unable to load caCert from %s: " + e, - caCertFilePath), - e); + throw new RuntimeException(String.format("Unable to load caCert from: %s due to: %s", + caCertFilePath, e.getMessage()), e); } }).orElse(null); @@ -183,20 +205,26 @@ public class SSLKsFactory implements NBMapConfigurable { kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(keyStore, keyPassword); } catch (Exception e) { - throw new RuntimeException("Unable to init KeyManagerFactory. Please check password and location: " + e, e); + throw new RuntimeException("Unable to init KeyManagerFactory. Please check password and location: " + + e.getMessage(), e); } TrustManagerFactory tmf; try { tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(trustStore != null ? trustStore : keyStore); + } catch (Exception e) { - throw new RuntimeException("Unable to init TrustManagerFactory: " + e, e); + throw new RuntimeException("Unable to init TrustManagerFactory: " + e.getMessage(), e); } try { SSLContext sslContext = SSLContext.getInstance(tlsVersion); - sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom()); + if (!activeSSLValidation) { + sslContext.init(kmf.getKeyManagers(), trustAllCerts, new SecureRandom()); + } else { + sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom()); + } return sslContext; } catch (Exception e) { throw new RuntimeException(e); @@ -229,18 +257,19 @@ public class SSLKsFactory implements NBMapConfigurable { public NBConfigModel getConfigModel() { return ConfigModel.of(SSLKsFactory.class, - Param.optional("ssl") - .setDescription("Enable ssl and set the mode") - .setRegex("jdk|openssl"), - Param.defaultTo("tlsversion", DEFAULT_TLSVERSION), - Param.optional("kspass"), - Param.optional("keyPassword"), - Param.optional("keystore"), - Param.optional("truststore"), - Param.optional("tspass"), - Param.optional(List.of("keyFilePath","keyfilepath")), - Param.optional("caCertFilePath"), - Param.optional("certFilePath") + Param.optional("ssl") + .setDescription("Enable ssl and set the mode") + .setRegex("jdk|openssl"), + Param.defaultTo("tlsversion", DEFAULT_TLSVERSION), + Param.optional("kspass"), + Param.optional("keyPassword"), + Param.optional("keystore"), + Param.optional("truststore"), + Param.optional("tspass"), + Param.optional(List.of("keyFilePath", "keyfilepath")), + Param.optional("caCertFilePath"), + Param.optional("certFilePath"), + Param.optional("sslValidation") ).asReadOnly(); } }