Add API for SSL certificate information (elastic/x-pack-elasticsearch#3088)

Exposes the certificate location (configured path), serial number, and expiry date

Closes: elastic/x-pack-elasticsearch#2795

Original commit: elastic/x-pack-elasticsearch@a0773f6840
This commit is contained in:
Tim Vernum 2017-12-06 19:57:25 +10:00 committed by GitHub
parent 7f553f391f
commit 628dfaa843
22 changed files with 769 additions and 80 deletions

View File

@ -177,6 +177,8 @@ import org.elasticsearch.xpack.security.user.AnonymousUser;
import org.elasticsearch.xpack.ssl.SSLConfigurationSettings;
import org.elasticsearch.xpack.ssl.SSLService;
import org.elasticsearch.xpack.ssl.TLSLicenseBootstrapCheck;
import org.elasticsearch.xpack.ssl.action.GetCertificateInfoAction;
import org.elasticsearch.xpack.ssl.rest.RestGetCertificateInfoAction;
import org.elasticsearch.xpack.template.TemplateUtils;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
@ -599,7 +601,8 @@ public class Security implements ActionPlugin, IngestPlugin, NetworkPlugin, Clus
new ActionHandler<>(PutRoleMappingAction.INSTANCE, TransportPutRoleMappingAction.class),
new ActionHandler<>(DeleteRoleMappingAction.INSTANCE, TransportDeleteRoleMappingAction.class),
new ActionHandler<>(CreateTokenAction.INSTANCE, TransportCreateTokenAction.class),
new ActionHandler<>(InvalidateTokenAction.INSTANCE, TransportInvalidateTokenAction.class)
new ActionHandler<>(InvalidateTokenAction.INSTANCE, TransportInvalidateTokenAction.class),
new ActionHandler<>(GetCertificateInfoAction.INSTANCE, GetCertificateInfoAction.TransportAction.class)
);
}
@ -639,7 +642,8 @@ public class Security implements ActionPlugin, IngestPlugin, NetworkPlugin, Clus
new RestPutRoleMappingAction(settings, restController, licenseState),
new RestDeleteRoleMappingAction(settings, restController, licenseState),
new RestGetTokenAction(settings, restController, licenseState),
new RestInvalidateTokenAction(settings, restController, licenseState)
new RestInvalidateTokenAction(settings, restController, licenseState),
new RestGetCertificateInfoAction(settings, restController)
);
}

View File

@ -51,10 +51,6 @@ import java.util.stream.Collectors;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1TaggedObject;
import org.bouncycastle.asn1.BERSequence;
import org.bouncycastle.asn1.BERTaggedObject;
import org.bouncycastle.asn1.DERIA5String;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.DERUTF8String;
@ -63,7 +59,6 @@ import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.DisplayText;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.ExtensionsGenerator;
import org.bouncycastle.asn1.x509.GeneralName;

View File

