diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackSettings.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackSettings.java index 9e1b948b9dce9..ed73c0cd26e97 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackSettings.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackSettings.java @@ -8,17 +8,15 @@ package org.elasticsearch.xpack.core; import org.apache.logging.log4j.LogManager; -import org.elasticsearch.common.ssl.SslVerificationMode; -import org.elasticsearch.jdk.JavaVersion; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Setting.Property; +import org.elasticsearch.common.ssl.SslClientAuthenticationMode; +import org.elasticsearch.common.ssl.SslVerificationMode; +import org.elasticsearch.jdk.JavaVersion; import org.elasticsearch.xpack.core.security.SecurityField; import org.elasticsearch.xpack.core.security.authc.support.Hasher; -import org.elasticsearch.common.ssl.SslClientAuthenticationMode; import org.elasticsearch.xpack.core.ssl.SSLConfigurationSettings; -import javax.crypto.SecretKeyFactory; -import javax.net.ssl.SSLContext; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; @@ -26,6 +24,8 @@ import java.util.List; import java.util.Locale; import java.util.function.Function; +import javax.crypto.SecretKeyFactory; +import javax.net.ssl.SSLContext; import static org.elasticsearch.xpack.core.security.SecurityField.USER_SETTING; 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 7f380fa076ee8..0cd8d6a95d600 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 @@ -7,21 +7,15 @@ package org.elasticsearch.xpack.core.ssl; -import org.elasticsearch.common.ssl.PemUtils; -import org.elasticsearch.core.Nullable; import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.ssl.KeyStoreUtil; +import org.elasticsearch.common.ssl.PemUtils; +import org.elasticsearch.common.ssl.SslKeyConfig; import org.elasticsearch.env.Environment; -import javax.net.ssl.KeyManager; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -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; @@ -36,14 +30,23 @@ import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509ExtendedKeyManager; +import javax.net.ssl.X509ExtendedTrustManager; +/** Miscellaneous utulity methods for reading certificates and keystores. + * @see KeyStoreUtil + * @see PemUtils + */ public class CertParsingUtils { private CertParsingUtils() { @@ -58,16 +61,6 @@ static List resolvePaths(List certPaths, Environment environment) return certPaths.stream().map(p -> environment.configFile().resolve(p)).collect(Collectors.toList()); } - public static KeyStore readKeyStore(Path path, String type, char[] password) - throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException { - try (InputStream in = Files.newInputStream(path)) { - KeyStore store = KeyStore.getInstance(type); - assert password != null; - store.load(in, password); - return store; - } - } - /** * Reads the provided paths and parses them into {@link Certificate} objects * @@ -78,21 +71,7 @@ public static KeyStore readKeyStore(Path path, String type, char[] password) public static Certificate[] readCertificates(List certPaths, Environment environment) throws CertificateException, IOException { final List resolvedPaths = resolvePaths(certPaths, environment); - return readCertificates(resolvedPaths); - } - - public static Certificate[] readCertificates(List certPaths) throws CertificateException, IOException { - Collection certificates = new ArrayList<>(); - CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); - for (Path path : certPaths) { - try (InputStream input = Files.newInputStream(path)) { - certificates.addAll(certFactory.generateCertificates(input)); - if (certificates.isEmpty()) { - throw new CertificateException("failed to parse any certificates from [" + path.toAbsolutePath() + "]"); - } - } - } - return certificates.toArray(new Certificate[0]); + return readX509Certificates(resolvedPaths); } public static X509Certificate readX509Certificate(Path path) throws CertificateException, IOException { @@ -114,14 +93,7 @@ public static X509Certificate readX509Certificate(Path path) throws CertificateE @SuppressWarnings("unchecked") public static X509Certificate[] readX509Certificates(List certPaths) throws CertificateException, IOException { - Collection certificates = new ArrayList<>(); - CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); - for (Path path : certPaths) { - try (InputStream input = Files.newInputStream(path)) { - certificates.addAll((Collection) certFactory.generateCertificates(input)); - } - } - return certificates.toArray(new X509Certificate[0]); + return PemUtils.readCertificates(certPaths).stream().map(X509Certificate.class::cast).toArray(X509Certificate[]::new); } public static List readCertificates(InputStream input) throws CertificateException, IOException { @@ -139,19 +111,18 @@ public static List readCertificates(InputStream input) throws Certi * return the password for that key. If it returns {@code null}, then the key-pair for that alias is not read. */ public static Map readPkcs12KeyPairs(Path path, char[] password, Function keyPassword) - throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, UnrecoverableKeyException { + throws GeneralSecurityException, IOException { return readKeyPairsFromKeystore(path, "PKCS12", password, keyPassword); } public static Map readKeyPairsFromKeystore(Path path, String storeType, char[] password, Function keyPassword) - throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException { - - final KeyStore store = readKeyStore(path, storeType, password); + throws IOException, GeneralSecurityException { + final KeyStore store = KeyStoreUtil.readKeyStore(path, storeType, password); return readKeyPairsFromKeystore(store, keyPassword); } - public static Map readKeyPairsFromKeystore(KeyStore store, Function keyPassword) + private static Map readKeyPairsFromKeystore(KeyStore store, Function keyPassword) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException { final Enumeration enumeration = store.aliases(); final Map map = new HashMap<>(store.size()); @@ -170,9 +141,18 @@ public static Map readKeyPairsFromKeystore(KeyStore store, Fun */ public static KeyStore getKeyStoreFromPEM(Path certificatePath, Path keyPath, char[] keyPassword) throws IOException, GeneralSecurityException { - final PrivateKey key = PemUtils.readPrivateKey(keyPath, () -> keyPassword); - final Certificate[] certificates = readCertificates(Collections.singletonList(certificatePath)); - return getKeyStore(certificates, key, keyPassword); + final PrivateKey privateKey = PemUtils.readPrivateKey(keyPath, () -> keyPassword); + final List certificates = PemUtils.readCertificates(List.of(certificatePath)); + return KeyStoreUtil.buildKeyStore(certificates, privateKey, keyPassword); + } + + /** + * Creates a {@link X509ExtendedKeyManager} from a PEM encoded certificate and key file + */ + public static X509ExtendedKeyManager getKeyManagerFromPEM(Path certificatePath, Path keyPath, char[] keyPassword) + throws IOException, GeneralSecurityException { + final KeyStore keyStore = getKeyStoreFromPEM(certificatePath, keyPath, keyPassword); + return KeyStoreUtil.createKeyManager(keyStore, keyPassword, KeyManagerFactory.getDefaultAlgorithm()); } /** @@ -209,19 +189,6 @@ public static X509ExtendedKeyManager keyManager(KeyStore keyStore, char[] passwo throw new IllegalStateException("failed to find a X509ExtendedKeyManager"); } - public static X509ExtendedKeyManager getKeyManager(X509KeyPairSettings keyPair, Settings settings, - @Nullable String trustStoreAlgorithm, Environment environment) { - if (trustStoreAlgorithm == null) { - trustStoreAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); - } - final KeyConfig keyConfig = createKeyConfig(keyPair, settings, trustStoreAlgorithm); - if (keyConfig == null) { - return null; - } else { - return keyConfig.createKeyManager(environment); - } - } - 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); @@ -260,6 +227,20 @@ static KeyConfig createKeyConfig(X509KeyPairSettings keyPair, Settings settings, return null; } + public static SslKeyConfig createKeyConfig(Settings settings, String prefix, Environment environment, + boolean acceptNonSecurePasswords) { + final SslSettingsLoader settingsLoader = new SslSettingsLoader(settings, prefix, acceptNonSecurePasswords); + return settingsLoader.buildKeyConfig(environment.configFile()); + } + + /** + * Creates a {@link X509ExtendedTrustManager} based on the provided PEM certificate authorities + */ + public static X509ExtendedTrustManager getTrustManagerFromPEM(List caPaths) throws GeneralSecurityException, IOException { + final List certificates = PemUtils.readCertificates(caPaths); + return KeyStoreUtil.createTrustManager(certificates); + } + /** * Creates a {@link X509ExtendedTrustManager} based on the provided certificates * @@ -285,22 +266,6 @@ public static KeyStore trustStore(Certificate[] certificates) return store; } - /** - * Loads the truststore and creates a {@link X509ExtendedTrustManager} - * - * @param trustStorePath the path to the truststore - * @param trustStorePassword the password to the truststore - * @param trustStoreAlgorithm the algorithm to use for the truststore - * @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, String trustStoreType, char[] trustStorePassword, - String trustStoreAlgorithm, Environment env) - throws NoSuchAlgorithmException, KeyStoreException, IOException, CertificateException { - KeyStore trustStore = readKeyStore(env.configFile().resolve(trustStorePath), trustStoreType, trustStorePassword); - return trustManager(trustStore, trustStoreAlgorithm); - } - /** * Creates a {@link X509ExtendedTrustManager} based on the trust material in the provided {@link KeyStore} */ diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/PEMKeyConfig.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/PEMKeyConfig.java index 3850b08288dd0..cc54fcb18e0d0 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/PEMKeyConfig.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/PEMKeyConfig.java @@ -80,7 +80,7 @@ X509ExtendedKeyManager createKeyManager(@Nullable Environment environment) { private Certificate[] getCertificateChain(@Nullable Environment environment) throws CertificateException, IOException { final Path certificate = CertParsingUtils.resolvePath(certPath, environment); try { - return CertParsingUtils.readCertificates(Collections.singletonList(certificate)); + return CertParsingUtils.readX509Certificates(Collections.singletonList(certificate)); } catch (FileNotFoundException | NoSuchFileException fileException) { throw missingKeyConfigFile(fileException, CERTIFICATE_FILE, certificate); } catch (AccessDeniedException accessException) { diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/SslSettingsLoader.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/SslSettingsLoader.java index 6786888ada58f..7ed9318450d93 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/SslSettingsLoader.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/SslSettingsLoader.java @@ -18,9 +18,11 @@ import org.elasticsearch.common.ssl.SslKeyConfig; import org.elasticsearch.common.ssl.SslTrustConfig; import org.elasticsearch.common.ssl.SslVerificationMode; +import org.elasticsearch.core.Nullable; import org.elasticsearch.env.Environment; import java.nio.file.Path; +import java.security.KeyStore; import java.util.List; import java.util.Map; import java.util.function.Function; @@ -113,7 +115,17 @@ public SslConfiguration load(Environment env) { } public static SslConfiguration load(Settings settings, String prefix, Environment env) { + return load(settings, prefix, env, null); + } + + public static SslConfiguration load( + Settings settings, + String prefix, + Environment env, + @Nullable Function keyStoreFilter + ) { final SslSettingsLoader settingsLoader = new SslSettingsLoader(settings, prefix, true); + settingsLoader.setKeyStoreFilter(keyStoreFilter); return settingsLoader.load(env); } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/cert/CertificateInfo.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/cert/CertificateInfo.java index 4615e88811bda..92b6c377f40c2 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/cert/CertificateInfo.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/cert/CertificateInfo.java @@ -6,23 +6,33 @@ */ package org.elasticsearch.xpack.core.ssl.cert; +import org.elasticsearch.Version; +import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.core.Nullable; import java.io.IOException; import java.security.cert.X509Certificate; import java.time.Instant; import java.time.ZoneOffset; import java.time.ZonedDateTime; +import java.util.Comparator; import java.util.Objects; /** - * Simple model of an X.509 certificate that is known to X-Pack + * Simple model of an X.509 certificate that is known to Elasticsearch */ -public class CertificateInfo implements ToXContentObject, Writeable { +public class CertificateInfo implements ToXContentObject, Writeable, Comparable { + + private static final Comparator COMPARATOR = + Comparator.comparing(CertificateInfo::path, Comparator.nullsLast(Comparator.naturalOrder())) + .thenComparing(CertificateInfo::alias, Comparator.nullsLast(Comparator.naturalOrder())) + .thenComparing(CertificateInfo::serialNumber); + private final String path; private final String format; private final String alias; @@ -33,7 +43,7 @@ public class CertificateInfo implements ToXContentObject, Writeable { public CertificateInfo(String path, String format, String alias, boolean hasPrivateKey, X509Certificate certificate) { Objects.requireNonNull(certificate, "Certificate cannot be null"); - this.path = Objects.requireNonNull(path, "Certificate path cannot be null"); + this.path = path; this.format = Objects.requireNonNull(format, "Certificate format cannot be null"); this.alias = alias; this.subjectDn = Objects.requireNonNull(certificate.getSubjectDN().getName()); @@ -43,7 +53,11 @@ public CertificateInfo(String path, String format, String alias, boolean hasPriv } public CertificateInfo(StreamInput in) throws IOException { - this.path = in.readString(); + if (in.getVersion().onOrAfter(Version.V_8_0_0)) { + this.path = in.readOptionalString(); + } else { + this.path = in.readString(); + } this.format = in.readString(); this.alias = in.readOptionalString(); this.subjectDn = in.readString(); @@ -54,7 +68,11 @@ public CertificateInfo(StreamInput in) throws IOException { @Override public void writeTo(StreamOutput out) throws IOException { - out.writeString(path); + if (out.getVersion().onOrAfter(Version.V_8_0_0)) { + out.writeOptionalString(this.path); + } else { + out.writeString(this.path == null ? "" : this.path); + } out.writeString(format); out.writeOptionalString(alias); out.writeString(subjectDn); @@ -63,6 +81,7 @@ public void writeTo(StreamOutput out) throws IOException { out.writeLong(expiry.toInstant().toEpochMilli()); } + @Nullable public String path() { return path; } @@ -104,6 +123,11 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws .endObject(); } + @Override + public String toString() { + return "Certificate" + Strings.toString(this); + } + @Override public boolean equals(Object other) { if (this == other) { @@ -114,7 +138,7 @@ public boolean equals(Object other) { } final CertificateInfo that = (CertificateInfo) other; - return this.path.equals(that.path) + return Objects.equals(this.path, that.path) && this.format.equals(that.format) && this.hasPrivateKey == that.hasPrivateKey && Objects.equals(this.alias, that.alias) @@ -125,9 +149,14 @@ public boolean equals(Object other) { @Override public int hashCode() { - int result = path.hashCode(); + int result = Objects.hashCode(path); result = 31 * result + (alias != null ? alias.hashCode() : 0); result = 31 * result + (serialNumber != null ? serialNumber.hashCode() : 0); return result; } + + @Override + public int compareTo(CertificateInfo o) { + return COMPARATOR.compare(this, o); + } } diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/RestrictedTrustManagerTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/RestrictedTrustManagerTests.java index 4629f3c744ac3..4464fad856690 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/RestrictedTrustManagerTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/RestrictedTrustManagerTests.java @@ -45,9 +45,9 @@ public class RestrictedTrustManagerTests extends ESTestCase { @Before public void readCertificates() throws GeneralSecurityException, IOException { - Certificate[] caCert - = CertParsingUtils.readCertificates(Collections.singletonList(getDataPath - ("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/nodes/ca.crt"))); + Certificate[] caCert = CertParsingUtils.readX509Certificates( + Collections.singletonList(getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/nodes/ca.crt")) + ); baseTrustManager = CertParsingUtils.trustManager(caCert); certificates = new HashMap<>(); Files.walkFileTree(getDataPath diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/SSLConfigurationReloaderTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/SSLConfigurationReloaderTests.java index fc29c554daa6e..1b50a136cbfb9 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/SSLConfigurationReloaderTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/SSLConfigurationReloaderTests.java @@ -602,7 +602,7 @@ private static MockWebServer getSslServer(Path keyPath, Path certPath, String pa KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(null, password.toCharArray()); keyStore.setKeyEntry("testnode_ec", PemUtils.readPrivateKey(keyPath, password::toCharArray), password.toCharArray(), - CertParsingUtils.readCertificates(Collections.singletonList(certPath))); + CertParsingUtils.readX509Certificates(Collections.singletonList(certPath))); final SSLContext sslContext = new SSLContextBuilder() .loadKeyMaterial(keyStore, password.toCharArray()) .build(); @@ -635,7 +635,7 @@ private static CloseableHttpClient getSSLClient(List trustedCertificatePat KeyManagementException, IOException, CertificateException { KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); trustStore.load(null, null); - for (Certificate cert : CertParsingUtils.readCertificates(trustedCertificatePaths)) { + for (Certificate cert : CertParsingUtils.readX509Certificates(trustedCertificatePaths)) { trustStore.setCertificateEntry(cert.toString(), cert); } final SSLContext sslContext = new SSLContextBuilder() diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/SslSettingsLoaderTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/SslSettingsLoaderTests.java index 3e9d2778d0c8e..a0349c85b23ad 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/SslSettingsLoaderTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/SslSettingsLoaderTests.java @@ -12,6 +12,7 @@ import org.elasticsearch.common.ssl.CompositeTrustConfig; import org.elasticsearch.common.ssl.DefaultJdkTrustConfig; import org.elasticsearch.common.ssl.EmptyKeyConfig; +import org.elasticsearch.common.ssl.KeyStoreUtil; import org.elasticsearch.common.ssl.PemKeyConfig; import org.elasticsearch.common.ssl.PemTrustConfig; import org.elasticsearch.common.ssl.SslConfiguration; @@ -94,6 +95,38 @@ public void testThatOnlyKeystoreInSettingsSetsTruststoreSettings() { assertCombiningTrustConfigContainsCorrectIssuers(sslConfiguration); } + public void testFilterAppliedToKeystore() { + final Path path = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.p12"); + MockSecureSettings secureSettings = new MockSecureSettings(); + secureSettings.setString("keystore.secure_password", "testnode"); + Settings settings = Settings.builder() + .put("keystore.path", path) + .setSecureSettings(secureSettings) + .build(); + final SslConfiguration sslConfiguration = SslSettingsLoader.load( + settings, + null, + environment, + ks -> KeyStoreUtil.filter(ks, e -> e.getAlias().endsWith("sa")) // "_dsa" & "_rsa" but not "_ec" + ); + assertThat(sslConfiguration.getKeyConfig(), instanceOf(StoreKeyConfig.class)); + StoreKeyConfig keyStore = (StoreKeyConfig) sslConfiguration.getKeyConfig(); + + assertThat(keyStore.getDependentFiles(), contains(path)); + assertThat(keyStore.hasKeyMaterial(), is(true)); + + assumeFalse("Cannot create Key Manager from a PKCS#12 file in FIPS", inFipsJvm()); + assertThat(keyStore.createKeyManager(), notNullValue()); + assertThat(keyStore.getKeys(false), hasSize(3)); // testnode_ec, testnode_rsa, testnode_dsa + assertThat(keyStore.getKeys(true), hasSize(2)); // testnode_rsa, testnode_dsa + assertThat( + keyStore.getKeys(true).stream().map(t -> t.v1().getAlgorithm()).collect(Collectors.toUnmodifiableSet()), + containsInAnyOrder("RSA", "DSA") + ); + + assertCombiningTrustConfigContainsCorrectIssuers(sslConfiguration); + } + private SslConfiguration getSslConfiguration(Settings settings) { return SslSettingsLoader.load(settings, null, environment); } diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/cert/CertificateInfoTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/cert/CertificateInfoTests.java index 4bd18b157fae6..d96be36ea6ae3 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/cert/CertificateInfoTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/cert/CertificateInfoTests.java @@ -12,17 +12,19 @@ import java.io.IOException; +import java.security.cert.CertificateException; import java.security.cert.X509Certificate; +import java.util.Arrays; import java.util.Collections; +import java.util.List; +import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.equalTo; public class CertificateInfoTests extends ESTestCase { public void testSerialization() throws Exception { - final X509Certificate certificate = CertParsingUtils. - readX509Certificates(Collections.singletonList(getDataPath - ("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.crt")))[0]; + final X509Certificate certificate = readSampleCertificate(); final CertificateInfo cert1 = new CertificateInfo("/path/to/cert.jks", "jks", "key", true, certificate); final CertificateInfo cert2 = serializeAndDeserialize(cert1); final CertificateInfo cert3 = serializeAndDeserialize(cert2); @@ -31,6 +33,28 @@ public void testSerialization() throws Exception { assertThat(cert2, equalTo(cert3)); } + public void testCompareTo() throws Exception { + final X509Certificate certificate = readSampleCertificate(); + CertificateInfo pkcs11 = new CertificateInfo(null, "PKCS11", "alias1", true, certificate); + CertificateInfo pkcs12 = new CertificateInfo("http.p12", "PKCS12", "http", true, certificate); + CertificateInfo pem1 = new CertificateInfo("cert.crt", "PEM", null, true, certificate); + CertificateInfo pem2 = new CertificateInfo("ca.crt", "PEM", null, false, certificate); + CertificateInfo jks1 = new CertificateInfo("keystore.jks", "jks", "instance", true, certificate); + CertificateInfo jks2 = new CertificateInfo("keystore.jks", "jks", "ca", false, certificate); + + List list = Arrays.asList(pkcs11, pkcs12, pem1, pem2, jks1, jks2); + Collections.shuffle(list, random()); + Collections.sort(list); + + assertThat(list, contains(pem2, pem1, pkcs12, jks2, jks1, pkcs11)); + } + + private X509Certificate readSampleCertificate() throws CertificateException, IOException { + return CertParsingUtils. + readX509Certificates(Collections.singletonList(getDataPath + ("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.crt")))[0]; + } + private CertificateInfo serializeAndDeserialize(CertificateInfo cert1) throws IOException { BytesStreamOutput output = new BytesStreamOutput(); cert1.writeTo(output); diff --git a/x-pack/plugin/identity-provider/src/main/java/org/elasticsearch/xpack/idp/saml/idp/SamlIdentityProviderBuilder.java b/x-pack/plugin/identity-provider/src/main/java/org/elasticsearch/xpack/idp/saml/idp/SamlIdentityProviderBuilder.java index 3c8a70b718ba8..977c2ef14d10f 100644 --- a/x-pack/plugin/identity-provider/src/main/java/org/elasticsearch/xpack/idp/saml/idp/SamlIdentityProviderBuilder.java +++ b/x-pack/plugin/identity-provider/src/main/java/org/elasticsearch/xpack/idp/saml/idp/SamlIdentityProviderBuilder.java @@ -12,9 +12,9 @@ import org.elasticsearch.common.ValidationException; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.ssl.SslKeyConfig; import org.elasticsearch.env.Environment; import org.elasticsearch.xpack.core.ssl.CertParsingUtils; -import org.elasticsearch.xpack.core.ssl.X509KeyPairSettings; import org.elasticsearch.xpack.idp.saml.sp.SamlServiceProviderResolver; import org.elasticsearch.xpack.idp.saml.sp.ServiceProviderDefaults; import org.elasticsearch.xpack.idp.saml.sp.WildcardServiceProviderResolver; @@ -28,7 +28,6 @@ import java.security.PrivateKey; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -288,10 +287,13 @@ static X509Credential buildSigningCredential(Environment environment, Settings s } static List buildCredentials(Environment env, Settings settings, String prefix, boolean allowMultiple) { - final X509KeyPairSettings keyPairSettings = X509KeyPairSettings.withPrefix(prefix, false); - final X509KeyManager keyManager = CertParsingUtils.getKeyManager(keyPairSettings, settings, null, env); + final SslKeyConfig keyConfig = CertParsingUtils.createKeyConfig(settings, prefix, env, false); + if (keyConfig.hasKeyMaterial() == false) { + return List.of(); + } + final X509KeyManager keyManager = keyConfig.createKeyManager(); if (keyManager == null) { - return Collections.emptyList(); + return List.of(); } final List credentials = new ArrayList<>(); diff --git a/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateGenerateTool.java b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateGenerateTool.java index 733146136b7bd..74db3b41407a8 100644 --- a/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateGenerateTool.java +++ b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateGenerateTool.java @@ -28,6 +28,7 @@ import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.core.PathUtils; import org.elasticsearch.common.network.InetAddresses; +import org.elasticsearch.common.ssl.PemUtils; import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.common.xcontent.ConstructingObjectParser; import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; @@ -368,15 +369,10 @@ static CAInfo getCAInfo(Terminal terminal, String dn, String caCertPath, String Environment env, int keysize, int days) throws Exception { if (caCertPath != null) { assert caKeyPath != null; - final String resolvedCaCertPath = resolvePath(caCertPath).toAbsolutePath().toString(); - Certificate[] certificates = CertParsingUtils.readCertificates(Collections.singletonList(resolvedCaCertPath), env); - if (certificates.length != 1) { - throw new IllegalArgumentException("expected a single certificate in file [" + caCertPath + "] but found [" + - certificates.length + "]"); - } - Certificate caCert = certificates[0]; + final Path resolvedCaCert = resolvePath(caCertPath); + X509Certificate caCert = CertParsingUtils.readX509Certificate(resolvedCaCert); PrivateKey privateKey = readPrivateKey(caKeyPath, keyPass, terminal, prompt); - return new CAInfo((X509Certificate) caCert, privateKey); + return new CAInfo(caCert, privateKey); } // generate the CA keys and cert diff --git a/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateTool.java b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateTool.java index 45fae3ccf69e1..228522afda970 100644 --- a/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateTool.java +++ b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateTool.java @@ -34,6 +34,7 @@ import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.core.PathUtils; import org.elasticsearch.common.network.InetAddresses; +import org.elasticsearch.common.ssl.PemUtils; import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.common.xcontent.ConstructingObjectParser; import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; @@ -373,13 +374,7 @@ private CAInfo loadPemCA(Terminal terminal, OptionSet options, Environment env) Path key = resolvePath(options, caKeyPathSpec); String password = caPasswordSpec.value(options); - final String resolvedCaCertPath = cert.toAbsolutePath().toString(); - Certificate[] certificates = CertParsingUtils.readCertificates(Collections.singletonList(resolvedCaCertPath), env); - if (certificates.length != 1) { - throw new IllegalArgumentException("expected a single certificate in file [" + resolvedCaCertPath + "] but found [" + - certificates.length + "]"); - } - X509Certificate caCert = (X509Certificate) certificates[0]; + X509Certificate caCert = CertParsingUtils.readX509Certificate(cert); PrivateKey privateKey = readPrivateKey(key, getChars(password), terminal); return new CAInfo(caCert, privateKey); } diff --git a/x-pack/plugin/security/cli/src/test/java/org/elasticsearch/xpack/security/cli/CertificateToolTests.java b/x-pack/plugin/security/cli/src/test/java/org/elasticsearch/xpack/security/cli/CertificateToolTests.java index 2ea064d44cd12..bdbb11cc009aa 100644 --- a/x-pack/plugin/security/cli/src/test/java/org/elasticsearch/xpack/security/cli/CertificateToolTests.java +++ b/x-pack/plugin/security/cli/src/test/java/org/elasticsearch/xpack/security/cli/CertificateToolTests.java @@ -34,6 +34,7 @@ import org.elasticsearch.core.PathUtils; import org.elasticsearch.common.network.NetworkAddress; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.ssl.KeyStoreUtil; import org.elasticsearch.common.util.CollectionUtils; import org.elasticsearch.core.internal.io.IOUtils; import org.elasticsearch.env.Environment; @@ -87,10 +88,13 @@ import java.util.function.Function; import java.util.stream.Collectors; +import static org.elasticsearch.common.ssl.KeyStoreUtil.createKeyManager; +import static org.elasticsearch.common.ssl.KeyStoreUtil.createTrustManager; import static org.elasticsearch.test.FileMatchers.pathExists; import static org.hamcrest.Matchers.arrayWithSize; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.in; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; @@ -434,7 +438,7 @@ public void testHandleLongPasswords() throws Exception { assertThat(pemZipFile, pathExists()); - final KeyStore caKeyStore = CertParsingUtils.readKeyStore(caFile, "PKCS12", longPassword.toCharArray()); + final KeyStore caKeyStore = KeyStoreUtil.readKeyStore(caFile, "PKCS12", longPassword.toCharArray()); Certificate caCert = caKeyStore.getCertificate("ca"); assertThat(caCert, notNullValue()); @@ -446,10 +450,10 @@ public void testHandleLongPasswords() throws Exception { assertThat(key, notNullValue()); final Path certPath = zipRoot.resolve("cert/cert.crt"); - final Certificate[] certificates = CertParsingUtils.readCertificates(List.of(certPath)); - assertThat(certificates, arrayWithSize(1)); + final List certificates = PemUtils.readCertificates(List.of(certPath)); + assertThat(certificates, hasSize(1)); assertThat( - ((X509Certificate) certificates[0]).getIssuerX500Principal(), + ((X509Certificate) certificates.get(0)).getIssuerX500Principal(), equalTo(((X509Certificate) caCert).getSubjectX500Principal()) ); } @@ -663,9 +667,9 @@ public void testCreateCaAndMultipleInstances() throws Exception { assertThat(node3File, pathExists()); - final KeyStore node1KeyStore = CertParsingUtils.readKeyStore(node1File, "PKCS12", node1Password.toCharArray()); - final KeyStore node2KeyStore = CertParsingUtils.readKeyStore(node2File, "PKCS12", node2Password.toCharArray()); - final KeyStore node3KeyStore = CertParsingUtils.readKeyStore(node3File, "PKCS12", node3Password.toCharArray()); + final KeyStore node1KeyStore = KeyStoreUtil.readKeyStore(node1File, "PKCS12", node1Password.toCharArray()); + final KeyStore node2KeyStore = KeyStoreUtil.readKeyStore(node2File, "PKCS12", node2Password.toCharArray()); + final KeyStore node3KeyStore = KeyStoreUtil.readKeyStore(node3File, "PKCS12", node3Password.toCharArray()); checkTrust(node1KeyStore, node1Password.toCharArray(), node1KeyStore, true); checkTrust(node1KeyStore, node1Password.toCharArray(), node2KeyStore, true); @@ -768,11 +772,11 @@ public void testTrustBetweenPEMandPKCS12() throws Exception { final Path node2Key = zip2Root.resolve("node02/node02.key"); assertThat(node2Key, pathExists()); - final KeyStore node1KeyStore = CertParsingUtils.readKeyStore(node1Pkcs12, "PKCS12", node1Password.toCharArray()); + final KeyStore node1KeyStore = KeyStoreUtil.readKeyStore(node1Pkcs12, "PKCS12", node1Password.toCharArray()); final KeyStore node1TrustStore = node1KeyStore; final KeyStore node2KeyStore = CertParsingUtils.getKeyStoreFromPEM(node2Cert, node2Key, new char[0]); - final KeyStore node2TrustStore = CertParsingUtils.readKeyStore(caFile, "PKCS12", caPassword.toCharArray()); + final KeyStore node2TrustStore = KeyStoreUtil.readKeyStore(caFile, "PKCS12", caPassword.toCharArray()); checkTrust(node1KeyStore, node1Password.toCharArray(), node2TrustStore, true); checkTrust(node2KeyStore, new char[0], node1TrustStore, true); @@ -836,9 +840,8 @@ private void assertSubjAltNames(Certificate certificate, String ip, String dns) * Checks whether there are keys in {@code keyStore} that are trusted by {@code trustStore}. */ private void checkTrust(KeyStore keyStore, char[] keyPassword, KeyStore trustStore, boolean trust) throws Exception { - final X509ExtendedKeyManager keyManager = CertParsingUtils.keyManager(keyStore, keyPassword, - KeyManagerFactory.getDefaultAlgorithm()); - final X509ExtendedTrustManager trustManager = CertParsingUtils.trustManager(trustStore, TrustManagerFactory.getDefaultAlgorithm()); + final X509ExtendedKeyManager keyManager = createKeyManager(keyStore, keyPassword, KeyManagerFactory.getDefaultAlgorithm()); + final X509ExtendedTrustManager trustManager = createTrustManager(trustStore, TrustManagerFactory.getDefaultAlgorithm()); final X509Certificate[] node1CertificateIssuers = trustManager.getAcceptedIssuers(); final Principal[] trustedPrincipals = new Principal[node1CertificateIssuers.length]; diff --git a/x-pack/plugin/security/cli/src/test/java/org/elasticsearch/xpack/security/cli/HttpCertificateCommandTests.java b/x-pack/plugin/security/cli/src/test/java/org/elasticsearch/xpack/security/cli/HttpCertificateCommandTests.java index f55cdaccbac96..02c096231ea6b 100644 --- a/x-pack/plugin/security/cli/src/test/java/org/elasticsearch/xpack/security/cli/HttpCertificateCommandTests.java +++ b/x-pack/plugin/security/cli/src/test/java/org/elasticsearch/xpack/security/cli/HttpCertificateCommandTests.java @@ -29,6 +29,7 @@ import org.elasticsearch.core.CheckedFunction; import org.elasticsearch.core.Tuple; import org.elasticsearch.common.network.NetworkAddress; +import org.elasticsearch.common.ssl.PemUtils; import org.elasticsearch.core.internal.io.IOUtils; import org.elasticsearch.env.Environment; import org.elasticsearch.test.ESTestCase; @@ -61,7 +62,6 @@ import java.security.PublicKey; import java.security.Signature; import java.security.cert.Certificate; -import java.security.cert.CertificateException; import java.security.cert.CertificateParsingException; import java.security.cert.X509Certificate; import java.security.interfaces.RSAKey; @@ -310,7 +310,7 @@ public void testGenerateSingleCertificateWithExistingCA() throws Exception { assertThat(getRSAKeySize(certAndKey.v1().getPublicKey()), is(HttpCertificateCommand.DEFAULT_CERT_KEY_SIZE)); assertThat(getRSAKeySize(certAndKey.v2()), is(HttpCertificateCommand.DEFAULT_CERT_KEY_SIZE)); - final X509Certificate caCert = readPemCertificate(caCertPath); + final X509Certificate caCert = CertParsingUtils.readX509Certificate(caCertPath); verifyChain(certAndKey.v1(), caCert); // Verify the README @@ -800,14 +800,6 @@ private Tuple readCertificateAndKey(Path pkcs12, return new Tuple<>((X509Certificate) cert, (PrivateKey) key); } - private X509Certificate readPemCertificate(Path caCertPath) throws CertificateException, IOException { - final Certificate[] certificates = CertParsingUtils.readCertificates(List.of(caCertPath)); - assertThat(certificates, arrayWithSize(1)); - final Certificate cert = certificates[0]; - assertThat(cert, instanceOf(X509Certificate.class)); - return (X509Certificate) cert; - } - private T readPemObject(Path path, String expectedType, CheckedFunction factory) throws IOException { assertThat(path, isRegularFile()); @@ -822,7 +814,7 @@ private void writeDummyKeystore(Path path, String type) throws GeneralSecurityEx KeyStore ks = KeyStore.getInstance(type); ks.load(null); if (randomBoolean()) { - final X509Certificate cert = readPemCertificate(getDataPath("ca.crt")); + final X509Certificate cert = CertParsingUtils.readX509Certificate(getDataPath("ca.crt")); ks.setCertificateEntry(randomAlphaOfLength(4), cert); } try (OutputStream out = Files.newOutputStream(path)) { diff --git a/x-pack/plugin/security/cli/src/test/java/org/elasticsearch/xpack/security/cli/PemToKeystore.java b/x-pack/plugin/security/cli/src/test/java/org/elasticsearch/xpack/security/cli/PemToKeystore.java index 0399e8ab280c2..aacdfa40def59 100644 --- a/x-pack/plugin/security/cli/src/test/java/org/elasticsearch/xpack/security/cli/PemToKeystore.java +++ b/x-pack/plugin/security/cli/src/test/java/org/elasticsearch/xpack/security/cli/PemToKeystore.java @@ -9,7 +9,6 @@ import org.elasticsearch.cli.SuppressForbidden; import org.elasticsearch.common.ssl.PemUtils; -import org.elasticsearch.xpack.core.ssl.CertParsingUtils; import java.io.IOException; import java.io.OutputStream; @@ -36,15 +35,15 @@ public static void main(String[] args) throws IOException, GeneralSecurityExcept Path keyPath = Paths.get(args[3]).toAbsolutePath(); char[] password = args[4].toCharArray(); - final Certificate[] certificates = CertParsingUtils.readCertificates(List.of(certPath)); - if (certificates.length == 0) { + final List certificates = PemUtils.readCertificates(List.of(certPath)); + if (certificates.isEmpty()) { throw new IllegalArgumentException("No certificates found in " + certPath); } final PrivateKey key = PemUtils.readPrivateKey(keyPath, () -> password); KeyStore keyStore = KeyStore.getInstance(keystoreType); keyStore.load(null); - keyStore.setKeyEntry("key", key, password, certificates); + keyStore.setKeyEntry("key", key, password, certificates.toArray(Certificate[]::new)); try (OutputStream out = Files.newOutputStream(keystorePath)) { keyStore.store(out, password); } diff --git a/x-pack/plugin/security/qa/smoke-test-all-realms/src/javaRestTest/java/org/elasticsearch/xpack/security/authc/PkiRealmAuthIT.java b/x-pack/plugin/security/qa/smoke-test-all-realms/src/javaRestTest/java/org/elasticsearch/xpack/security/authc/PkiRealmAuthIT.java index fa502f9800427..c32dbbc07a8dc 100644 --- a/x-pack/plugin/security/qa/smoke-test-all-realms/src/javaRestTest/java/org/elasticsearch/xpack/security/authc/PkiRealmAuthIT.java +++ b/x-pack/plugin/security/qa/smoke-test-all-realms/src/javaRestTest/java/org/elasticsearch/xpack/security/authc/PkiRealmAuthIT.java @@ -33,7 +33,7 @@ protected Settings restClientSettings() { return builder.build(); } - public void testAuthenticationUsingFileRealm() throws IOException { + public void testAuthenticationUsingPkiRealm() throws IOException { Map authenticate = super.authenticate(RequestOptions.DEFAULT.toBuilder()); assertUsername(authenticate, USERNAME); diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/pki/PkiAuthenticationTests.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/pki/PkiAuthenticationTests.java index dc0093b98918e..65e001368dc00 100644 --- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/pki/PkiAuthenticationTests.java +++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/pki/PkiAuthenticationTests.java @@ -13,21 +13,20 @@ import org.apache.http.util.EntityUtils; import org.elasticsearch.common.network.NetworkAddress; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.ssl.PemUtils; +import org.elasticsearch.common.ssl.SslClientAuthenticationMode; import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.http.HttpServerTransport; import org.elasticsearch.test.SecuritySingleNodeTestCase; import org.elasticsearch.xpack.core.common.socket.SocketAccess; import org.elasticsearch.xpack.core.ssl.CertParsingUtils; -import org.elasticsearch.common.ssl.SslClientAuthenticationMode; import javax.net.ssl.KeyManager; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import java.net.InetSocketAddress; +import java.nio.file.Path; import java.security.SecureRandom; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.stream.Collectors; @@ -48,7 +47,7 @@ protected boolean addMockHttpTransport() { @Override protected Settings nodeSettings() { - SslClientAuthenticationMode sslClientAuth = randomBoolean() + SslClientAuthenticationMode clientAuth = randomBoolean() ? SslClientAuthenticationMode.REQUIRED : SslClientAuthenticationMode.OPTIONAL; @@ -56,7 +55,7 @@ protected Settings nodeSettings() { .put(super.nodeSettings()); addSSLSettingsForNodePEMFiles(builder, "xpack.security.http.", true); builder.put("xpack.security.http.ssl.enabled", true) - .put("xpack.security.http.ssl.client_authentication", sslClientAuth) + .put("xpack.security.http.ssl.client_authentication", clientAuth) .put("xpack.security.authc.realms.file.file.order", "0") .put("xpack.security.authc.realms.pki.pki1.order", "2") .putList("xpack.security.authc.realms.pki.pki1.certificate_authorities", @@ -118,11 +117,10 @@ public void testRestAuthenticationFailure() throws Exception { private SSLContext getRestSSLContext(String keyPath, String password, String certPath, List trustedCertPaths) throws Exception { SSLContext context = SSLContext.getInstance("TLS"); - TrustManager tm = CertParsingUtils.trustManager(CertParsingUtils.readCertificates(trustedCertPaths.stream().map(p -> getDataPath - (p)).collect(Collectors.toList()))); - KeyManager km = CertParsingUtils.keyManager(CertParsingUtils.readCertificates(Collections.singletonList(getDataPath - (certPath))), PemUtils.readPrivateKey(getDataPath(keyPath), password::toCharArray), password.toCharArray()); - context.init(new KeyManager[]{km}, new TrustManager[]{tm}, new SecureRandom()); + final List resolvedPaths = trustedCertPaths.stream().map(p -> getDataPath(p)).collect(Collectors.toList()); + TrustManager tm = CertParsingUtils.getTrustManagerFromPEM(resolvedPaths); + KeyManager km = CertParsingUtils.getKeyManagerFromPEM(getDataPath(certPath), getDataPath(keyPath), password.toCharArray()); + context.init(new KeyManager[] { km }, new TrustManager[] { tm }, new SecureRandom()); return context; } diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/pki/PkiOptionalClientAuthTests.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/pki/PkiOptionalClientAuthTests.java index 5fcb1c63ed517..87a4b1922c9e6 100644 --- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/pki/PkiOptionalClientAuthTests.java +++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/pki/PkiOptionalClientAuthTests.java @@ -14,11 +14,11 @@ import org.elasticsearch.client.RestClient; import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.ssl.SslClientAuthenticationMode; import org.elasticsearch.test.SecuritySettingsSource; import org.elasticsearch.test.SecuritySettingsSourceField; import org.elasticsearch.test.SecuritySingleNodeTestCase; import org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken; -import org.elasticsearch.common.ssl.SslClientAuthenticationMode; import org.junit.BeforeClass; import javax.net.ssl.SSLContext; diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/transport/ssl/EllipticCurveSSLTests.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/transport/ssl/EllipticCurveSSLTests.java index d9c3a62a16a9e..14b9bfaf6d353 100644 --- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/transport/ssl/EllipticCurveSSLTests.java +++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/transport/ssl/EllipticCurveSSLTests.java @@ -8,32 +8,30 @@ import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.ssl.PemUtils; import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.test.SecurityIntegTestCase; import org.elasticsearch.transport.TransportInfo; import org.elasticsearch.xpack.core.ssl.CertParsingUtils; import org.junit.BeforeClass; -import javax.net.ssl.HandshakeCompletedEvent; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLSession; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.SSLSocketFactory; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509ExtendedKeyManager; - import java.nio.file.Path; import java.security.AccessController; -import java.security.PrivateKey; import java.security.PrivilegedExceptionAction; import java.security.SecureRandom; import java.security.cert.Certificate; import java.util.Arrays; -import java.util.Collections; +import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReference; +import javax.net.ssl.HandshakeCompletedEvent; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509ExtendedKeyManager; +import javax.net.ssl.X509ExtendedTrustManager; import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.containsString; @@ -66,12 +64,11 @@ public void testConnection() throws Exception { assumeFalse("Fails on BCTLS with 'Closed engine without receiving the close alert message.'", inFipsJvm()); final Path keyPath = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/private_" + CURVE + ".pem"); final Path certPath = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/certificate_" + CURVE + ".pem"); - PrivateKey privateKey = PemUtils.readPrivateKey(keyPath, () -> null); - Certificate[] certs = CertParsingUtils.readCertificates(Collections.singletonList(certPath.toString()), newEnvironment()); - X509ExtendedKeyManager x509ExtendedKeyManager = CertParsingUtils.keyManager(certs, privateKey, new char[0]); + final X509ExtendedKeyManager x509ExtendedKeyManager = CertParsingUtils.getKeyManagerFromPEM(certPath, keyPath, new char[0]); + final X509ExtendedTrustManager trustManager = CertParsingUtils.getTrustManagerFromPEM(List.of(certPath)); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(new X509ExtendedKeyManager[]{x509ExtendedKeyManager}, - new TrustManager[]{CertParsingUtils.trustManager(CertParsingUtils.readCertificates(Collections.singletonList(certPath)))}, + new TrustManager[]{trustManager}, new SecureRandom()); SSLSocketFactory socketFactory = sslContext.getSocketFactory(); NodesInfoResponse response = client().admin().cluster().prepareNodesInfo().setTransport(true).get(); @@ -96,7 +93,7 @@ public SSLSocket run() throws Exception { SSLSession session = event.getSession(); Certificate[] peerChain = session.getPeerCertificates(); assertEquals(1, peerChain.length); - assertEquals(certs[0], peerChain[0]); + assertEquals(CertParsingUtils.readX509Certificate(certPath), peerChain[0]); assertThat(session.getCipherSuite(), anyOf(containsString("ECDSA"), is("TLS_AES_256_GCM_SHA384"), is("TLS_AES_128_GCM_SHA256"))); } diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/ssl/SslClientAuthenticationTests.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/ssl/SslClientAuthenticationTests.java index 231995352ded6..6d1868bc7ce60 100644 --- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/ssl/SslClientAuthenticationTests.java +++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/ssl/SslClientAuthenticationTests.java @@ -12,8 +12,6 @@ import org.apache.http.util.EntityUtils; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ExceptionsHelper; -import org.elasticsearch.common.ssl.PemUtils; -import org.elasticsearch.jdk.JavaVersion; import org.elasticsearch.client.Request; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.Response; @@ -21,26 +19,26 @@ import org.elasticsearch.common.settings.MockSecureSettings; import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.ssl.SslClientAuthenticationMode; +import org.elasticsearch.jdk.JavaVersion; import org.elasticsearch.test.SecurityIntegTestCase; import org.elasticsearch.xpack.core.XPackSettings; import org.elasticsearch.xpack.core.ssl.CertParsingUtils; -import org.elasticsearch.common.ssl.SslClientAuthenticationMode; -import javax.net.ssl.KeyManager; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; +import java.nio.file.Path; import java.security.AccessController; import java.security.PrivilegedAction; import java.security.SecureRandom; import java.security.cert.CertPathBuilderException; -import java.util.Arrays; -import java.util.Collections; import java.util.HashSet; import java.util.List; +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue; import static org.hamcrest.Matchers.containsString; @@ -134,14 +132,18 @@ public void testThatHttpWorksWithSslClientAuth() throws IOException { private SSLContext getSSLContext() { try { - String certPath = "/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.crt"; + String certPathName = "/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.crt"; String nodeCertPath = "/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.crt"; String nodeEcCertPath = "/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode_ec.crt"; - String keyPath = "/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.pem"; - TrustManager tm = CertParsingUtils.trustManager(CertParsingUtils.readCertificates(Arrays.asList(getDataPath - (certPath), getDataPath(nodeCertPath), getDataPath(nodeEcCertPath)))); - KeyManager km = CertParsingUtils.keyManager(CertParsingUtils.readCertificates(Collections.singletonList(getDataPath - (certPath))), PemUtils.readPrivateKey(getDataPath(keyPath), "testclient"::toCharArray), "testclient".toCharArray()); + String keyPathName = "/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.pem"; + + final Path certPath = getDataPath(certPathName); + final Path keyPath = getDataPath(keyPathName); + + final List caCerts = List.of(certPath, getDataPath(nodeCertPath), getDataPath(nodeEcCertPath)); + final TrustManager tm = CertParsingUtils.getTrustManagerFromPEM(caCerts); + + KeyManager km = CertParsingUtils.getKeyManagerFromPEM(certPath, keyPath, "testclient".toCharArray()); SSLContext context = SSLContext.getInstance(inFipsJvm() ? "TLSv1.2" : randomFrom("TLSv1.3", "TLSv1.2")); context.init(new KeyManager[] { km }, new TrustManager[] { tm }, new SecureRandom()); return context; diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/pki/PkiRealm.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/pki/PkiRealm.java index 243c045477715..ecc643266cf2e 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/pki/PkiRealm.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/pki/PkiRealm.java @@ -9,16 +9,15 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; import org.apache.logging.log4j.util.Supplier; -import org.elasticsearch.ElasticsearchException; import org.elasticsearch.action.ActionListener; import org.elasticsearch.common.Strings; import org.elasticsearch.common.cache.Cache; import org.elasticsearch.common.cache.CacheBuilder; import org.elasticsearch.common.hash.MessageDigests; -import org.elasticsearch.common.settings.SecureString; +import org.elasticsearch.common.ssl.SslConfiguration; +import org.elasticsearch.common.ssl.SslTrustConfig; import org.elasticsearch.common.util.concurrent.ReleasableLock; import org.elasticsearch.common.util.concurrent.ThreadContext; -import org.elasticsearch.env.Environment; import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xpack.core.XPackSettings; @@ -28,20 +27,19 @@ import org.elasticsearch.xpack.core.security.authc.RealmConfig; import org.elasticsearch.xpack.core.security.authc.RealmSettings; import org.elasticsearch.xpack.core.security.authc.pki.PkiRealmSettings; +import org.elasticsearch.xpack.core.security.authc.support.CachingRealm; +import org.elasticsearch.xpack.core.security.authc.support.UserRoleMapper; import org.elasticsearch.xpack.core.security.user.User; -import org.elasticsearch.xpack.core.ssl.CertParsingUtils; -import org.elasticsearch.xpack.core.ssl.SSLConfigurationSettings; +import org.elasticsearch.xpack.core.ssl.SslSettingsLoader; import org.elasticsearch.xpack.security.authc.BytesKey; import org.elasticsearch.xpack.security.authc.TokenService; -import org.elasticsearch.xpack.core.security.authc.support.CachingRealm; import org.elasticsearch.xpack.security.authc.support.DelegatedAuthorizationSupport; -import org.elasticsearch.xpack.core.security.authc.support.UserRoleMapper; import org.elasticsearch.xpack.security.authc.support.mapper.CompositeRoleMapper; import org.elasticsearch.xpack.security.authc.support.mapper.NativeRoleMappingStore; +import javax.net.ssl.X509ExtendedTrustManager; import javax.net.ssl.X509TrustManager; import java.security.MessageDigest; -import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; @@ -258,61 +256,22 @@ private boolean isCertificateChainTrusted(X509AuthenticationToken token) { } private X509TrustManager trustManagers(RealmConfig realmConfig) { - final List certificateAuthorities = realmConfig.hasSetting(PkiRealmSettings.CAPATH_SETTING) ? - realmConfig.getSetting(PkiRealmSettings.CAPATH_SETTING) : null; - String truststorePath = realmConfig.getSetting(PkiRealmSettings.TRUST_STORE_PATH).orElse(null); - if (truststorePath == null && certificateAuthorities == null) { + final SslConfiguration sslConfiguration = SslSettingsLoader.load( + realmConfig.settings(), + RealmSettings.realmSettingPrefix(realmConfig.identifier()), + realmConfig.env() + ); + final SslTrustConfig trustConfig = sslConfiguration.getTrustConfig(); + if (trustConfig.isSystemDefault()) { return null; - } else if (truststorePath != null && certificateAuthorities != null) { - final String pathKey = RealmSettings.getFullSettingKey(realmConfig, PkiRealmSettings.TRUST_STORE_PATH); - final String caKey = RealmSettings.getFullSettingKey(realmConfig, PkiRealmSettings.CAPATH_SETTING); - throw new IllegalArgumentException("[" + pathKey + "] and [" + caKey + "] cannot be used at the same time"); - } else if (truststorePath != null) { - final X509TrustManager trustManager = trustManagersFromTruststore(truststorePath, realmConfig); - if (trustManager.getAcceptedIssuers().length == 0) { - logger.warn("PKI Realm {} uses truststore {} which has no accepted certificate issuers", this, truststorePath); - } - return trustManager; } - final X509TrustManager trustManager = trustManagersFromCAs(certificateAuthorities, realmConfig.env()); + final X509ExtendedTrustManager trustManager = trustConfig.createTrustManager(); if (trustManager.getAcceptedIssuers().length == 0) { - logger.warn("PKI Realm {} uses CAs {} with no accepted certificate issuers", this, certificateAuthorities); + logger.warn("PKI Realm [{}] uses trust configuration [{}] which has no accepted certificate issuers", this, trustConfig); } return trustManager; } - private static X509TrustManager trustManagersFromTruststore(String truststorePath, RealmConfig realmConfig) { - if (realmConfig.hasSetting(PkiRealmSettings.TRUST_STORE_PASSWORD) == false - && realmConfig.hasSetting(PkiRealmSettings.LEGACY_TRUST_STORE_PASSWORD) == false) { - throw new IllegalArgumentException("Neither [" + - RealmSettings.getFullSettingKey(realmConfig, PkiRealmSettings.TRUST_STORE_PASSWORD) + "] or [" + - RealmSettings.getFullSettingKey(realmConfig, PkiRealmSettings.LEGACY_TRUST_STORE_PASSWORD) - + "] is configured"); - } - try (SecureString password = realmConfig.getSetting(PkiRealmSettings.TRUST_STORE_PASSWORD)) { - String trustStoreAlgorithm = realmConfig.getSetting(PkiRealmSettings.TRUST_STORE_ALGORITHM); - String trustStoreType = SSLConfigurationSettings.getKeyStoreType( - realmConfig.getConcreteSetting(PkiRealmSettings.TRUST_STORE_TYPE), realmConfig.settings(), - truststorePath); - try { - return CertParsingUtils.trustManager(truststorePath, trustStoreType, password.getChars(), trustStoreAlgorithm, realmConfig - .env()); - } catch (Exception e) { - throw new IllegalArgumentException("failed to load specified truststore", e); - } - } - } - - private static X509TrustManager trustManagersFromCAs(List certificateAuthorities, Environment env) { - assert certificateAuthorities != null; - try { - Certificate[] certificates = CertParsingUtils.readCertificates(certificateAuthorities, env); - return CertParsingUtils.trustManager(certificates); - } catch (Exception e) { - throw new ElasticsearchException("failed to load certificate authorities for PKI realm", e); - } - } - @Override public void expire(String username) { try (ReleasableLock ignored = writeLock.acquire()) { diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlMetadataCommand.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlMetadataCommand.java index 543ed3d4788a5..7f5389c204d3c 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlMetadataCommand.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlMetadataCommand.java @@ -16,7 +16,6 @@ import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.Arrays; -import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; @@ -30,6 +29,7 @@ import joptsimple.OptionParser; import joptsimple.OptionSet; import joptsimple.OptionSpec; + import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -38,15 +38,15 @@ import org.elasticsearch.cli.SuppressForbidden; import org.elasticsearch.cli.Terminal; import org.elasticsearch.cli.UserException; -import org.elasticsearch.common.ssl.PemUtils; -import org.elasticsearch.core.CheckedFunction; import org.elasticsearch.common.Strings; -import org.elasticsearch.core.PathUtils; import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.settings.KeyStoreWrapper; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.ssl.PemUtils; import org.elasticsearch.common.util.LocaleUtils; import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.core.CheckedFunction; +import org.elasticsearch.core.PathUtils; import org.elasticsearch.env.Environment; import org.elasticsearch.xpack.core.security.authc.RealmConfig; import org.elasticsearch.xpack.core.security.authc.RealmSettings; @@ -299,13 +299,7 @@ private Credential buildSigningCredential(Terminal terminal, OptionSet options, } else { Path cert = resolvePath(signingCertPathSpec.value(options)); Path key = resolvePath(signingKeyPathSpec.value(options)); - final String resolvedSigningCertPath = cert.toAbsolutePath().toString(); - Certificate[] certificates = CertParsingUtils.readCertificates(Collections.singletonList(resolvedSigningCertPath), env); - if (certificates.length != 1) { - throw new IllegalArgumentException("expected a single certificate in file [" + resolvedSigningCertPath + "] but found [" + - certificates.length + "]"); - } - signingCertificate = (X509Certificate) certificates[0]; + signingCertificate = CertParsingUtils.readX509Certificate(cert); signingKey = readSigningKey(key, password, terminal); } return new BasicX509Credential(signingCertificate, signingKey); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlRealm.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlRealm.java index 927fb2600a296..ffd38d20a21ef 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlRealm.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlRealm.java @@ -13,8 +13,8 @@ import org.apache.http.client.HttpClient; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; import org.elasticsearch.ElasticsearchSecurityException; import org.elasticsearch.ExceptionsHelper; @@ -28,6 +28,7 @@ import org.elasticsearch.core.Releasables; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.SettingsException; +import org.elasticsearch.common.ssl.SslKeyConfig; import org.elasticsearch.core.TimeValue; import org.elasticsearch.common.util.CollectionUtils; import org.elasticsearch.common.util.concurrent.ThreadContext; @@ -43,15 +44,14 @@ import org.elasticsearch.xpack.core.security.authc.RealmConfig; import org.elasticsearch.xpack.core.security.authc.RealmSettings; import org.elasticsearch.xpack.core.security.authc.saml.SamlRealmSettings; +import org.elasticsearch.xpack.core.security.authc.support.UserRoleMapper; import org.elasticsearch.xpack.core.security.user.User; -import org.elasticsearch.xpack.core.ssl.SSLConfiguration; import org.elasticsearch.xpack.core.ssl.CertParsingUtils; +import org.elasticsearch.xpack.core.ssl.SSLConfiguration; import org.elasticsearch.xpack.core.ssl.SSLService; -import org.elasticsearch.xpack.core.ssl.X509KeyPairSettings; import org.elasticsearch.xpack.security.authc.Realms; import org.elasticsearch.xpack.security.authc.TokenService; import org.elasticsearch.xpack.security.authc.support.DelegatedAuthorizationSupport; -import org.elasticsearch.xpack.core.security.authc.support.UserRoleMapper; import org.opensaml.core.criterion.EntityIdCriterion; import org.opensaml.saml.common.xml.SAMLConstants; import org.opensaml.saml.criterion.EntityRoleCriterion; @@ -330,8 +330,11 @@ static SigningConfiguration buildSigningConfiguration(RealmConfig config) throws private static List buildCredential(RealmConfig config, String prefix, Setting.AffixSetting aliasSetting, boolean allowMultiple) { - final X509KeyPairSettings keyPairSettings = X509KeyPairSettings.withPrefix(prefix, false); - final X509KeyManager keyManager = CertParsingUtils.getKeyManager(keyPairSettings, config.settings(), null, config.env()); + final SslKeyConfig keyConfig = CertParsingUtils.createKeyConfig(config.settings(), prefix, config.env(), false); + if (keyConfig.hasKeyMaterial() == false) { + return null; + } + final X509KeyManager keyManager = keyConfig.createKeyManager(); if (keyManager == null) { return null; } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/support/LdapTestCase.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/support/LdapTestCase.java index 8b181f4805af6..44e4b52f3d4bf 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/support/LdapTestCase.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/support/LdapTestCase.java @@ -23,7 +23,9 @@ import org.elasticsearch.common.network.NetworkAddress; import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.ssl.KeyStoreUtil; import org.elasticsearch.common.ssl.SslVerificationMode; +import org.elasticsearch.core.TimeValue; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.core.TimeValue; import org.elasticsearch.env.TestEnvironment; @@ -92,7 +94,8 @@ public void startLdap() throws Exception { getDataPath("/org/elasticsearch/xpack/security/authc/ldap/support/ldap-test-case.key"), ldapPassword ); - X509ExtendedKeyManager keyManager = CertParsingUtils.keyManager(ks, ldapPassword, KeyManagerFactory.getDefaultAlgorithm()); + final X509ExtendedKeyManager keyManager + = KeyStoreUtil.createKeyManager(ks, ldapPassword, KeyManagerFactory.getDefaultAlgorithm()); final SSLContext context = SSLContext.getInstance(XPackSettings.DEFAULT_SUPPORTED_PROTOCOLS.get(0)); context.init(new KeyManager[] { keyManager }, null, null); SSLServerSocketFactory serverSocketFactory = context.getServerSocketFactory(); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/pki/PkiRealmTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/pki/PkiRealmTests.java index 6aa1d515d4c12..26537e0beaceb 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/pki/PkiRealmTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/pki/PkiRealmTests.java @@ -13,6 +13,7 @@ import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.ssl.SslConfigException; import org.elasticsearch.common.util.CollectionUtils; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.env.TestEnvironment; @@ -50,6 +51,7 @@ import java.util.regex.Pattern; import javax.security.auth.x500.X500Principal; +import static org.elasticsearch.test.TestMatchers.throwableWithMessage; import static org.elasticsearch.test.ActionListenerUtils.anyActionListener; import static org.hamcrest.Matchers.arrayContainingInAnyOrder; import static org.hamcrest.Matchers.containsString; @@ -271,6 +273,30 @@ public void testVerificationUsingATruststore() throws Exception { assertThat(user.roles().length, is(0)); } + public void testVerificationUsingCertificateAuthorities() throws Exception { + final Path caPath = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/nodes/ca.crt"); + final Path certPath = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/nodes/trusted.crt"); + final X509Certificate certificate = readCert(certPath); + + UserRoleMapper roleMapper = buildRoleMapper(); + Settings settings = Settings.builder() + .put(globalSettings) + .putList("xpack.security.authc.realms.pki.my_pki.certificate_authorities", caPath.toString()) + .build(); + ThreadContext threadContext = new ThreadContext(globalSettings); + PkiRealm realm = buildRealm(roleMapper, settings); + assertRealmUsageStats(realm, true, false, true, false); + + threadContext.putTransient(PkiRealm.PKI_CERT_HEADER_NAME, new X509Certificate[] { certificate }); + + X509AuthenticationToken token = realm.token(threadContext); + User user = authenticate(token, realm).getUser(); + assertThat(user, is(notNullValue())); + assertThat(user.principal(), is("trusted")); + assertThat(user.roles(), is(notNullValue())); + assertThat(user.roles().length, is(0)); + } + public void testAuthenticationDelegationFailsWithoutTokenServiceAndTruststore() throws Exception { ThreadContext threadContext = new ThreadContext(Settings.EMPTY); Settings settings = Settings.builder() @@ -396,13 +422,12 @@ public void testTruststorePathWithoutPasswordThrowsException() throws Exception .put("xpack.security.authc.realms.pki.my_pki.truststore.path", getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode-client-profile.jks")) .build(); - IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> + SslConfigException e = expectThrows(SslConfigException.class, () -> new PkiRealm(new RealmConfig(new RealmConfig.RealmIdentifier(PkiRealmSettings.TYPE, REALM_NAME), settings, TestEnvironment.newEnvironment(settings), new ThreadContext(settings)), mock(UserRoleMapper.class)) ); - assertThat(e.getMessage(), containsString("Neither [xpack.security.authc.realms.pki.my_pki.truststore.secure_password] or [" + - "xpack.security.authc.realms.pki.my_pki.truststore.password] is configured")); + assertThat(e, throwableWithMessage(containsString("incorrect password; (no password"))); } public void testTruststorePathWithLegacyPasswordDoesNotThrow() throws Exception { diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlMetadataCommandTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlMetadataCommandTests.java index 20931c8928c00..c0ee4d55cb469 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlMetadataCommandTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlMetadataCommandTests.java @@ -157,7 +157,7 @@ public void testDefaultOptions() throws Exception { // Verify that OpenSAML things the XML representation is the same as our input final java.security.cert.X509Certificate javaCert = KeyInfoSupport.getCertificate(xmlCert); - assertThat(CertParsingUtils.readCertificates(Collections.singletonList(certPath)), arrayContaining(javaCert)); + assertThat(CertParsingUtils.readX509Certificates(Collections.singletonList(certPath)), arrayContaining(javaCert)); } else { assertThat(spDescriptor.getKeyDescriptors(), iterableWithSize(0)); } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlSpMetadataBuilderTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlSpMetadataBuilderTests.java index 0b63d7a2bb624..03ad3dcfdcbc4 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlSpMetadataBuilderTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlSpMetadataBuilderTests.java @@ -41,7 +41,7 @@ public class SamlSpMetadataBuilderTests extends SamlTestCase { public void setup() throws Exception { SamlUtils.initialize(logger); final Path certPath = getDataPath("saml.crt"); - final Certificate[] certs = CertParsingUtils.readCertificates(Collections.singletonList(certPath)); + final Certificate[] certs = CertParsingUtils.readX509Certificates(Collections.singletonList(certPath)); if (certs.length != 1) { fail("Expected exactly 1 certificate in " + certPath); } @@ -52,7 +52,7 @@ public void setup() throws Exception { } final Path threeCertsPath = getDataPath("saml-three-certs.crt"); - final Certificate[] threeCerts = CertParsingUtils.readCertificates(Collections.singletonList(threeCertsPath)); + final Certificate[] threeCerts = CertParsingUtils.readX509Certificates(Collections.singletonList(threeCertsPath)); if (threeCerts.length != 3) { fail("Expected exactly 3 certificate in " + certPath); } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlTestCase.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlTestCase.java index 46ff92c71bf36..3bfb726d2b4d4 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlTestCase.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlTestCase.java @@ -10,6 +10,7 @@ import org.apache.logging.log4j.Logger; import org.elasticsearch.ElasticsearchSecurityException; import org.elasticsearch.ExceptionsHelper; +import org.elasticsearch.common.ssl.KeyStoreUtil; import org.elasticsearch.common.ssl.PemUtils; import org.elasticsearch.core.Tuple; import org.elasticsearch.core.PathUtils; @@ -124,7 +125,7 @@ protected static Tuple readKeyPair(String keyName) protected static List buildOpenSamlCredential(final Tuple keyPair) { try { return Arrays.asList(new X509KeyManagerX509CredentialAdapter( - CertParsingUtils.keyManager(new Certificate[]{keyPair.v1()}, keyPair.v2(), new char[0]), "key")); + KeyStoreUtil.createKeyManager(new Certificate[]{keyPair.v1()}, keyPair.v2(), new char[0]), "key")); } catch (Exception e) { throw ExceptionsHelper.convertToRuntime(e); @@ -135,7 +136,7 @@ protected static List buildOpenSamlCredential(final List credentials = keyPairs.stream().map((keyPair) -> { try { return new X509KeyManagerX509CredentialAdapter( - CertParsingUtils.keyManager(new Certificate[]{keyPair.v1()}, keyPair.v2(), new char[0]), "key"); + KeyStoreUtil.createKeyManager(new Certificate[]{keyPair.v1()}, keyPair.v2(), new char[0]), "key"); } catch (Exception e) { throw ExceptionsHelper.convertToRuntime(e); } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/nio/SSLDriverTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/nio/SSLDriverTests.java index f4b116e36ec22..9379d4c5f8b72 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/nio/SSLDriverTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/nio/SSLDriverTests.java @@ -325,9 +325,9 @@ private SSLContext getSSLContext() throws Exception { String certPath = "/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.crt"; String keyPath = "/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.pem"; SSLContext sslContext; - TrustManager tm = CertParsingUtils.trustManager(CertParsingUtils.readCertificates(Collections.singletonList(getDataPath + TrustManager tm = CertParsingUtils.trustManager(CertParsingUtils.readX509Certificates(Collections.singletonList(getDataPath (certPath)))); - KeyManager km = CertParsingUtils.keyManager(CertParsingUtils.readCertificates(Collections.singletonList(getDataPath + KeyManager km = CertParsingUtils.keyManager(CertParsingUtils.readX509Certificates(Collections.singletonList(getDataPath (certPath))), PemUtils.readPrivateKey(getDataPath(keyPath), "testclient"::toCharArray), "testclient".toCharArray()); sslContext = SSLContext.getInstance(inFipsJvm() ? "TLSv1.2" : randomFrom("TLSv1.2", "TLSv1.3")); sslContext.init(new KeyManager[] { km }, new TrustManager[] { tm }, new SecureRandom()); diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/email/EmailSslTests.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/email/EmailSslTests.java index dd50cdcd14e6f..a5f9064416f3e 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/email/EmailSslTests.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/email/EmailSslTests.java @@ -64,7 +64,7 @@ public void startSmtpServer() throws GeneralSecurityException, IOException { KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(null, keystorePassword); keyStore.setKeyEntry("test-smtp", PemUtils.readPrivateKey(keyPath, keystorePassword::clone), keystorePassword, - CertParsingUtils.readCertificates(Collections.singletonList(certPath))); + CertParsingUtils.readX509Certificates(Collections.singletonList(certPath))); final SSLContext sslContext = new SSLContextBuilder().loadKeyMaterial(keyStore, keystorePassword).build(); server = EmailServer.localhost(logger, sslContext); } diff --git a/x-pack/qa/saml-idp-tests/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticationIT.java b/x-pack/qa/saml-idp-tests/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticationIT.java index 46fc1aafe63d0..6786ea27f36cb 100644 --- a/x-pack/qa/saml-idp-tests/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticationIT.java +++ b/x-pack/qa/saml-idp-tests/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticationIT.java @@ -462,7 +462,7 @@ private CloseableHttpClient getHttpClient() throws Exception { private SSLContext getClientSslContext() throws Exception { final Path pem = getDataPath("/idp-browser.pem"); - final Certificate[] certificates = CertParsingUtils.readCertificates(Collections.singletonList(pem)); + final Certificate[] certificates = CertParsingUtils.readX509Certificates(Collections.singletonList(pem)); final X509ExtendedTrustManager trustManager = CertParsingUtils.trustManager(certificates); SSLContext context = SSLContext.getInstance("TLS"); context.init(new KeyManager[0], new TrustManager[] { trustManager }, new SecureRandom());