Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update "ssl-config" to support X-Pack features #74887

Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

package org.elasticsearch.common.ssl;

import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedTrustManager;
import java.nio.file.Path;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
* A TrustConfiguration that merges trust anchors from a number of other trust configs to produce a single {@link X509ExtendedTrustManager}.
*/
public class CompositeTrustConfig implements SslTrustConfig {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In X-Pack, specifying a keystore, but no truststore gives you a set of trust anchors that support everything in your JDK trust, and all the trust anchors in your keystore.
This class was added so we can also support that behaviour from ssl-config.

private final List<SslTrustConfig> configs;

CompositeTrustConfig(List<SslTrustConfig> configs) {
this.configs = List.copyOf(configs);
}

@Override
public Collection<Path> getDependentFiles() {
return configs.stream().map(SslTrustConfig::getDependentFiles).flatMap(Collection::stream).collect(Collectors.toUnmodifiableSet());
}

@Override
public boolean isSystemDefault() {
return configs.stream().allMatch(SslTrustConfig::isSystemDefault);
}

@Override
public X509ExtendedTrustManager createTrustManager() {
try {
Collection<Certificate> trustedIssuers = configs.stream()
.map(c -> c.createTrustManager())
.map(tm -> tm.getAcceptedIssuers())
.flatMap(Arrays::stream)
.collect(Collectors.toSet());
final KeyStore store = KeyStoreUtil.buildTrustStore(trustedIssuers);
return KeyStoreUtil.createTrustManager(store, TrustManagerFactory.getDefaultAlgorithm());
} catch (GeneralSecurityException e) {
throw new SslConfigException("Cannot combine trust configurations ["
+ configs.stream().map(SslTrustConfig::toString).collect(Collectors.joining(","))
+ "]",
e);
}
}

@Override
public Collection<? extends StoredCertificate> getConfiguredCertificates() {
return configs.stream().map(SslTrustConfig::getConfiguredCertificates)
.flatMap(Collection::stream)
.collect(Collectors.toUnmodifiableList());
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CompositeTrustConfig that = (CompositeTrustConfig) o;
return configs.equals(that.configs);
}

@Override
public int hashCode() {
return Objects.hash(configs);
}

@Override
public String toString() {
return "Composite-Trust{" + configs.stream().map(SslTrustConfig::toString).collect(Collectors.joining(",")) + '}';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@
import java.security.KeyStore;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.BiFunction;

/**
* This class represents a trust configuration that corresponds to the default trusted CAs of the JDK
*/
final class DefaultJdkTrustConfig implements SslTrustConfig {
public final class DefaultJdkTrustConfig implements SslTrustConfig {

public static final DefaultJdkTrustConfig DEFAULT_INSTANCE = new DefaultJdkTrustConfig();

private final BiFunction<String, String, String> systemProperties;
private final char[] trustStorePassword;
Expand All @@ -51,6 +53,11 @@ final class DefaultJdkTrustConfig implements SslTrustConfig {
this.trustStorePassword = trustStorePassword;
}

@Override
public boolean isSystemDefault() {
return true;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method needed for the PKI realm - it will not trust the JDK default issuers.

}

@Override
public X509ExtendedTrustManager createTrustManager() {
try {
Expand Down Expand Up @@ -90,7 +97,12 @@ private static char[] getSystemTrustStorePassword(BiFunction<String, String, Str

@Override
public Collection<Path> getDependentFiles() {
return Collections.emptyList();
return List.of();
}

@Override
public Collection<? extends StoredCertificate> getConfiguredCertificates() {
return List.of();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
* Based on https://github.com/groovenauts/jmeter_oauth_plugin/blob/master/jmeter/src/
* main/java/org/apache/jmeter/protocol/oauth/sampler/PrivateKeyReader.java
*/
final class DerParser {
public final class DerParser {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The RestrictedTrustConfig in X-Pack needs to be able to use this parser.

// Constructed Flag
private static final int CONSTRUCTED = 0x20;

Expand All @@ -55,12 +55,12 @@ final class DerParser {
private InputStream derInputStream;
private int maxAsnObjectLength;

DerParser(byte[] bytes) {
public DerParser(byte[] bytes) {
this.derInputStream = new ByteArrayInputStream(bytes);
this.maxAsnObjectLength = bytes.length;
}

Asn1Object readAsn1Object() throws IOException {
public Asn1Object readAsn1Object() throws IOException {
int tag = derInputStream.read();
if (tag == -1) {
throw new IOException("Invalid DER: stream too short, missing tag");
Expand Down Expand Up @@ -133,7 +133,7 @@ private int getLength() throws IOException {
*
* @author zhang
*/
static class Asn1Object {
public static class Asn1Object {

protected final int type;
protected final int length;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,45 @@

package org.elasticsearch.common.ssl;

import javax.net.ssl.X509ExtendedKeyManager;
import org.elasticsearch.core.Tuple;

import java.nio.file.Path;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import javax.net.ssl.X509ExtendedKeyManager;

/**
* A {@link SslKeyConfig} that does nothing (provides a null key manager)
*/
final class EmptyKeyConfig implements SslKeyConfig {
public final class EmptyKeyConfig implements SslKeyConfig {

static final EmptyKeyConfig INSTANCE = new EmptyKeyConfig();
public static final EmptyKeyConfig INSTANCE = new EmptyKeyConfig();

private EmptyKeyConfig() {
// Enforce a single instance
}

@Override
public Collection<Path> getDependentFiles() {
return Collections.emptyList();
return List.of();
}

@Override
public List<Tuple<PrivateKey, X509Certificate>> getKeys() {
return List.of();
}

@Override
public Collection<StoredCertificate> getConfiguredCertificates() {
return List.of();
}

@Override
public boolean hasKeyMaterial() {
return false;
}

@Override
Expand Down
Loading