@ -8,12 +8,16 @@ package org.elasticsearch.xpack.ssl;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.env.Environment;
import org.elasticsearch.xpack.ssl.cert.CertificateInfo;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedTrustManager;
import java.io.IOException;
import java.nio.file.Path;
import java.security.GeneralSecurityException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@ -36,6 +40,15 @@ class DefaultJDKTrustConfig extends TrustConfig {
}
}
@Override
/**
* We don't return the list of JDK certificates here, because they are not managed by Elasticsearch, and the purpose
* of this method is to obtain information about certificate (files/stores) that X-Pack directly manages.
*/
Collection<CertificateInfo> certificates(Environment environment) throws GeneralSecurityException, IOException {
return Collections.emptyList();
}
@Override
List<Path> filesToMonitor(@Nullable Environment environment) {
return Collections.emptyList();

View File

@ -7,11 +7,13 @@ package org.elasticsearch.xpack.ssl;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.env.Environment;
import org.elasticsearch.xpack.ssl.cert.CertificateInfo;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager;
import java.nio.file.Path;
import java.security.PrivateKey;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@ -28,6 +30,11 @@ abstract class KeyConfig extends TrustConfig {
return null;
}
@Override
Collection<CertificateInfo> certificates(Environment environment) {
return Collections.emptyList();
}
@Override
List<Path> filesToMonitor(@Nullable Environment environment) {
return Collections.emptyList();
@ -57,4 +64,5 @@ abstract class KeyConfig extends TrustConfig {
abstract X509ExtendedKeyManager createKeyManager(@Nullable Environment environment);
abstract List<PrivateKey> privateKeys(@Nullable Environment environment);
}

View File

@ -19,16 +19,18 @@ import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import org.apache.lucene.util.IOUtils;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.settings.SecureString;
import org.elasticsearch.env.Environment;
import org.elasticsearch.xpack.ssl.cert.CertificateInfo;
/**
* Implementation of a key configuration that is backed by a PEM encoded key file and one or more certificates
@ -41,8 +43,9 @@ class PEMKeyConfig extends KeyConfig {
/**
* Creates a new key configuration backed by the key and certificate chain provided
* @param keyPath the path to the key file
* @param keyPassword the password for the key.
*
* @param keyPath the path to the key file
* @param keyPassword the password for the key.
* @param certChainPath the path to the file containing the certificate chain
*/
PEMKeyConfig(String keyPath, SecureString keyPassword, String certChainPath) {
@ -58,7 +61,7 @@ class PEMKeyConfig extends KeyConfig {
if (privateKey == null) {
throw new IllegalArgumentException("private key [" + keyPath + "] could not be loaded");
}
Certificate[] certificateChain = CertUtils.readCertificates(Collections.singletonList(certPath), environment);
Certificate[] certificateChain = getCertificateChain(environment);
return CertUtils.keyManager(certificateChain, privateKey, keyPassword.getChars());
} catch (IOException | UnrecoverableKeyException | NoSuchAlgorithmException | CertificateException | KeyStoreException e) {
@ -66,6 +69,23 @@ class PEMKeyConfig extends KeyConfig {
}
}
private Certificate[] getCertificateChain(@Nullable Environment environment) throws CertificateException, IOException {
return CertUtils.readCertificates(Collections.singletonList(certPath), environment);
}
@Override
Collection<CertificateInfo> certificates(Environment environment) throws CertificateException, IOException {
final Certificate[] chain = getCertificateChain(environment);
final List<CertificateInfo> info = new ArrayList<>(chain.length);
for (int i = 0; i < chain.length; i++) {
final Certificate cert = chain[i];
if (cert instanceof X509Certificate) {
info.add(new CertificateInfo(certPath, "PEM", null, i == 0, (X509Certificate) cert));
}
}
return info;
}
@Override
List<PrivateKey> privateKeys(@Nullable Environment environment) {
try {
@ -84,7 +104,7 @@ class PEMKeyConfig extends KeyConfig {
@Override
X509ExtendedTrustManager createTrustManager(@Nullable Environment environment) {
try {
Certificate[] certificates = CertUtils.readCertificates(Collections.singletonList(certPath), environment);
Certificate[] certificates = getCertificateChain(environment);
return CertUtils.trustManager(certificates);
} catch (Exception e) {
throw new ElasticsearchException("failed to initialize a TrustManagerFactory", e);

View File

@ -5,17 +5,23 @@
*/
package org.elasticsearch.xpack.ssl;
import javax.net.ssl.X509ExtendedTrustManager;
import java.io.IOException;
import java.nio.file.Path;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.Strings;
import org.elasticsearch.env.Environment;
import javax.net.ssl.X509ExtendedTrustManager;
import java.nio.file.Path;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.elasticsearch.xpack.ssl.cert.CertificateInfo;
/**
* Implementation of trust configuration that is backed by PEM encoded certificate files.
@ -42,6 +48,20 @@ class PEMTrustConfig extends TrustConfig {
}
}
@Override
Collection<CertificateInfo> certificates(Environment environment) throws CertificateException, IOException {
final List<CertificateInfo> info = new ArrayList<>(caPaths.size());
for (String path : caPaths) {
Certificate[] chain = CertUtils.readCertificates(Collections.singletonList(path), environment);
for (final Certificate cert : chain) {
if (cert instanceof X509Certificate) {
info.add(new CertificateInfo(path, "PEM", null, false, (X509Certificate) cert));
}
}
}
return info;
}
@Override
List<Path> filesToMonitor(@Nullable Environment environment) {
List<Path> paths = new ArrayList<>(caPaths.size());

View File

@ -8,7 +8,9 @@ package org.elasticsearch.xpack.ssl;
import javax.net.ssl.X509ExtendedTrustManager;
import java.io.IOException;
import java.nio.file.Path;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@ -17,6 +19,7 @@ import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.xpack.ssl.cert.CertificateInfo;
/**
* An implementation of {@link TrustConfig} that constructs a {@link RestrictedTrustManager}.
@ -47,6 +50,11 @@ public final class RestrictedTrustConfig extends TrustConfig {
}
}
@Override
Collection<CertificateInfo> certificates(Environment environment) throws GeneralSecurityException, IOException {
return delegate.certificates(environment);
}
@Override
List<Path> filesToMonitor(@Nullable Environment environment) {
List<Path> files = new ArrayList<>(delegate.filesToMonitor(environment));

View File

@ -5,19 +5,23 @@
*/
package org.elasticsearch.xpack.ssl;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
import java.io.IOException;
import java.nio.file.Path;
import java.security.GeneralSecurityException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.settings.SecureString;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.xpack.XPackSettings;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.elasticsearch.xpack.ssl.cert.CertificateInfo;
/**
* Represents the configuration for an SSLContext
@ -187,8 +191,8 @@ public final class SSLConfiguration {
// TODO: we should not support loading a keystore from sysprops...
try (SecureString keystorePassword = new SecureString(System.getProperty("javax.net.ssl.keyStorePassword", ""))) {
return new StoreKeyConfig(System.getProperty("javax.net.ssl.keyStore"), "jks", keystorePassword, keystorePassword,
System.getProperty("ssl.KeyManagerFactory.algorithm", KeyManagerFactory.getDefaultAlgorithm()),
System.getProperty("ssl.TrustManagerFactory.algorithm", TrustManagerFactory.getDefaultAlgorithm()));
System.getProperty("ssl.KeyManagerFactory.algorithm", KeyManagerFactory.getDefaultAlgorithm()),
System.getProperty("ssl.TrustManagerFactory.algorithm", TrustManagerFactory.getDefaultAlgorithm()));
}
}
return KeyConfig.NONE;
@ -248,7 +252,7 @@ public final class SSLConfiguration {
} else if (global == null && System.getProperty("javax.net.ssl.trustStore") != null) {
try (SecureString truststorePassword = new SecureString(System.getProperty("javax.net.ssl.trustStorePassword", ""))) {
return new StoreTrustConfig(System.getProperty("javax.net.ssl.trustStore"), "jks", 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();
@ -269,4 +273,17 @@ public final class SSLConfiguration {
}
return defaultValue;
}
/**
* Returns information about each certificate that referenced by this SSL configurations.
* This includes certificates used for identity (with a private key) and those used for trust, but excludes
* certificates that are provided by the JRE.
* @see TrustConfig#certificates(Environment)
*/
List<CertificateInfo> getDefinedCertificates(@Nullable Environment environment) throws GeneralSecurityException, IOException {
List<CertificateInfo> certificates = new ArrayList<>();
certificates.addAll(keyConfig.certificates(environment));
certificates.addAll(trustConfig.certificates(environment));
return certificates;
}
}

View File

@ -5,20 +5,6 @@
*/
package org.elasticsearch.xpack.ssl;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy;
import org.apache.lucene.util.SetOnce;
import org.bouncycastle.operator.OperatorCreationException;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.CheckedSupplier;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.xpack.XPackSettings;
import org.elasticsearch.xpack.common.socket.SocketAccess;
import org.elasticsearch.xpack.security.Security;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
@ -32,6 +18,7 @@ import javax.security.auth.DestroyFailedException;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.security.GeneralSecurityException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
@ -51,6 +38,22 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy;
import org.apache.lucene.util.SetOnce;
import org.bouncycastle.operator.OperatorCreationException;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.CheckedSupplier;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.xpack.XPackSettings;
import org.elasticsearch.xpack.common.socket.SocketAccess;
import org.elasticsearch.xpack.security.Security;
import org.elasticsearch.xpack.ssl.cert.CertificateInfo;
/**
* Provides access to {@link SSLEngine} and {@link SSLSocketFactory} objects based on a provided configuration. All
@ -451,6 +454,23 @@ public class SSLService extends AbstractComponent {
return Collections.unmodifiableMap(sslConfigurations);
}
/**
* Returns information about each certificate that is referenced by any SSL configuration.
* This includes certificates used for identity (with a private key) and those used for trust, but excludes
* certificates that are provided by the JRE.
* Due to the nature of KeyStores, this may include certificates that are available, but never used
* such as a CA certificate that is no longer in use, or a server certificate for an unrelated host.
* @see TrustConfig#certificates(Environment)
*/
public Set<CertificateInfo> getLoadedCertificates() throws GeneralSecurityException, IOException {
Set<CertificateInfo> certificates = new HashSet<>();
for (SSLConfiguration config : this.getLoadedSSLConfigurations()) {
certificates.addAll(config.getDefinedCertificates(env));
}
return certificates;
}
/**
* This socket factory wraps an existing SSLSocketFactory and sets the protocols and ciphers on each SSLSocket after it is created. This
* is needed even though the SSLContext is configured properly as the configuration does not flow down to the sockets created by the

View File

@ -5,30 +5,35 @@
*/
package org.elasticsearch.xpack.ssl;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.settings.SecureString;
import org.elasticsearch.env.Environment;
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;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Objects;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.settings.SecureString;
import org.elasticsearch.env.Environment;
import org.elasticsearch.xpack.ssl.cert.CertificateInfo;
/**
* A key configuration that is backed by a {@link KeyStore}
*/
@ -82,6 +87,28 @@ class StoreKeyConfig extends KeyConfig {
}
}
@Override
Collection<CertificateInfo> certificates(Environment environment) throws GeneralSecurityException, IOException {
final Path path = CertUtils.resolvePath(keyStorePath, environment);
final KeyStore trustStore = CertUtils.readKeyStore(path, keyStoreType, keyStorePassword.getChars());
final List<CertificateInfo> certificates = new ArrayList<>();
final Enumeration<String> aliases = trustStore.aliases();
while (aliases.hasMoreElements()) {
String alias = aliases.nextElement();
final Certificate[] chain = trustStore.getCertificateChain(alias);
if (chain == null) {
continue;
}
for (int i = 0; i < chain.length; i++) {
final Certificate certificate = chain[i];
if (certificate instanceof X509Certificate) {
certificates.add(new CertificateInfo(keyStorePath, keyStoreType, alias, i == 0, (X509Certificate) certificate));
}
}
}
return certificates;
}
@Override
List<Path> filesToMonitor(@Nullable Environment environment) {
return Collections.singletonList(CertUtils.resolvePath(keyStorePath, environment));

View File

@ -5,16 +5,25 @@
*/
package org.elasticsearch.xpack.ssl;
import javax.net.ssl.X509ExtendedTrustManager;
import java.io.IOException;
import java.nio.file.Path;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Objects;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.settings.SecureString;
import org.elasticsearch.env.Environment;
import javax.net.ssl.X509ExtendedTrustManager;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import org.elasticsearch.xpack.ssl.cert.CertificateInfo;
/**
* Trust configuration that is backed by a {@link java.security.KeyStore}
@ -28,8 +37,9 @@ class StoreTrustConfig extends TrustConfig {
/**
* Create a new configuration based on the provided parameters
* @param trustStorePath the path to the truststore
* @param trustStorePassword the password for the truststore
*
* @param trustStorePath the path to the truststore
* @param trustStorePassword the password for the truststore
* @param trustStoreAlgorithm the algorithm to use for reading the truststore
*/
StoreTrustConfig(String trustStorePath, String trustStoreType, SecureString trustStorePassword, String trustStoreAlgorithm) {
@ -50,6 +60,23 @@ class StoreTrustConfig extends TrustConfig {
}
}
@Override
Collection<CertificateInfo> certificates(Environment environment) throws GeneralSecurityException, IOException {
final Path path = CertUtils.resolvePath(trustStorePath, environment);
final KeyStore trustStore = CertUtils.readKeyStore(path, trustStoreType, trustStorePassword.getChars());
final List<CertificateInfo> certificates = new ArrayList<>();
final Enumeration<String> aliases = trustStore.aliases();
while (aliases.hasMoreElements()) {
String alias = aliases.nextElement();
final Certificate certificate = trustStore.getCertificate(alias);
if (certificate instanceof X509Certificate) {
final boolean hasKey = trustStore.isKeyEntry(alias);
certificates.add(new CertificateInfo(trustStorePath, trustStoreType, alias, hasKey, (X509Certificate) certificate));
}
}
return certificates;
}
@Override
List<Path> filesToMonitor(@Nullable Environment environment) {
if (trustStorePath == null) {

View File

@ -7,13 +7,17 @@ package org.elasticsearch.xpack.ssl;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.env.Environment;
import org.elasticsearch.xpack.ssl.cert.CertificateInfo;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.X509ExtendedTrustManager;
import java.io.IOException;
import java.net.Socket;
import java.nio.file.Path;
import java.security.GeneralSecurityException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@ -67,6 +71,11 @@ class TrustAllConfig extends TrustConfig {
return TRUST_MANAGER;
}
@Override
Collection<CertificateInfo> certificates(Environment environment) throws GeneralSecurityException, IOException {
return Collections.emptyList();
}
@Override
List<Path> filesToMonitor(@Nullable Environment environment) {
return Collections.emptyList();

View File

@ -5,19 +5,24 @@
*/
package org.elasticsearch.xpack.ssl;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.env.Environment;
import javax.net.ssl.X509ExtendedTrustManager;
import java.io.IOException;
import java.nio.file.Path;
import java.security.GeneralSecurityException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.env.Environment;
import org.elasticsearch.xpack.ssl.cert.CertificateInfo;
/**
* The configuration of trust material for SSL usage
*/
@ -29,6 +34,8 @@ abstract class TrustConfig {
*/
abstract X509ExtendedTrustManager createTrustManager(@Nullable Environment environment);
abstract Collection<CertificateInfo> certificates(@Nullable Environment environment) throws GeneralSecurityException, IOException;
/**
* Returns a list of files that should be monitored for changes
* @param environment the environment to resolve files against or null in the case of running in a transport client
@ -80,6 +87,15 @@ abstract class TrustConfig {
}
}
@Override
Collection<CertificateInfo> certificates(Environment environment) throws GeneralSecurityException, IOException {
List<CertificateInfo> certificates = new ArrayList<>();
for (TrustConfig tc : trustConfigs) {
certificates.addAll(tc.certificates(environment));
}
return certificates;
}
@Override
List<Path> filesToMonitor(@Nullable Environment environment) {
return trustConfigs.stream().flatMap((tc) -> tc.filesToMonitor(environment).stream()).collect(Collectors.toList());

View File

@ -0,0 +1,141 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.ssl.action;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Collection;
import org.elasticsearch.action.Action;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionRequestBuilder;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.action.support.HandledTransportAction;
import org.elasticsearch.client.ElasticsearchClient;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
import org.elasticsearch.xpack.ssl.SSLService;
import org.elasticsearch.xpack.ssl.cert.CertificateInfo;
/**
* Action to obtain information about X.509 (SSL/TLS) certificates that are being used by X-Pack.
* The primary use case is for tracking the expiry dates of certificates.
*/
public class GetCertificateInfoAction
extends Action<GetCertificateInfoAction.Request, GetCertificateInfoAction.Response, GetCertificateInfoAction.RequestBuilder> {
public static final GetCertificateInfoAction INSTANCE = new GetCertificateInfoAction();
public static final String NAME = "cluster:monitor/xpack/ssl/certificates/get";
private GetCertificateInfoAction() {
super(NAME);
}
@Override
public GetCertificateInfoAction.RequestBuilder newRequestBuilder(ElasticsearchClient client) {
return new GetCertificateInfoAction.RequestBuilder(client, this);
}
@Override
public GetCertificateInfoAction.Response newResponse() {
return new GetCertificateInfoAction.Response();
}
public static class Request extends ActionRequest {
@Override
public ActionRequestValidationException validate() {
return null;
}
}
public static class Response extends ActionResponse implements ToXContentObject {
private Collection<CertificateInfo> certificates;
public Response() {
}
public Response(Collection<CertificateInfo> certificates) {
this.certificates = certificates;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startArray();
for (CertificateInfo cert : certificates) {
cert.toXContent(builder, params);
}
return builder.endArray();
}
@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeVInt(certificates.size());
for (CertificateInfo cert : certificates) {
cert.writeTo(out);
}
}
@Override
public void readFrom(StreamInput in) throws IOException {
super.readFrom(in);
this.certificates = new ArrayList<>();
int count = in.readVInt();
for (int i = 0; i < count; i++) {
certificates.add(new CertificateInfo(in));
}
}
}
public static class RequestBuilder extends ActionRequestBuilder<Request, Response, RequestBuilder> {
public RequestBuilder(ElasticsearchClient client, GetCertificateInfoAction action) {
super(client, action, new Request());
}
public RequestBuilder(ElasticsearchClient client) {
this(client, GetCertificateInfoAction.INSTANCE);
}
}
public static class TransportAction extends HandledTransportAction<Request, Response> {
private final SSLService sslService;
@Inject
public TransportAction(Settings settings, ThreadPool threadPool, TransportService transportService,
ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver,
SSLService sslService) {
super(settings, GetCertificateInfoAction.NAME, threadPool, transportService, actionFilters, indexNameExpressionResolver,
Request::new);
this.sslService = sslService;
}
@Override
protected void doExecute(Request request, ActionListener<Response> listener) {
try {
Collection<CertificateInfo> certificates = sslService.getLoadedCertificates();
listener.onResponse(new Response(certificates));
} catch (GeneralSecurityException | IOException e) {
listener.onFailure(e);
}
}
}
}

View File

@ -0,0 +1,133 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.ssl.cert;
import java.io.IOException;
import java.security.cert.X509Certificate;
import java.util.Objects;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Streamable;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.xpack.ssl.CertUtils;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
/**
* Simple model of an X.509 certificate that is known to X-Pack
*/
public class CertificateInfo implements ToXContentObject, Writeable {
private final String path;
private final String format;
private final String alias;
private final String subjectDn;
private final String serialNumber;
private final boolean hasPrivateKey;
private final DateTime expiry;
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.format = Objects.requireNonNull(format, "Certificate format cannot be null");
this.alias = alias;
this.subjectDn = Objects.requireNonNull(certificate.getSubjectDN().getName());
this.serialNumber = certificate.getSerialNumber().toString(16);
this.hasPrivateKey = hasPrivateKey;
this.expiry = new DateTime(certificate.getNotAfter(), DateTimeZone.UTC);
}
public CertificateInfo(StreamInput in) throws IOException {
this.path = in.readString();
this.format = in.readString();
this.alias = in.readOptionalString();
this.subjectDn = in.readString();
this.serialNumber = in.readString();
this.hasPrivateKey = in.readBoolean();
this.expiry = new DateTime(in.readLong(), DateTimeZone.UTC);
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeString(path);
out.writeString(format);
out.writeOptionalString(alias);
out.writeString(subjectDn);
out.writeString(serialNumber);
out.writeBoolean(hasPrivateKey);
out.writeLong(expiry.getMillis());
}
public String path() {
return path;
}
public String format() {
return format;
}
public String alias() {
return alias;
}
public String subjectDn() {
return subjectDn;
}
public String serialNumber() {
return serialNumber;
}
public DateTime expiry() {
return expiry;
}
public boolean hasPrivateKey() {
return hasPrivateKey;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return builder.startObject()
.field("path", path)
.field("format", format)
.field("alias", alias)
.field("subject_dn", subjectDn)
.field("serial_number", serialNumber)
.field("has_private_key", hasPrivateKey)
.field("expiry", expiry)
.endObject();
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (other == null || getClass() != other.getClass()) {
return false;
}
final CertificateInfo that = (CertificateInfo) other;
return this.path.equals(that.path)
&& this.format.equals(that.format)
&& this.hasPrivateKey == that.hasPrivateKey
&& Objects.equals(this.alias, that.alias)
&& Objects.equals(this.serialNumber, that.serialNumber)
&& Objects.equals(this.subjectDn, that.subjectDn)
&& Objects.equals(this.expiry, that.expiry);
}
@Override
public int hashCode() {
int result = path.hashCode();
result = 31 * result + (alias != null ? alias.hashCode() : 0);
result = 31 * result + (serialNumber != null ? serialNumber.hashCode() : 0);
return result;
}
}

View File

@ -0,0 +1,52 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.ssl.rest;
import java.io.IOException;
import org.elasticsearch.client.ElasticsearchClient;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.rest.BytesRestResponse;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.RestResponse;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.rest.action.RestBuilderListener;
import org.elasticsearch.xpack.ssl.action.GetCertificateInfoAction;
import org.elasticsearch.xpack.ssl.action.GetCertificateInfoAction.Response;
import static org.elasticsearch.rest.RestRequest.Method.GET;
/**
* A REST handler to obtain information about TLS/SSL (X.509) certificates
* @see GetCertificateInfoAction
*/
public class RestGetCertificateInfoAction extends BaseRestHandler {
public RestGetCertificateInfoAction(Settings settings, RestController controller) {
super(settings);
controller.registerHandler(GET, "/_xpack/ssl/certificates", this);
}
@Override
public String getName() {
return "xpack_ssl_get_certificates";
}
@Override
protected final RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
return channel -> new GetCertificateInfoAction.RequestBuilder(client, GetCertificateInfoAction.INSTANCE)
.execute(new RestBuilderListener<Response>(channel) {
@Override
public RestResponse buildResponse(Response response, XContentBuilder builder) throws Exception {
return new BytesRestResponse(RestStatus.OK, response.toXContent(builder, request));
}
});
}
}

View File

@ -5,15 +5,6 @@
*/
package org.elasticsearch.xpack.ssl;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.bouncycastle.jce.spec.ECNamedCurveSpec;
import org.elasticsearch.common.SuppressForbidden;
import org.elasticsearch.common.network.InetAddresses;
import org.elasticsearch.common.network.NetworkAddress;
import org.elasticsearch.test.ESTestCase;
import java.io.InputStream;
import java.io.Reader;
import java.math.BigInteger;
@ -29,12 +20,18 @@ import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.hamcrest.Matchers.containsString;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.jce.spec.ECNamedCurveSpec;
import org.elasticsearch.common.SuppressForbidden;
import org.elasticsearch.common.network.InetAddresses;
import org.elasticsearch.common.network.NetworkAddress;
import org.elasticsearch.test.ESTestCase;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
@ -186,4 +183,5 @@ public class CertUtilsTests extends ESTestCase {
ECNamedCurveSpec namedCurveSpec = (ECNamedCurveSpec) ecPrivateKey.getParams();
assertEquals("prime256v1", namedCurveSpec.getName());
}
}

View File

@ -5,18 +5,23 @@
*/
package org.elasticsearch.xpack.ssl;
import javax.net.ssl.X509ExtendedTrustManager;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.env.TestEnvironment;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.ssl.cert.CertificateInfo;
import org.hamcrest.Matchers;
import javax.net.ssl.X509ExtendedTrustManager;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
public class RestrictedTrustConfigTests extends ESTestCase {
public void testDelegationOfFilesToMonitor() throws Exception {
@ -37,6 +42,11 @@ public class RestrictedTrustConfigTests extends ESTestCase {
return null;
}
@Override
Collection<CertificateInfo> certificates(Environment environment) throws GeneralSecurityException, IOException {
return Collections.emptyList();
}
@Override
List<Path> filesToMonitor(Environment environment) {
return otherFiles;

View File

@ -20,9 +20,11 @@ import java.security.PrivilegedExceptionAction;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import org.elasticsearch.ElasticsearchException;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
@ -34,6 +36,7 @@ import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.CheckedRunnable;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.MockSecureSettings;
@ -43,6 +46,8 @@ import org.elasticsearch.env.TestEnvironment;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.junit.annotations.Network;
import org.elasticsearch.xpack.XPackSettings;
import org.elasticsearch.xpack.ssl.cert.CertificateInfo;
import org.joda.time.DateTime;
import org.junit.Before;
import org.mockito.ArgumentCaptor;
@ -50,9 +55,11 @@ import static org.hamcrest.Matchers.arrayContainingInAnyOrder;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.emptyArray;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.iterableWithSize;
import static org.hamcrest.Matchers.lessThan;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
@ -78,7 +85,7 @@ public class SSLServiceTests extends ESTestCase {
testnodeStoreType = randomBoolean() ? "jks" : null;
} else {
testnodeStore = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.p12");
testnodeStoreType = "PKCS12";
testnodeStoreType = randomBoolean() ? "PKCS12" : null;
}
logger.info("Using [{}] key/truststore [{}]", testnodeStoreType, testnodeStore);
testclientStore = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.jks");
@ -333,7 +340,7 @@ public class SSLServiceTests extends ESTestCase {
.put("xpack.ssl.keystore.path", testnodeStore)
.put("xpack.ssl.keystore.type", testnodeStoreType)
.setSecureSettings(secureSettings)
.putList("xpack.ssl.cipher_suites", new String[]{"foo", "bar"})
.putList("xpack.ssl.cipher_suites", new String[] { "foo", "bar" })
.build();
IllegalArgumentException e =
expectThrows(IllegalArgumentException.class, () -> new SSLService(settings, env));
@ -456,6 +463,98 @@ public class SSLServiceTests extends ESTestCase {
assertEquals(message, ce.getMessage());
}
public void testReadCertificateInformation() throws Exception {
final Path jksPath = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks");
final Path p12Path = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.p12");
final Path pemPath = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/active-directory-ca.crt");
final MockSecureSettings secureSettings = new MockSecureSettings();
secureSettings.setString("xpack.ssl.keystore.secure_password", "testnode");
secureSettings.setString("xpack.ssl.truststore.secure_password", "testnode");
secureSettings.setString("xpack.http.ssl.keystore.secure_password", "testnode");
final Settings settings = Settings.builder()
.put("xpack.ssl.keystore.path", jksPath)
.put("xpack.ssl.truststore.path", jksPath)
.put("xpack.http.ssl.keystore.path", p12Path)
.put("xpack.security.authc.realms.ad.type", "ad")
.put("xpack.security.authc.realms.ad.ssl.certificate_authorities", pemPath)
.setSecureSettings(secureSettings)
.build();
final SSLService sslService = new SSLService(settings, env);
final List<CertificateInfo> certificates = new ArrayList<>(sslService.getLoadedCertificates());
assertThat(certificates, iterableWithSize(7));
Collections.sort(certificates,
Comparator.comparing((CertificateInfo c) -> c.alias() == null ? "" : c.alias()).thenComparing(CertificateInfo::path));
final Iterator<CertificateInfo> iterator = certificates.iterator();
CertificateInfo cert = iterator.next();
assertThat(cert.alias(), nullValue());
assertThat(cert.path(), equalTo(pemPath.toString()));
assertThat(cert.format(), equalTo("PEM"));
assertThat(cert.serialNumber(), equalTo("580db8ad52bb168a4080e1df122a3f56"));
assertThat(cert.subjectDn(), equalTo("CN=ad-ELASTICSEARCHAD-CA, DC=ad, DC=test, DC=elasticsearch, DC=com"));
assertThat(cert.expiry(), equalTo(DateTime.parse("2029-08-27T16:32:42Z")));
assertThat(cert.hasPrivateKey(), equalTo(false));
cert = iterator.next();
assertThat(cert.alias(), equalTo("activedir"));
assertThat(cert.path(), equalTo(jksPath.toString()));
assertThat(cert.format(), equalTo("jks"));
assertThat(cert.serialNumber(), equalTo("580db8ad52bb168a4080e1df122a3f56"));
assertThat(cert.subjectDn(), equalTo("CN=ad-ELASTICSEARCHAD-CA, DC=ad, DC=test, DC=elasticsearch, DC=com"));
assertThat(cert.expiry(), equalTo(DateTime.parse("2029-08-27T16:32:42Z")));
assertThat(cert.hasPrivateKey(), equalTo(false));
cert = iterator.next();
assertThat(cert.alias(), equalTo("openldap"));
assertThat(cert.path(), equalTo(jksPath.toString()));
assertThat(cert.format(), equalTo("jks"));
assertThat(cert.serialNumber(), equalTo("d3850b2b1995ad5f"));
assertThat(cert.subjectDn(), equalTo("CN=OpenLDAP, OU=Elasticsearch, O=Elastic, L=Mountain View, ST=CA, C=US"));
assertThat(cert.expiry(), equalTo(DateTime.parse("2027-07-23T16:41:14Z")));
assertThat(cert.hasPrivateKey(), equalTo(false));
cert = iterator.next();
assertThat(cert.alias(), equalTo("testclient"));
assertThat(cert.path(), equalTo(jksPath.toString()));
assertThat(cert.format(), equalTo("jks"));
assertThat(cert.serialNumber(), equalTo("b9d497f2924bbe29"));
assertThat(cert.subjectDn(), equalTo("CN=Elasticsearch Test Client, OU=elasticsearch, O=org"));
assertThat(cert.expiry(), equalTo(DateTime.parse("2019-09-22T18:52:55Z")));
assertThat(cert.hasPrivateKey(), equalTo(false));
cert = iterator.next();
assertThat(cert.alias(), equalTo("testnode"));
assertThat(cert.path(), equalTo(jksPath.toString()));
assertThat(cert.format(), equalTo("jks"));
assertThat(cert.serialNumber(), equalTo("b8b96c37e332cccb"));
assertThat(cert.subjectDn(), equalTo("CN=Elasticsearch Test Node, OU=elasticsearch, O=org"));
assertThat(cert.expiry(), equalTo(DateTime.parse("2019-09-22T18:52:57Z")));
assertThat(cert.hasPrivateKey(), equalTo(true));
cert = iterator.next();
assertThat(cert.alias(), equalTo("testnode"));
assertThat(cert.path(), equalTo(p12Path.toString()));
assertThat(cert.format(), equalTo("PKCS12"));
assertThat(cert.serialNumber(), equalTo("b8b96c37e332cccb"));
assertThat(cert.subjectDn(), equalTo("CN=Elasticsearch Test Node, OU=elasticsearch, O=org"));
assertThat(cert.expiry(), equalTo(DateTime.parse("2019-09-22T18:52:57Z")));
assertThat(cert.hasPrivateKey(), equalTo(true));
cert = iterator.next();
assertThat(cert.alias(), equalTo("testnode-client-profile"));
assertThat(cert.path(), equalTo(jksPath.toString()));
assertThat(cert.format(), equalTo("jks"));
assertThat(cert.serialNumber(), equalTo("c0ea4216e8ff0fd8"));
assertThat(cert.subjectDn(), equalTo("CN=testnode-client-profile"));
assertThat(cert.expiry(), equalTo(DateTime.parse("2019-09-22T18:52:56Z")));
assertThat(cert.hasPrivateKey(), equalTo(false));
assertFalse(iterator.hasNext());
}
@Network
public void testThatSSLContextWithoutSettingsWorks() throws Exception {
SSLService sslService = new SSLService(Settings.EMPTY, env);

View File

@ -0,0 +1,49 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.ssl.cert;
import javax.security.auth.x500.X500Principal;
import java.io.IOException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.elasticsearch.common.io.stream.BytesStreamOutput;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.ssl.CertUtils;
import static org.hamcrest.Matchers.equalTo;
public class CertificateInfoTests extends ESTestCase {
public void testSerialization() throws Exception {
final X500Principal principal = new X500Principal("CN=foo");
final X509Certificate certificate = CertUtils.generateSignedCertificate(principal, new GeneralNames(new GeneralName[0]),
getKeyPair(), null, null, 90);
final CertificateInfo cert1 = new CertificateInfo("/path/to/cert.jks", "jks", "key", true, certificate);
final CertificateInfo cert2 = serializeAndDeserialize(cert1);
final CertificateInfo cert3 = serializeAndDeserialize(cert2);
assertThat(cert1, equalTo(cert2));
assertThat(cert1, equalTo(cert3));
assertThat(cert2, equalTo(cert3));
}
private CertificateInfo serializeAndDeserialize(CertificateInfo cert1) throws IOException {
BytesStreamOutput output = new BytesStreamOutput();
cert1.writeTo(output);
return new CertificateInfo(output.bytes().streamInput());
}
private KeyPair getKeyPair() throws NoSuchAlgorithmException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
return keyPairGenerator.generateKeyPair();
}
}

View File

@ -0,0 +1,13 @@
{
"xpack.ssl.certificates": {
"documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-ssl.html",
"methods": [ "GET" ],
"url": {
"path": "/_xpack/ssl/certificates",
"paths": [ "/_xpack/ssl/certificates" ],
"parts": {},
"params": {}
},
"body": null
}
}

View File

@ -0,0 +1,10 @@
---
"Test get SSL certificates":
- do:
xpack.ssl.certificates: {}
- length: { $body: 1 }
- match: { $body.0.path: "test-node.jks" }
- match: { $body.0.format: "jks" }
- match: { $body.0.alias: "test-node" }
- match: { $body.0.has_private_key: true }