diff --git a/docs/en/settings/security-settings.asciidoc b/docs/en/settings/security-settings.asciidoc index a9e969e7465..faa26b1945f 100644 --- a/docs/en/settings/security-settings.asciidoc +++ b/docs/en/settings/security-settings.asciidoc @@ -298,6 +298,10 @@ List of paths to PEM encoded certificate files that should be trusted. The path to the Java Keystore file that contains a private key and certificate. `ssl.key` and `ssl.keystore.path` may not be used at the same time. +`ssl.keystore.type`:: +The format of the keystore file. Should be either `jks` to use the Java +Keystore format, or `PKCS12` to use PKCS#12 files. The default is `jks`. + `ssl.keystore.password`:: The password to the keystore. @@ -308,6 +312,10 @@ The password for the key in the keystore. Defaults to the keystore password. The path to the Java Keystore file that contains the certificates to trust. `ssl.certificate_authorities` and `ssl.truststore.path` may not be used at the same time. +`ssl.truststore.type`:: +The format of the keystore file. Should be either `jks` to use the Java +Keystore format, or `PKCS12` to use PKCS#12 files. The default is `jks`. + `ssl.truststore.password`:: The password to the truststore. @@ -482,6 +490,10 @@ List of paths to PEM encoded certificate files that should be trusted. `ssl.keystore.path`:: The path to the Java Keystore file that contains a private key and certificate. +`ssl.keystore.type`:: +The format of the keystore file. Should be either `jks` to use the Java +Keystore format, or `PKCS12` to use PKCS#12 files. The default is `jks`. + `ssl.keystore.password`:: The password to the keystore. @@ -491,6 +503,10 @@ The password for the key in the keystore. Defaults to the keystore password. `ssl.truststore.path`:: The path to the Java Keystore file that contains the certificates to trust. +`ssl.truststore.type`:: +The format of the truststore file. Should be either `jks` to use the Java +Keystore format, or `PKCS12` to use PKCS#12 files. The default is `jks`. + `ssl.truststore.password`:: The password to the truststore. @@ -636,6 +652,35 @@ Path to the truststore file. `xpack.ssl.truststore.password`:: Password to the truststore. +[float] +===== PKCS#12 Files + +When using PKCS#12 container files (`.p12` or `.pfx`), which contain the +private key, certificate, and certificates that should be trusted, use +the following settings: + +`xpack.ssl.keystore.path`:: +Path to the PKCS#12 file that holds the private key and certificate. + +`xpack.ssl.keystore.type`:: +Set this to `PKCS12`. + +`xpack.ssl.keystore.password`:: +Password to the PKCS#12 file. + +`xpack.ssl.keystore.key_password`:: +Password for the private key in the PKCS12 file. +Defaults to the same value as `xpack.ssl.keystore.password`. + +`xpack.ssl.truststore.path`:: +Path to the truststore file. + +`xpack.ssl.truststore.type`:: +Set this to `PKCS12`. + +`xpack.ssl.truststore.password`:: +Password to the truststore. + [[http-tls-ssl-settings]] :ssl-prefix: xpack.security.http :component: HTTP diff --git a/docs/en/settings/ssl-settings.asciidoc b/docs/en/settings/ssl-settings.asciidoc index e54bbce6008..3a4f42eca55 100644 --- a/docs/en/settings/ssl-settings.asciidoc +++ b/docs/en/settings/ssl-settings.asciidoc @@ -93,3 +93,32 @@ Path to the truststore file. +{ssl-prefix}.ssl.truststore.password+:: Password to the truststore. + +===== PKCS#12 Files + +{security} can be configured to use PKCS#12 container files (`.p12` or `.pfx` files) +that contain the private key, certificate and certificates that should be trusted. + +PKCS#12 files are configured in the same way as Java Keystore Files: + ++{ssl-prefix}.ssl.keystore.path+:: +Path to the PKCS#12 file that holds the private key and certificate. + ++{ssl-prefix}.ssl.keystore.type+:: +Set this to `PKCS12` to indicate that the keystore is a PKCS#12 file. + ++{ssl-prefix}.ssl.keystore.password+:: +Password to the PKCS#12 file. + ++{ssl-prefix}.ssl.keystore.key_password+:: +Password for the private key stored in the PKCS#12 file. +Defaults to the same value as +{ssl-prefix}.ssl.keystore.password+. + ++{ssl-prefix}.ssl.truststore.path+:: +Path to the PKCS#12 file that holds the certificates to be trusted. + ++{ssl-prefix}.ssl.truststore.type+:: +Set this to `PKCS12` to indicate that the truststore is a PKCS#12 file. + ++{ssl-prefix}.ssl.truststore.password+:: +Password to the PKCS#12 file. \ No newline at end of file diff --git a/plugin/src/main/java/org/elasticsearch/xpack/security/authc/pki/PkiRealm.java b/plugin/src/main/java/org/elasticsearch/xpack/security/authc/pki/PkiRealm.java index 3e9b27df7d1..78f9b9e9766 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/security/authc/pki/PkiRealm.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/security/authc/pki/PkiRealm.java @@ -180,8 +180,9 @@ public class PkiRealm extends Realm { } try (SecureString password = SSL_SETTINGS.truststorePassword.get(settings)) { String trustStoreAlgorithm = SSL_SETTINGS.truststoreAlgorithm.get(settings); + String trustStoreType = SSL_SETTINGS.truststoreType.get(settings); try { - return CertUtils.trustManager(truststorePath, password.getChars(), trustStoreAlgorithm, realmConfig.env()); + return CertUtils.trustManager(truststorePath, trustStoreType, password.getChars(), trustStoreAlgorithm, realmConfig.env()); } catch (Exception e) { throw new IllegalArgumentException("failed to load specified truststore", e); } diff --git a/plugin/src/main/java/org/elasticsearch/xpack/ssl/CertUtils.java b/plugin/src/main/java/org/elasticsearch/xpack/ssl/CertUtils.java index b66a01b0efe..2e66cec0767 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/ssl/CertUtils.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/ssl/CertUtils.java @@ -160,12 +160,11 @@ public class CertUtils { * @param env the environment to use for file resolution. May be {@code null} * @return a trust manager with the trust material from the store */ - public static X509ExtendedTrustManager trustManager(String trustStorePath, char[] trustStorePassword, String trustStoreAlgorithm, - @Nullable Environment env) + public static X509ExtendedTrustManager trustManager(String trustStorePath, String trustStoreType, char[] trustStorePassword, + String trustStoreAlgorithm, @Nullable Environment env) throws NoSuchAlgorithmException, UnrecoverableKeyException, KeyStoreException, IOException, CertificateException { try (InputStream in = Files.newInputStream(resolvePath(trustStorePath, env))) { - // TODO remove reliance on JKS since we can PKCS12 stores... - KeyStore trustStore = KeyStore.getInstance("jks"); + KeyStore trustStore = KeyStore.getInstance(trustStoreType); assert trustStorePassword != null; trustStore.load(in, trustStorePassword); return trustManager(trustStore, trustStoreAlgorithm); diff --git a/plugin/src/main/java/org/elasticsearch/xpack/ssl/SSLConfiguration.java b/plugin/src/main/java/org/elasticsearch/xpack/ssl/SSLConfiguration.java index ebd5fd3c0bc..cdcae4e214b 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/ssl/SSLConfiguration.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/ssl/SSLConfiguration.java @@ -15,6 +15,7 @@ import org.elasticsearch.xpack.XPackSettings; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.TrustManagerFactory; import java.nio.file.Path; +import java.security.KeyStore; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -186,8 +187,7 @@ public final class SSLConfiguration { } else if (System.getProperty("javax.net.ssl.keyStore") != null) { // TODO: we should not support loading a keystore from sysprops... try (SecureString keystorePassword = new SecureString(System.getProperty("javax.net.ssl.keyStorePassword", ""))) { - return new StoreKeyConfig(System.getProperty("javax.net.ssl.keyStore"), - keystorePassword, keystorePassword, + return new StoreKeyConfig(System.getProperty("javax.net.ssl.keyStore"), "jks", keystorePassword, keystorePassword, System.getProperty("ssl.KeyManagerFactory.algorithm", KeyManagerFactory.getDefaultAlgorithm()), System.getProperty("ssl.TrustManagerFactory.algorithm", TrustManagerFactory.getDefaultAlgorithm())); } @@ -205,12 +205,14 @@ public final class SSLConfiguration { } else { SecureString keyStorePassword = SETTINGS_PARSER.keystorePassword.get(settings); String keyStoreAlgorithm = SETTINGS_PARSER.keystoreAlgorithm.get(settings); + String keyStoreType = SETTINGS_PARSER.keystoreType.get(settings); SecureString keyStoreKeyPassword = SETTINGS_PARSER.keystoreKeyPassword.get(settings);; if (keyStoreKeyPassword.length() == 0) { keyStoreKeyPassword = keyStorePassword; } String trustStoreAlgorithm = SETTINGS_PARSER.truststoreAlgorithm.get(settings); - return new StoreKeyConfig(keyStorePath, keyStorePassword, keyStoreKeyPassword, keyStoreAlgorithm, trustStoreAlgorithm); + return new StoreKeyConfig(keyStorePath, keyStoreType, keyStorePassword, keyStoreKeyPassword, keyStoreAlgorithm, + trustStoreAlgorithm); } } @@ -242,10 +244,11 @@ public final class SSLConfiguration { } else if (trustStorePath != null) { SecureString trustStorePassword = SETTINGS_PARSER.truststorePassword.get(settings); String trustStoreAlgorithm = SETTINGS_PARSER.truststoreAlgorithm.get(settings); - return new StoreTrustConfig(trustStorePath, trustStorePassword, trustStoreAlgorithm); + String trustStoreType = SETTINGS_PARSER.truststoreType.get(settings); + return new StoreTrustConfig(trustStorePath, trustStoreType, trustStorePassword, trustStoreAlgorithm); } else if (global == null && System.getProperty("javax.net.ssl.trustStore") != null) { try (SecureString truststorePassword = new SecureString(System.getProperty("javax.net.ssl.trustStorePassword", ""))) { - return new StoreTrustConfig(System.getProperty("javax.net.ssl.trustStore"), truststorePassword, + return new StoreTrustConfig(System.getProperty("javax.net.ssl.trustStore"), "jks", truststorePassword, System.getProperty("ssl.TrustManagerFactory.algorithm", TrustManagerFactory.getDefaultAlgorithm())); } } else if (global != null && keyConfig == global.keyConfig()) { diff --git a/plugin/src/main/java/org/elasticsearch/xpack/ssl/SSLConfigurationSettings.java b/plugin/src/main/java/org/elasticsearch/xpack/ssl/SSLConfigurationSettings.java index 54a3786c4dc..248181b9b23 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/ssl/SSLConfigurationSettings.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/ssl/SSLConfigurationSettings.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.ssl; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.TrustManagerFactory; +import java.security.KeyStore; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -32,10 +33,12 @@ public class SSLConfigurationSettings { public final Setting> keystorePath; public final Setting keystorePassword; public final Setting keystoreAlgorithm; + public final Setting keystoreType; public final Setting keystoreKeyPassword; public final Setting> truststorePath; public final Setting truststorePassword; public final Setting truststoreAlgorithm; + public final Setting truststoreType; public final Setting> trustRestrictionsPath; public final Setting> keyPath; public final Setting keyPassword; @@ -52,6 +55,12 @@ public class SSLConfigurationSettings { private final List> allSettings; + /** + * We explicitly default to "jks" here (rather than {@link KeyStore#getDefaultType()}) for backwards compatibility. + * Older versions of X-Pack only supported JKS and never looked at the JVM's configured default. + */ + private static final String DEFAULT_KEYSTORE_TYPE = "jks"; + private static final Function>> CIPHERS_SETTING_TEMPLATE = key -> Setting.listSetting(key, Collections .emptyList(), Function.identity(), Property.NodeScope, Property.Filtered); public static final Setting> CIPHERS_SETTING_PROFILES = Setting.affixKeySetting("transport.profiles.", @@ -121,6 +130,16 @@ public class SSLConfigurationSettings { public static final Setting TRUST_STORE_ALGORITHM_PROFILES = Setting.affixKeySetting("transport.profiles.", "xpack.security.ssl.truststore.algorithm", TRUST_STORE_ALGORITHM_TEMPLATE); + private static final Function> KEY_STORE_TYPE_TEMPLATE = key -> + new Setting<>(key, DEFAULT_KEYSTORE_TYPE, Function.identity(), Property.NodeScope, Property.Filtered); + public static final Setting KEY_STORE_TYPE_PROFILES = Setting.affixKeySetting("transport.profiles.", + "xpack.security.ssl.keystore.type", KEY_STORE_TYPE_TEMPLATE); + + private static final Function> TRUST_STORE_TYPE_TEMPLATE = key -> + new Setting<>(key, DEFAULT_KEYSTORE_TYPE, Function.identity(), Property.NodeScope, Property.Filtered); + public static final Setting TRUST_STORE_TYPE_PROFILES = Setting.affixKeySetting("transport.profiles.", + "xpack.security.ssl.truststore.type", TRUST_STORE_TYPE_TEMPLATE); + private static final Function>> TRUST_RESTRICTIONS_TEMPLATE = key -> new Setting<>(key, s -> null, Optional::ofNullable, Property.NodeScope, Property.Filtered); public static final Setting> TRUST_RESTRICTIONS_PROFILES = Setting.affixKeySetting("transport.profiles.", @@ -179,6 +198,8 @@ public class SSLConfigurationSettings { truststorePassword = TRUSTSTORE_PASSWORD_TEMPLATE.apply(prefix + "truststore.secure_password"); keystoreAlgorithm = KEY_STORE_ALGORITHM_TEMPLATE.apply(prefix + "keystore.algorithm"); truststoreAlgorithm = TRUST_STORE_ALGORITHM_TEMPLATE.apply(prefix + "truststore.algorithm"); + keystoreType = KEY_STORE_TYPE_TEMPLATE.apply(prefix + "keystore.type"); + truststoreType = KEY_STORE_TYPE_TEMPLATE.apply(prefix + "truststore.type"); trustRestrictionsPath = TRUST_RESTRICTIONS_TEMPLATE.apply(prefix + "trust_restrictions.path"); keyPath = KEY_PATH_TEMPLATE.apply(prefix + "key"); legacyKeyPassword = LEGACY_KEY_PASSWORD_TEMPLATE.apply(prefix + "key_passphrase"); @@ -189,8 +210,8 @@ public class SSLConfigurationSettings { verificationMode = VERIFICATION_MODE_SETTING_TEMPLATE.apply(prefix + "verification_mode"); this.allSettings = Arrays.asList(ciphers, supportedProtocols, - keystorePath, keystorePassword, keystoreAlgorithm, keystoreKeyPassword, - truststorePath, truststorePassword, truststoreAlgorithm, trustRestrictionsPath, + keystorePath, keystorePassword, keystoreAlgorithm, keystoreType, keystoreKeyPassword, + truststorePath, truststorePassword, truststoreAlgorithm, truststoreType, trustRestrictionsPath, keyPath, keyPassword, cert, caPaths, clientAuth, verificationMode, legacyKeystorePassword, legacyKeystoreKeyPassword, legacyKeyPassword, legacyTruststorePassword); } @@ -222,7 +243,8 @@ public class SSLConfigurationSettings { return Arrays.asList(CIPHERS_SETTING_PROFILES, SUPPORTED_PROTOCOLS_PROFILES, KEYSTORE_PATH_PROFILES, LEGACY_KEYSTORE_PASSWORD_PROFILES, KEYSTORE_PASSWORD_PROFILES, LEGACY_KEYSTORE_KEY_PASSWORD_PROFILES, KEYSTORE_KEY_PASSWORD_PROFILES, TRUST_STORE_PATH_PROFILES, LEGACY_TRUSTSTORE_PASSWORD_PROFILES, - TRUSTSTORE_PASSWORD_PROFILES, KEY_STORE_ALGORITHM_PROFILES, TRUST_STORE_ALGORITHM_PROFILES, TRUST_RESTRICTIONS_PROFILES, + TRUSTSTORE_PASSWORD_PROFILES, KEY_STORE_ALGORITHM_PROFILES, TRUST_STORE_ALGORITHM_PROFILES, + KEY_STORE_TYPE_PROFILES, TRUST_STORE_TYPE_PROFILES, TRUST_RESTRICTIONS_PROFILES, KEY_PATH_PROFILES, LEGACY_KEY_PASSWORD_PROFILES, KEY_PASSWORD_PROFILES,CERT_PROFILES,CAPATH_SETTING_PROFILES, CLIENT_AUTH_SETTING_PROFILES, VERIFICATION_MODE_SETTING_PROFILES); } diff --git a/plugin/src/main/java/org/elasticsearch/xpack/ssl/StoreKeyConfig.java b/plugin/src/main/java/org/elasticsearch/xpack/ssl/StoreKeyConfig.java index d095ce95828..a94c4195ff3 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/ssl/StoreKeyConfig.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/ssl/StoreKeyConfig.java @@ -35,6 +35,7 @@ import java.util.Objects; class StoreKeyConfig extends KeyConfig { final String keyStorePath; + final String keyStoreType; final SecureString keyStorePassword; final String keyStoreAlgorithm; final SecureString keyPassword; @@ -43,14 +44,16 @@ class StoreKeyConfig extends KeyConfig { /** * Creates a new configuration that can be used to load key and trust material from a {@link KeyStore} * @param keyStorePath the path to the keystore file + * @param keyStoreType the type of the keystore file * @param keyStorePassword the password for the keystore * @param keyPassword the password for the private key in the keystore * @param keyStoreAlgorithm the algorithm for the keystore * @param trustStoreAlgorithm the algorithm to use when loading as a truststore */ - StoreKeyConfig(String keyStorePath, SecureString keyStorePassword, SecureString keyPassword, String keyStoreAlgorithm, - String trustStoreAlgorithm) { + StoreKeyConfig(String keyStorePath, String keyStoreType, SecureString keyStorePassword, SecureString keyPassword, + String keyStoreAlgorithm, String trustStoreAlgorithm) { this.keyStorePath = Objects.requireNonNull(keyStorePath, "keystore path must be specified"); + this.keyStoreType = Objects.requireNonNull(keyStoreType, "keystore type must be specified"); // since we support reloading the keystore, we must store the passphrase in memory for the life of the node, so we // clone the password and never close it during our uses below this.keyStorePassword = Objects.requireNonNull(keyStorePassword, "keystore password must be specified").clone(); @@ -73,7 +76,7 @@ class StoreKeyConfig extends KeyConfig { @Override X509ExtendedTrustManager createTrustManager(@Nullable Environment environment) { try { - return CertUtils.trustManager(keyStorePath, keyStorePassword.getChars(), trustStoreAlgorithm, environment); + return CertUtils.trustManager(keyStorePath, keyStoreType, keyStorePassword.getChars(), trustStoreAlgorithm, environment); } catch (Exception e) { throw new ElasticsearchException("failed to initialize a TrustManagerFactory", e); } @@ -107,8 +110,7 @@ class StoreKeyConfig extends KeyConfig { private KeyStore getKeyStore(@Nullable Environment environment) throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException { try (InputStream in = Files.newInputStream(CertUtils.resolvePath(keyStorePath, environment))) { - // TODO remove reliance on JKS since we can use PKCS12 stores in JDK8+... - KeyStore ks = KeyStore.getInstance("jks"); + KeyStore ks = KeyStore.getInstance(keyStoreType); ks.load(in, keyStorePassword.getChars()); return ks; } @@ -154,6 +156,7 @@ class StoreKeyConfig extends KeyConfig { @Override public String toString() { return "keyStorePath=[" + keyStorePath + + "], keyStoreType=[" + keyStoreType + "], keyStoreAlgorithm=[" + keyStoreAlgorithm + "], trustStoreAlgorithm=[" + trustStoreAlgorithm + "]"; diff --git a/plugin/src/main/java/org/elasticsearch/xpack/ssl/StoreTrustConfig.java b/plugin/src/main/java/org/elasticsearch/xpack/ssl/StoreTrustConfig.java index 608b86742e3..f767e3316d4 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/ssl/StoreTrustConfig.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/ssl/StoreTrustConfig.java @@ -22,6 +22,7 @@ import java.util.Objects; class StoreTrustConfig extends TrustConfig { final String trustStorePath; + final String trustStoreType; final SecureString trustStorePassword; final String trustStoreAlgorithm; @@ -31,8 +32,9 @@ class StoreTrustConfig extends TrustConfig { * @param trustStorePassword the password for the truststore * @param trustStoreAlgorithm the algorithm to use for reading the truststore */ - StoreTrustConfig(String trustStorePath, SecureString trustStorePassword, String trustStoreAlgorithm) { + StoreTrustConfig(String trustStorePath, String trustStoreType, SecureString trustStorePassword, String trustStoreAlgorithm) { this.trustStorePath = trustStorePath; + this.trustStoreType = trustStoreType; // since we support reloading the truststore, we must store the passphrase in memory for the life of the node, so we // clone the password and never close it during our uses below this.trustStorePassword = Objects.requireNonNull(trustStorePassword, "truststore password must be specified").clone(); @@ -42,7 +44,7 @@ class StoreTrustConfig extends TrustConfig { @Override X509ExtendedTrustManager createTrustManager(@Nullable Environment environment) { try { - return CertUtils.trustManager(trustStorePath, trustStorePassword.getChars(), trustStoreAlgorithm, environment); + return CertUtils.trustManager(trustStorePath, trustStoreType, trustStorePassword.getChars(), trustStoreAlgorithm, environment); } catch (Exception e) { throw new ElasticsearchException("failed to initialize a TrustManagerFactory", e); } diff --git a/plugin/src/test/java/org/elasticsearch/xpack/ssl/SSLConfigurationSettingsTests.java b/plugin/src/test/java/org/elasticsearch/xpack/ssl/SSLConfigurationSettingsTests.java index 935052840f7..135f5e259d4 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/ssl/SSLConfigurationSettingsTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/ssl/SSLConfigurationSettingsTests.java @@ -75,13 +75,16 @@ public class SSLConfigurationSettingsTests extends ESTestCase { assertThat(ssl.keyPassword.exists(settings), is(false)); assertThat(ssl.keyPath.get(settings).isPresent(), is(false)); assertThat(ssl.keystoreAlgorithm.get(settings), is(KeyManagerFactory.getDefaultAlgorithm())); + assertThat(ssl.keystoreType.get(settings), is("jks")); assertThat(ssl.keystoreKeyPassword.exists(settings), is(false)); assertThat(ssl.keystorePassword.exists(settings), is(false)); assertThat(ssl.keystorePath.get(settings).isPresent(), is(false)); assertThat(ssl.supportedProtocols.get(settings).size(), is(0)); assertThat(ssl.truststoreAlgorithm.get(settings), is(TrustManagerFactory.getDefaultAlgorithm())); + assertThat(ssl.truststoreType.get(settings), is("jks")); assertThat(ssl.truststorePassword.exists(settings), is(false)); assertThat(ssl.truststorePath.get(settings).isPresent(), is(false)); + assertThat(ssl.trustRestrictionsPath.get(settings).isPresent(), is(false)); assertThat(ssl.verificationMode.get(settings).isPresent(), is(false)); } diff --git a/plugin/src/test/java/org/elasticsearch/xpack/ssl/SSLServiceTests.java b/plugin/src/test/java/org/elasticsearch/xpack/ssl/SSLServiceTests.java index 04a7aff49f2..abbd0851fc4 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/ssl/SSLServiceTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/ssl/SSLServiceTests.java @@ -5,29 +5,6 @@ */ package org.elasticsearch.xpack.ssl; -import org.elasticsearch.client.http.HttpHost; -import org.elasticsearch.client.http.HttpResponse; -import org.elasticsearch.client.http.client.methods.HttpGet; -import org.elasticsearch.client.http.concurrent.FutureCallback; -import org.elasticsearch.client.http.conn.ssl.DefaultHostnameVerifier; -import org.elasticsearch.client.http.conn.ssl.NoopHostnameVerifier; -import org.elasticsearch.client.http.impl.client.CloseableHttpClient; -import org.elasticsearch.client.http.impl.client.HttpClients; -import org.elasticsearch.client.http.impl.nio.client.CloseableHttpAsyncClient; -import org.elasticsearch.client.http.impl.nio.client.HttpAsyncClientBuilder; -import org.elasticsearch.client.http.nio.conn.ssl.SSLIOSessionStrategy; -import org.elasticsearch.ElasticsearchException; -import org.elasticsearch.common.CheckedRunnable; -import org.elasticsearch.common.Strings; -import org.elasticsearch.common.settings.MockSecureSettings; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.env.Environment; -import org.elasticsearch.test.ESTestCase; -import org.elasticsearch.test.junit.annotations.Network; -import org.elasticsearch.xpack.XPackSettings; -import org.junit.Before; -import org.mockito.ArgumentCaptor; - import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; @@ -45,6 +22,29 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.client.http.HttpHost; +import org.elasticsearch.client.http.HttpResponse; +import org.elasticsearch.client.http.client.methods.HttpGet; +import org.elasticsearch.client.http.concurrent.FutureCallback; +import org.elasticsearch.client.http.conn.ssl.DefaultHostnameVerifier; +import org.elasticsearch.client.http.conn.ssl.NoopHostnameVerifier; +import org.elasticsearch.client.http.impl.client.CloseableHttpClient; +import org.elasticsearch.client.http.impl.client.HttpClients; +import org.elasticsearch.client.http.impl.nio.client.CloseableHttpAsyncClient; +import org.elasticsearch.client.http.impl.nio.client.HttpAsyncClientBuilder; +import org.elasticsearch.client.http.nio.conn.ssl.SSLIOSessionStrategy; +import org.elasticsearch.common.CheckedRunnable; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.settings.MockSecureSettings; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.env.Environment; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.junit.annotations.Network; +import org.elasticsearch.xpack.XPackSettings; +import org.junit.Before; +import org.mockito.ArgumentCaptor; + import static org.hamcrest.Matchers.arrayContainingInAnyOrder; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsString; @@ -64,12 +64,22 @@ import static org.mockito.Mockito.when; public class SSLServiceTests extends ESTestCase { private Path testnodeStore; + private String testnodeStoreType; private Path testclientStore; private Environment env; @Before public void setup() throws Exception { - testnodeStore = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks"); + // Randomise the keystore type (jks/PKCS#12) + if (randomBoolean()) { + testnodeStore = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks"); + // The default is to use JKS. Randomly test with explicit and with the default value. + testnodeStoreType = randomBoolean() ? "jks" : null; + } else { + testnodeStore = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.p12"); + testnodeStoreType = "PKCS12"; + } + logger.info("Using [{}] key/truststore [{}]", testnodeStoreType, testnodeStore); testclientStore = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.jks"); env = new Environment(Settings.builder().put("path.home", createTempDir()).build()); } @@ -81,6 +91,7 @@ public class SSLServiceTests extends ESTestCase { secureSettings.setString("transport.profiles.foo.xpack.security.ssl.truststore.secure_password", "testclient"); Settings settings = Settings.builder() .put("xpack.ssl.truststore.path", testnodeStore) + .put("xpack.ssl.truststore.type", testnodeStoreType) .setSecureSettings(secureSettings) .put("transport.profiles.foo.xpack.security.ssl.truststore.path", testClientStore) .build(); @@ -105,6 +116,7 @@ public class SSLServiceTests extends ESTestCase { secureSettings.setString("xpack.ssl.keystore.secure_password", "testnode"); Settings settings = Settings.builder() .put("xpack.ssl.keystore.path", testnodeStore) + .put("xpack.ssl.keystore.type", testnodeStoreType) .setSecureSettings(secureSettings) .build(); SSLService sslService = new SSLService(settings, env); @@ -150,6 +162,7 @@ public class SSLServiceTests extends ESTestCase { secureSettings.setString("xpack.ssl.keystore.secure_password", "testnode"); Settings settings = Settings.builder() .put("xpack.ssl.keystore.path", testnodeStore) + .put("xpack.ssl.keystore.type", testnodeStoreType) .setSecureSettings(secureSettings) .build(); SSLService sslService = new SSLService(settings, env); @@ -181,6 +194,7 @@ public class SSLServiceTests extends ESTestCase { secureSettings.setString("xpack.ssl.keystore.secure_password", "testnode"); Settings settings = Settings.builder() .put("xpack.ssl.keystore.path", testnodeStore) + .put("xpack.ssl.keystore.type", testnodeStoreType) .setSecureSettings(secureSettings) .build(); SSLService sslService = new SSLService(settings, env); @@ -193,6 +207,7 @@ public class SSLServiceTests extends ESTestCase { secureSettings.setString("xpack.ssl.truststore.secure_password", "testnode"); Settings settings = Settings.builder() .put("xpack.ssl.truststore.path", testnodeStore) + .put("xpack.ssl.truststore.type", testnodeStoreType) .setSecureSettings(secureSettings) .build(); SSLService sslService = new SSLService(settings, env); @@ -201,8 +216,10 @@ public class SSLServiceTests extends ESTestCase { secureSettings.setString("xpack.security.transport.ssl.keystore.secure_password", "testnode"); settings = Settings.builder() .put("xpack.ssl.truststore.path", testnodeStore) + .put("xpack.ssl.truststore.type", testnodeStoreType) .setSecureSettings(secureSettings) .put("xpack.security.transport.ssl.keystore.path", testnodeStore) + .put("xpack.security.transport.ssl.keystore.type", testnodeStoreType) .build(); sslService = new SSLService(settings, env); assertFalse(sslService.isConfigurationValidForServerUsage(sslService.sslConfiguration(Settings.EMPTY))); @@ -268,8 +285,10 @@ public class SSLServiceTests extends ESTestCase { secureSettings.setString("xpack.ssl.keystore.secure_password", "testnode"); Settings settings = Settings.builder() .put("xpack.ssl.keystore.path", testnodeStore) + .put("xpack.ssl.keystore.type", testnodeStoreType) .setSecureSettings(secureSettings) .put("xpack.ssl.truststore.path", testnodeStore) + .put("xpack.ssl.truststore.type", testnodeStoreType) .build(); ElasticsearchException e = expectThrows(ElasticsearchException.class, () -> new SSLService(settings, env)); @@ -279,6 +298,7 @@ public class SSLServiceTests extends ESTestCase { public void testThatKeystorePasswordIsRequired() throws Exception { Settings settings = Settings.builder() .put("xpack.ssl.keystore.path", testnodeStore) + .put("xpack.ssl.keystore.type", testnodeStoreType) .build(); ElasticsearchException e = expectThrows(ElasticsearchException.class, () -> new SSLService(settings, env)); @@ -293,6 +313,7 @@ public class SSLServiceTests extends ESTestCase { secureSettings.setString("xpack.ssl.keystore.secure_password", "testnode"); Settings settings = Settings.builder() .put("xpack.ssl.keystore.path", testnodeStore) + .put("xpack.ssl.keystore.type", testnodeStoreType) .setSecureSettings(secureSettings) .putArray("xpack.ssl.ciphers", ciphers.toArray(new String[ciphers.size()])) .build(); @@ -309,6 +330,7 @@ public class SSLServiceTests extends ESTestCase { Settings settings = Settings.builder() .put("xpack.ssl.keystore.path", testnodeStore) + .put("xpack.ssl.keystore.type", testnodeStoreType) .setSecureSettings(secureSettings) .putArray("xpack.ssl.cipher_suites", new String[]{"foo", "bar"}) .build(); @@ -322,6 +344,7 @@ public class SSLServiceTests extends ESTestCase { secureSettings.setString("xpack.ssl.keystore.secure_password", "testnode"); Settings settings = Settings.builder() .put("xpack.ssl.keystore.path", testnodeStore) + .put("xpack.ssl.keystore.type", testnodeStoreType) .setSecureSettings(secureSettings) .build(); SSLService sslService = new SSLService(settings, env); @@ -335,6 +358,7 @@ public class SSLServiceTests extends ESTestCase { secureSettings.setString("xpack.ssl.keystore.secure_password", "testnode"); Settings settings = Settings.builder() .put("xpack.ssl.keystore.path", testnodeStore) + .put("xpack.ssl.keystore.type", testnodeStoreType) .setSecureSettings(secureSettings) .build(); SSLService sslService = new SSLService(settings, env); @@ -359,6 +383,7 @@ public class SSLServiceTests extends ESTestCase { secureSettings.setString("xpack.ssl.keystore.secure_password", "testnode"); Settings settings = Settings.builder() .put("xpack.ssl.keystore.path", testnodeStore) + .put("xpack.ssl.keystore.type", testnodeStoreType) .setSecureSettings(secureSettings) .build(); SSLService sslService = new SSLService(settings, env); diff --git a/plugin/src/test/java/org/elasticsearch/xpack/ssl/StoreKeyConfigTests.java b/plugin/src/test/java/org/elasticsearch/xpack/ssl/StoreKeyConfigTests.java new file mode 100644 index 00000000000..025a5138295 --- /dev/null +++ b/plugin/src/test/java/org/elasticsearch/xpack/ssl/StoreKeyConfigTests.java @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.xpack.ssl; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509ExtendedKeyManager; +import java.security.PrivateKey; + +import org.elasticsearch.common.settings.SecureString; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.env.Environment; +import org.elasticsearch.test.ESTestCase; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.notNullValue; + +public class StoreKeyConfigTests extends ESTestCase { + + public void testCreateKeyManagerUsingJKS() throws Exception { + tryReadPrivateKeyFromKeyStore("jks", ".jks"); + } + + public void testCreateKeyManagerUsingPKCS12() throws Exception { + tryReadPrivateKeyFromKeyStore("PKCS12", ".p12"); + } + + private void tryReadPrivateKeyFromKeyStore(String type, String extension) { + final Settings settings = Settings.builder().put("path.home", createTempDir()).build(); + final String path = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode" + extension).toString(); + final SecureString keyStorePassword = new SecureString("testnode".toCharArray()); + final StoreKeyConfig keyConfig = new StoreKeyConfig(path, type, keyStorePassword, keyStorePassword, + KeyManagerFactory.getDefaultAlgorithm(), TrustManagerFactory.getDefaultAlgorithm()); + final X509ExtendedKeyManager keyManager = keyConfig.createKeyManager(new Environment(settings)); + final PrivateKey key = keyManager.getPrivateKey("testnode"); + assertThat(key, notNullValue()); + assertThat(key.getAlgorithm(), equalTo("RSA")); + assertThat(key.getFormat(), equalTo("PKCS#8")); + } +}