diff --git a/docs/reference/settings/security-settings.asciidoc b/docs/reference/settings/security-settings.asciidoc index 2176d0d3ee6..cda5ac4977b 100644 --- a/docs/reference/settings/security-settings.asciidoc +++ b/docs/reference/settings/security-settings.asciidoc @@ -400,8 +400,9 @@ 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`. +The format of the keystore file. Should be `jks` to use the Java +Keystore format, `PKCS12` to use PKCS#12 files, or `PKCS11` to use a PKCS#11 token. +The default is `jks`. `ssl.keystore.password`:: The password to the keystore. @@ -426,8 +427,9 @@ The password to the truststore. The password to the truststore. `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`. +The format of the keystore file. Should be `jks` to use the Java +Keystore format, `PKCS12` to use PKCS#12 files, or `PKCS11` to use a PKCS#11 token. +The default is `jks`. `ssl.verification_mode`:: Indicates the type of verification when using `ldaps` to protect against man @@ -649,8 +651,9 @@ The path to the Java Keystore file that contains a private key and certificate. `ssl.key` and `ssl.keystore.path` cannot 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`. +The format of the keystore file. Should be `jks` to use the Java +Keystore format, `PKCS12` to use PKCS#12 files, or `PKCS11` to use a PKCS#11 token. +The default is `jks`. `ssl.truststore.password`:: The password to the truststore. @@ -664,8 +667,9 @@ The path to the Java Keystore file that contains the certificates to trust. same time. `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`. +The format of the truststore file. Should be `jks` to use the Java +Keystore format, `PKCS12` to use PKCS#12 files, or `PKCS11` to use a PKCS#11 token. +The default is `jks`. `ssl.verification_mode`:: Indicates the type of verification when using `ldaps` to protect against man @@ -1062,7 +1066,7 @@ Must be either a Java Keystore (jks) or a PKCS#12 file. same time. `ssl.truststore.type`:: -The type of the truststore (`ssl.truststore.path`). Must be either `jks` or +The type of the truststore (`ssl.truststore.path`). Must be either `jks` or `PKCS12`. If the keystore path ends in ".p12", ".pfx" or "pkcs12", this setting defaults to `PKCS12`. Otherwise, it defaults to `jks`. @@ -1316,6 +1320,32 @@ a PKCS#12 container includes trusted certificate ("anchor") entries look for `openssl pkcs12 -info` output, or `trustedCertEntry` in the `keytool -list` output. +===== PKCS#11 tokens + +When using a PKCS#11 cryptographic token, which contains the +private key, certificate, and certificates that should be trusted, use +the following settings: + +`xpack.ssl.keystore.type`:: +Set this to `PKCS11`. + +`xpack.ssl.truststore.type`:: +Set this to `PKCS11`. + + +[[pkcs11-truststore-note]] +[NOTE] +When configuring the PKCS#11 token that your JVM is configured to use as +a keystore or a truststore for Elasticsearch, the PIN for the token can be +configured by setting the appropriate value to `xpack.ssl.truststore.password` +or `xpack.ssl.truststore.secure_password`. In the absence of the above, {es} will +fallback to use he appropriate JVM setting (`-Djavax.net.ssl.trustStorePassword`) +if that s set. +Since there can only be one PKCS#11 token configured, only one keystore and +truststore will be usable for configuration in {es}. This in turn means +that only one certificate can be used for TLS both in the transport and the +http layer. + [[http-tls-ssl-settings]] :ssl-prefix: xpack.security.http :component: HTTP diff --git a/docs/reference/settings/ssl-settings.asciidoc b/docs/reference/settings/ssl-settings.asciidoc index 12729abf4ab..2d513c74237 100644 --- a/docs/reference/settings/ssl-settings.asciidoc +++ b/docs/reference/settings/ssl-settings.asciidoc @@ -145,3 +145,17 @@ Password to the PKCS#12 file. +{ssl-prefix}.ssl.truststore.secure_password+ (<>):: Password to the PKCS#12 file. + +===== PKCS#11 Tokens + +{security} can be configured to use a PKCS#11 token that contains the private key, +certificate and certificates that should be trusted. + +PKCS#11 token require additional configuration on the JVM level and can be enabled +via the following settings: + ++{ssl-prefix}.keystore.type+:: +Set this to `PKCS11` to indicate that the PKCS#11 token should be used as a keystore. + ++{ssl-prefix}.truststore.type+:: +Set this to `PKCS11` to indicate that the PKCS#11 token should be used as a truststore. \ No newline at end of file diff --git a/x-pack/docs/en/rest-api/security/ssl.asciidoc b/x-pack/docs/en/rest-api/security/ssl.asciidoc index 6462699570f..3593dbfbbc2 100644 --- a/x-pack/docs/en/rest-api/security/ssl.asciidoc +++ b/x-pack/docs/en/rest-api/security/ssl.asciidoc @@ -34,6 +34,10 @@ The list does not include certificates that are sourced from the default SSL context of the Java Runtime Environment (JRE), even if those certificates are in use within {xpack}. +NOTE: When a PKCS#11 token is configured as the truststore of the JRE, the API +will return all the certificates that are included in the PKCS#11 token +irrespectively to whether these are used in the {es} TLS configuration or not. + If {xpack} is configured to use a keystore or truststore, the API output includes all certificates in that store, even though some of the certificates might not be in active use within the cluster. diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/CertParsingUtils.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/CertParsingUtils.java index 6503f686b64..11843a40020 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/CertParsingUtils.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/CertParsingUtils.java @@ -197,6 +197,7 @@ public class CertParsingUtils { static KeyConfig createKeyConfig(X509KeyPairSettings keyPair, Settings settings, String trustStoreAlgorithm) { String keyPath = keyPair.keyPath.get(settings).orElse(null); String keyStorePath = keyPair.keystorePath.get(settings).orElse(null); + String keyStoreType = getKeyStoreType(keyPair.keystoreType, settings, keyStorePath); if (keyPath != null && keyStorePath != null) { throw new IllegalArgumentException("you cannot specify a keystore and key file"); @@ -212,10 +213,9 @@ public class CertParsingUtils { return new PEMKeyConfig(keyPath, keyPassword, certPath); } - if (keyStorePath != null) { + if (keyStorePath != null || keyStoreType.equalsIgnoreCase("pkcs11")) { SecureString keyStorePassword = keyPair.keystorePassword.get(settings); String keyStoreAlgorithm = keyPair.keystoreAlgorithm.get(settings); - String keyStoreType = getKeyStoreType(keyPair.keystoreType, settings, keyStorePath); SecureString keyStoreKeyPassword = keyPair.keystoreKeyPassword.get(settings); if (keyStoreKeyPassword.length() == 0) { keyStoreKeyPassword = keyStorePassword; @@ -224,7 +224,6 @@ public class CertParsingUtils { trustStoreAlgorithm); } return null; - } /** diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/DefaultJDKTrustConfig.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/DefaultJDKTrustConfig.java index 0a4c0552f69..4b5055a9e86 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/DefaultJDKTrustConfig.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/DefaultJDKTrustConfig.java @@ -7,6 +7,7 @@ package org.elasticsearch.xpack.core.ssl; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.env.Environment; import org.elasticsearch.xpack.core.ssl.cert.CertificateInfo; @@ -30,9 +31,14 @@ import java.util.List; */ class DefaultJDKTrustConfig extends TrustConfig { - static final DefaultJDKTrustConfig INSTANCE = new DefaultJDKTrustConfig(); + private SecureString trustStorePassword; - private DefaultJDKTrustConfig() { + /** + * @param trustStorePassword the password for the default jdk truststore defined either as a system property or in the Elasticsearch + * configuration. It applies only when PKCS#11 tokens are user, is null otherwise + */ + DefaultJDKTrustConfig(@Nullable SecureString trustStorePassword) { + this.trustStorePassword = trustStorePassword; } @Override @@ -76,13 +82,14 @@ class DefaultJDKTrustConfig extends TrustConfig { /** * Merges the default trust configuration with the provided {@link TrustConfig} * @param trustConfig the trust configuration to merge with + * @param trustStorePassword the password for the default jdk truststore. It applies only to PKCS#11 tokens * @return a {@link TrustConfig} that represents a combination of both trust configurations */ - static TrustConfig merge(TrustConfig trustConfig) { + static TrustConfig merge(TrustConfig trustConfig, SecureString trustStorePassword) { if (trustConfig == null) { - return INSTANCE; + return new DefaultJDKTrustConfig(trustStorePassword); } else { - return new CombiningTrustConfig(Arrays.asList(INSTANCE, trustConfig)); + return new CombiningTrustConfig(Arrays.asList(new DefaultJDKTrustConfig(trustStorePassword), trustConfig)); } } @@ -94,9 +101,10 @@ class DefaultJDKTrustConfig extends TrustConfig { * @return the KeyStore used as truststore for PKCS#11 initialized with the password, null otherwise */ private KeyStore getSystemTrustStore() throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException { - if (System.getProperty("javax.net.ssl.trustStoreType", "").equalsIgnoreCase("PKCS11")) { + if (System.getProperty("javax.net.ssl.trustStoreType", "").equalsIgnoreCase("PKCS11") + && trustStorePassword != null) { KeyStore keyStore = KeyStore.getInstance("PKCS11"); - keyStore.load(null, System.getProperty("javax.net.ssl.trustStorePassword", "").toCharArray()); + keyStore.load(null, trustStorePassword.getChars()); return keyStore; } return null; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/SSLConfiguration.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/SSLConfiguration.java index 9054d664eec..0862cb929ef 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/SSLConfiguration.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/SSLConfiguration.java @@ -212,12 +212,11 @@ public final class SSLConfiguration { private static TrustConfig createCertChainTrustConfig(Settings settings, KeyConfig keyConfig, SSLConfiguration global) { String trustStorePath = SETTINGS_PARSER.truststorePath.get(settings).orElse(null); - + String trustStoreType = getKeyStoreType(SETTINGS_PARSER.truststoreType, settings, trustStorePath); List caPaths = getListOrNull(SETTINGS_PARSER.caPaths, settings); if (trustStorePath != null && caPaths != null) { throw new IllegalArgumentException("you cannot specify a truststore and ca files"); } - VerificationMode verificationMode = SETTINGS_PARSER.verificationMode.get(settings).orElseGet(() -> { if (global != null) { return global.verificationMode(); @@ -228,26 +227,41 @@ public final class SSLConfiguration { return TrustAllConfig.INSTANCE; } else if (caPaths != null) { return new PEMTrustConfig(caPaths); - } else if (trustStorePath != null) { - SecureString trustStorePassword = SETTINGS_PARSER.truststorePassword.get(settings); + } else if (trustStorePath != null || trustStoreType.equalsIgnoreCase("pkcs11")) { String trustStoreAlgorithm = SETTINGS_PARSER.truststoreAlgorithm.get(settings); - String trustStoreType = getKeyStoreType(SETTINGS_PARSER.truststoreType, settings, trustStorePath); + SecureString trustStorePassword = SETTINGS_PARSER.truststorePassword.get(settings); return new StoreTrustConfig(trustStorePath, trustStoreType, trustStorePassword, trustStoreAlgorithm); } else if (global == null && System.getProperty("javax.net.ssl.trustStore") != null && System.getProperty("javax.net.ssl.trustStore").equals("NONE") == false) { try (SecureString truststorePassword = new SecureString(System.getProperty("javax.net.ssl.trustStorePassword", ""))) { return new StoreTrustConfig(System.getProperty("javax.net.ssl.trustStore"), KeyStore.getDefaultType(), truststorePassword, - System.getProperty("ssl.TrustManagerFactory.algorithm", TrustManagerFactory.getDefaultAlgorithm())); + System.getProperty("ssl.TrustManagerFactory.algorithm", TrustManagerFactory.getDefaultAlgorithm())); } } else if (global != null && keyConfig == global.keyConfig()) { return global.trustConfig(); } else if (keyConfig != KeyConfig.NONE) { - return DefaultJDKTrustConfig.merge(keyConfig); + return DefaultJDKTrustConfig.merge(keyConfig, getDefaultTrustStorePassword(settings)); } else { - return DefaultJDKTrustConfig.INSTANCE; + return new DefaultJDKTrustConfig(getDefaultTrustStorePassword(settings)); } } + private static SecureString getDefaultTrustStorePassword(Settings settings) { + // We only handle the default store password if it's a PKCS#11 token + if (System.getProperty("javax.net.ssl.trustStoreType", "").equalsIgnoreCase("PKCS11")) { + try (SecureString systemTrustStorePassword = + new SecureString(System.getProperty("javax.net.ssl.trustStorePassword", "").toCharArray())) { + if (systemTrustStorePassword.length() == 0) { + try (SecureString trustStorePassword = SETTINGS_PARSER.truststorePassword.get(settings)) { + return trustStorePassword; + } + } + return systemTrustStorePassword; + } + } + return null; + } + private static List getListOrNull(Setting> listSetting, Settings settings) { return getListOrDefault(listSetting, settings, null); } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/StoreKeyConfig.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/StoreKeyConfig.java index a47745c1334..3337465994c 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/StoreKeyConfig.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/StoreKeyConfig.java @@ -15,8 +15,6 @@ import javax.net.ssl.X509ExtendedKeyManager; import javax.net.ssl.X509ExtendedTrustManager; import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; import java.nio.file.Path; import java.security.GeneralSecurityException; import java.security.Key; @@ -49,7 +47,7 @@ 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 keyStorePath the path to the keystore file or null when keyStoreType is pkcs11 * @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 @@ -58,7 +56,7 @@ class StoreKeyConfig extends KeyConfig { */ StoreKeyConfig(String keyStorePath, String keyStoreType, SecureString keyStorePassword, SecureString keyPassword, String keyStoreAlgorithm, String trustStoreAlgorithm) { - this.keyStorePath = Objects.requireNonNull(keyStorePath, "keystore path must be specified"); + this.keyStorePath = keyStorePath; 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 @@ -71,7 +69,7 @@ class StoreKeyConfig extends KeyConfig { @Override X509ExtendedKeyManager createKeyManager(@Nullable Environment environment) { try { - KeyStore ks = getKeyStore(environment); + KeyStore ks = getStore(environment, keyStorePath, keyStoreType, keyStorePassword); checkKeyStore(ks); return CertParsingUtils.keyManager(ks, keyPassword.getChars(), keyStoreAlgorithm); } catch (IOException | CertificateException | NoSuchAlgorithmException | UnrecoverableKeyException | KeyStoreException e) { @@ -82,16 +80,16 @@ class StoreKeyConfig extends KeyConfig { @Override X509ExtendedTrustManager createTrustManager(@Nullable Environment environment) { try { - return CertParsingUtils.trustManager(keyStorePath, keyStoreType, keyStorePassword.getChars(), trustStoreAlgorithm, environment); - } catch (Exception e) { + KeyStore ks = getStore(environment, keyStorePath, keyStoreType, keyStorePassword); + return CertParsingUtils.trustManager(ks, trustStoreAlgorithm); + } catch (IOException | CertificateException | NoSuchAlgorithmException | KeyStoreException e) { throw new ElasticsearchException("failed to initialize a TrustManagerFactory", e); } } @Override Collection certificates(Environment environment) throws GeneralSecurityException, IOException { - final Path path = CertParsingUtils.resolvePath(keyStorePath, environment); - final KeyStore trustStore = CertParsingUtils.readKeyStore(path, keyStoreType, keyStorePassword.getChars()); + final KeyStore trustStore = getStore(environment, keyStorePath, keyStoreType, keyStorePassword); final List certificates = new ArrayList<>(); final Enumeration aliases = trustStore.aliases(); while (aliases.hasMoreElements()) { @@ -112,13 +110,16 @@ class StoreKeyConfig extends KeyConfig { @Override List filesToMonitor(@Nullable Environment environment) { + if (keyStorePath == null) { + return Collections.emptyList(); + } return Collections.singletonList(CertParsingUtils.resolvePath(keyStorePath, environment)); } @Override List privateKeys(@Nullable Environment environment) { try { - KeyStore keyStore = getKeyStore(environment); + KeyStore keyStore = getStore(environment, keyStorePath, keyStoreType, keyStorePassword); List privateKeys = new ArrayList<>(); for (Enumeration e = keyStore.aliases(); e.hasMoreElements(); ) { final String alias = e.nextElement(); @@ -135,15 +136,6 @@ class StoreKeyConfig extends KeyConfig { } } - private KeyStore getKeyStore(@Nullable Environment environment) - throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException { - try (InputStream in = Files.newInputStream(CertParsingUtils.resolvePath(keyStorePath, environment))) { - KeyStore ks = KeyStore.getInstance(keyStoreType); - ks.load(in, keyStorePassword.getChars()); - return ks; - } - } - private void checkKeyStore(KeyStore keyStore) throws KeyStoreException { Enumeration aliases = keyStore.aliases(); while (aliases.hasMoreElements()) { @@ -152,9 +144,11 @@ class StoreKeyConfig extends KeyConfig { return; } } - throw new IllegalArgumentException("the keystore [" + keyStorePath + "] does not contain a private key entry"); + final String message = null != keyStorePath ? + "the keystore [" + keyStorePath + "] does not contain a private key entry" : + "the configured PKCS#11 token does not contain a private key entry"; + throw new IllegalArgumentException(message); } - @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/StoreTrustConfig.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/StoreTrustConfig.java index 7398d32a61c..d4848f98339 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/StoreTrustConfig.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/StoreTrustConfig.java @@ -55,8 +55,8 @@ class StoreTrustConfig extends TrustConfig { @Override X509ExtendedTrustManager createTrustManager(@Nullable Environment environment) { try { - return CertParsingUtils.trustManager(trustStorePath, trustStoreType, trustStorePassword.getChars(), - trustStoreAlgorithm, environment); + KeyStore trustStore = getStore(environment, trustStorePath, trustStoreType, trustStorePassword); + return CertParsingUtils.trustManager(trustStore, trustStoreAlgorithm); } catch (Exception e) { throw new ElasticsearchException("failed to initialize a TrustManagerFactory", e); } @@ -64,8 +64,7 @@ class StoreTrustConfig extends TrustConfig { @Override Collection certificates(Environment environment) throws GeneralSecurityException, IOException { - final Path path = CertParsingUtils.resolvePath(trustStorePath, environment); - final KeyStore trustStore = CertParsingUtils.readKeyStore(path, trustStoreType, trustStorePassword.getChars()); + final KeyStore trustStore = getStore(environment, trustStorePath, trustStoreType, trustStorePassword); final List certificates = new ArrayList<>(); final Enumeration aliases = trustStore.aliases(); while (aliases.hasMoreElements()) { diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/TrustConfig.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/TrustConfig.java index f7f6d28f176..a9bc737c943 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/TrustConfig.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/TrustConfig.java @@ -7,14 +7,21 @@ package org.elasticsearch.xpack.core.ssl; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.env.Environment; import org.elasticsearch.xpack.core.ssl.cert.CertificateInfo; import javax.net.ssl.X509ExtendedTrustManager; import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; import java.nio.file.Path; import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; @@ -58,6 +65,38 @@ abstract class TrustConfig { */ public abstract int hashCode(); + /** + * Loads and returns the appropriate {@link KeyStore} for the given configuration. The KeyStore can be backed by a file + * in any format that the Security Provider might support, or a cryptographic software or hardware token in the case + * of a PKCS#11 Provider. + * + * @param environment the environment to resolve files against or null in the case of running in a transport client + * @param storePath the path to the {@link KeyStore} to load, or null if a PKCS11 token is configured as the keystore/truststore + * of the JVM + * @param storeType the type of the {@link KeyStore} + * @param storePassword the password to be used for decrypting the {@link KeyStore} + * @return the loaded KeyStore to be used as a keystore or a truststore + * @throws KeyStoreException if an instance of the specified type cannot be loaded + * @throws CertificateException if any of the certificates in the keystore could not be loaded + * @throws NoSuchAlgorithmException if the algorithm used to check the integrity of the keystore cannot be found + * @throws IOException if there is an I/O issue with the KeyStore data or the password is incorrect + */ + KeyStore getStore(@Nullable Environment environment, @Nullable String storePath, String storeType, SecureString storePassword) + throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException { + if (null != storePath) { + try (InputStream in = Files.newInputStream(CertParsingUtils.resolvePath(storePath, environment))) { + KeyStore ks = KeyStore.getInstance(storeType); + ks.load(in, storePassword.getChars()); + return ks; + } + } else if (storeType.equalsIgnoreCase("pkcs11")) { + KeyStore ks = KeyStore.getInstance(storeType); + ks.load(null, storePassword.getChars()); + return ks; + } + throw new IllegalArgumentException("keystore.path or truststore.path can only be empty when using a PKCS#11 token"); + } + /** * A trust configuration that is a combination of a trust configuration with the default JDK trust configuration. This trust * configuration returns a trust manager verifies certificates against both the default JDK trusted configurations and the specific diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/SSLConfigurationTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/SSLConfigurationTests.java index bb6fd279eec..e0b70c09add 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/SSLConfigurationTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/SSLConfigurationTests.java @@ -228,20 +228,6 @@ public class SSLConfigurationTests extends ESTestCase { assertThat(ksTrustInfo.trustStoreAlgorithm, is(equalTo("trusted"))); } - public void testThatEmptySettingsAreEqual() { - SSLConfiguration sslConfiguration = new SSLConfiguration(Settings.EMPTY); - SSLConfiguration sslConfiguration1 = new SSLConfiguration(Settings.EMPTY); - assertThat(sslConfiguration.equals(sslConfiguration1), is(equalTo(true))); - assertThat(sslConfiguration1.equals(sslConfiguration), is(equalTo(true))); - assertThat(sslConfiguration.equals(sslConfiguration), is(equalTo(true))); - assertThat(sslConfiguration1.equals(sslConfiguration1), is(equalTo(true))); - - SSLConfiguration profileSSLConfiguration = new SSLConfiguration(Settings.EMPTY, sslConfiguration); - assertThat(sslConfiguration.equals(profileSSLConfiguration), is(equalTo(true))); - assertThat(profileSSLConfiguration.equals(sslConfiguration), is(equalTo(true))); - assertThat(profileSSLConfiguration.equals(profileSSLConfiguration), is(equalTo(true))); - } - public void testThatSettingsWithDifferentKeystoresAreNotEqual() { SSLConfiguration sslConfiguration = new SSLConfiguration(Settings.builder() .put("keystore.path", "path") @@ -268,15 +254,6 @@ public class SSLConfigurationTests extends ESTestCase { assertThat(sslConfiguration1.equals(sslConfiguration1), is(equalTo(true))); } - public void testThatEmptySettingsHaveSameHashCode() { - SSLConfiguration sslConfiguration = new SSLConfiguration(Settings.EMPTY); - SSLConfiguration sslConfiguration1 = new SSLConfiguration(Settings.EMPTY); - assertThat(sslConfiguration.hashCode(), is(equalTo(sslConfiguration1.hashCode()))); - - SSLConfiguration profileSettings = new SSLConfiguration(Settings.EMPTY, sslConfiguration); - assertThat(profileSettings.hashCode(), is(equalTo(sslConfiguration.hashCode()))); - } - public void testThatSettingsWithDifferentKeystoresHaveDifferentHashCode() { SSLConfiguration sslConfiguration = new SSLConfiguration(Settings.builder() .put("keystore.path", "path") @@ -390,7 +367,8 @@ public class SSLConfigurationTests extends ESTestCase { private void assertCombiningTrustConfigContainsCorrectIssuers(SSLConfiguration sslConfiguration) { X509Certificate[] trustConfAcceptedIssuers = sslConfiguration.trustConfig().createTrustManager(null).getAcceptedIssuers(); X509Certificate[] keyConfAcceptedIssuers = sslConfiguration.keyConfig().createTrustManager(null).getAcceptedIssuers(); - X509Certificate[] defaultAcceptedIssuers = DefaultJDKTrustConfig.INSTANCE.createTrustManager(null).getAcceptedIssuers(); + X509Certificate[] defaultAcceptedIssuers = new DefaultJDKTrustConfig(null).createTrustManager(null) + .getAcceptedIssuers(); assertEquals(keyConfAcceptedIssuers.length + defaultAcceptedIssuers.length, trustConfAcceptedIssuers.length); assertThat(Arrays.asList(keyConfAcceptedIssuers), everyItem(isIn(trustConfAcceptedIssuers))); assertThat(Arrays.asList(defaultAcceptedIssuers), everyItem(isIn(trustConfAcceptedIssuers))); diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/SSLServiceTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/SSLServiceTests.java index e0fee670d8d..88d10071e85 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/SSLServiceTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/SSLServiceTests.java @@ -477,8 +477,8 @@ public class SSLServiceTests extends ESTestCase { public void testEmptyTrustManager() throws Exception { Settings settings = Settings.builder().build(); final SSLService sslService = new SSLService(settings, env); - SSLConfiguration sslConfig = new SSLConfiguration(settings); - X509ExtendedTrustManager trustManager = sslService.sslContextHolder(sslConfig).getEmptyTrustManager(); + X509ExtendedTrustManager trustManager = sslService.sslContextHolder(sslService.getSSLConfiguration("xpack.ssl")) + .getEmptyTrustManager(); assertThat(trustManager.getAcceptedIssuers(), emptyArray()); } diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/StoreKeyConfigTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/StoreKeyConfigTests.java index 511fd778113..a7d6088bc7a 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/StoreKeyConfigTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/StoreKeyConfigTests.java @@ -5,6 +5,7 @@ */ package org.elasticsearch.xpack.core.ssl; +import org.elasticsearch.ElasticsearchException; import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.TestEnvironment; @@ -16,6 +17,7 @@ import javax.net.ssl.X509ExtendedKeyManager; import java.security.PrivateKey; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.notNullValue; @@ -31,6 +33,23 @@ public class StoreKeyConfigTests extends ESTestCase { tryReadPrivateKeyFromKeyStore("PKCS12", ".p12"); } + public void testKeyStorePathCanBeEmptyForPkcs11() throws Exception { + assumeFalse("Can't run in a FIPS JVM", inFipsJvm()); + final Settings settings = Settings.builder().put("path.home", createTempDir()).build(); + final SecureString keyStorePassword = new SecureString("password".toCharArray()); + final StoreKeyConfig keyConfig = new StoreKeyConfig(null, "PKCS12", keyStorePassword, keyStorePassword, + KeyManagerFactory.getDefaultAlgorithm(), TrustManagerFactory.getDefaultAlgorithm()); + Exception e = expectThrows(IllegalArgumentException.class, () -> + keyConfig.createKeyManager(TestEnvironment.newEnvironment(settings))); + assertThat(e.getMessage(), equalTo("keystore.path or truststore.path can only be empty when using a PKCS#11 token")); + final StoreKeyConfig keyConfigPkcs11 = new StoreKeyConfig(null, "PKCS11", keyStorePassword, keyStorePassword, + KeyManagerFactory.getDefaultAlgorithm(), TrustManagerFactory.getDefaultAlgorithm()); + ElasticsearchException ee = expectThrows(ElasticsearchException.class, () -> + keyConfigPkcs11.createKeyManager(TestEnvironment.newEnvironment(settings))); + assertThat(ee.getMessage(), containsString("failed to initialize a KeyManagerFactory")); + assertThat(ee.getCause().getMessage(), containsString("PKCS11 not found")); + } + 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(); diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/TestsSSLService.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/TestsSSLService.java index 1d1dfe222c7..25dc0172618 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/TestsSSLService.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/TestsSSLService.java @@ -30,4 +30,8 @@ public class TestsSSLService extends SSLService { public SSLContext sslContext(Settings settings) { return sslContextHolder(super.sslConfiguration(settings)).sslContext(); } + + public SSLContext sslContext(String context) { + return sslContextHolder(super.getSSLConfiguration(context)).sslContext(); + } } diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/webhook/WebhookHttpsIntegrationTests.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/webhook/WebhookHttpsIntegrationTests.java index 0ba733ea4c5..4e62eedd221 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/webhook/WebhookHttpsIntegrationTests.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/webhook/WebhookHttpsIntegrationTests.java @@ -58,7 +58,7 @@ public class WebhookHttpsIntegrationTests extends AbstractWatcherIntegrationTest public void startWebservice() throws Exception { Settings settings = getInstanceFromMaster(Settings.class); TestsSSLService sslService = new TestsSSLService(settings, getInstanceFromMaster(Environment.class)); - webServer = new MockWebServer(sslService.sslContext(settings.getByPrefix("xpack.http.ssl.")), false); + webServer = new MockWebServer(sslService.sslContext("xpack.http.ssl"), false); webServer.start(); }