From 15d1b904a1a053c3d26072c604fa2ff0026bc87e Mon Sep 17 00:00:00 2001
From: Hendrik Muhs
Date: Wed, 16 Jan 2019 08:10:13 +0100
Subject: [PATCH 01/16] =?UTF-8?q?[ML]=20log=20minimum=20diskspace=20settin?=
=?UTF-8?q?g=20if=20forecast=20fails=20due=20to=20insufficient=20d?=
=?UTF-8?q?=E2=80=A6=20(#37486)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
log minimum disk space setting if forecast fails due to insufficient disk space
---
.../xpack/ml/action/TransportForecastJobAction.java | 9 ++++++++-
.../job/process/autodetect/AutodetectProcessManager.java | 4 ++++
.../xpack/ml/process/NativeStorageProvider.java | 4 ++++
3 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportForecastJobAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportForecastJobAction.java
index 6bcadf1ddb06f..3a26f9f863cdb 100644
--- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportForecastJobAction.java
+++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportForecastJobAction.java
@@ -97,8 +97,15 @@ private void getForecastRequestStats(String jobId, String forecastId, ActionList
} else if (forecastRequestStats.getStatus() == ForecastRequestStats.ForecastRequestStatus.FAILED) {
List messages = forecastRequestStats.getMessages();
if (messages.size() > 0) {
+ String message = messages.get(0);
+
+ // special case: if forecast failed due to insufficient disk space, log the setting
+ if (message.contains("disk space is insufficient")) {
+ message += " Minimum disk space required: [" + processManager.getMinLocalStorageAvailable() + "]";
+ }
+
listener.onFailure(ExceptionsHelper.badRequestException("Cannot run forecast: "
- + messages.get(0)));
+ + message));
} else {
// paranoia case, it should not be possible to have an empty message list
listener.onFailure(
diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/autodetect/AutodetectProcessManager.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/autodetect/AutodetectProcessManager.java
index ef03b4f9e7160..fb6e9d46f4064 100644
--- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/autodetect/AutodetectProcessManager.java
+++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/autodetect/AutodetectProcessManager.java
@@ -749,6 +749,10 @@ ExecutorService createAutodetectExecutorService(ExecutorService executorService)
return autoDetectWorkerExecutor;
}
+ public ByteSizeValue getMinLocalStorageAvailable() {
+ return nativeStorageProvider.getMinLocalStorageAvailable();
+ }
+
/*
* The autodetect native process can only handle a single operation at a time. In order to guarantee that, all
* operations are initially added to a queue and a worker thread from ml autodetect threadpool will process each
diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/process/NativeStorageProvider.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/process/NativeStorageProvider.java
index 1f9aee7bbeea3..9f366ab11312e 100644
--- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/process/NativeStorageProvider.java
+++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/process/NativeStorageProvider.java
@@ -111,6 +111,10 @@ public void cleanupLocalTmpStorage(Path path) throws IOException {
}
}
+ public ByteSizeValue getMinLocalStorageAvailable() {
+ return minLocalStorageAvailable;
+ }
+
long getUsableSpace(Path path) throws IOException {
long freeSpaceInBytes = Environment.getFileStore(path).getUsableSpace();
From 5e94f384c41fecc94c412c3162863c93895dfe4c Mon Sep 17 00:00:00 2001
From: Przemyslaw Gomulka
Date: Wed, 16 Jan 2019 09:05:30 +0100
Subject: [PATCH 02/16] Remove the use of AbstracLifecycleComponent constructor
#37488 (#37488)
The AbstracLifecycleComponent used to extend AbstractComponent, so it had to pass settings to the constractor of its supper class.
It no longer extends the AbstractComponent so there is no need for this constructor
There is also no need for AbstracLifecycleComponent subclasses to have Settings in their constructors if they were only passing it over to super constructor.
This is part 1. which will be backported to 6.x with a migration guide/deprecation log.
part 2 will have this constructor removed in 7
relates #35560
relates #34488
---
.../azure/classic/management/AzureComputeServiceImpl.java | 1 -
.../org/elasticsearch/cloud/gce/GceMetadataService.java | 1 -
.../org/elasticsearch/cluster/NodeConnectionsService.java | 1 -
.../elasticsearch/cluster/coordination/Coordinator.java | 1 -
.../cluster/routing/DelayedAllocationService.java | 4 +---
.../org/elasticsearch/cluster/routing/RoutingService.java | 4 +---
.../cluster/service/ClusterApplierService.java | 1 -
.../org/elasticsearch/cluster/service/ClusterService.java | 1 -
.../org/elasticsearch/cluster/service/MasterService.java | 1 -
.../common/component/AbstractLifecycleComponent.java | 3 +++
.../discovery/UnicastConfiguredHostsResolver.java | 1 -
.../discovery/single/SingleNodeDiscovery.java | 1 -
.../org/elasticsearch/discovery/zen/ZenDiscovery.java | 1 -
.../java/org/elasticsearch/gateway/GatewayService.java | 1 -
.../elasticsearch/http/AbstractHttpServerTransport.java | 1 -
.../java/org/elasticsearch/indices/IndicesService.java | 1 -
.../indices/breaker/CircuitBreakerService.java | 4 +---
.../indices/breaker/HierarchyCircuitBreakerService.java | 2 +-
.../indices/breaker/NoneCircuitBreakerService.java | 3 +--
.../indices/cluster/IndicesClusterStateService.java | 1 -
.../java/org/elasticsearch/monitor/MonitorService.java | 1 -
.../elasticsearch/monitor/jvm/JvmGcMonitorService.java | 1 -
.../repositories/blobstore/BlobStoreRepository.java | 1 -
.../main/java/org/elasticsearch/search/SearchService.java | 1 -
.../elasticsearch/snapshots/SnapshotShardsService.java | 1 -
.../org/elasticsearch/snapshots/SnapshotsService.java | 1 -
.../java/org/elasticsearch/transport/TcpTransport.java | 1 -
.../org/elasticsearch/transport/TransportService.java | 1 -
.../org/elasticsearch/watcher/ResourceWatcherService.java | 1 -
.../cluster/routing/DelayedAllocationServiceTests.java | 8 ++++----
.../cluster/routing/RoutingServiceTests.java | 3 +--
.../elasticsearch/common/network/NetworkModuleTests.java | 3 ---
.../org/elasticsearch/index/shard/IndexShardTests.java | 1 -
.../java/org/elasticsearch/rest/RestControllerTests.java | 1 -
.../elasticsearch/snapshots/SnapshotsServiceTests.java | 2 +-
.../java/org/elasticsearch/test/MockHttpTransport.java | 5 -----
.../src/main/java/org/elasticsearch/xpack/ccr/Ccr.java | 2 +-
.../org/elasticsearch/xpack/ccr/CcrRepositoryManager.java | 1 -
.../elasticsearch/xpack/ccr/repository/CcrRepository.java | 1 -
.../xpack/ccr/repository/CcrRestoreSourceService.java | 4 ----
.../ccr/repository/CcrRestoreSourceServiceTests.java | 2 +-
.../java/org/elasticsearch/license/LicenseService.java | 1 -
.../elasticsearch/xpack/monitoring/MonitoringService.java | 1 -
.../xpack/monitoring/cleaner/CleanerService.java | 1 -
.../xpack/monitoring/exporter/Exporters.java | 1 -
.../xpack/monitoring/MonitoringServiceTests.java | 1 -
46 files changed, 16 insertions(+), 65 deletions(-)
diff --git a/plugins/discovery-azure-classic/src/main/java/org/elasticsearch/cloud/azure/classic/management/AzureComputeServiceImpl.java b/plugins/discovery-azure-classic/src/main/java/org/elasticsearch/cloud/azure/classic/management/AzureComputeServiceImpl.java
index 710dfa7e71df7..b8e885b947006 100644
--- a/plugins/discovery-azure-classic/src/main/java/org/elasticsearch/cloud/azure/classic/management/AzureComputeServiceImpl.java
+++ b/plugins/discovery-azure-classic/src/main/java/org/elasticsearch/cloud/azure/classic/management/AzureComputeServiceImpl.java
@@ -52,7 +52,6 @@ public class AzureComputeServiceImpl extends AbstractLifecycleComponent
private final String serviceName;
public AzureComputeServiceImpl(Settings settings) {
- super(settings);
String subscriptionId = getRequiredSetting(settings, Management.SUBSCRIPTION_ID_SETTING);
serviceName = getRequiredSetting(settings, Management.SERVICE_NAME_SETTING);
diff --git a/plugins/discovery-gce/src/main/java/org/elasticsearch/cloud/gce/GceMetadataService.java b/plugins/discovery-gce/src/main/java/org/elasticsearch/cloud/gce/GceMetadataService.java
index 248d9a0447915..1fae7e63854c1 100644
--- a/plugins/discovery-gce/src/main/java/org/elasticsearch/cloud/gce/GceMetadataService.java
+++ b/plugins/discovery-gce/src/main/java/org/elasticsearch/cloud/gce/GceMetadataService.java
@@ -53,7 +53,6 @@ public class GceMetadataService extends AbstractLifecycleComponent {
private HttpTransport gceHttpTransport;
public GceMetadataService(Settings settings) {
- super(settings);
this.settings = settings;
}
diff --git a/server/src/main/java/org/elasticsearch/cluster/NodeConnectionsService.java b/server/src/main/java/org/elasticsearch/cluster/NodeConnectionsService.java
index 8010765854016..90526aaa9fd21 100644
--- a/server/src/main/java/org/elasticsearch/cluster/NodeConnectionsService.java
+++ b/server/src/main/java/org/elasticsearch/cluster/NodeConnectionsService.java
@@ -75,7 +75,6 @@ public class NodeConnectionsService extends AbstractLifecycleComponent {
@Inject
public NodeConnectionsService(Settings settings, ThreadPool threadPool, TransportService transportService) {
- super(settings);
this.threadPool = threadPool;
this.transportService = transportService;
this.reconnectInterval = NodeConnectionsService.CLUSTER_NODE_RECONNECT_INTERVAL_SETTING.get(settings);
diff --git a/server/src/main/java/org/elasticsearch/cluster/coordination/Coordinator.java b/server/src/main/java/org/elasticsearch/cluster/coordination/Coordinator.java
index 805eef9a89c06..72fe2e081de74 100644
--- a/server/src/main/java/org/elasticsearch/cluster/coordination/Coordinator.java
+++ b/server/src/main/java/org/elasticsearch/cluster/coordination/Coordinator.java
@@ -145,7 +145,6 @@ public Coordinator(String nodeName, Settings settings, ClusterSettings clusterSe
NamedWriteableRegistry namedWriteableRegistry, AllocationService allocationService, MasterService masterService,
Supplier persistedStateSupplier, UnicastHostsProvider unicastHostsProvider,
ClusterApplier clusterApplier, Collection> onJoinValidators, Random random) {
- super(settings);
this.settings = settings;
this.transportService = transportService;
this.masterService = masterService;
diff --git a/server/src/main/java/org/elasticsearch/cluster/routing/DelayedAllocationService.java b/server/src/main/java/org/elasticsearch/cluster/routing/DelayedAllocationService.java
index b8fb432f311ce..9349de94a66e2 100644
--- a/server/src/main/java/org/elasticsearch/cluster/routing/DelayedAllocationService.java
+++ b/server/src/main/java/org/elasticsearch/cluster/routing/DelayedAllocationService.java
@@ -30,7 +30,6 @@
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.component.AbstractLifecycleComponent;
import org.elasticsearch.common.inject.Inject;
-import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.util.concurrent.AbstractRunnable;
import org.elasticsearch.common.util.concurrent.FutureUtils;
@@ -130,9 +129,8 @@ public void onFailure(String source, Exception e) {
}
@Inject
- public DelayedAllocationService(Settings settings, ThreadPool threadPool, ClusterService clusterService,
+ public DelayedAllocationService(ThreadPool threadPool, ClusterService clusterService,
AllocationService allocationService) {
- super(settings);
this.threadPool = threadPool;
this.clusterService = clusterService;
this.allocationService = allocationService;
diff --git a/server/src/main/java/org/elasticsearch/cluster/routing/RoutingService.java b/server/src/main/java/org/elasticsearch/cluster/routing/RoutingService.java
index 5d711cabce763..57f7a313942bb 100644
--- a/server/src/main/java/org/elasticsearch/cluster/routing/RoutingService.java
+++ b/server/src/main/java/org/elasticsearch/cluster/routing/RoutingService.java
@@ -30,7 +30,6 @@
import org.elasticsearch.common.Priority;
import org.elasticsearch.common.component.AbstractLifecycleComponent;
import org.elasticsearch.common.inject.Inject;
-import org.elasticsearch.common.settings.Settings;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -57,8 +56,7 @@ public class RoutingService extends AbstractLifecycleComponent {
private AtomicBoolean rerouting = new AtomicBoolean();
@Inject
- public RoutingService(Settings settings, ClusterService clusterService, AllocationService allocationService) {
- super(settings);
+ public RoutingService(ClusterService clusterService, AllocationService allocationService) {
this.clusterService = clusterService;
this.allocationService = allocationService;
}
diff --git a/server/src/main/java/org/elasticsearch/cluster/service/ClusterApplierService.java b/server/src/main/java/org/elasticsearch/cluster/service/ClusterApplierService.java
index 5bd441419d136..fa3d4997efb4b 100644
--- a/server/src/main/java/org/elasticsearch/cluster/service/ClusterApplierService.java
+++ b/server/src/main/java/org/elasticsearch/cluster/service/ClusterApplierService.java
@@ -102,7 +102,6 @@ public class ClusterApplierService extends AbstractLifecycleComponent implements
private NodeConnectionsService nodeConnectionsService;
public ClusterApplierService(String nodeName, Settings settings, ClusterSettings clusterSettings, ThreadPool threadPool) {
- super(settings);
this.clusterSettings = clusterSettings;
this.threadPool = threadPool;
this.state = new AtomicReference<>();
diff --git a/server/src/main/java/org/elasticsearch/cluster/service/ClusterService.java b/server/src/main/java/org/elasticsearch/cluster/service/ClusterService.java
index 12d45c4fb88f4..f66ca0738954b 100644
--- a/server/src/main/java/org/elasticsearch/cluster/service/ClusterService.java
+++ b/server/src/main/java/org/elasticsearch/cluster/service/ClusterService.java
@@ -78,7 +78,6 @@ public ClusterService(Settings settings, ClusterSettings clusterSettings, Thread
public ClusterService(Settings settings, ClusterSettings clusterSettings, MasterService masterService,
ClusterApplierService clusterApplierService) {
- super(settings);
this.settings = settings;
this.nodeName = Node.NODE_NAME_SETTING.get(settings);
this.masterService = masterService;
diff --git a/server/src/main/java/org/elasticsearch/cluster/service/MasterService.java b/server/src/main/java/org/elasticsearch/cluster/service/MasterService.java
index 472ffcd73effd..50cf4ffe9de8a 100644
--- a/server/src/main/java/org/elasticsearch/cluster/service/MasterService.java
+++ b/server/src/main/java/org/elasticsearch/cluster/service/MasterService.java
@@ -87,7 +87,6 @@ public class MasterService extends AbstractLifecycleComponent {
private volatile Batcher taskBatcher;
public MasterService(String nodeName, Settings settings, ThreadPool threadPool) {
- super(settings);
this.nodeName = nodeName;
// TODO: introduce a dedicated setting for master service
this.slowTaskLoggingThreshold = CLUSTER_SERVICE_SLOW_TASK_LOGGING_THRESHOLD_SETTING.get(settings);
diff --git a/server/src/main/java/org/elasticsearch/common/component/AbstractLifecycleComponent.java b/server/src/main/java/org/elasticsearch/common/component/AbstractLifecycleComponent.java
index f7dc6c55506f1..2caaa43fbcd05 100644
--- a/server/src/main/java/org/elasticsearch/common/component/AbstractLifecycleComponent.java
+++ b/server/src/main/java/org/elasticsearch/common/component/AbstractLifecycleComponent.java
@@ -34,6 +34,9 @@ public abstract class AbstractLifecycleComponent implements LifecycleComponent {
private final List listeners = new CopyOnWriteArrayList<>();
+ protected AbstractLifecycleComponent() {}
+
+ @Deprecated
protected AbstractLifecycleComponent(Settings settings) {
// TODO drop settings from ctor
}
diff --git a/server/src/main/java/org/elasticsearch/discovery/UnicastConfiguredHostsResolver.java b/server/src/main/java/org/elasticsearch/discovery/UnicastConfiguredHostsResolver.java
index 5200d9d36f60e..fcc716a7785a4 100644
--- a/server/src/main/java/org/elasticsearch/discovery/UnicastConfiguredHostsResolver.java
+++ b/server/src/main/java/org/elasticsearch/discovery/UnicastConfiguredHostsResolver.java
@@ -56,7 +56,6 @@ public class UnicastConfiguredHostsResolver extends AbstractLifecycleComponent i
public UnicastConfiguredHostsResolver(String nodeName, Settings settings, TransportService transportService,
UnicastHostsProvider hostsProvider) {
- super(settings);
this.settings = settings;
this.nodeName = nodeName;
this.transportService = transportService;
diff --git a/server/src/main/java/org/elasticsearch/discovery/single/SingleNodeDiscovery.java b/server/src/main/java/org/elasticsearch/discovery/single/SingleNodeDiscovery.java
index 238f72f72f464..2a415a74cd0cc 100644
--- a/server/src/main/java/org/elasticsearch/discovery/single/SingleNodeDiscovery.java
+++ b/server/src/main/java/org/elasticsearch/discovery/single/SingleNodeDiscovery.java
@@ -59,7 +59,6 @@ public class SingleNodeDiscovery extends AbstractLifecycleComponent implements D
public SingleNodeDiscovery(final Settings settings, final TransportService transportService,
final MasterService masterService, final ClusterApplier clusterApplier,
final GatewayMetaState gatewayMetaState) {
- super(Objects.requireNonNull(settings));
this.clusterName = ClusterName.CLUSTER_NAME_SETTING.get(settings);
this.transportService = Objects.requireNonNull(transportService);
masterService.setClusterStateSupplier(() -> clusterState);
diff --git a/server/src/main/java/org/elasticsearch/discovery/zen/ZenDiscovery.java b/server/src/main/java/org/elasticsearch/discovery/zen/ZenDiscovery.java
index 05d0bfa27188a..0ee69bbfeec93 100644
--- a/server/src/main/java/org/elasticsearch/discovery/zen/ZenDiscovery.java
+++ b/server/src/main/java/org/elasticsearch/discovery/zen/ZenDiscovery.java
@@ -161,7 +161,6 @@ public ZenDiscovery(Settings settings, ThreadPool threadPool, TransportService t
NamedWriteableRegistry namedWriteableRegistry, MasterService masterService, ClusterApplier clusterApplier,
ClusterSettings clusterSettings, UnicastHostsProvider hostsProvider, AllocationService allocationService,
Collection> onJoinValidators, GatewayMetaState gatewayMetaState) {
- super(settings);
this.onJoinValidators = JoinTaskExecutor.addBuiltInJoinValidators(onJoinValidators);
this.masterService = masterService;
this.clusterApplier = clusterApplier;
diff --git a/server/src/main/java/org/elasticsearch/gateway/GatewayService.java b/server/src/main/java/org/elasticsearch/gateway/GatewayService.java
index 1574874634cf5..c71ae40235056 100644
--- a/server/src/main/java/org/elasticsearch/gateway/GatewayService.java
+++ b/server/src/main/java/org/elasticsearch/gateway/GatewayService.java
@@ -95,7 +95,6 @@ public GatewayService(final Settings settings, final AllocationService allocatio
final ThreadPool threadPool,
final TransportNodesListGatewayMetaState listGatewayMetaState,
final IndicesService indicesService, final Discovery discovery) {
- super(settings);
this.allocationService = allocationService;
this.clusterService = clusterService;
this.threadPool = threadPool;
diff --git a/server/src/main/java/org/elasticsearch/http/AbstractHttpServerTransport.java b/server/src/main/java/org/elasticsearch/http/AbstractHttpServerTransport.java
index 4b53ac902d96c..92f305cb67a1f 100644
--- a/server/src/main/java/org/elasticsearch/http/AbstractHttpServerTransport.java
+++ b/server/src/main/java/org/elasticsearch/http/AbstractHttpServerTransport.java
@@ -87,7 +87,6 @@ public abstract class AbstractHttpServerTransport extends AbstractLifecycleCompo
protected AbstractHttpServerTransport(Settings settings, NetworkService networkService, BigArrays bigArrays, ThreadPool threadPool,
NamedXContentRegistry xContentRegistry, Dispatcher dispatcher) {
- super(settings);
this.settings = settings;
this.networkService = networkService;
this.bigArrays = bigArrays;
diff --git a/server/src/main/java/org/elasticsearch/indices/IndicesService.java b/server/src/main/java/org/elasticsearch/indices/IndicesService.java
index e3c1a77b84300..b881ba73a28e6 100644
--- a/server/src/main/java/org/elasticsearch/indices/IndicesService.java
+++ b/server/src/main/java/org/elasticsearch/indices/IndicesService.java
@@ -205,7 +205,6 @@ public IndicesService(Settings settings, PluginsService pluginsService, NodeEnvi
ScriptService scriptService, Client client, MetaStateService metaStateService,
Collection>> engineFactoryProviders,
Map> indexStoreFactories) {
- super(settings);
this.settings = settings;
this.threadPool = threadPool;
this.pluginsService = pluginsService;
diff --git a/server/src/main/java/org/elasticsearch/indices/breaker/CircuitBreakerService.java b/server/src/main/java/org/elasticsearch/indices/breaker/CircuitBreakerService.java
index cc6b7d5ef85bc..90a814b29b3c5 100644
--- a/server/src/main/java/org/elasticsearch/indices/breaker/CircuitBreakerService.java
+++ b/server/src/main/java/org/elasticsearch/indices/breaker/CircuitBreakerService.java
@@ -23,7 +23,6 @@
import org.apache.logging.log4j.Logger;
import org.elasticsearch.common.breaker.CircuitBreaker;
import org.elasticsearch.common.component.AbstractLifecycleComponent;
-import org.elasticsearch.common.settings.Settings;
/**
* Interface for Circuit Breaker services, which provide breakers to classes
@@ -32,8 +31,7 @@
public abstract class CircuitBreakerService extends AbstractLifecycleComponent {
private static final Logger logger = LogManager.getLogger(CircuitBreakerService.class);
- protected CircuitBreakerService(Settings settings) {
- super(settings);
+ protected CircuitBreakerService() {
}
/**
diff --git a/server/src/main/java/org/elasticsearch/indices/breaker/HierarchyCircuitBreakerService.java b/server/src/main/java/org/elasticsearch/indices/breaker/HierarchyCircuitBreakerService.java
index 31ee4b8286918..48045e219252a 100644
--- a/server/src/main/java/org/elasticsearch/indices/breaker/HierarchyCircuitBreakerService.java
+++ b/server/src/main/java/org/elasticsearch/indices/breaker/HierarchyCircuitBreakerService.java
@@ -104,7 +104,7 @@ public class HierarchyCircuitBreakerService extends CircuitBreakerService {
private final AtomicLong parentTripCount = new AtomicLong(0);
public HierarchyCircuitBreakerService(Settings settings, ClusterSettings clusterSettings) {
- super(settings);
+ super();
this.fielddataSettings = new BreakerSettings(CircuitBreaker.FIELDDATA,
FIELDDATA_CIRCUIT_BREAKER_LIMIT_SETTING.get(settings).getBytes(),
FIELDDATA_CIRCUIT_BREAKER_OVERHEAD_SETTING.get(settings),
diff --git a/server/src/main/java/org/elasticsearch/indices/breaker/NoneCircuitBreakerService.java b/server/src/main/java/org/elasticsearch/indices/breaker/NoneCircuitBreakerService.java
index c686412bc29b2..69de18e380b63 100644
--- a/server/src/main/java/org/elasticsearch/indices/breaker/NoneCircuitBreakerService.java
+++ b/server/src/main/java/org/elasticsearch/indices/breaker/NoneCircuitBreakerService.java
@@ -21,7 +21,6 @@
import org.elasticsearch.common.breaker.CircuitBreaker;
import org.elasticsearch.common.breaker.NoopCircuitBreaker;
-import org.elasticsearch.common.settings.Settings;
/**
* Class that returns a breaker that never breaks
@@ -31,7 +30,7 @@ public class NoneCircuitBreakerService extends CircuitBreakerService {
private final CircuitBreaker breaker = new NoopCircuitBreaker(CircuitBreaker.FIELDDATA);
public NoneCircuitBreakerService() {
- super(Settings.EMPTY);
+ super();
}
@Override
diff --git a/server/src/main/java/org/elasticsearch/indices/cluster/IndicesClusterStateService.java b/server/src/main/java/org/elasticsearch/indices/cluster/IndicesClusterStateService.java
index 41050d7d47486..c8afe92be8d37 100644
--- a/server/src/main/java/org/elasticsearch/indices/cluster/IndicesClusterStateService.java
+++ b/server/src/main/java/org/elasticsearch/indices/cluster/IndicesClusterStateService.java
@@ -171,7 +171,6 @@ public IndicesClusterStateService(
final SnapshotShardsService snapshotShardsService,
final PrimaryReplicaSyncer primaryReplicaSyncer,
final Consumer globalCheckpointSyncer) {
- super(settings);
this.settings = settings;
this.buildInIndexListener =
Arrays.asList(
diff --git a/server/src/main/java/org/elasticsearch/monitor/MonitorService.java b/server/src/main/java/org/elasticsearch/monitor/MonitorService.java
index 9467bf29923d8..df91487332435 100644
--- a/server/src/main/java/org/elasticsearch/monitor/MonitorService.java
+++ b/server/src/main/java/org/elasticsearch/monitor/MonitorService.java
@@ -42,7 +42,6 @@ public class MonitorService extends AbstractLifecycleComponent {
public MonitorService(Settings settings, NodeEnvironment nodeEnvironment, ThreadPool threadPool,
ClusterInfoService clusterInfoService) throws IOException {
- super(settings);
this.jvmGcMonitorService = new JvmGcMonitorService(settings, threadPool);
this.osService = new OsService(settings);
this.processService = new ProcessService(settings);
diff --git a/server/src/main/java/org/elasticsearch/monitor/jvm/JvmGcMonitorService.java b/server/src/main/java/org/elasticsearch/monitor/jvm/JvmGcMonitorService.java
index 94c66c79d2ee7..70de50710c999 100644
--- a/server/src/main/java/org/elasticsearch/monitor/jvm/JvmGcMonitorService.java
+++ b/server/src/main/java/org/elasticsearch/monitor/jvm/JvmGcMonitorService.java
@@ -107,7 +107,6 @@ public String toString() {
}
public JvmGcMonitorService(Settings settings, ThreadPool threadPool) {
- super(settings);
this.threadPool = threadPool;
this.enabled = ENABLED_SETTING.get(settings);
diff --git a/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java b/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java
index 9f2297b48775b..c8cdf0d4e0308 100644
--- a/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java
+++ b/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java
@@ -228,7 +228,6 @@ public abstract class BlobStoreRepository extends AbstractLifecycleComponent imp
* @param settings Settings for the node this repository object is created on
*/
protected BlobStoreRepository(RepositoryMetaData metadata, Settings settings, NamedXContentRegistry namedXContentRegistry) {
- super(settings);
this.settings = settings;
this.metadata = metadata;
this.namedXContentRegistry = namedXContentRegistry;
diff --git a/server/src/main/java/org/elasticsearch/search/SearchService.java b/server/src/main/java/org/elasticsearch/search/SearchService.java
index f6e91c03af6e1..4fee684276288 100644
--- a/server/src/main/java/org/elasticsearch/search/SearchService.java
+++ b/server/src/main/java/org/elasticsearch/search/SearchService.java
@@ -193,7 +193,6 @@ public class SearchService extends AbstractLifecycleComponent implements IndexEv
public SearchService(ClusterService clusterService, IndicesService indicesService,
ThreadPool threadPool, ScriptService scriptService, BigArrays bigArrays, FetchPhase fetchPhase,
ResponseCollectorService responseCollectorService) {
- super(clusterService.getSettings());
Settings settings = clusterService.getSettings();
this.threadPool = threadPool;
this.clusterService = clusterService;
diff --git a/server/src/main/java/org/elasticsearch/snapshots/SnapshotShardsService.java b/server/src/main/java/org/elasticsearch/snapshots/SnapshotShardsService.java
index c0e196f1f4eb3..6b7b506114361 100644
--- a/server/src/main/java/org/elasticsearch/snapshots/SnapshotShardsService.java
+++ b/server/src/main/java/org/elasticsearch/snapshots/SnapshotShardsService.java
@@ -119,7 +119,6 @@ public class SnapshotShardsService extends AbstractLifecycleComponent implements
public SnapshotShardsService(Settings settings, ClusterService clusterService, SnapshotsService snapshotsService,
ThreadPool threadPool, TransportService transportService, IndicesService indicesService,
ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver) {
- super(settings);
this.indicesService = indicesService;
this.snapshotsService = snapshotsService;
this.transportService = transportService;
diff --git a/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java b/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java
index 65802377be032..e76dde274557c 100644
--- a/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java
+++ b/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java
@@ -124,7 +124,6 @@ public class SnapshotsService extends AbstractLifecycleComponent implements Clus
@Inject
public SnapshotsService(Settings settings, ClusterService clusterService, IndexNameExpressionResolver indexNameExpressionResolver,
RepositoriesService repositoriesService, ThreadPool threadPool) {
- super(settings);
this.clusterService = clusterService;
this.indexNameExpressionResolver = indexNameExpressionResolver;
this.repositoriesService = repositoriesService;
diff --git a/server/src/main/java/org/elasticsearch/transport/TcpTransport.java b/server/src/main/java/org/elasticsearch/transport/TcpTransport.java
index a10bad9e88b69..dd6a21acc8fbd 100644
--- a/server/src/main/java/org/elasticsearch/transport/TcpTransport.java
+++ b/server/src/main/java/org/elasticsearch/transport/TcpTransport.java
@@ -156,7 +156,6 @@ public abstract class TcpTransport extends AbstractLifecycleComponent implements
public TcpTransport(String transportName, Settings settings, Version version, ThreadPool threadPool,
PageCacheRecycler pageCacheRecycler, CircuitBreakerService circuitBreakerService,
NamedWriteableRegistry namedWriteableRegistry, NetworkService networkService) {
- super(settings);
this.settings = settings;
this.profileSettings = getProfileSettings(settings);
this.version = version;
diff --git a/server/src/main/java/org/elasticsearch/transport/TransportService.java b/server/src/main/java/org/elasticsearch/transport/TransportService.java
index b5e97ac3ae6cb..57aaba671518e 100644
--- a/server/src/main/java/org/elasticsearch/transport/TransportService.java
+++ b/server/src/main/java/org/elasticsearch/transport/TransportService.java
@@ -155,7 +155,6 @@ public TransportService(Settings settings, Transport transport, ThreadPool threa
public TransportService(Settings settings, Transport transport, ThreadPool threadPool, TransportInterceptor transportInterceptor,
Function localNodeFactory, @Nullable ClusterSettings clusterSettings,
Set taskHeaders, ConnectionManager connectionManager) {
- super(settings);
// The only time we do not want to validate node connections is when this is a transport client using the simple node sampler
this.validateConnections = TransportClient.CLIENT_TYPE.equals(settings.get(Client.CLIENT_TYPE_SETTING_S.getKey())) == false ||
TransportClient.CLIENT_TRANSPORT_SNIFF.get(settings);
diff --git a/server/src/main/java/org/elasticsearch/watcher/ResourceWatcherService.java b/server/src/main/java/org/elasticsearch/watcher/ResourceWatcherService.java
index 3edab68e1580f..4216bd817b68c 100644
--- a/server/src/main/java/org/elasticsearch/watcher/ResourceWatcherService.java
+++ b/server/src/main/java/org/elasticsearch/watcher/ResourceWatcherService.java
@@ -90,7 +90,6 @@ public enum Frequency {
@Inject
public ResourceWatcherService(Settings settings, ThreadPool threadPool) {
- super(settings);
this.enabled = ENABLED.get(settings);
this.threadPool = threadPool;
diff --git a/server/src/test/java/org/elasticsearch/cluster/routing/DelayedAllocationServiceTests.java b/server/src/test/java/org/elasticsearch/cluster/routing/DelayedAllocationServiceTests.java
index e38f09e567a39..b168bae87de41 100644
--- a/server/src/test/java/org/elasticsearch/cluster/routing/DelayedAllocationServiceTests.java
+++ b/server/src/test/java/org/elasticsearch/cluster/routing/DelayedAllocationServiceTests.java
@@ -69,7 +69,7 @@ public void createDelayedAllocationService() {
threadPool = new TestThreadPool(getTestName());
clusterService = mock(ClusterService.class);
allocationService = createAllocationService(Settings.EMPTY, new DelayedShardsMockGatewayAllocator());
- delayedAllocationService = new TestDelayAllocationService(Settings.EMPTY, threadPool, clusterService, allocationService);
+ delayedAllocationService = new TestDelayAllocationService(threadPool, clusterService, allocationService);
verify(clusterService).addListener(delayedAllocationService);
}
@@ -464,9 +464,9 @@ public void testDelayedUnassignedScheduleRerouteRescheduledOnShorterDelay() thro
private static class TestDelayAllocationService extends DelayedAllocationService {
private volatile long nanoTimeOverride = -1L;
- TestDelayAllocationService(Settings settings, ThreadPool threadPool, ClusterService clusterService,
- AllocationService allocationService) {
- super(settings, threadPool, clusterService, allocationService);
+ private TestDelayAllocationService(ThreadPool threadPool, ClusterService clusterService,
+ AllocationService allocationService) {
+ super(threadPool, clusterService, allocationService);
}
@Override
diff --git a/server/src/test/java/org/elasticsearch/cluster/routing/RoutingServiceTests.java b/server/src/test/java/org/elasticsearch/cluster/routing/RoutingServiceTests.java
index 09b957069a624..f9d3e0ab95c01 100644
--- a/server/src/test/java/org/elasticsearch/cluster/routing/RoutingServiceTests.java
+++ b/server/src/test/java/org/elasticsearch/cluster/routing/RoutingServiceTests.java
@@ -19,7 +19,6 @@
package org.elasticsearch.cluster.routing;
-import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.cluster.ESAllocationTestCase;
import org.junit.Before;
@@ -47,7 +46,7 @@ private class TestRoutingService extends RoutingService {
private AtomicBoolean rerouted = new AtomicBoolean();
TestRoutingService() {
- super(Settings.EMPTY, null, null);
+ super(null, null);
}
public boolean hasReroutedAndClear() {
diff --git a/server/src/test/java/org/elasticsearch/common/network/NetworkModuleTests.java b/server/src/test/java/org/elasticsearch/common/network/NetworkModuleTests.java
index 0dbc3efe0750e..32cc6785d57a2 100644
--- a/server/src/test/java/org/elasticsearch/common/network/NetworkModuleTests.java
+++ b/server/src/test/java/org/elasticsearch/common/network/NetworkModuleTests.java
@@ -72,9 +72,6 @@ public void tearDown() throws Exception {
}
static class FakeHttpTransport extends AbstractLifecycleComponent implements HttpServerTransport {
- FakeHttpTransport() {
- super(null);
- }
@Override
protected void doStart() {}
@Override
diff --git a/server/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java b/server/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java
index d2a73caa32d3a..0d58239fd4a56 100644
--- a/server/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java
+++ b/server/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java
@@ -3126,7 +3126,6 @@ private abstract static class RestoreOnlyRepository extends AbstractLifecycleCom
private final String indexName;
RestoreOnlyRepository(String indexName) {
- super(Settings.EMPTY);
this.indexName = indexName;
}
diff --git a/server/src/test/java/org/elasticsearch/rest/RestControllerTests.java b/server/src/test/java/org/elasticsearch/rest/RestControllerTests.java
index 29cbb8f48ec9f..c04dc7be026b9 100644
--- a/server/src/test/java/org/elasticsearch/rest/RestControllerTests.java
+++ b/server/src/test/java/org/elasticsearch/rest/RestControllerTests.java
@@ -474,7 +474,6 @@ private static final class TestHttpServerTransport extends AbstractLifecycleComp
HttpServerTransport {
TestHttpServerTransport() {
- super(Settings.EMPTY);
}
@Override
diff --git a/server/src/test/java/org/elasticsearch/snapshots/SnapshotsServiceTests.java b/server/src/test/java/org/elasticsearch/snapshots/SnapshotsServiceTests.java
index 5cd75df22a48a..1531744a13cac 100644
--- a/server/src/test/java/org/elasticsearch/snapshots/SnapshotsServiceTests.java
+++ b/server/src/test/java/org/elasticsearch/snapshots/SnapshotsServiceTests.java
@@ -463,7 +463,7 @@ protected void assertSnapshotOrGenericThread() {
transportService, indicesService, actionFilters, indexNameExpressionResolver);
final ShardStateAction shardStateAction = new ShardStateAction(
clusterService, transportService, allocationService,
- new RoutingService(settings, clusterService, allocationService),
+ new RoutingService(clusterService, allocationService),
deterministicTaskQueue.getThreadPool()
);
indicesClusterStateService = new IndicesClusterStateService(
diff --git a/test/framework/src/main/java/org/elasticsearch/test/MockHttpTransport.java b/test/framework/src/main/java/org/elasticsearch/test/MockHttpTransport.java
index 5955bcead67eb..f9dec4ee1ec5f 100644
--- a/test/framework/src/main/java/org/elasticsearch/test/MockHttpTransport.java
+++ b/test/framework/src/main/java/org/elasticsearch/test/MockHttpTransport.java
@@ -20,7 +20,6 @@
package org.elasticsearch.test;
import org.elasticsearch.common.component.AbstractLifecycleComponent;
-import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.BoundTransportAddress;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.http.HttpInfo;
@@ -46,10 +45,6 @@ public static class TestPlugin extends Plugin {}
private static final HttpInfo DUMMY_HTTP_INFO = new HttpInfo(DUMMY_BOUND_ADDRESS, 0);
private static final HttpStats DUMMY_HTTP_STATS = new HttpStats(0, 0);
- public MockHttpTransport() {
- super(Settings.EMPTY);
- }
-
@Override
protected void doStart() {}
diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/Ccr.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/Ccr.java
index 370d017a4bde7..8bbacac3d8054 100644
--- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/Ccr.java
+++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/Ccr.java
@@ -157,7 +157,7 @@ public Collection createComponents(
return emptyList();
}
- CcrRestoreSourceService restoreSourceService = new CcrRestoreSourceService(settings);
+ CcrRestoreSourceService restoreSourceService = new CcrRestoreSourceService();
this.restoreSourceService.set(restoreSourceService);
return Arrays.asList(
ccrLicenseChecker,
diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/CcrRepositoryManager.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/CcrRepositoryManager.java
index 54403df367809..c1a28b72cf8fe 100644
--- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/CcrRepositoryManager.java
+++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/CcrRepositoryManager.java
@@ -29,7 +29,6 @@ class CcrRepositoryManager extends AbstractLifecycleComponent {
private final RemoteSettingsUpdateListener updateListener;
CcrRepositoryManager(Settings settings, ClusterService clusterService, Client client) {
- super(settings);
this.client = client;
updateListener = new RemoteSettingsUpdateListener(settings);
updateListener.listenForUpdates(clusterService.getClusterSettings());
diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/repository/CcrRepository.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/repository/CcrRepository.java
index 025892f80d834..5c3e0edda6177 100644
--- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/repository/CcrRepository.java
+++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/repository/CcrRepository.java
@@ -84,7 +84,6 @@ public class CcrRepository extends AbstractLifecycleComponent implements Reposit
private final CcrLicenseChecker ccrLicenseChecker;
public CcrRepository(RepositoryMetaData metadata, Client client, CcrLicenseChecker ccrLicenseChecker, Settings settings) {
- super(settings);
this.metadata = metadata;
assert metadata.name().startsWith(NAME_PREFIX) : "CcrRepository metadata.name() must start with: " + NAME_PREFIX;
this.remoteClusterAlias = Strings.split(metadata.name(), NAME_PREFIX)[1];
diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/repository/CcrRestoreSourceService.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/repository/CcrRestoreSourceService.java
index 197d5ddbf38ff..785600dd5f8fc 100644
--- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/repository/CcrRestoreSourceService.java
+++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/repository/CcrRestoreSourceService.java
@@ -48,10 +48,6 @@ public class CcrRestoreSourceService extends AbstractLifecycleComponent implemen
private final CopyOnWriteArrayList> openSessionListeners = new CopyOnWriteArrayList<>();
private final CopyOnWriteArrayList> closeSessionListeners = new CopyOnWriteArrayList<>();
- public CcrRestoreSourceService(Settings settings) {
- super(settings);
- }
-
@Override
public synchronized void afterIndexShardClosed(ShardId shardId, @Nullable IndexShard indexShard, Settings indexSettings) {
if (indexShard != null) {
diff --git a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/repository/CcrRestoreSourceServiceTests.java b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/repository/CcrRestoreSourceServiceTests.java
index efcd93e90fd07..c0b7863edf25a 100644
--- a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/repository/CcrRestoreSourceServiceTests.java
+++ b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/repository/CcrRestoreSourceServiceTests.java
@@ -29,7 +29,7 @@ public class CcrRestoreSourceServiceTests extends IndexShardTestCase {
@Before
public void setUp() throws Exception {
super.setUp();
- restoreSourceService = new CcrRestoreSourceService(Settings.EMPTY);
+ restoreSourceService = new CcrRestoreSourceService();
}
public void testOpenSession() throws IOException {
diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicenseService.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicenseService.java
index dbf11026f4709..68e094511a3e8 100644
--- a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicenseService.java
+++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicenseService.java
@@ -121,7 +121,6 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
public LicenseService(Settings settings, ClusterService clusterService, Clock clock, Environment env,
ResourceWatcherService resourceWatcherService, XPackLicenseState licenseState) {
- super(settings);
this.settings = settings;
this.clusterService = clusterService;
this.clock = clock;
diff --git a/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/MonitoringService.java b/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/MonitoringService.java
index 8759d493c8b97..1aa7ef230a390 100644
--- a/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/MonitoringService.java
+++ b/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/MonitoringService.java
@@ -91,7 +91,6 @@ public class MonitoringService extends AbstractLifecycleComponent {
MonitoringService(Settings settings, ClusterService clusterService, ThreadPool threadPool,
Set collectors, Exporters exporters) {
- super(settings);
this.clusterService = Objects.requireNonNull(clusterService);
this.threadPool = Objects.requireNonNull(threadPool);
this.collectors = Objects.requireNonNull(collectors);
diff --git a/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/cleaner/CleanerService.java b/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/cleaner/CleanerService.java
index f9f41974402b1..e6af45ee2bca0 100644
--- a/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/cleaner/CleanerService.java
+++ b/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/cleaner/CleanerService.java
@@ -40,7 +40,6 @@ public class CleanerService extends AbstractLifecycleComponent {
CleanerService(Settings settings, ClusterSettings clusterSettings, XPackLicenseState licenseState, ThreadPool threadPool,
ExecutionScheduler executionScheduler) {
- super(settings);
this.licenseState = licenseState;
this.threadPool = threadPool;
this.executionScheduler = executionScheduler;
diff --git a/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/exporter/Exporters.java b/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/exporter/Exporters.java
index 54d4fde4fbbdf..484361ddc542e 100644
--- a/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/exporter/Exporters.java
+++ b/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/exporter/Exporters.java
@@ -51,7 +51,6 @@ public class Exporters extends AbstractLifecycleComponent {
public Exporters(Settings settings, Map factories,
ClusterService clusterService, XPackLicenseState licenseState,
ThreadContext threadContext) {
- super(settings);
this.settings = settings;
this.factories = factories;
this.exporters = new AtomicReference<>(emptyMap());
diff --git a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/MonitoringServiceTests.java b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/MonitoringServiceTests.java
index 3d23933f8dcc0..f67b828606d07 100644
--- a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/MonitoringServiceTests.java
+++ b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/MonitoringServiceTests.java
@@ -173,7 +173,6 @@ class BlockingExporter extends CountingExporter {
private final CountDownLatch latch;
BlockingExporter(CountDownLatch latch) {
- super();
this.latch = latch;
}
From 5a5e44d1de98f79ae19a6b86af0b0fdf12f0ef72 Mon Sep 17 00:00:00 2001
From: Armin Braun
Date: Wed, 16 Jan 2019 11:08:48 +0100
Subject: [PATCH 03/16] Simplify Snapshot Create Request Handling (#37464)
* The internal create request is absolutely redundant, the only difference to the transport request is that we resolved the snapshot
name when moving from the transport to the internal version
* Removed it and passed the transport request into the snapshot service instead
* nicer way of resolve snapshot name in callback
---
.../create/TransportCreateSnapshotAction.java | 19 +-
.../snapshots/SnapshotsService.java | 219 ++----------------
2 files changed, 23 insertions(+), 215 deletions(-)
diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/create/TransportCreateSnapshotAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/create/TransportCreateSnapshotAction.java
index b4320f5b4f72e..abd50f0785a83 100644
--- a/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/create/TransportCreateSnapshotAction.java
+++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/create/TransportCreateSnapshotAction.java
@@ -73,24 +73,14 @@ protected ClusterBlockException checkBlock(CreateSnapshotRequest request, Cluste
@Override
protected void masterOperation(final CreateSnapshotRequest request, ClusterState state,
final ActionListener listener) {
- final String snapshotName = indexNameExpressionResolver.resolveDateMathExpression(request.snapshot());
- SnapshotsService.SnapshotRequest snapshotRequest =
- new SnapshotsService.SnapshotRequest(request.repository(), snapshotName, "create_snapshot [" + snapshotName + "]")
- .indices(request.indices())
- .indicesOptions(request.indicesOptions())
- .partial(request.partial())
- .settings(request.settings())
- .includeGlobalState(request.includeGlobalState())
- .masterNodeTimeout(request.masterNodeTimeout());
- snapshotsService.createSnapshot(snapshotRequest, new SnapshotsService.CreateSnapshotListener() {
+ snapshotsService.createSnapshot(request, new SnapshotsService.CreateSnapshotListener() {
@Override
- public void onResponse() {
+ public void onResponse(Snapshot snapshotCreated) {
if (request.waitForCompletion()) {
snapshotsService.addListener(new SnapshotsService.SnapshotCompletionListener() {
@Override
public void onSnapshotCompletion(Snapshot snapshot, SnapshotInfo snapshotInfo) {
- if (snapshot.getRepository().equals(request.repository()) &&
- snapshot.getSnapshotId().getName().equals(snapshotName)) {
+ if (snapshotCreated.equals(snapshot)) {
listener.onResponse(new CreateSnapshotResponse(snapshotInfo));
snapshotsService.removeListener(this);
}
@@ -98,8 +88,7 @@ public void onSnapshotCompletion(Snapshot snapshot, SnapshotInfo snapshotInfo) {
@Override
public void onSnapshotFailure(Snapshot snapshot, Exception e) {
- if (snapshot.getRepository().equals(request.repository()) &&
- snapshot.getSnapshotId().getName().equals(snapshotName)) {
+ if (snapshotCreated.equals(snapshot)) {
listener.onFailure(e);
snapshotsService.removeListener(this);
}
diff --git a/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java b/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java
index e76dde274557c..153bb1fbf2fcf 100644
--- a/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java
+++ b/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java
@@ -27,7 +27,7 @@
import org.apache.lucene.util.CollectionUtil;
import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.action.ActionListener;
-import org.elasticsearch.action.support.IndicesOptions;
+import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotRequest;
import org.elasticsearch.cluster.ClusterChangedEvent;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.ClusterStateApplier;
@@ -78,7 +78,6 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
-import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -92,8 +91,8 @@
*
* A typical snapshot creating process looks like this:
*
- * On the master node the {@link #createSnapshot(SnapshotRequest, CreateSnapshotListener)} is called and makes sure that no snapshots
- * is currently running and registers the new snapshot in cluster state
+ * On the master node the {@link #createSnapshot(CreateSnapshotRequest, CreateSnapshotListener)} is called and makes sure that
+ * no snapshot is currently running and registers the new snapshot in cluster state
* When cluster state is updated
* the {@link #beginSnapshot(ClusterState, SnapshotsInProgress.Entry, boolean, CreateSnapshotListener)} method kicks in and initializes
* the snapshot in the repository and then populates list of shards that needs to be snapshotted in cluster state
@@ -235,20 +234,20 @@ public List currentSnapshots(final String repositoryName) {
* @param request snapshot request
* @param listener snapshot creation listener
*/
- public void createSnapshot(final SnapshotRequest request, final CreateSnapshotListener listener) {
- final String repositoryName = request.repositoryName;
- final String snapshotName = request.snapshotName;
+ public void createSnapshot(final CreateSnapshotRequest request, final CreateSnapshotListener listener) {
+ final String repositoryName = request.repository();
+ final String snapshotName = indexNameExpressionResolver.resolveDateMathExpression(request.snapshot());
validate(repositoryName, snapshotName);
final SnapshotId snapshotId = new SnapshotId(snapshotName, UUIDs.randomBase64UUID()); // new UUID for the snapshot
final RepositoryData repositoryData = repositoriesService.repository(repositoryName).getRepositoryData();
- clusterService.submitStateUpdateTask(request.cause(), new ClusterStateUpdateTask() {
+ clusterService.submitStateUpdateTask("create_snapshot [" + snapshotName + ']', new ClusterStateUpdateTask() {
private SnapshotsInProgress.Entry newSnapshot = null;
@Override
public ClusterState execute(ClusterState currentState) {
- validate(request, currentState);
+ validate(repositoryName, snapshotName, currentState);
SnapshotDeletionsInProgress deletionsInProgress = currentState.custom(SnapshotDeletionsInProgress.TYPE);
if (deletionsInProgress != null && deletionsInProgress.hasDeletionsInProgress()) {
throw new ConcurrentSnapshotExecutionException(repositoryName, snapshotName,
@@ -301,16 +300,16 @@ public TimeValue timeout() {
/**
* Validates snapshot request
*
- * @param request snapshot request
+ * @param repositoryName repository name
+ * @param snapshotName snapshot name
* @param state current cluster state
*/
- private void validate(SnapshotRequest request, ClusterState state) {
+ private void validate(String repositoryName, String snapshotName, ClusterState state) {
RepositoriesMetaData repositoriesMetaData = state.getMetaData().custom(RepositoriesMetaData.TYPE);
- final String repository = request.repositoryName;
- if (repositoriesMetaData == null || repositoriesMetaData.repository(repository) == null) {
- throw new RepositoryMissingException(repository);
+ if (repositoriesMetaData == null || repositoriesMetaData.repository(repositoryName) == null) {
+ throw new RepositoryMissingException(repositoryName);
}
- validate(repository, request.snapshotName);
+ validate(repositoryName, snapshotName);
}
private static void validate(final String repositoryName, final String snapshotName) {
@@ -377,7 +376,7 @@ protected void doRun() {
logger.info("snapshot [{}] started", snapshot.snapshot());
if (snapshot.indices().isEmpty()) {
// No indices in this snapshot - we are done
- userCreateSnapshotListener.onResponse();
+ userCreateSnapshotListener.onResponse(snapshot.snapshot());
endSnapshot(snapshot);
return;
}
@@ -465,7 +464,7 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS
// for processing. If client wants to wait for the snapshot completion, it can register snapshot
// completion listener in this method. For the snapshot completion to work properly, the snapshot
// should still exist when listener is registered.
- userCreateSnapshotListener.onResponse();
+ userCreateSnapshotListener.onResponse(snapshot.snapshot());
// Now that snapshot completion listener is registered we can end the snapshot if needed
// We should end snapshot only if 1) we didn't accept it for processing (which happens when there
@@ -1544,8 +1543,10 @@ public interface CreateSnapshotListener {
/**
* Called when snapshot has successfully started
+ *
+ * @param snapshot snapshot that was created
*/
- void onResponse();
+ void onResponse(Snapshot snapshot);
/**
* Called if a snapshot operation couldn't start
@@ -1575,186 +1576,4 @@ public interface SnapshotCompletionListener {
void onSnapshotFailure(Snapshot snapshot, Exception e);
}
-
- /**
- * Snapshot creation request
- */
- public static class SnapshotRequest {
-
- private final String cause;
-
- private final String repositoryName;
-
- private final String snapshotName;
-
- private String[] indices;
-
- private IndicesOptions indicesOptions = IndicesOptions.strictExpandOpen();
-
- private boolean partial;
-
- private Settings settings;
-
- private boolean includeGlobalState;
-
- private TimeValue masterNodeTimeout;
-
- /**
- * Constructs new snapshot creation request
- *
- * @param repositoryName repository name
- * @param snapshotName snapshot name
- * @param cause cause for snapshot operation
- */
- public SnapshotRequest(final String repositoryName, final String snapshotName, final String cause) {
- this.repositoryName = Objects.requireNonNull(repositoryName);
- this.snapshotName = Objects.requireNonNull(snapshotName);
- this.cause = Objects.requireNonNull(cause);
- }
-
- /**
- * Sets the list of indices to be snapshotted
- *
- * @param indices list of indices
- * @return this request
- */
- public SnapshotRequest indices(String[] indices) {
- this.indices = indices;
- return this;
- }
-
- /**
- * Sets repository-specific snapshot settings
- *
- * @param settings snapshot settings
- * @return this request
- */
- public SnapshotRequest settings(Settings settings) {
- this.settings = settings;
- return this;
- }
-
- /**
- * Set to true if global state should be stored as part of the snapshot
- *
- * @param includeGlobalState true if global state should be stored as part of the snapshot
- * @return this request
- */
- public SnapshotRequest includeGlobalState(boolean includeGlobalState) {
- this.includeGlobalState = includeGlobalState;
- return this;
- }
-
- /**
- * Sets master node timeout
- *
- * @param masterNodeTimeout master node timeout
- * @return this request
- */
- public SnapshotRequest masterNodeTimeout(TimeValue masterNodeTimeout) {
- this.masterNodeTimeout = masterNodeTimeout;
- return this;
- }
-
- /**
- * Sets the indices options
- *
- * @param indicesOptions indices options
- * @return this request
- */
- public SnapshotRequest indicesOptions(IndicesOptions indicesOptions) {
- this.indicesOptions = indicesOptions;
- return this;
- }
-
- /**
- * Set to true if partial snapshot should be allowed
- *
- * @param partial true if partial snapshots should be allowed
- * @return this request
- */
- public SnapshotRequest partial(boolean partial) {
- this.partial = partial;
- return this;
- }
-
- /**
- * Returns cause for snapshot operation
- *
- * @return cause for snapshot operation
- */
- public String cause() {
- return cause;
- }
-
- /**
- * Returns the repository name
- */
- public String repositoryName() {
- return repositoryName;
- }
-
- /**
- * Returns the snapshot name
- */
- public String snapshotName() {
- return snapshotName;
- }
-
- /**
- * Returns the list of indices to be snapshotted
- *
- * @return the list of indices
- */
- public String[] indices() {
- return indices;
- }
-
- /**
- * Returns indices options
- *
- * @return indices options
- */
- public IndicesOptions indicesOptions() {
- return indicesOptions;
- }
-
- /**
- * Returns repository-specific settings for the snapshot operation
- *
- * @return repository-specific settings
- */
- public Settings settings() {
- return settings;
- }
-
- /**
- * Returns true if global state should be stored as part of the snapshot
- *
- * @return true if global state should be stored as part of the snapshot
- */
- public boolean includeGlobalState() {
- return includeGlobalState;
- }
-
- /**
- * Returns true if partial snapshot should be allowed
- *
- * @return true if partial snapshot should be allowed
- */
- public boolean partial() {
- return partial;
- }
-
- /**
- * Returns master node timeout
- *
- * @return master node timeout
- */
- public TimeValue masterNodeTimeout() {
- return masterNodeTimeout;
- }
-
- }
}
-
From 023bb2f1e473f4ad009e22c1947faf844d39dbad Mon Sep 17 00:00:00 2001
From: Costin Leau
Date: Wed, 16 Jan 2019 12:36:35 +0200
Subject: [PATCH 04/16] SQL: Remove slightly used meta commands (#37506)
Remove SYS CATALOGS and SYS TABLE TYPES as they are a subset of SYS
TABLES (and thus somewhat redundant) and used only by JDBC.
Close #37409
---
.../xpack/sql/jdbc/JdbcDatabaseMetaData.java | 32 +-
.../sql/qa/jdbc/DatabaseMetaDataTestCase.java | 21 +-
.../sql/qa/rest/RestSqlUsageTestCase.java | 4 +-
.../setup_mock_metadata_get_table_types.sql | 12 +-
...setup_mock_metadata_get_types_of_table.sql | 15 +
x-pack/plugin/sql/src/main/antlr/SqlBase.g4 | 2 -
.../xpack/sql/parser/CommandBuilder.java | 14 -
.../xpack/sql/parser/SqlBaseBaseListener.java | 24 -
.../xpack/sql/parser/SqlBaseBaseVisitor.java | 14 -
.../xpack/sql/parser/SqlBaseListener.java | 24 -
.../xpack/sql/parser/SqlBaseParser.java | 1503 ++++++++---------
.../xpack/sql/parser/SqlBaseVisitor.java | 14 -
.../plan/logical/command/sys/SysCatalogs.java | 64 -
.../logical/command/sys/SysTableTypes.java | 70 -
.../logical/command/sys/SysCatalogsTests.java | 61 -
.../command/sys/SysTableTypesTests.java | 52 -
.../xpack/sql/stats/VerifierMetricsTests.java | 3 +-
17 files changed, 788 insertions(+), 1141 deletions(-)
create mode 100644 x-pack/plugin/sql/qa/src/main/resources/setup_mock_metadata_get_types_of_table.sql
delete mode 100644 x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysCatalogs.java
delete mode 100644 x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTableTypes.java
delete mode 100644 x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysCatalogsTests.java
delete mode 100644 x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTableTypesTests.java
diff --git a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcDatabaseMetaData.java b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcDatabaseMetaData.java
index 2650e5892d59b..5697453730455 100644
--- a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcDatabaseMetaData.java
+++ b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcDatabaseMetaData.java
@@ -773,12 +773,16 @@ public ResultSet getSchemas(String catalog, String schemaPattern) throws SQLExce
@Override
public ResultSet getCatalogs() throws SQLException {
- return con.createStatement().executeQuery("SYS CATALOGS");
+ // TABLE_CAT is the first column
+ Object[][] data = queryColumn(con, "SYS TABLES CATALOG LIKE '%'", 1);
+ return memorySet(con.cfg, columnInfo("", "TABLE_CAT"), data);
}
@Override
public ResultSet getTableTypes() throws SQLException {
- return con.createStatement().executeQuery("SYS TABLE TYPES");
+ // TABLE_TYPE (4)
+ Object[][] data = queryColumn(con, "SYS TABLES TYPE '%'", 4);
+ return memorySet(con.cfg, columnInfo("", "TABLE_TYPE"), data);
}
@Override
@@ -1114,6 +1118,26 @@ public boolean generatedKeyAlwaysReturned() throws SQLException {
return false;
}
+ //
+ // Utility methods
+ //
+
+ private static Object[][] queryColumn(JdbcConnection con, String query, int... cols) throws SQLException {
+ List data = new ArrayList<>();
+ try (ResultSet rs = con.createStatement().executeQuery(query)) {
+ while (rs.next()) {
+ Object[] row = new Object[cols.length];
+ for (int i = 0; i < cols.length; i++) {
+ row[i] = rs.getObject(cols[i]);
+ }
+ data.add(row);
+ }
+ }
+
+ return data.toArray(new Object[][] {});
+ }
+
+
private static List columnInfo(String tableName, Object... cols) throws JdbcSQLException {
List columns = new ArrayList<>();
@@ -1156,7 +1180,7 @@ private static ResultSet memorySet(JdbcConfiguration cfg, List c
return new JdbcResultSet(cfg, null, new InMemoryCursor(columns, data));
}
- static class InMemoryCursor implements Cursor {
+ private static class InMemoryCursor implements Cursor {
private final List columns;
private final Object[][] data;
@@ -1197,4 +1221,4 @@ public void close() throws SQLException {
// this cursor doesn't hold any resource - no need to clean up
}
}
-}
+}
\ No newline at end of file
diff --git a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/DatabaseMetaDataTestCase.java b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/DatabaseMetaDataTestCase.java
index 9e4252cc27391..08f56b0058244 100644
--- a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/DatabaseMetaDataTestCase.java
+++ b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/DatabaseMetaDataTestCase.java
@@ -71,12 +71,12 @@ public void testGetTables() throws Exception {
}
}
- public void testGetTableTypes() throws Exception {
+ public void testGetTypeOfTables() throws Exception {
index("test1", body -> body.field("name", "bob"));
index("test2", body -> body.field("name", "bob"));
try (Connection h2 = LocalH2.anonymousDb(); Connection es = esJdbc()) {
- h2.createStatement().executeUpdate("RUNSCRIPT FROM 'classpath:/setup_mock_metadata_get_table_types.sql'");
+ h2.createStatement().executeUpdate("RUNSCRIPT FROM 'classpath:/setup_mock_metadata_get_types_of_table.sql'");
CheckedSupplier all = () -> h2.createStatement()
.executeQuery("SELECT '" + clusterName() + "' AS TABLE_CAT, * FROM mock");
@@ -88,6 +88,23 @@ public void testGetTableTypes() throws Exception {
}
}
+ public void testGetTableTypes() throws Exception {
+ index("test1", body -> body.field("name", "bob"));
+ index("test2", body -> body.field("name", "bob"));
+
+ try (Connection h2 = LocalH2.anonymousDb(); Connection es = esJdbc()) {
+ h2.createStatement().executeUpdate("RUNSCRIPT FROM 'classpath:/setup_mock_metadata_get_table_types.sql'");
+ assertResultSets(h2.createStatement().executeQuery("SELECT * FROM mock"), es.getMetaData().getTableTypes());
+ }
+ }
+
+ public void testGetCatalogs() throws Exception {
+ try (Connection h2 = LocalH2.anonymousDb(); Connection es = esJdbc()) {
+ assertResultSets(h2.createStatement().executeQuery("SELECT '" + clusterName() + "' AS TABLE_CAT"),
+ es.getMetaData().getCatalogs());
+ }
+ }
+
public void testColumns() throws Exception {
index("test1", body -> body.field("name", "bob"));
index("test2", body -> {
diff --git a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/rest/RestSqlUsageTestCase.java b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/rest/RestSqlUsageTestCase.java
index 7573fe8abaa0b..42f9e59840f86 100644
--- a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/rest/RestSqlUsageTestCase.java
+++ b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/rest/RestSqlUsageTestCase.java
@@ -160,8 +160,8 @@ public void testSqlRestUsage() throws IOException {
allTotalQueries += randomCommandExecutions;
for (int i = 0; i < randomCommandExecutions; i++) {
runSql(randomFrom("SHOW FUNCTIONS", "SHOW COLUMNS FROM library", "SHOW SCHEMAS",
- "SHOW TABLES", "SYS CATALOGS", "SYS COLUMNS LIKE '%name'",
- "SYS TABLES", "SYS TYPES"));
+ "SHOW TABLES", "SYS TABLES", "SYS COLUMNS LIKE '%name'",
+ "SYS TABLES TYPE '%'", "SYS TYPES"));
}
responseAsMap = getStats();
assertFeatureMetric(baseMetrics.get("command") + randomCommandExecutions, responseAsMap, "command");
diff --git a/x-pack/plugin/sql/qa/src/main/resources/setup_mock_metadata_get_table_types.sql b/x-pack/plugin/sql/qa/src/main/resources/setup_mock_metadata_get_table_types.sql
index db40c6b90865f..52c767cb73485 100644
--- a/x-pack/plugin/sql/qa/src/main/resources/setup_mock_metadata_get_table_types.sql
+++ b/x-pack/plugin/sql/qa/src/main/resources/setup_mock_metadata_get_table_types.sql
@@ -1,15 +1,7 @@
CREATE TABLE mock (
- TABLE_SCHEM VARCHAR,
- TABLE_NAME VARCHAR,
TABLE_TYPE VARCHAR,
- REMARKS VARCHAR,
- TYPE_CAT VARCHAR,
- TYPE_SCHEM VARCHAR,
- TYPE_NAME VARCHAR,
- SELF_REFERENCING_COL_NAME VARCHAR,
- REF_GENERATION VARCHAR
) AS
-SELECT '', 'test1', 'BASE TABLE', '', null, null, null, null, null FROM DUAL
+SELECT 'ALIAS' FROM DUAL
UNION ALL
-SELECT '', 'test2', 'BASE TABLE', '', null, null, null, null, null FROM DUAL
+SELECT 'BASE TABLE' FROM DUAL
;
diff --git a/x-pack/plugin/sql/qa/src/main/resources/setup_mock_metadata_get_types_of_table.sql b/x-pack/plugin/sql/qa/src/main/resources/setup_mock_metadata_get_types_of_table.sql
new file mode 100644
index 0000000000000..db40c6b90865f
--- /dev/null
+++ b/x-pack/plugin/sql/qa/src/main/resources/setup_mock_metadata_get_types_of_table.sql
@@ -0,0 +1,15 @@
+CREATE TABLE mock (
+ TABLE_SCHEM VARCHAR,
+ TABLE_NAME VARCHAR,
+ TABLE_TYPE VARCHAR,
+ REMARKS VARCHAR,
+ TYPE_CAT VARCHAR,
+ TYPE_SCHEM VARCHAR,
+ TYPE_NAME VARCHAR,
+ SELF_REFERENCING_COL_NAME VARCHAR,
+ REF_GENERATION VARCHAR
+) AS
+SELECT '', 'test1', 'BASE TABLE', '', null, null, null, null, null FROM DUAL
+UNION ALL
+SELECT '', 'test2', 'BASE TABLE', '', null, null, null, null, null FROM DUAL
+;
diff --git a/x-pack/plugin/sql/src/main/antlr/SqlBase.g4 b/x-pack/plugin/sql/src/main/antlr/SqlBase.g4
index 6435d80d04073..e7cb2e2b1b258 100644
--- a/x-pack/plugin/sql/src/main/antlr/SqlBase.g4
+++ b/x-pack/plugin/sql/src/main/antlr/SqlBase.g4
@@ -55,7 +55,6 @@ statement
| (DESCRIBE | DESC) (tableLike=likePattern | tableIdent=tableIdentifier) #showColumns
| SHOW FUNCTIONS (likePattern)? #showFunctions
| SHOW SCHEMAS #showSchemas
- | SYS CATALOGS #sysCatalogs
| SYS TABLES (CATALOG clusterLike=likePattern)?
(tableLike=likePattern | tableIdent=tableIdentifier)?
(TYPE string (',' string)* )? #sysTables
@@ -63,7 +62,6 @@ statement
(TABLE tableLike=likePattern | tableIdent=tableIdentifier)?
(columnPattern=likePattern)? #sysColumns
| SYS TYPES ((PLUS | MINUS)? type=number)? #sysTypes
- | SYS TABLE TYPES #sysTableTypes
;
query
diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/CommandBuilder.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/CommandBuilder.java
index 532cef01f555e..87709ac104e08 100644
--- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/CommandBuilder.java
+++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/CommandBuilder.java
@@ -16,9 +16,7 @@
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.ShowSchemasContext;
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.ShowTablesContext;
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.StringContext;
-import org.elasticsearch.xpack.sql.parser.SqlBaseParser.SysCatalogsContext;
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.SysColumnsContext;
-import org.elasticsearch.xpack.sql.parser.SqlBaseParser.SysTableTypesContext;
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.SysTablesContext;
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.SysTypesContext;
import org.elasticsearch.xpack.sql.plan.TableIdentifier;
@@ -29,9 +27,7 @@
import org.elasticsearch.xpack.sql.plan.logical.command.ShowFunctions;
import org.elasticsearch.xpack.sql.plan.logical.command.ShowSchemas;
import org.elasticsearch.xpack.sql.plan.logical.command.ShowTables;
-import org.elasticsearch.xpack.sql.plan.logical.command.sys.SysCatalogs;
import org.elasticsearch.xpack.sql.plan.logical.command.sys.SysColumns;
-import org.elasticsearch.xpack.sql.plan.logical.command.sys.SysTableTypes;
import org.elasticsearch.xpack.sql.plan.logical.command.sys.SysTables;
import org.elasticsearch.xpack.sql.plan.logical.command.sys.SysTypes;
import org.elasticsearch.xpack.sql.proto.SqlTypedParamValue;
@@ -144,11 +140,6 @@ public Object visitShowColumns(ShowColumnsContext ctx) {
return new ShowColumns(source(ctx), index, visitLikePattern(ctx.likePattern()));
}
- @Override
- public Object visitSysCatalogs(SysCatalogsContext ctx) {
- return new SysCatalogs(source(ctx));
- }
-
@Override
public SysTables visitSysTables(SysTablesContext ctx) {
List types = new ArrayList<>();
@@ -199,9 +190,4 @@ public SysTypes visitSysTypes(SysTypesContext ctx) {
return new SysTypes(source(ctx), Integer.valueOf(type));
}
-
- @Override
- public Object visitSysTableTypes(SysTableTypesContext ctx) {
- return new SysTableTypes(source(ctx));
- }
}
\ No newline at end of file
diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseBaseListener.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseBaseListener.java
index af7243cbe984a..a62c5b4083fa3 100644
--- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseBaseListener.java
+++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseBaseListener.java
@@ -119,18 +119,6 @@ class SqlBaseBaseListener implements SqlBaseListener {
* The default implementation does nothing.
*/
@Override public void exitShowSchemas(SqlBaseParser.ShowSchemasContext ctx) { }
- /**
- * {@inheritDoc}
- *
- * The default implementation does nothing.
- */
- @Override public void enterSysCatalogs(SqlBaseParser.SysCatalogsContext ctx) { }
- /**
- * {@inheritDoc}
- *
- * The default implementation does nothing.
- */
- @Override public void exitSysCatalogs(SqlBaseParser.SysCatalogsContext ctx) { }
/**
* {@inheritDoc}
*
@@ -167,18 +155,6 @@ class SqlBaseBaseListener implements SqlBaseListener {
* The default implementation does nothing.
*/
@Override public void exitSysTypes(SqlBaseParser.SysTypesContext ctx) { }
- /**
- * {@inheritDoc}
- *
- * The default implementation does nothing.
- */
- @Override public void enterSysTableTypes(SqlBaseParser.SysTableTypesContext ctx) { }
- /**
- * {@inheritDoc}
- *
- * The default implementation does nothing.
- */
- @Override public void exitSysTableTypes(SqlBaseParser.SysTableTypesContext ctx) { }
/**
* {@inheritDoc}
*
diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseBaseVisitor.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseBaseVisitor.java
index 72f517cab8d40..13722407570a7 100644
--- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseBaseVisitor.java
+++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseBaseVisitor.java
@@ -74,13 +74,6 @@ class SqlBaseBaseVisitor extends AbstractParseTreeVisitor implements SqlBa
* {@link #visitChildren} on {@code ctx}.
*/
@Override public T visitShowSchemas(SqlBaseParser.ShowSchemasContext ctx) { return visitChildren(ctx); }
- /**
- * {@inheritDoc}
- *
- * The default implementation returns the result of calling
- * {@link #visitChildren} on {@code ctx}.
- */
- @Override public T visitSysCatalogs(SqlBaseParser.SysCatalogsContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
@@ -102,13 +95,6 @@ class SqlBaseBaseVisitor extends AbstractParseTreeVisitor implements SqlBa
* {@link #visitChildren} on {@code ctx}.
*/
@Override public T visitSysTypes(SqlBaseParser.SysTypesContext ctx) { return visitChildren(ctx); }
- /**
- * {@inheritDoc}
- *
- * The default implementation returns the result of calling
- * {@link #visitChildren} on {@code ctx}.
- */
- @Override public T visitSysTableTypes(SqlBaseParser.SysTableTypesContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseListener.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseListener.java
index 67180685c8cbe..7b5a8ea5fbad9 100644
--- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseListener.java
+++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseListener.java
@@ -111,18 +111,6 @@ interface SqlBaseListener extends ParseTreeListener {
* @param ctx the parse tree
*/
void exitShowSchemas(SqlBaseParser.ShowSchemasContext ctx);
- /**
- * Enter a parse tree produced by the {@code sysCatalogs}
- * labeled alternative in {@link SqlBaseParser#statement}.
- * @param ctx the parse tree
- */
- void enterSysCatalogs(SqlBaseParser.SysCatalogsContext ctx);
- /**
- * Exit a parse tree produced by the {@code sysCatalogs}
- * labeled alternative in {@link SqlBaseParser#statement}.
- * @param ctx the parse tree
- */
- void exitSysCatalogs(SqlBaseParser.SysCatalogsContext ctx);
/**
* Enter a parse tree produced by the {@code sysTables}
* labeled alternative in {@link SqlBaseParser#statement}.
@@ -159,18 +147,6 @@ interface SqlBaseListener extends ParseTreeListener {
* @param ctx the parse tree
*/
void exitSysTypes(SqlBaseParser.SysTypesContext ctx);
- /**
- * Enter a parse tree produced by the {@code sysTableTypes}
- * labeled alternative in {@link SqlBaseParser#statement}.
- * @param ctx the parse tree
- */
- void enterSysTableTypes(SqlBaseParser.SysTableTypesContext ctx);
- /**
- * Exit a parse tree produced by the {@code sysTableTypes}
- * labeled alternative in {@link SqlBaseParser#statement}.
- * @param ctx the parse tree
- */
- void exitSysTableTypes(SqlBaseParser.SysTableTypesContext ctx);
/**
* Enter a parse tree produced by {@link SqlBaseParser#query}.
* @param ctx the parse tree
diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseParser.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseParser.java
index 8ba886404536b..7549bfab8320a 100644
--- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseParser.java
+++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseParser.java
@@ -334,24 +334,6 @@ public T accept(ParseTreeVisitor extends T> visitor) {
else return visitor.visitChildren(this);
}
}
- public static class SysCatalogsContext extends StatementContext {
- public TerminalNode SYS() { return getToken(SqlBaseParser.SYS, 0); }
- public TerminalNode CATALOGS() { return getToken(SqlBaseParser.CATALOGS, 0); }
- public SysCatalogsContext(StatementContext ctx) { copyFrom(ctx); }
- @Override
- public void enterRule(ParseTreeListener listener) {
- if ( listener instanceof SqlBaseListener ) ((SqlBaseListener)listener).enterSysCatalogs(this);
- }
- @Override
- public void exitRule(ParseTreeListener listener) {
- if ( listener instanceof SqlBaseListener ) ((SqlBaseListener)listener).exitSysCatalogs(this);
- }
- @Override
- public T accept(ParseTreeVisitor extends T> visitor) {
- if ( visitor instanceof SqlBaseVisitor ) return ((SqlBaseVisitor extends T>)visitor).visitSysCatalogs(this);
- else return visitor.visitChildren(this);
- }
- }
public static class SysColumnsContext extends StatementContext {
public StringContext cluster;
public LikePatternContext tableLike;
@@ -458,25 +440,6 @@ public T accept(ParseTreeVisitor extends T> visitor) {
else return visitor.visitChildren(this);
}
}
- public static class SysTableTypesContext extends StatementContext {
- public TerminalNode SYS() { return getToken(SqlBaseParser.SYS, 0); }
- public TerminalNode TABLE() { return getToken(SqlBaseParser.TABLE, 0); }
- public TerminalNode TYPES() { return getToken(SqlBaseParser.TYPES, 0); }
- public SysTableTypesContext(StatementContext ctx) { copyFrom(ctx); }
- @Override
- public void enterRule(ParseTreeListener listener) {
- if ( listener instanceof SqlBaseListener ) ((SqlBaseListener)listener).enterSysTableTypes(this);
- }
- @Override
- public void exitRule(ParseTreeListener listener) {
- if ( listener instanceof SqlBaseListener ) ((SqlBaseListener)listener).exitSysTableTypes(this);
- }
- @Override
- public T accept(ParseTreeVisitor extends T> visitor) {
- if ( visitor instanceof SqlBaseVisitor ) return ((SqlBaseVisitor extends T>)visitor).visitSysTableTypes(this);
- else return visitor.visitChildren(this);
- }
- }
public static class StatementDefaultContext extends StatementContext {
public QueryContext query() {
return getRuleContext(QueryContext.class,0);
@@ -635,7 +598,7 @@ public final StatementContext statement() throws RecognitionException {
enterRule(_localctx, 4, RULE_statement);
int _la;
try {
- setState(220);
+ setState(215);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,19,_ctx) ) {
case 1:
@@ -1030,71 +993,61 @@ public final StatementContext statement() throws RecognitionException {
}
break;
case 9:
- _localctx = new SysCatalogsContext(_localctx);
+ _localctx = new SysTablesContext(_localctx);
enterOuterAlt(_localctx, 9);
{
setState(172);
match(SYS);
setState(173);
- match(CATALOGS);
- }
- break;
- case 10:
- _localctx = new SysTablesContext(_localctx);
- enterOuterAlt(_localctx, 10);
- {
- setState(174);
- match(SYS);
- setState(175);
match(TABLES);
- setState(178);
+ setState(176);
_la = _input.LA(1);
if (_la==CATALOG) {
{
- setState(176);
+ setState(174);
match(CATALOG);
- setState(177);
+ setState(175);
((SysTablesContext)_localctx).clusterLike = likePattern();
}
}
- setState(182);
+ setState(180);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,11,_ctx) ) {
case 1:
{
- setState(180);
+ setState(178);
((SysTablesContext)_localctx).tableLike = likePattern();
}
break;
case 2:
{
- setState(181);
+ setState(179);
((SysTablesContext)_localctx).tableIdent = tableIdentifier();
}
break;
}
- setState(193);
+ setState(191);
_la = _input.LA(1);
if (_la==TYPE) {
{
- setState(184);
+ setState(182);
match(TYPE);
- setState(185);
+ setState(183);
string();
- setState(190);
+ setState(188);
_errHandler.sync(this);
_la = _input.LA(1);
while (_la==T__2) {
{
{
- setState(186);
+ setState(184);
match(T__2);
- setState(187);
+ setState(185);
string();
}
}
- setState(192);
+ setState(190);
_errHandler.sync(this);
_la = _input.LA(1);
}
@@ -1103,32 +1056,32 @@ public final StatementContext statement() throws RecognitionException {
}
break;
- case 11:
+ case 10:
_localctx = new SysColumnsContext(_localctx);
- enterOuterAlt(_localctx, 11);
+ enterOuterAlt(_localctx, 10);
{
- setState(195);
+ setState(193);
match(SYS);
- setState(196);
+ setState(194);
match(COLUMNS);
- setState(199);
+ setState(197);
_la = _input.LA(1);
if (_la==CATALOG) {
{
- setState(197);
+ setState(195);
match(CATALOG);
- setState(198);
+ setState(196);
((SysColumnsContext)_localctx).cluster = string();
}
}
- setState(204);
+ setState(202);
switch (_input.LA(1)) {
case TABLE:
{
- setState(201);
+ setState(199);
match(TABLE);
- setState(202);
+ setState(200);
((SysColumnsContext)_localctx).tableLike = likePattern();
}
break;
@@ -1175,7 +1128,7 @@ public final StatementContext statement() throws RecognitionException {
case QUOTED_IDENTIFIER:
case BACKQUOTED_IDENTIFIER:
{
- setState(203);
+ setState(201);
((SysColumnsContext)_localctx).tableIdent = tableIdentifier();
}
break;
@@ -1185,34 +1138,34 @@ public final StatementContext statement() throws RecognitionException {
default:
throw new NoViableAltException(this);
}
- setState(207);
+ setState(205);
_la = _input.LA(1);
if (_la==LIKE) {
{
- setState(206);
+ setState(204);
((SysColumnsContext)_localctx).columnPattern = likePattern();
}
}
}
break;
- case 12:
+ case 11:
_localctx = new SysTypesContext(_localctx);
- enterOuterAlt(_localctx, 12);
+ enterOuterAlt(_localctx, 11);
{
- setState(209);
+ setState(207);
match(SYS);
- setState(210);
+ setState(208);
match(TYPES);
- setState(215);
+ setState(213);
_la = _input.LA(1);
if (((((_la - 107)) & ~0x3f) == 0 && ((1L << (_la - 107)) & ((1L << (PLUS - 107)) | (1L << (MINUS - 107)) | (1L << (INTEGER_VALUE - 107)) | (1L << (DECIMAL_VALUE - 107)))) != 0)) {
{
- setState(212);
+ setState(210);
_la = _input.LA(1);
if (_la==PLUS || _la==MINUS) {
{
- setState(211);
+ setState(209);
_la = _input.LA(1);
if ( !(_la==PLUS || _la==MINUS) ) {
_errHandler.recoverInline(this);
@@ -1222,23 +1175,11 @@ public final StatementContext statement() throws RecognitionException {
}
}
- setState(214);
+ setState(212);
((SysTypesContext)_localctx).type = number();
}
}
- }
- break;
- case 13:
- _localctx = new SysTableTypesContext(_localctx);
- enterOuterAlt(_localctx, 13);
- {
- setState(217);
- match(SYS);
- setState(218);
- match(TABLE);
- setState(219);
- match(TYPES);
}
break;
}
@@ -1291,34 +1232,34 @@ public final QueryContext query() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(231);
+ setState(226);
_la = _input.LA(1);
if (_la==WITH) {
{
- setState(222);
+ setState(217);
match(WITH);
- setState(223);
+ setState(218);
namedQuery();
- setState(228);
+ setState(223);
_errHandler.sync(this);
_la = _input.LA(1);
while (_la==T__2) {
{
{
- setState(224);
+ setState(219);
match(T__2);
- setState(225);
+ setState(220);
namedQuery();
}
}
- setState(230);
+ setState(225);
_errHandler.sync(this);
_la = _input.LA(1);
}
}
}
- setState(233);
+ setState(228);
queryNoWith();
}
}
@@ -1374,42 +1315,42 @@ public final QueryNoWithContext queryNoWith() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(235);
+ setState(230);
queryTerm();
- setState(246);
+ setState(241);
_la = _input.LA(1);
if (_la==ORDER) {
{
- setState(236);
+ setState(231);
match(ORDER);
- setState(237);
+ setState(232);
match(BY);
- setState(238);
+ setState(233);
orderBy();
- setState(243);
+ setState(238);
_errHandler.sync(this);
_la = _input.LA(1);
while (_la==T__2) {
{
{
- setState(239);
+ setState(234);
match(T__2);
- setState(240);
+ setState(235);
orderBy();
}
}
- setState(245);
+ setState(240);
_errHandler.sync(this);
_la = _input.LA(1);
}
}
}
- setState(249);
+ setState(244);
_la = _input.LA(1);
if (_la==LIMIT || _la==LIMIT_ESC) {
{
- setState(248);
+ setState(243);
limitClause();
}
}
@@ -1458,14 +1399,14 @@ public final LimitClauseContext limitClause() throws RecognitionException {
enterRule(_localctx, 10, RULE_limitClause);
int _la;
try {
- setState(256);
+ setState(251);
switch (_input.LA(1)) {
case LIMIT:
enterOuterAlt(_localctx, 1);
{
- setState(251);
+ setState(246);
match(LIMIT);
- setState(252);
+ setState(247);
((LimitClauseContext)_localctx).limit = _input.LT(1);
_la = _input.LA(1);
if ( !(_la==ALL || _la==INTEGER_VALUE) ) {
@@ -1478,9 +1419,9 @@ public final LimitClauseContext limitClause() throws RecognitionException {
case LIMIT_ESC:
enterOuterAlt(_localctx, 2);
{
- setState(253);
+ setState(248);
match(LIMIT_ESC);
- setState(254);
+ setState(249);
((LimitClauseContext)_localctx).limit = _input.LT(1);
_la = _input.LA(1);
if ( !(_la==ALL || _la==INTEGER_VALUE) ) {
@@ -1488,7 +1429,7 @@ public final LimitClauseContext limitClause() throws RecognitionException {
} else {
consume();
}
- setState(255);
+ setState(250);
match(ESC_END);
}
break;
@@ -1561,13 +1502,13 @@ public final QueryTermContext queryTerm() throws RecognitionException {
QueryTermContext _localctx = new QueryTermContext(_ctx, getState());
enterRule(_localctx, 12, RULE_queryTerm);
try {
- setState(263);
+ setState(258);
switch (_input.LA(1)) {
case SELECT:
_localctx = new QueryPrimaryDefaultContext(_localctx);
enterOuterAlt(_localctx, 1);
{
- setState(258);
+ setState(253);
querySpecification();
}
break;
@@ -1575,11 +1516,11 @@ public final QueryTermContext queryTerm() throws RecognitionException {
_localctx = new SubqueryContext(_localctx);
enterOuterAlt(_localctx, 2);
{
- setState(259);
+ setState(254);
match(T__0);
- setState(260);
+ setState(255);
queryNoWith();
- setState(261);
+ setState(256);
match(T__1);
}
break;
@@ -1635,13 +1576,13 @@ public final OrderByContext orderBy() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(265);
+ setState(260);
expression();
- setState(267);
+ setState(262);
_la = _input.LA(1);
if (_la==ASC || _la==DESC) {
{
- setState(266);
+ setState(261);
((OrderByContext)_localctx).ordering = _input.LT(1);
_la = _input.LA(1);
if ( !(_la==ASC || _la==DESC) ) {
@@ -1652,13 +1593,13 @@ public final OrderByContext orderBy() throws RecognitionException {
}
}
- setState(271);
+ setState(266);
_la = _input.LA(1);
if (_la==NULLS) {
{
- setState(269);
+ setState(264);
match(NULLS);
- setState(270);
+ setState(265);
((OrderByContext)_localctx).nullOrdering = _input.LT(1);
_la = _input.LA(1);
if ( !(_la==FIRST || _la==LAST) ) {
@@ -1737,75 +1678,75 @@ public final QuerySpecificationContext querySpecification() throws RecognitionEx
try {
enterOuterAlt(_localctx, 1);
{
- setState(273);
+ setState(268);
match(SELECT);
- setState(275);
+ setState(270);
_la = _input.LA(1);
if (_la==ALL || _la==DISTINCT) {
{
- setState(274);
+ setState(269);
setQuantifier();
}
}
- setState(277);
+ setState(272);
selectItem();
- setState(282);
+ setState(277);
_errHandler.sync(this);
_la = _input.LA(1);
while (_la==T__2) {
{
{
- setState(278);
+ setState(273);
match(T__2);
- setState(279);
+ setState(274);
selectItem();
}
}
- setState(284);
+ setState(279);
_errHandler.sync(this);
_la = _input.LA(1);
}
- setState(286);
+ setState(281);
_la = _input.LA(1);
if (_la==FROM) {
{
- setState(285);
+ setState(280);
fromClause();
}
}
- setState(290);
+ setState(285);
_la = _input.LA(1);
if (_la==WHERE) {
{
- setState(288);
+ setState(283);
match(WHERE);
- setState(289);
+ setState(284);
((QuerySpecificationContext)_localctx).where = booleanExpression(0);
}
}
- setState(295);
+ setState(290);
_la = _input.LA(1);
if (_la==GROUP) {
{
- setState(292);
+ setState(287);
match(GROUP);
- setState(293);
+ setState(288);
match(BY);
- setState(294);
+ setState(289);
groupBy();
}
}
- setState(299);
+ setState(294);
_la = _input.LA(1);
if (_la==HAVING) {
{
- setState(297);
+ setState(292);
match(HAVING);
- setState(298);
+ setState(293);
((QuerySpecificationContext)_localctx).having = booleanExpression(0);
}
}
@@ -1857,23 +1798,23 @@ public final FromClauseContext fromClause() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(301);
+ setState(296);
match(FROM);
- setState(302);
+ setState(297);
relation();
- setState(307);
+ setState(302);
_errHandler.sync(this);
_la = _input.LA(1);
while (_la==T__2) {
{
{
- setState(303);
+ setState(298);
match(T__2);
- setState(304);
+ setState(299);
relation();
}
}
- setState(309);
+ setState(304);
_errHandler.sync(this);
_la = _input.LA(1);
}
@@ -1926,30 +1867,30 @@ public final GroupByContext groupBy() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(311);
+ setState(306);
_la = _input.LA(1);
if (_la==ALL || _la==DISTINCT) {
{
- setState(310);
+ setState(305);
setQuantifier();
}
}
- setState(313);
+ setState(308);
groupingElement();
- setState(318);
+ setState(313);
_errHandler.sync(this);
_la = _input.LA(1);
while (_la==T__2) {
{
{
- setState(314);
+ setState(309);
match(T__2);
- setState(315);
+ setState(310);
groupingElement();
}
}
- setState(320);
+ setState(315);
_errHandler.sync(this);
_la = _input.LA(1);
}
@@ -2004,7 +1945,7 @@ public final GroupingElementContext groupingElement() throws RecognitionExceptio
_localctx = new SingleGroupingSetContext(_localctx);
enterOuterAlt(_localctx, 1);
{
- setState(321);
+ setState(316);
groupingExpressions();
}
}
@@ -2050,47 +1991,47 @@ public final GroupingExpressionsContext groupingExpressions() throws Recognition
enterRule(_localctx, 24, RULE_groupingExpressions);
int _la;
try {
- setState(336);
+ setState(331);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,40,_ctx) ) {
case 1:
enterOuterAlt(_localctx, 1);
{
- setState(323);
+ setState(318);
match(T__0);
- setState(332);
+ setState(327);
_la = _input.LA(1);
if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__0) | (1L << ANALYZE) | (1L << ANALYZED) | (1L << CAST) | (1L << CATALOGS) | (1L << COLUMNS) | (1L << CONVERT) | (1L << CURRENT) | (1L << CURRENT_TIMESTAMP) | (1L << DAY) | (1L << DEBUG) | (1L << EXECUTABLE) | (1L << EXISTS) | (1L << EXPLAIN) | (1L << EXTRACT) | (1L << FALSE) | (1L << FIRST) | (1L << FORMAT) | (1L << FULL) | (1L << FUNCTIONS) | (1L << GRAPHVIZ) | (1L << HOUR) | (1L << INTERVAL) | (1L << LAST) | (1L << LEFT) | (1L << LIMIT) | (1L << MAPPED) | (1L << MATCH) | (1L << MINUTE) | (1L << MONTH) | (1L << NOT) | (1L << NULL) | (1L << OPTIMIZED))) != 0) || ((((_la - 67)) & ~0x3f) == 0 && ((1L << (_la - 67)) & ((1L << (PARSED - 67)) | (1L << (PHYSICAL - 67)) | (1L << (PLAN - 67)) | (1L << (RIGHT - 67)) | (1L << (RLIKE - 67)) | (1L << (QUERY - 67)) | (1L << (SCHEMAS - 67)) | (1L << (SECOND - 67)) | (1L << (SHOW - 67)) | (1L << (SYS - 67)) | (1L << (TABLES - 67)) | (1L << (TEXT - 67)) | (1L << (TRUE - 67)) | (1L << (TYPE - 67)) | (1L << (TYPES - 67)) | (1L << (VERIFY - 67)) | (1L << (YEAR - 67)) | (1L << (FUNCTION_ESC - 67)) | (1L << (DATE_ESC - 67)) | (1L << (TIME_ESC - 67)) | (1L << (TIMESTAMP_ESC - 67)) | (1L << (GUID_ESC - 67)) | (1L << (PLUS - 67)) | (1L << (MINUS - 67)) | (1L << (ASTERISK - 67)) | (1L << (PARAM - 67)) | (1L << (STRING - 67)) | (1L << (INTEGER_VALUE - 67)) | (1L << (DECIMAL_VALUE - 67)) | (1L << (IDENTIFIER - 67)) | (1L << (DIGIT_IDENTIFIER - 67)) | (1L << (QUOTED_IDENTIFIER - 67)) | (1L << (BACKQUOTED_IDENTIFIER - 67)))) != 0)) {
{
- setState(324);
+ setState(319);
expression();
- setState(329);
+ setState(324);
_errHandler.sync(this);
_la = _input.LA(1);
while (_la==T__2) {
{
{
- setState(325);
+ setState(320);
match(T__2);
- setState(326);
+ setState(321);
expression();
}
}
- setState(331);
+ setState(326);
_errHandler.sync(this);
_la = _input.LA(1);
}
}
}
- setState(334);
+ setState(329);
match(T__1);
}
break;
case 2:
enterOuterAlt(_localctx, 2);
{
- setState(335);
+ setState(330);
expression();
}
break;
@@ -2141,15 +2082,15 @@ public final NamedQueryContext namedQuery() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(338);
+ setState(333);
((NamedQueryContext)_localctx).name = identifier();
- setState(339);
+ setState(334);
match(AS);
- setState(340);
+ setState(335);
match(T__0);
- setState(341);
+ setState(336);
queryNoWith();
- setState(342);
+ setState(337);
match(T__1);
}
}
@@ -2193,7 +2134,7 @@ public final SetQuantifierContext setQuantifier() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(344);
+ setState(339);
_la = _input.LA(1);
if ( !(_la==ALL || _la==DISTINCT) ) {
_errHandler.recoverInline(this);
@@ -2256,23 +2197,23 @@ public final SelectItemContext selectItem() throws RecognitionException {
_localctx = new SelectExpressionContext(_localctx);
enterOuterAlt(_localctx, 1);
{
- setState(346);
+ setState(341);
expression();
- setState(351);
+ setState(346);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,42,_ctx) ) {
case 1:
{
- setState(348);
+ setState(343);
_la = _input.LA(1);
if (_la==AS) {
{
- setState(347);
+ setState(342);
match(AS);
}
}
- setState(350);
+ setState(345);
identifier();
}
break;
@@ -2326,19 +2267,19 @@ public final RelationContext relation() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(353);
+ setState(348);
relationPrimary();
- setState(357);
+ setState(352);
_errHandler.sync(this);
_la = _input.LA(1);
while (((((_la - 36)) & ~0x3f) == 0 && ((1L << (_la - 36)) & ((1L << (FULL - 36)) | (1L << (INNER - 36)) | (1L << (JOIN - 36)) | (1L << (LEFT - 36)) | (1L << (NATURAL - 36)) | (1L << (RIGHT - 36)))) != 0)) {
{
{
- setState(354);
+ setState(349);
joinRelation();
}
}
- setState(359);
+ setState(354);
_errHandler.sync(this);
_la = _input.LA(1);
}
@@ -2392,7 +2333,7 @@ public final JoinRelationContext joinRelation() throws RecognitionException {
enterRule(_localctx, 34, RULE_joinRelation);
int _la;
try {
- setState(371);
+ setState(366);
switch (_input.LA(1)) {
case FULL:
case INNER:
@@ -2402,18 +2343,18 @@ public final JoinRelationContext joinRelation() throws RecognitionException {
enterOuterAlt(_localctx, 1);
{
{
- setState(360);
+ setState(355);
joinType();
}
- setState(361);
+ setState(356);
match(JOIN);
- setState(362);
+ setState(357);
((JoinRelationContext)_localctx).right = relationPrimary();
- setState(364);
+ setState(359);
_la = _input.LA(1);
if (_la==ON || _la==USING) {
{
- setState(363);
+ setState(358);
joinCriteria();
}
}
@@ -2423,13 +2364,13 @@ public final JoinRelationContext joinRelation() throws RecognitionException {
case NATURAL:
enterOuterAlt(_localctx, 2);
{
- setState(366);
+ setState(361);
match(NATURAL);
- setState(367);
+ setState(362);
joinType();
- setState(368);
+ setState(363);
match(JOIN);
- setState(369);
+ setState(364);
((JoinRelationContext)_localctx).right = relationPrimary();
}
break;
@@ -2478,17 +2419,17 @@ public final JoinTypeContext joinType() throws RecognitionException {
enterRule(_localctx, 36, RULE_joinType);
int _la;
try {
- setState(388);
+ setState(383);
switch (_input.LA(1)) {
case INNER:
case JOIN:
enterOuterAlt(_localctx, 1);
{
- setState(374);
+ setState(369);
_la = _input.LA(1);
if (_la==INNER) {
{
- setState(373);
+ setState(368);
match(INNER);
}
}
@@ -2498,13 +2439,13 @@ public final JoinTypeContext joinType() throws RecognitionException {
case LEFT:
enterOuterAlt(_localctx, 2);
{
- setState(376);
+ setState(371);
match(LEFT);
- setState(378);
+ setState(373);
_la = _input.LA(1);
if (_la==OUTER) {
{
- setState(377);
+ setState(372);
match(OUTER);
}
}
@@ -2514,13 +2455,13 @@ public final JoinTypeContext joinType() throws RecognitionException {
case RIGHT:
enterOuterAlt(_localctx, 3);
{
- setState(380);
+ setState(375);
match(RIGHT);
- setState(382);
+ setState(377);
_la = _input.LA(1);
if (_la==OUTER) {
{
- setState(381);
+ setState(376);
match(OUTER);
}
}
@@ -2530,13 +2471,13 @@ public final JoinTypeContext joinType() throws RecognitionException {
case FULL:
enterOuterAlt(_localctx, 4);
{
- setState(384);
+ setState(379);
match(FULL);
- setState(386);
+ setState(381);
_la = _input.LA(1);
if (_la==OUTER) {
{
- setState(385);
+ setState(380);
match(OUTER);
}
}
@@ -2594,43 +2535,43 @@ public final JoinCriteriaContext joinCriteria() throws RecognitionException {
enterRule(_localctx, 38, RULE_joinCriteria);
int _la;
try {
- setState(404);
+ setState(399);
switch (_input.LA(1)) {
case ON:
enterOuterAlt(_localctx, 1);
{
- setState(390);
+ setState(385);
match(ON);
- setState(391);
+ setState(386);
booleanExpression(0);
}
break;
case USING:
enterOuterAlt(_localctx, 2);
{
- setState(392);
+ setState(387);
match(USING);
- setState(393);
+ setState(388);
match(T__0);
- setState(394);
+ setState(389);
identifier();
- setState(399);
+ setState(394);
_errHandler.sync(this);
_la = _input.LA(1);
while (_la==T__2) {
{
{
- setState(395);
+ setState(390);
match(T__2);
- setState(396);
+ setState(391);
identifier();
}
}
- setState(401);
+ setState(396);
_errHandler.sync(this);
_la = _input.LA(1);
}
- setState(402);
+ setState(397);
match(T__1);
}
break;
@@ -2735,30 +2676,30 @@ public final RelationPrimaryContext relationPrimary() throws RecognitionExceptio
enterRule(_localctx, 40, RULE_relationPrimary);
int _la;
try {
- setState(431);
+ setState(426);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,59,_ctx) ) {
case 1:
_localctx = new TableNameContext(_localctx);
enterOuterAlt(_localctx, 1);
{
- setState(406);
+ setState(401);
tableIdentifier();
- setState(411);
+ setState(406);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,54,_ctx) ) {
case 1:
{
- setState(408);
+ setState(403);
_la = _input.LA(1);
if (_la==AS) {
{
- setState(407);
+ setState(402);
match(AS);
}
}
- setState(410);
+ setState(405);
qualifiedName();
}
break;
@@ -2769,27 +2710,27 @@ public final RelationPrimaryContext relationPrimary() throws RecognitionExceptio
_localctx = new AliasedQueryContext(_localctx);
enterOuterAlt(_localctx, 2);
{
- setState(413);
+ setState(408);
match(T__0);
- setState(414);
+ setState(409);
queryNoWith();
- setState(415);
+ setState(410);
match(T__1);
- setState(420);
+ setState(415);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,56,_ctx) ) {
case 1:
{
- setState(417);
+ setState(412);
_la = _input.LA(1);
if (_la==AS) {
{
- setState(416);
+ setState(411);
match(AS);
}
}
- setState(419);
+ setState(414);
qualifiedName();
}
break;
@@ -2800,27 +2741,27 @@ public final RelationPrimaryContext relationPrimary() throws RecognitionExceptio
_localctx = new AliasedRelationContext(_localctx);
enterOuterAlt(_localctx, 3);
{
- setState(422);
+ setState(417);
match(T__0);
- setState(423);
+ setState(418);
relation();
- setState(424);
+ setState(419);
match(T__1);
- setState(429);
+ setState(424);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,58,_ctx) ) {
case 1:
{
- setState(426);
+ setState(421);
_la = _input.LA(1);
if (_la==AS) {
{
- setState(425);
+ setState(420);
match(AS);
}
}
- setState(428);
+ setState(423);
qualifiedName();
}
break;
@@ -2869,7 +2810,7 @@ public final ExpressionContext expression() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(433);
+ setState(428);
booleanExpression(0);
}
}
@@ -3077,7 +3018,7 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc
int _alt;
enterOuterAlt(_localctx, 1);
{
- setState(466);
+ setState(461);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,60,_ctx) ) {
case 1:
@@ -3086,9 +3027,9 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc
_ctx = _localctx;
_prevctx = _localctx;
- setState(436);
+ setState(431);
match(NOT);
- setState(437);
+ setState(432);
booleanExpression(8);
}
break;
@@ -3097,13 +3038,13 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc
_localctx = new ExistsContext(_localctx);
_ctx = _localctx;
_prevctx = _localctx;
- setState(438);
+ setState(433);
match(EXISTS);
- setState(439);
+ setState(434);
match(T__0);
- setState(440);
+ setState(435);
query();
- setState(441);
+ setState(436);
match(T__1);
}
break;
@@ -3112,15 +3053,15 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc
_localctx = new StringQueryContext(_localctx);
_ctx = _localctx;
_prevctx = _localctx;
- setState(443);
+ setState(438);
match(QUERY);
- setState(444);
+ setState(439);
match(T__0);
- setState(445);
+ setState(440);
((StringQueryContext)_localctx).queryString = string();
- setState(446);
+ setState(441);
matchQueryOptions();
- setState(447);
+ setState(442);
match(T__1);
}
break;
@@ -3129,19 +3070,19 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc
_localctx = new MatchQueryContext(_localctx);
_ctx = _localctx;
_prevctx = _localctx;
- setState(449);
+ setState(444);
match(MATCH);
- setState(450);
+ setState(445);
match(T__0);
- setState(451);
+ setState(446);
((MatchQueryContext)_localctx).singleField = qualifiedName();
- setState(452);
+ setState(447);
match(T__2);
- setState(453);
+ setState(448);
((MatchQueryContext)_localctx).queryString = string();
- setState(454);
+ setState(449);
matchQueryOptions();
- setState(455);
+ setState(450);
match(T__1);
}
break;
@@ -3150,19 +3091,19 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc
_localctx = new MultiMatchQueryContext(_localctx);
_ctx = _localctx;
_prevctx = _localctx;
- setState(457);
+ setState(452);
match(MATCH);
- setState(458);
+ setState(453);
match(T__0);
- setState(459);
+ setState(454);
((MultiMatchQueryContext)_localctx).multiFields = string();
- setState(460);
+ setState(455);
match(T__2);
- setState(461);
+ setState(456);
((MultiMatchQueryContext)_localctx).queryString = string();
- setState(462);
+ setState(457);
matchQueryOptions();
- setState(463);
+ setState(458);
match(T__1);
}
break;
@@ -3171,13 +3112,13 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc
_localctx = new BooleanDefaultContext(_localctx);
_ctx = _localctx;
_prevctx = _localctx;
- setState(465);
+ setState(460);
predicated();
}
break;
}
_ctx.stop = _input.LT(-1);
- setState(476);
+ setState(471);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,62,_ctx);
while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
@@ -3185,7 +3126,7 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc
if ( _parseListeners!=null ) triggerExitRuleEvent();
_prevctx = _localctx;
{
- setState(474);
+ setState(469);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,61,_ctx) ) {
case 1:
@@ -3193,11 +3134,11 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc
_localctx = new LogicalBinaryContext(new BooleanExpressionContext(_parentctx, _parentState));
((LogicalBinaryContext)_localctx).left = _prevctx;
pushNewRecursionContext(_localctx, _startState, RULE_booleanExpression);
- setState(468);
+ setState(463);
if (!(precpred(_ctx, 2))) throw new FailedPredicateException(this, "precpred(_ctx, 2)");
- setState(469);
+ setState(464);
((LogicalBinaryContext)_localctx).operator = match(AND);
- setState(470);
+ setState(465);
((LogicalBinaryContext)_localctx).right = booleanExpression(3);
}
break;
@@ -3206,18 +3147,18 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc
_localctx = new LogicalBinaryContext(new BooleanExpressionContext(_parentctx, _parentState));
((LogicalBinaryContext)_localctx).left = _prevctx;
pushNewRecursionContext(_localctx, _startState, RULE_booleanExpression);
- setState(471);
+ setState(466);
if (!(precpred(_ctx, 1))) throw new FailedPredicateException(this, "precpred(_ctx, 1)");
- setState(472);
+ setState(467);
((LogicalBinaryContext)_localctx).operator = match(OR);
- setState(473);
+ setState(468);
((LogicalBinaryContext)_localctx).right = booleanExpression(2);
}
break;
}
}
}
- setState(478);
+ setState(473);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,62,_ctx);
}
@@ -3267,19 +3208,19 @@ public final MatchQueryOptionsContext matchQueryOptions() throws RecognitionExce
try {
enterOuterAlt(_localctx, 1);
{
- setState(483);
+ setState(478);
_errHandler.sync(this);
_la = _input.LA(1);
while (_la==T__2) {
{
{
- setState(479);
+ setState(474);
match(T__2);
- setState(480);
+ setState(475);
string();
}
}
- setState(485);
+ setState(480);
_errHandler.sync(this);
_la = _input.LA(1);
}
@@ -3328,14 +3269,14 @@ public final PredicatedContext predicated() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(486);
+ setState(481);
valueExpression(0);
- setState(488);
+ setState(483);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,64,_ctx) ) {
case 1:
{
- setState(487);
+ setState(482);
predicate();
}
break;
@@ -3405,142 +3346,142 @@ public final PredicateContext predicate() throws RecognitionException {
enterRule(_localctx, 50, RULE_predicate);
int _la;
try {
- setState(536);
+ setState(531);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,72,_ctx) ) {
case 1:
enterOuterAlt(_localctx, 1);
{
- setState(491);
+ setState(486);
_la = _input.LA(1);
if (_la==NOT) {
{
- setState(490);
+ setState(485);
match(NOT);
}
}
- setState(493);
+ setState(488);
((PredicateContext)_localctx).kind = match(BETWEEN);
- setState(494);
+ setState(489);
((PredicateContext)_localctx).lower = valueExpression(0);
- setState(495);
+ setState(490);
match(AND);
- setState(496);
+ setState(491);
((PredicateContext)_localctx).upper = valueExpression(0);
}
break;
case 2:
enterOuterAlt(_localctx, 2);
{
- setState(499);
+ setState(494);
_la = _input.LA(1);
if (_la==NOT) {
{
- setState(498);
+ setState(493);
match(NOT);
}
}
- setState(501);
+ setState(496);
((PredicateContext)_localctx).kind = match(IN);
- setState(502);
+ setState(497);
match(T__0);
- setState(503);
+ setState(498);
valueExpression(0);
- setState(508);
+ setState(503);
_errHandler.sync(this);
_la = _input.LA(1);
while (_la==T__2) {
{
{
- setState(504);
+ setState(499);
match(T__2);
- setState(505);
+ setState(500);
valueExpression(0);
}
}
- setState(510);
+ setState(505);
_errHandler.sync(this);
_la = _input.LA(1);
}
- setState(511);
+ setState(506);
match(T__1);
}
break;
case 3:
enterOuterAlt(_localctx, 3);
{
- setState(514);
+ setState(509);
_la = _input.LA(1);
if (_la==NOT) {
{
- setState(513);
+ setState(508);
match(NOT);
}
}
- setState(516);
+ setState(511);
((PredicateContext)_localctx).kind = match(IN);
- setState(517);
+ setState(512);
match(T__0);
- setState(518);
+ setState(513);
query();
- setState(519);
+ setState(514);
match(T__1);
}
break;
case 4:
enterOuterAlt(_localctx, 4);
{
- setState(522);
+ setState(517);
_la = _input.LA(1);
if (_la==NOT) {
{
- setState(521);
+ setState(516);
match(NOT);
}
}
- setState(524);
+ setState(519);
((PredicateContext)_localctx).kind = match(LIKE);
- setState(525);
+ setState(520);
pattern();
}
break;
case 5:
enterOuterAlt(_localctx, 5);
{
- setState(527);
+ setState(522);
_la = _input.LA(1);
if (_la==NOT) {
{
- setState(526);
+ setState(521);
match(NOT);
}
}
- setState(529);
+ setState(524);
((PredicateContext)_localctx).kind = match(RLIKE);
- setState(530);
+ setState(525);
((PredicateContext)_localctx).regex = string();
}
break;
case 6:
enterOuterAlt(_localctx, 6);
{
- setState(531);
+ setState(526);
match(IS);
- setState(533);
+ setState(528);
_la = _input.LA(1);
if (_la==NOT) {
{
- setState(532);
+ setState(527);
match(NOT);
}
}
- setState(535);
+ setState(530);
((PredicateContext)_localctx).kind = match(NULL);
}
break;
@@ -3587,9 +3528,9 @@ public final LikePatternContext likePattern() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(538);
+ setState(533);
match(LIKE);
- setState(539);
+ setState(534);
pattern();
}
}
@@ -3637,14 +3578,14 @@ public final PatternContext pattern() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(541);
+ setState(536);
((PatternContext)_localctx).value = string();
- setState(543);
+ setState(538);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,73,_ctx) ) {
case 1:
{
- setState(542);
+ setState(537);
patternEscape();
}
break;
@@ -3692,25 +3633,25 @@ public final PatternEscapeContext patternEscape() throws RecognitionException {
PatternEscapeContext _localctx = new PatternEscapeContext(_ctx, getState());
enterRule(_localctx, 56, RULE_patternEscape);
try {
- setState(551);
+ setState(546);
switch (_input.LA(1)) {
case ESCAPE:
enterOuterAlt(_localctx, 1);
{
- setState(545);
+ setState(540);
match(ESCAPE);
- setState(546);
+ setState(541);
((PatternEscapeContext)_localctx).escape = string();
}
break;
case ESCAPE_ESC:
enterOuterAlt(_localctx, 2);
{
- setState(547);
+ setState(542);
match(ESCAPE_ESC);
- setState(548);
+ setState(543);
((PatternEscapeContext)_localctx).escape = string();
- setState(549);
+ setState(544);
match(ESC_END);
}
break;
@@ -3855,7 +3796,7 @@ private ValueExpressionContext valueExpression(int _p) throws RecognitionExcepti
int _alt;
enterOuterAlt(_localctx, 1);
{
- setState(557);
+ setState(552);
switch (_input.LA(1)) {
case T__0:
case ANALYZE:
@@ -3923,7 +3864,7 @@ private ValueExpressionContext valueExpression(int _p) throws RecognitionExcepti
_ctx = _localctx;
_prevctx = _localctx;
- setState(554);
+ setState(549);
primaryExpression();
}
break;
@@ -3933,7 +3874,7 @@ private ValueExpressionContext valueExpression(int _p) throws RecognitionExcepti
_localctx = new ArithmeticUnaryContext(_localctx);
_ctx = _localctx;
_prevctx = _localctx;
- setState(555);
+ setState(550);
((ArithmeticUnaryContext)_localctx).operator = _input.LT(1);
_la = _input.LA(1);
if ( !(_la==PLUS || _la==MINUS) ) {
@@ -3941,7 +3882,7 @@ private ValueExpressionContext valueExpression(int _p) throws RecognitionExcepti
} else {
consume();
}
- setState(556);
+ setState(551);
valueExpression(4);
}
break;
@@ -3949,7 +3890,7 @@ private ValueExpressionContext valueExpression(int _p) throws RecognitionExcepti
throw new NoViableAltException(this);
}
_ctx.stop = _input.LT(-1);
- setState(571);
+ setState(566);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,77,_ctx);
while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
@@ -3957,7 +3898,7 @@ private ValueExpressionContext valueExpression(int _p) throws RecognitionExcepti
if ( _parseListeners!=null ) triggerExitRuleEvent();
_prevctx = _localctx;
{
- setState(569);
+ setState(564);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,76,_ctx) ) {
case 1:
@@ -3965,9 +3906,9 @@ private ValueExpressionContext valueExpression(int _p) throws RecognitionExcepti
_localctx = new ArithmeticBinaryContext(new ValueExpressionContext(_parentctx, _parentState));
((ArithmeticBinaryContext)_localctx).left = _prevctx;
pushNewRecursionContext(_localctx, _startState, RULE_valueExpression);
- setState(559);
+ setState(554);
if (!(precpred(_ctx, 3))) throw new FailedPredicateException(this, "precpred(_ctx, 3)");
- setState(560);
+ setState(555);
((ArithmeticBinaryContext)_localctx).operator = _input.LT(1);
_la = _input.LA(1);
if ( !(((((_la - 109)) & ~0x3f) == 0 && ((1L << (_la - 109)) & ((1L << (ASTERISK - 109)) | (1L << (SLASH - 109)) | (1L << (PERCENT - 109)))) != 0)) ) {
@@ -3975,7 +3916,7 @@ private ValueExpressionContext valueExpression(int _p) throws RecognitionExcepti
} else {
consume();
}
- setState(561);
+ setState(556);
((ArithmeticBinaryContext)_localctx).right = valueExpression(4);
}
break;
@@ -3984,9 +3925,9 @@ private ValueExpressionContext valueExpression(int _p) throws RecognitionExcepti
_localctx = new ArithmeticBinaryContext(new ValueExpressionContext(_parentctx, _parentState));
((ArithmeticBinaryContext)_localctx).left = _prevctx;
pushNewRecursionContext(_localctx, _startState, RULE_valueExpression);
- setState(562);
+ setState(557);
if (!(precpred(_ctx, 2))) throw new FailedPredicateException(this, "precpred(_ctx, 2)");
- setState(563);
+ setState(558);
((ArithmeticBinaryContext)_localctx).operator = _input.LT(1);
_la = _input.LA(1);
if ( !(_la==PLUS || _la==MINUS) ) {
@@ -3994,7 +3935,7 @@ private ValueExpressionContext valueExpression(int _p) throws RecognitionExcepti
} else {
consume();
}
- setState(564);
+ setState(559);
((ArithmeticBinaryContext)_localctx).right = valueExpression(3);
}
break;
@@ -4003,18 +3944,18 @@ private ValueExpressionContext valueExpression(int _p) throws RecognitionExcepti
_localctx = new ComparisonContext(new ValueExpressionContext(_parentctx, _parentState));
((ComparisonContext)_localctx).left = _prevctx;
pushNewRecursionContext(_localctx, _startState, RULE_valueExpression);
- setState(565);
+ setState(560);
if (!(precpred(_ctx, 1))) throw new FailedPredicateException(this, "precpred(_ctx, 1)");
- setState(566);
+ setState(561);
comparisonOperator();
- setState(567);
+ setState(562);
((ComparisonContext)_localctx).right = valueExpression(2);
}
break;
}
}
}
- setState(573);
+ setState(568);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,77,_ctx);
}
@@ -4221,14 +4162,14 @@ public final PrimaryExpressionContext primaryExpression() throws RecognitionExce
enterRule(_localctx, 60, RULE_primaryExpression);
int _la;
try {
- setState(594);
+ setState(589);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,79,_ctx) ) {
case 1:
_localctx = new CastContext(_localctx);
enterOuterAlt(_localctx, 1);
{
- setState(574);
+ setState(569);
castExpression();
}
break;
@@ -4236,7 +4177,7 @@ public final PrimaryExpressionContext primaryExpression() throws RecognitionExce
_localctx = new ExtractContext(_localctx);
enterOuterAlt(_localctx, 2);
{
- setState(575);
+ setState(570);
extractExpression();
}
break;
@@ -4244,7 +4185,7 @@ public final PrimaryExpressionContext primaryExpression() throws RecognitionExce
_localctx = new CurrentDateTimeFunctionContext(_localctx);
enterOuterAlt(_localctx, 3);
{
- setState(576);
+ setState(571);
builtinDateTimeFunction();
}
break;
@@ -4252,7 +4193,7 @@ public final PrimaryExpressionContext primaryExpression() throws RecognitionExce
_localctx = new ConstantDefaultContext(_localctx);
enterOuterAlt(_localctx, 4);
{
- setState(577);
+ setState(572);
constant();
}
break;
@@ -4260,18 +4201,18 @@ public final PrimaryExpressionContext primaryExpression() throws RecognitionExce
_localctx = new StarContext(_localctx);
enterOuterAlt(_localctx, 5);
{
- setState(581);
+ setState(576);
_la = _input.LA(1);
if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << ANALYZE) | (1L << ANALYZED) | (1L << CATALOGS) | (1L << COLUMNS) | (1L << CURRENT) | (1L << DAY) | (1L << DEBUG) | (1L << EXECUTABLE) | (1L << EXPLAIN) | (1L << FIRST) | (1L << FORMAT) | (1L << FULL) | (1L << FUNCTIONS) | (1L << GRAPHVIZ) | (1L << HOUR) | (1L << INTERVAL) | (1L << LAST) | (1L << LIMIT) | (1L << MAPPED) | (1L << MINUTE) | (1L << MONTH) | (1L << OPTIMIZED))) != 0) || ((((_la - 67)) & ~0x3f) == 0 && ((1L << (_la - 67)) & ((1L << (PARSED - 67)) | (1L << (PHYSICAL - 67)) | (1L << (PLAN - 67)) | (1L << (RLIKE - 67)) | (1L << (QUERY - 67)) | (1L << (SCHEMAS - 67)) | (1L << (SECOND - 67)) | (1L << (SHOW - 67)) | (1L << (SYS - 67)) | (1L << (TABLES - 67)) | (1L << (TEXT - 67)) | (1L << (TYPE - 67)) | (1L << (TYPES - 67)) | (1L << (VERIFY - 67)) | (1L << (YEAR - 67)) | (1L << (IDENTIFIER - 67)) | (1L << (DIGIT_IDENTIFIER - 67)) | (1L << (QUOTED_IDENTIFIER - 67)) | (1L << (BACKQUOTED_IDENTIFIER - 67)))) != 0)) {
{
- setState(578);
+ setState(573);
qualifiedName();
- setState(579);
+ setState(574);
match(DOT);
}
}
- setState(583);
+ setState(578);
match(ASTERISK);
}
break;
@@ -4279,7 +4220,7 @@ public final PrimaryExpressionContext primaryExpression() throws RecognitionExce
_localctx = new FunctionContext(_localctx);
enterOuterAlt(_localctx, 6);
{
- setState(584);
+ setState(579);
functionExpression();
}
break;
@@ -4287,11 +4228,11 @@ public final PrimaryExpressionContext primaryExpression() throws RecognitionExce
_localctx = new SubqueryExpressionContext(_localctx);
enterOuterAlt(_localctx, 7);
{
- setState(585);
+ setState(580);
match(T__0);
- setState(586);
+ setState(581);
query();
- setState(587);
+ setState(582);
match(T__1);
}
break;
@@ -4299,7 +4240,7 @@ public final PrimaryExpressionContext primaryExpression() throws RecognitionExce
_localctx = new DereferenceContext(_localctx);
enterOuterAlt(_localctx, 8);
{
- setState(589);
+ setState(584);
qualifiedName();
}
break;
@@ -4307,11 +4248,11 @@ public final PrimaryExpressionContext primaryExpression() throws RecognitionExce
_localctx = new ParenthesizedExpressionContext(_localctx);
enterOuterAlt(_localctx, 9);
{
- setState(590);
+ setState(585);
match(T__0);
- setState(591);
+ setState(586);
expression();
- setState(592);
+ setState(587);
match(T__1);
}
break;
@@ -4360,42 +4301,42 @@ public final CastExpressionContext castExpression() throws RecognitionException
CastExpressionContext _localctx = new CastExpressionContext(_ctx, getState());
enterRule(_localctx, 62, RULE_castExpression);
try {
- setState(606);
+ setState(601);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,80,_ctx) ) {
case 1:
enterOuterAlt(_localctx, 1);
{
- setState(596);
+ setState(591);
castTemplate();
}
break;
case 2:
enterOuterAlt(_localctx, 2);
{
- setState(597);
+ setState(592);
match(FUNCTION_ESC);
- setState(598);
+ setState(593);
castTemplate();
- setState(599);
+ setState(594);
match(ESC_END);
}
break;
case 3:
enterOuterAlt(_localctx, 3);
{
- setState(601);
+ setState(596);
convertTemplate();
}
break;
case 4:
enterOuterAlt(_localctx, 4);
{
- setState(602);
+ setState(597);
match(FUNCTION_ESC);
- setState(603);
+ setState(598);
convertTemplate();
- setState(604);
+ setState(599);
match(ESC_END);
}
break;
@@ -4446,17 +4387,17 @@ public final CastTemplateContext castTemplate() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(608);
+ setState(603);
match(CAST);
- setState(609);
+ setState(604);
match(T__0);
- setState(610);
+ setState(605);
expression();
- setState(611);
+ setState(606);
match(AS);
- setState(612);
+ setState(607);
dataType();
- setState(613);
+ setState(608);
match(T__1);
}
}
@@ -4502,25 +4443,25 @@ public final BuiltinDateTimeFunctionContext builtinDateTimeFunction() throws Rec
try {
enterOuterAlt(_localctx, 1);
{
- setState(615);
+ setState(610);
((BuiltinDateTimeFunctionContext)_localctx).name = match(CURRENT_TIMESTAMP);
- setState(621);
+ setState(616);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,82,_ctx) ) {
case 1:
{
- setState(616);
+ setState(611);
match(T__0);
- setState(618);
+ setState(613);
_la = _input.LA(1);
if (_la==INTEGER_VALUE) {
{
- setState(617);
+ setState(612);
((BuiltinDateTimeFunctionContext)_localctx).precision = match(INTEGER_VALUE);
}
}
- setState(620);
+ setState(615);
match(T__1);
}
break;
@@ -4571,17 +4512,17 @@ public final ConvertTemplateContext convertTemplate() throws RecognitionExceptio
try {
enterOuterAlt(_localctx, 1);
{
- setState(623);
+ setState(618);
match(CONVERT);
- setState(624);
+ setState(619);
match(T__0);
- setState(625);
+ setState(620);
expression();
- setState(626);
+ setState(621);
match(T__2);
- setState(627);
+ setState(622);
dataType();
- setState(628);
+ setState(623);
match(T__1);
}
}
@@ -4625,23 +4566,23 @@ public final ExtractExpressionContext extractExpression() throws RecognitionExce
ExtractExpressionContext _localctx = new ExtractExpressionContext(_ctx, getState());
enterRule(_localctx, 70, RULE_extractExpression);
try {
- setState(635);
+ setState(630);
switch (_input.LA(1)) {
case EXTRACT:
enterOuterAlt(_localctx, 1);
{
- setState(630);
+ setState(625);
extractTemplate();
}
break;
case FUNCTION_ESC:
enterOuterAlt(_localctx, 2);
{
- setState(631);
+ setState(626);
match(FUNCTION_ESC);
- setState(632);
+ setState(627);
extractTemplate();
- setState(633);
+ setState(628);
match(ESC_END);
}
break;
@@ -4695,17 +4636,17 @@ public final ExtractTemplateContext extractTemplate() throws RecognitionExceptio
try {
enterOuterAlt(_localctx, 1);
{
- setState(637);
+ setState(632);
match(EXTRACT);
- setState(638);
+ setState(633);
match(T__0);
- setState(639);
+ setState(634);
((ExtractTemplateContext)_localctx).field = identifier();
- setState(640);
+ setState(635);
match(FROM);
- setState(641);
+ setState(636);
valueExpression(0);
- setState(642);
+ setState(637);
match(T__1);
}
}
@@ -4748,7 +4689,7 @@ public final FunctionExpressionContext functionExpression() throws RecognitionEx
FunctionExpressionContext _localctx = new FunctionExpressionContext(_ctx, getState());
enterRule(_localctx, 74, RULE_functionExpression);
try {
- setState(649);
+ setState(644);
switch (_input.LA(1)) {
case ANALYZE:
case ANALYZED:
@@ -4795,18 +4736,18 @@ public final FunctionExpressionContext functionExpression() throws RecognitionEx
case BACKQUOTED_IDENTIFIER:
enterOuterAlt(_localctx, 1);
{
- setState(644);
+ setState(639);
functionTemplate();
}
break;
case FUNCTION_ESC:
enterOuterAlt(_localctx, 2);
{
- setState(645);
+ setState(640);
match(FUNCTION_ESC);
- setState(646);
+ setState(641);
functionTemplate();
- setState(647);
+ setState(642);
match(ESC_END);
}
break;
@@ -4864,45 +4805,45 @@ public final FunctionTemplateContext functionTemplate() throws RecognitionExcept
try {
enterOuterAlt(_localctx, 1);
{
- setState(651);
+ setState(646);
functionName();
- setState(652);
+ setState(647);
match(T__0);
- setState(664);
+ setState(659);
_la = _input.LA(1);
if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__0) | (1L << ALL) | (1L << ANALYZE) | (1L << ANALYZED) | (1L << CAST) | (1L << CATALOGS) | (1L << COLUMNS) | (1L << CONVERT) | (1L << CURRENT) | (1L << CURRENT_TIMESTAMP) | (1L << DAY) | (1L << DEBUG) | (1L << DISTINCT) | (1L << EXECUTABLE) | (1L << EXISTS) | (1L << EXPLAIN) | (1L << EXTRACT) | (1L << FALSE) | (1L << FIRST) | (1L << FORMAT) | (1L << FULL) | (1L << FUNCTIONS) | (1L << GRAPHVIZ) | (1L << HOUR) | (1L << INTERVAL) | (1L << LAST) | (1L << LEFT) | (1L << LIMIT) | (1L << MAPPED) | (1L << MATCH) | (1L << MINUTE) | (1L << MONTH) | (1L << NOT) | (1L << NULL) | (1L << OPTIMIZED))) != 0) || ((((_la - 67)) & ~0x3f) == 0 && ((1L << (_la - 67)) & ((1L << (PARSED - 67)) | (1L << (PHYSICAL - 67)) | (1L << (PLAN - 67)) | (1L << (RIGHT - 67)) | (1L << (RLIKE - 67)) | (1L << (QUERY - 67)) | (1L << (SCHEMAS - 67)) | (1L << (SECOND - 67)) | (1L << (SHOW - 67)) | (1L << (SYS - 67)) | (1L << (TABLES - 67)) | (1L << (TEXT - 67)) | (1L << (TRUE - 67)) | (1L << (TYPE - 67)) | (1L << (TYPES - 67)) | (1L << (VERIFY - 67)) | (1L << (YEAR - 67)) | (1L << (FUNCTION_ESC - 67)) | (1L << (DATE_ESC - 67)) | (1L << (TIME_ESC - 67)) | (1L << (TIMESTAMP_ESC - 67)) | (1L << (GUID_ESC - 67)) | (1L << (PLUS - 67)) | (1L << (MINUS - 67)) | (1L << (ASTERISK - 67)) | (1L << (PARAM - 67)) | (1L << (STRING - 67)) | (1L << (INTEGER_VALUE - 67)) | (1L << (DECIMAL_VALUE - 67)) | (1L << (IDENTIFIER - 67)) | (1L << (DIGIT_IDENTIFIER - 67)) | (1L << (QUOTED_IDENTIFIER - 67)) | (1L << (BACKQUOTED_IDENTIFIER - 67)))) != 0)) {
{
- setState(654);
+ setState(649);
_la = _input.LA(1);
if (_la==ALL || _la==DISTINCT) {
{
- setState(653);
+ setState(648);
setQuantifier();
}
}
- setState(656);
+ setState(651);
expression();
- setState(661);
+ setState(656);
_errHandler.sync(this);
_la = _input.LA(1);
while (_la==T__2) {
{
{
- setState(657);
+ setState(652);
match(T__2);
- setState(658);
+ setState(653);
expression();
}
}
- setState(663);
+ setState(658);
_errHandler.sync(this);
_la = _input.LA(1);
}
}
}
- setState(666);
+ setState(661);
match(T__1);
}
}
@@ -4946,19 +4887,19 @@ public final FunctionNameContext functionName() throws RecognitionException {
FunctionNameContext _localctx = new FunctionNameContext(_ctx, getState());
enterRule(_localctx, 78, RULE_functionName);
try {
- setState(671);
+ setState(666);
switch (_input.LA(1)) {
case LEFT:
enterOuterAlt(_localctx, 1);
{
- setState(668);
+ setState(663);
match(LEFT);
}
break;
case RIGHT:
enterOuterAlt(_localctx, 2);
{
- setState(669);
+ setState(664);
match(RIGHT);
}
break;
@@ -5005,7 +4946,7 @@ public final FunctionNameContext functionName() throws RecognitionException {
case BACKQUOTED_IDENTIFIER:
enterOuterAlt(_localctx, 3);
{
- setState(670);
+ setState(665);
identifier();
}
break;
@@ -5236,13 +5177,13 @@ public final ConstantContext constant() throws RecognitionException {
enterRule(_localctx, 80, RULE_constant);
try {
int _alt;
- setState(699);
+ setState(694);
switch (_input.LA(1)) {
case NULL:
_localctx = new NullLiteralContext(_localctx);
enterOuterAlt(_localctx, 1);
{
- setState(673);
+ setState(668);
match(NULL);
}
break;
@@ -5250,7 +5191,7 @@ public final ConstantContext constant() throws RecognitionException {
_localctx = new IntervalLiteralContext(_localctx);
enterOuterAlt(_localctx, 2);
{
- setState(674);
+ setState(669);
interval();
}
break;
@@ -5259,7 +5200,7 @@ public final ConstantContext constant() throws RecognitionException {
_localctx = new NumericLiteralContext(_localctx);
enterOuterAlt(_localctx, 3);
{
- setState(675);
+ setState(670);
number();
}
break;
@@ -5268,7 +5209,7 @@ public final ConstantContext constant() throws RecognitionException {
_localctx = new BooleanLiteralContext(_localctx);
enterOuterAlt(_localctx, 4);
{
- setState(676);
+ setState(671);
booleanValue();
}
break;
@@ -5276,7 +5217,7 @@ public final ConstantContext constant() throws RecognitionException {
_localctx = new StringLiteralContext(_localctx);
enterOuterAlt(_localctx, 5);
{
- setState(678);
+ setState(673);
_errHandler.sync(this);
_alt = 1;
do {
@@ -5284,7 +5225,7 @@ public final ConstantContext constant() throws RecognitionException {
case 1:
{
{
- setState(677);
+ setState(672);
match(STRING);
}
}
@@ -5292,7 +5233,7 @@ public final ConstantContext constant() throws RecognitionException {
default:
throw new NoViableAltException(this);
}
- setState(680);
+ setState(675);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,89,_ctx);
} while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER );
@@ -5302,7 +5243,7 @@ public final ConstantContext constant() throws RecognitionException {
_localctx = new ParamLiteralContext(_localctx);
enterOuterAlt(_localctx, 6);
{
- setState(682);
+ setState(677);
match(PARAM);
}
break;
@@ -5310,11 +5251,11 @@ public final ConstantContext constant() throws RecognitionException {
_localctx = new DateEscapedLiteralContext(_localctx);
enterOuterAlt(_localctx, 7);
{
- setState(683);
+ setState(678);
match(DATE_ESC);
- setState(684);
+ setState(679);
string();
- setState(685);
+ setState(680);
match(ESC_END);
}
break;
@@ -5322,11 +5263,11 @@ public final ConstantContext constant() throws RecognitionException {
_localctx = new TimeEscapedLiteralContext(_localctx);
enterOuterAlt(_localctx, 8);
{
- setState(687);
+ setState(682);
match(TIME_ESC);
- setState(688);
+ setState(683);
string();
- setState(689);
+ setState(684);
match(ESC_END);
}
break;
@@ -5334,11 +5275,11 @@ public final ConstantContext constant() throws RecognitionException {
_localctx = new TimestampEscapedLiteralContext(_localctx);
enterOuterAlt(_localctx, 9);
{
- setState(691);
+ setState(686);
match(TIMESTAMP_ESC);
- setState(692);
+ setState(687);
string();
- setState(693);
+ setState(688);
match(ESC_END);
}
break;
@@ -5346,11 +5287,11 @@ public final ConstantContext constant() throws RecognitionException {
_localctx = new GuidEscapedLiteralContext(_localctx);
enterOuterAlt(_localctx, 10);
{
- setState(695);
+ setState(690);
match(GUID_ESC);
- setState(696);
+ setState(691);
string();
- setState(697);
+ setState(692);
match(ESC_END);
}
break;
@@ -5403,7 +5344,7 @@ public final ComparisonOperatorContext comparisonOperator() throws RecognitionEx
try {
enterOuterAlt(_localctx, 1);
{
- setState(701);
+ setState(696);
_la = _input.LA(1);
if ( !(((((_la - 100)) & ~0x3f) == 0 && ((1L << (_la - 100)) & ((1L << (EQ - 100)) | (1L << (NULLEQ - 100)) | (1L << (NEQ - 100)) | (1L << (LT - 100)) | (1L << (LTE - 100)) | (1L << (GT - 100)) | (1L << (GTE - 100)))) != 0)) ) {
_errHandler.recoverInline(this);
@@ -5452,7 +5393,7 @@ public final BooleanValueContext booleanValue() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(703);
+ setState(698);
_la = _input.LA(1);
if ( !(_la==FALSE || _la==TRUE) ) {
_errHandler.recoverInline(this);
@@ -5520,13 +5461,13 @@ public final IntervalContext interval() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(705);
+ setState(700);
match(INTERVAL);
- setState(707);
+ setState(702);
_la = _input.LA(1);
if (_la==PLUS || _la==MINUS) {
{
- setState(706);
+ setState(701);
((IntervalContext)_localctx).sign = _input.LT(1);
_la = _input.LA(1);
if ( !(_la==PLUS || _la==MINUS) ) {
@@ -5537,35 +5478,35 @@ public final IntervalContext interval() throws RecognitionException {
}
}
- setState(711);
+ setState(706);
switch (_input.LA(1)) {
case INTEGER_VALUE:
case DECIMAL_VALUE:
{
- setState(709);
+ setState(704);
((IntervalContext)_localctx).valueNumeric = number();
}
break;
case PARAM:
case STRING:
{
- setState(710);
+ setState(705);
((IntervalContext)_localctx).valuePattern = string();
}
break;
default:
throw new NoViableAltException(this);
}
- setState(713);
+ setState(708);
((IntervalContext)_localctx).leading = intervalField();
- setState(716);
+ setState(711);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,93,_ctx) ) {
case 1:
{
- setState(714);
+ setState(709);
match(TO);
- setState(715);
+ setState(710);
((IntervalContext)_localctx).trailing = intervalField();
}
break;
@@ -5622,7 +5563,7 @@ public final IntervalFieldContext intervalField() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(718);
+ setState(713);
_la = _input.LA(1);
if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << DAY) | (1L << DAYS) | (1L << HOUR) | (1L << HOURS) | (1L << MINUTE) | (1L << MINUTES) | (1L << MONTH) | (1L << MONTHS))) != 0) || ((((_la - 74)) & ~0x3f) == 0 && ((1L << (_la - 74)) & ((1L << (SECOND - 74)) | (1L << (SECONDS - 74)) | (1L << (YEAR - 74)) | (1L << (YEARS - 74)))) != 0)) ) {
_errHandler.recoverInline(this);
@@ -5680,7 +5621,7 @@ public final DataTypeContext dataType() throws RecognitionException {
_localctx = new PrimitiveDataTypeContext(_localctx);
enterOuterAlt(_localctx, 1);
{
- setState(720);
+ setState(715);
identifier();
}
}
@@ -5732,25 +5673,25 @@ public final QualifiedNameContext qualifiedName() throws RecognitionException {
int _alt;
enterOuterAlt(_localctx, 1);
{
- setState(727);
+ setState(722);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,94,_ctx);
while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
if ( _alt==1 ) {
{
{
- setState(722);
+ setState(717);
identifier();
- setState(723);
+ setState(718);
match(DOT);
}
}
}
- setState(729);
+ setState(724);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,94,_ctx);
}
- setState(730);
+ setState(725);
identifier();
}
}
@@ -5795,13 +5736,13 @@ public final IdentifierContext identifier() throws RecognitionException {
IdentifierContext _localctx = new IdentifierContext(_ctx, getState());
enterRule(_localctx, 94, RULE_identifier);
try {
- setState(734);
+ setState(729);
switch (_input.LA(1)) {
case QUOTED_IDENTIFIER:
case BACKQUOTED_IDENTIFIER:
enterOuterAlt(_localctx, 1);
{
- setState(732);
+ setState(727);
quoteIdentifier();
}
break;
@@ -5846,7 +5787,7 @@ public final IdentifierContext identifier() throws RecognitionException {
case DIGIT_IDENTIFIER:
enterOuterAlt(_localctx, 2);
{
- setState(733);
+ setState(728);
unquoteIdentifier();
}
break;
@@ -5899,43 +5840,43 @@ public final TableIdentifierContext tableIdentifier() throws RecognitionExceptio
enterRule(_localctx, 96, RULE_tableIdentifier);
int _la;
try {
- setState(748);
+ setState(743);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,98,_ctx) ) {
case 1:
enterOuterAlt(_localctx, 1);
{
- setState(739);
+ setState(734);
_la = _input.LA(1);
if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << ANALYZE) | (1L << ANALYZED) | (1L << CATALOGS) | (1L << COLUMNS) | (1L << CURRENT) | (1L << DAY) | (1L << DEBUG) | (1L << EXECUTABLE) | (1L << EXPLAIN) | (1L << FIRST) | (1L << FORMAT) | (1L << FULL) | (1L << FUNCTIONS) | (1L << GRAPHVIZ) | (1L << HOUR) | (1L << INTERVAL) | (1L << LAST) | (1L << LIMIT) | (1L << MAPPED) | (1L << MINUTE) | (1L << MONTH) | (1L << OPTIMIZED))) != 0) || ((((_la - 67)) & ~0x3f) == 0 && ((1L << (_la - 67)) & ((1L << (PARSED - 67)) | (1L << (PHYSICAL - 67)) | (1L << (PLAN - 67)) | (1L << (RLIKE - 67)) | (1L << (QUERY - 67)) | (1L << (SCHEMAS - 67)) | (1L << (SECOND - 67)) | (1L << (SHOW - 67)) | (1L << (SYS - 67)) | (1L << (TABLES - 67)) | (1L << (TEXT - 67)) | (1L << (TYPE - 67)) | (1L << (TYPES - 67)) | (1L << (VERIFY - 67)) | (1L << (YEAR - 67)) | (1L << (IDENTIFIER - 67)) | (1L << (DIGIT_IDENTIFIER - 67)) | (1L << (QUOTED_IDENTIFIER - 67)) | (1L << (BACKQUOTED_IDENTIFIER - 67)))) != 0)) {
{
- setState(736);
+ setState(731);
((TableIdentifierContext)_localctx).catalog = identifier();
- setState(737);
+ setState(732);
match(T__3);
}
}
- setState(741);
+ setState(736);
match(TABLE_IDENTIFIER);
}
break;
case 2:
enterOuterAlt(_localctx, 2);
{
- setState(745);
+ setState(740);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,97,_ctx) ) {
case 1:
{
- setState(742);
+ setState(737);
((TableIdentifierContext)_localctx).catalog = identifier();
- setState(743);
+ setState(738);
match(T__3);
}
break;
}
- setState(747);
+ setState(742);
((TableIdentifierContext)_localctx).name = identifier();
}
break;
@@ -6002,13 +5943,13 @@ public final QuoteIdentifierContext quoteIdentifier() throws RecognitionExceptio
QuoteIdentifierContext _localctx = new QuoteIdentifierContext(_ctx, getState());
enterRule(_localctx, 98, RULE_quoteIdentifier);
try {
- setState(752);
+ setState(747);
switch (_input.LA(1)) {
case QUOTED_IDENTIFIER:
_localctx = new QuotedIdentifierContext(_localctx);
enterOuterAlt(_localctx, 1);
{
- setState(750);
+ setState(745);
match(QUOTED_IDENTIFIER);
}
break;
@@ -6016,7 +5957,7 @@ public final QuoteIdentifierContext quoteIdentifier() throws RecognitionExceptio
_localctx = new BackQuotedIdentifierContext(_localctx);
enterOuterAlt(_localctx, 2);
{
- setState(751);
+ setState(746);
match(BACKQUOTED_IDENTIFIER);
}
break;
@@ -6088,13 +6029,13 @@ public final UnquoteIdentifierContext unquoteIdentifier() throws RecognitionExce
UnquoteIdentifierContext _localctx = new UnquoteIdentifierContext(_ctx, getState());
enterRule(_localctx, 100, RULE_unquoteIdentifier);
try {
- setState(757);
+ setState(752);
switch (_input.LA(1)) {
case IDENTIFIER:
_localctx = new UnquotedIdentifierContext(_localctx);
enterOuterAlt(_localctx, 1);
{
- setState(754);
+ setState(749);
match(IDENTIFIER);
}
break;
@@ -6138,7 +6079,7 @@ public final UnquoteIdentifierContext unquoteIdentifier() throws RecognitionExce
_localctx = new UnquotedIdentifierContext(_localctx);
enterOuterAlt(_localctx, 2);
{
- setState(755);
+ setState(750);
nonReserved();
}
break;
@@ -6146,7 +6087,7 @@ public final UnquoteIdentifierContext unquoteIdentifier() throws RecognitionExce
_localctx = new DigitIdentifierContext(_localctx);
enterOuterAlt(_localctx, 3);
{
- setState(756);
+ setState(751);
match(DIGIT_IDENTIFIER);
}
break;
@@ -6215,13 +6156,13 @@ public final NumberContext number() throws RecognitionException {
NumberContext _localctx = new NumberContext(_ctx, getState());
enterRule(_localctx, 102, RULE_number);
try {
- setState(761);
+ setState(756);
switch (_input.LA(1)) {
case DECIMAL_VALUE:
_localctx = new DecimalLiteralContext(_localctx);
enterOuterAlt(_localctx, 1);
{
- setState(759);
+ setState(754);
match(DECIMAL_VALUE);
}
break;
@@ -6229,7 +6170,7 @@ public final NumberContext number() throws RecognitionException {
_localctx = new IntegerLiteralContext(_localctx);
enterOuterAlt(_localctx, 2);
{
- setState(760);
+ setState(755);
match(INTEGER_VALUE);
}
break;
@@ -6277,7 +6218,7 @@ public final StringContext string() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(763);
+ setState(758);
_la = _input.LA(1);
if ( !(_la==PARAM || _la==STRING) ) {
_errHandler.recoverInline(this);
@@ -6361,7 +6302,7 @@ public final NonReservedContext nonReserved() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(765);
+ setState(760);
_la = _input.LA(1);
if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << ANALYZE) | (1L << ANALYZED) | (1L << CATALOGS) | (1L << COLUMNS) | (1L << CURRENT) | (1L << DAY) | (1L << DEBUG) | (1L << EXECUTABLE) | (1L << EXPLAIN) | (1L << FIRST) | (1L << FORMAT) | (1L << FULL) | (1L << FUNCTIONS) | (1L << GRAPHVIZ) | (1L << HOUR) | (1L << INTERVAL) | (1L << LAST) | (1L << LIMIT) | (1L << MAPPED) | (1L << MINUTE) | (1L << MONTH) | (1L << OPTIMIZED))) != 0) || ((((_la - 67)) & ~0x3f) == 0 && ((1L << (_la - 67)) & ((1L << (PARSED - 67)) | (1L << (PHYSICAL - 67)) | (1L << (PLAN - 67)) | (1L << (RLIKE - 67)) | (1L << (QUERY - 67)) | (1L << (SCHEMAS - 67)) | (1L << (SECOND - 67)) | (1L << (SHOW - 67)) | (1L << (SYS - 67)) | (1L << (TABLES - 67)) | (1L << (TEXT - 67)) | (1L << (TYPE - 67)) | (1L << (TYPES - 67)) | (1L << (VERIFY - 67)) | (1L << (YEAR - 67)))) != 0)) ) {
_errHandler.recoverInline(this);
@@ -6412,7 +6353,7 @@ private boolean valueExpression_sempred(ValueExpressionContext _localctx, int pr
}
public static final String _serializedATN =
- "\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\3\u0081\u0302\4\2\t"+
+ "\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\3\u0081\u02fd\4\2\t"+
"\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13"+
"\t\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22"+
"\4\23\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30\4\31\t\31"+
@@ -6424,300 +6365,298 @@ private boolean valueExpression_sempred(ValueExpressionContext _localctx, int pr
"\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\7\4\u008d\n\4\f\4\16\4\u0090\13\4\3\4\5"+
"\4\u0093\n\4\3\4\3\4\3\4\3\4\3\4\5\4\u009a\n\4\3\4\3\4\3\4\3\4\3\4\5\4"+
"\u00a1\n\4\3\4\3\4\3\4\5\4\u00a6\n\4\3\4\3\4\3\4\5\4\u00ab\n\4\3\4\3\4"+
- "\3\4\3\4\3\4\3\4\3\4\3\4\5\4\u00b5\n\4\3\4\3\4\5\4\u00b9\n\4\3\4\3\4\3"+
- "\4\3\4\7\4\u00bf\n\4\f\4\16\4\u00c2\13\4\5\4\u00c4\n\4\3\4\3\4\3\4\3\4"+
- "\5\4\u00ca\n\4\3\4\3\4\3\4\5\4\u00cf\n\4\3\4\5\4\u00d2\n\4\3\4\3\4\3\4"+
- "\5\4\u00d7\n\4\3\4\5\4\u00da\n\4\3\4\3\4\3\4\5\4\u00df\n\4\3\5\3\5\3\5"+
- "\3\5\7\5\u00e5\n\5\f\5\16\5\u00e8\13\5\5\5\u00ea\n\5\3\5\3\5\3\6\3\6\3"+
- "\6\3\6\3\6\3\6\7\6\u00f4\n\6\f\6\16\6\u00f7\13\6\5\6\u00f9\n\6\3\6\5\6"+
- "\u00fc\n\6\3\7\3\7\3\7\3\7\3\7\5\7\u0103\n\7\3\b\3\b\3\b\3\b\3\b\5\b\u010a"+
- "\n\b\3\t\3\t\5\t\u010e\n\t\3\t\3\t\5\t\u0112\n\t\3\n\3\n\5\n\u0116\n\n"+
- "\3\n\3\n\3\n\7\n\u011b\n\n\f\n\16\n\u011e\13\n\3\n\5\n\u0121\n\n\3\n\3"+
- "\n\5\n\u0125\n\n\3\n\3\n\3\n\5\n\u012a\n\n\3\n\3\n\5\n\u012e\n\n\3\13"+
- "\3\13\3\13\3\13\7\13\u0134\n\13\f\13\16\13\u0137\13\13\3\f\5\f\u013a\n"+
- "\f\3\f\3\f\3\f\7\f\u013f\n\f\f\f\16\f\u0142\13\f\3\r\3\r\3\16\3\16\3\16"+
- "\3\16\7\16\u014a\n\16\f\16\16\16\u014d\13\16\5\16\u014f\n\16\3\16\3\16"+
- "\5\16\u0153\n\16\3\17\3\17\3\17\3\17\3\17\3\17\3\20\3\20\3\21\3\21\5\21"+
- "\u015f\n\21\3\21\5\21\u0162\n\21\3\22\3\22\7\22\u0166\n\22\f\22\16\22"+
- "\u0169\13\22\3\23\3\23\3\23\3\23\5\23\u016f\n\23\3\23\3\23\3\23\3\23\3"+
- "\23\5\23\u0176\n\23\3\24\5\24\u0179\n\24\3\24\3\24\5\24\u017d\n\24\3\24"+
- "\3\24\5\24\u0181\n\24\3\24\3\24\5\24\u0185\n\24\5\24\u0187\n\24\3\25\3"+
- "\25\3\25\3\25\3\25\3\25\3\25\7\25\u0190\n\25\f\25\16\25\u0193\13\25\3"+
- "\25\3\25\5\25\u0197\n\25\3\26\3\26\5\26\u019b\n\26\3\26\5\26\u019e\n\26"+
- "\3\26\3\26\3\26\3\26\5\26\u01a4\n\26\3\26\5\26\u01a7\n\26\3\26\3\26\3"+
- "\26\3\26\5\26\u01ad\n\26\3\26\5\26\u01b0\n\26\5\26\u01b2\n\26\3\27\3\27"+
- "\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30"+
+ "\3\4\3\4\3\4\3\4\5\4\u00b3\n\4\3\4\3\4\5\4\u00b7\n\4\3\4\3\4\3\4\3\4\7"+
+ "\4\u00bd\n\4\f\4\16\4\u00c0\13\4\5\4\u00c2\n\4\3\4\3\4\3\4\3\4\5\4\u00c8"+
+ "\n\4\3\4\3\4\3\4\5\4\u00cd\n\4\3\4\5\4\u00d0\n\4\3\4\3\4\3\4\5\4\u00d5"+
+ "\n\4\3\4\5\4\u00d8\n\4\5\4\u00da\n\4\3\5\3\5\3\5\3\5\7\5\u00e0\n\5\f\5"+
+ "\16\5\u00e3\13\5\5\5\u00e5\n\5\3\5\3\5\3\6\3\6\3\6\3\6\3\6\3\6\7\6\u00ef"+
+ "\n\6\f\6\16\6\u00f2\13\6\5\6\u00f4\n\6\3\6\5\6\u00f7\n\6\3\7\3\7\3\7\3"+
+ "\7\3\7\5\7\u00fe\n\7\3\b\3\b\3\b\3\b\3\b\5\b\u0105\n\b\3\t\3\t\5\t\u0109"+
+ "\n\t\3\t\3\t\5\t\u010d\n\t\3\n\3\n\5\n\u0111\n\n\3\n\3\n\3\n\7\n\u0116"+
+ "\n\n\f\n\16\n\u0119\13\n\3\n\5\n\u011c\n\n\3\n\3\n\5\n\u0120\n\n\3\n\3"+
+ "\n\3\n\5\n\u0125\n\n\3\n\3\n\5\n\u0129\n\n\3\13\3\13\3\13\3\13\7\13\u012f"+
+ "\n\13\f\13\16\13\u0132\13\13\3\f\5\f\u0135\n\f\3\f\3\f\3\f\7\f\u013a\n"+
+ "\f\f\f\16\f\u013d\13\f\3\r\3\r\3\16\3\16\3\16\3\16\7\16\u0145\n\16\f\16"+
+ "\16\16\u0148\13\16\5\16\u014a\n\16\3\16\3\16\5\16\u014e\n\16\3\17\3\17"+
+ "\3\17\3\17\3\17\3\17\3\20\3\20\3\21\3\21\5\21\u015a\n\21\3\21\5\21\u015d"+
+ "\n\21\3\22\3\22\7\22\u0161\n\22\f\22\16\22\u0164\13\22\3\23\3\23\3\23"+
+ "\3\23\5\23\u016a\n\23\3\23\3\23\3\23\3\23\3\23\5\23\u0171\n\23\3\24\5"+
+ "\24\u0174\n\24\3\24\3\24\5\24\u0178\n\24\3\24\3\24\5\24\u017c\n\24\3\24"+
+ "\3\24\5\24\u0180\n\24\5\24\u0182\n\24\3\25\3\25\3\25\3\25\3\25\3\25\3"+
+ "\25\7\25\u018b\n\25\f\25\16\25\u018e\13\25\3\25\3\25\5\25\u0192\n\25\3"+
+ "\26\3\26\5\26\u0196\n\26\3\26\5\26\u0199\n\26\3\26\3\26\3\26\3\26\5\26"+
+ "\u019f\n\26\3\26\5\26\u01a2\n\26\3\26\3\26\3\26\3\26\5\26\u01a8\n\26\3"+
+ "\26\5\26\u01ab\n\26\5\26\u01ad\n\26\3\27\3\27\3\30\3\30\3\30\3\30\3\30"+
"\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30"+
- "\3\30\3\30\3\30\5\30\u01d5\n\30\3\30\3\30\3\30\3\30\3\30\3\30\7\30\u01dd"+
- "\n\30\f\30\16\30\u01e0\13\30\3\31\3\31\7\31\u01e4\n\31\f\31\16\31\u01e7"+
- "\13\31\3\32\3\32\5\32\u01eb\n\32\3\33\5\33\u01ee\n\33\3\33\3\33\3\33\3"+
- "\33\3\33\3\33\5\33\u01f6\n\33\3\33\3\33\3\33\3\33\3\33\7\33\u01fd\n\33"+
- "\f\33\16\33\u0200\13\33\3\33\3\33\3\33\5\33\u0205\n\33\3\33\3\33\3\33"+
- "\3\33\3\33\3\33\5\33\u020d\n\33\3\33\3\33\3\33\5\33\u0212\n\33\3\33\3"+
- "\33\3\33\3\33\5\33\u0218\n\33\3\33\5\33\u021b\n\33\3\34\3\34\3\34\3\35"+
- "\3\35\5\35\u0222\n\35\3\36\3\36\3\36\3\36\3\36\3\36\5\36\u022a\n\36\3"+
- "\37\3\37\3\37\3\37\5\37\u0230\n\37\3\37\3\37\3\37\3\37\3\37\3\37\3\37"+
- "\3\37\3\37\3\37\7\37\u023c\n\37\f\37\16\37\u023f\13\37\3 \3 \3 \3 \3 "+
- "\3 \3 \5 \u0248\n \3 \3 \3 \3 \3 \3 \3 \3 \3 \3 \3 \5 \u0255\n \3!\3!"+
- "\3!\3!\3!\3!\3!\3!\3!\3!\5!\u0261\n!\3\"\3\"\3\"\3\"\3\"\3\"\3\"\3#\3"+
- "#\3#\5#\u026d\n#\3#\5#\u0270\n#\3$\3$\3$\3$\3$\3$\3$\3%\3%\3%\3%\3%\5"+
- "%\u027e\n%\3&\3&\3&\3&\3&\3&\3&\3\'\3\'\3\'\3\'\3\'\5\'\u028c\n\'\3(\3"+
- "(\3(\5(\u0291\n(\3(\3(\3(\7(\u0296\n(\f(\16(\u0299\13(\5(\u029b\n(\3("+
- "\3(\3)\3)\3)\5)\u02a2\n)\3*\3*\3*\3*\3*\6*\u02a9\n*\r*\16*\u02aa\3*\3"+
- "*\3*\3*\3*\3*\3*\3*\3*\3*\3*\3*\3*\3*\3*\3*\3*\5*\u02be\n*\3+\3+\3,\3"+
- ",\3-\3-\5-\u02c6\n-\3-\3-\5-\u02ca\n-\3-\3-\3-\5-\u02cf\n-\3.\3.\3/\3"+
- "/\3\60\3\60\3\60\7\60\u02d8\n\60\f\60\16\60\u02db\13\60\3\60\3\60\3\61"+
- "\3\61\5\61\u02e1\n\61\3\62\3\62\3\62\5\62\u02e6\n\62\3\62\3\62\3\62\3"+
- "\62\5\62\u02ec\n\62\3\62\5\62\u02ef\n\62\3\63\3\63\5\63\u02f3\n\63\3\64"+
- "\3\64\3\64\5\64\u02f8\n\64\3\65\3\65\5\65\u02fc\n\65\3\66\3\66\3\67\3"+
- "\67\3\67\2\4.<8\2\4\6\b\n\f\16\20\22\24\26\30\32\34\36 \"$&(*,.\60\62"+
- "\64\668:<>@BDFHJLNPRTVXZ\\^`bdfhjl\2\22\b\2\7\7\t\t\36\36\66\66AAEE\4"+
- "\2((SS\4\2\t\tAA\4\2%%--\3\2\32\33\3\2mn\4\2\7\7vv\4\2\r\r\32\32\4\2#"+
- "#\62\62\4\2\7\7\34\34\3\2oq\3\2fl\4\2\"\"TT\7\2\27\30+,8;LM\\]\3\2tu\31"+
- "\2\b\t\22\23\25\25\27\27\31\31\36\36 #$&(++//\62\62\65\6688::AAEGILO"+
- "PRSVWYY\\\\\u035f\2n\3\2\2\2\4q\3\2\2\2\6\u00de\3\2\2\2\b\u00e9\3\2\2"+
- "\2\n\u00ed\3\2\2\2\f\u0102\3\2\2\2\16\u0109\3\2\2\2\20\u010b\3\2\2\2\22"+
- "\u0113\3\2\2\2\24\u012f\3\2\2\2\26\u0139\3\2\2\2\30\u0143\3\2\2\2\32\u0152"+
- "\3\2\2\2\34\u0154\3\2\2\2\36\u015a\3\2\2\2 \u015c\3\2\2\2\"\u0163\3\2"+
- "\2\2$\u0175\3\2\2\2&\u0186\3\2\2\2(\u0196\3\2\2\2*\u01b1\3\2\2\2,\u01b3"+
- "\3\2\2\2.\u01d4\3\2\2\2\60\u01e5\3\2\2\2\62\u01e8\3\2\2\2\64\u021a\3\2"+
- "\2\2\66\u021c\3\2\2\28\u021f\3\2\2\2:\u0229\3\2\2\2<\u022f\3\2\2\2>\u0254"+
- "\3\2\2\2@\u0260\3\2\2\2B\u0262\3\2\2\2D\u0269\3\2\2\2F\u0271\3\2\2\2H"+
- "\u027d\3\2\2\2J\u027f\3\2\2\2L\u028b\3\2\2\2N\u028d\3\2\2\2P\u02a1\3\2"+
- "\2\2R\u02bd\3\2\2\2T\u02bf\3\2\2\2V\u02c1\3\2\2\2X\u02c3\3\2\2\2Z\u02d0"+
- "\3\2\2\2\\\u02d2\3\2\2\2^\u02d9\3\2\2\2`\u02e0\3\2\2\2b\u02ee\3\2\2\2"+
- "d\u02f2\3\2\2\2f\u02f7\3\2\2\2h\u02fb\3\2\2\2j\u02fd\3\2\2\2l\u02ff\3"+
- "\2\2\2no\5\6\4\2op\7\2\2\3p\3\3\2\2\2qr\5,\27\2rs\7\2\2\3s\5\3\2\2\2t"+
- "\u00df\5\b\5\2u\u0083\7 \2\2v\177\7\3\2\2wx\7G\2\2x~\t\2\2\2yz\7$\2\2"+
- "z~\t\3\2\2{|\7Y\2\2|~\5V,\2}w\3\2\2\2}y\3\2\2\2}{\3\2\2\2~\u0081\3\2\2"+
- "\2\177}\3\2\2\2\177\u0080\3\2\2\2\u0080\u0082\3\2\2\2\u0081\177\3\2\2"+
- "\2\u0082\u0084\7\4\2\2\u0083v\3\2\2\2\u0083\u0084\3\2\2\2\u0084\u0085"+
- "\3\2\2\2\u0085\u00df\5\6\4\2\u0086\u0092\7\31\2\2\u0087\u008e\7\3\2\2"+
- "\u0088\u0089\7G\2\2\u0089\u008d\t\4\2\2\u008a\u008b\7$\2\2\u008b\u008d"+
- "\t\3\2\2\u008c\u0088\3\2\2\2\u008c\u008a\3\2\2\2\u008d\u0090\3\2\2\2\u008e"+
- "\u008c\3\2\2\2\u008e\u008f\3\2\2\2\u008f\u0091\3\2\2\2\u0090\u008e\3\2"+
- "\2\2\u0091\u0093\7\4\2\2\u0092\u0087\3\2\2\2\u0092\u0093\3\2\2\2\u0093"+
- "\u0094\3\2\2\2\u0094\u00df\5\6\4\2\u0095\u0096\7O\2\2\u0096\u0099\7R\2"+
- "\2\u0097\u009a\5\66\34\2\u0098\u009a\5b\62\2\u0099\u0097\3\2\2\2\u0099"+
- "\u0098\3\2\2\2\u0099\u009a\3\2\2\2\u009a\u00df\3\2\2\2\u009b\u009c\7O"+
- "\2\2\u009c\u009d\7\23\2\2\u009d\u00a0\t\5\2\2\u009e\u00a1\5\66\34\2\u009f"+
- "\u00a1\5b\62\2\u00a0\u009e\3\2\2\2\u00a0\u009f\3\2\2\2\u00a1\u00df\3\2"+
- "\2\2\u00a2\u00a5\t\6\2\2\u00a3\u00a6\5\66\34\2\u00a4\u00a6\5b\62\2\u00a5"+
- "\u00a3\3\2\2\2\u00a5\u00a4\3\2\2\2\u00a6\u00df\3\2\2\2\u00a7\u00a8\7O"+
- "\2\2\u00a8\u00aa\7\'\2\2\u00a9\u00ab\5\66\34\2\u00aa\u00a9\3\2\2\2\u00aa"+
- "\u00ab\3\2\2\2\u00ab\u00df\3\2\2\2\u00ac\u00ad\7O\2\2\u00ad\u00df\7K\2"+
- "\2\u00ae\u00af\7P\2\2\u00af\u00df\7\22\2\2\u00b0\u00b1\7P\2\2\u00b1\u00b4"+
- "\7R\2\2\u00b2\u00b3\7\21\2\2\u00b3\u00b5\5\66\34\2\u00b4\u00b2\3\2\2\2"+
- "\u00b4\u00b5\3\2\2\2\u00b5\u00b8\3\2\2\2\u00b6\u00b9\5\66\34\2\u00b7\u00b9"+
- "\5b\62\2\u00b8\u00b6\3\2\2\2\u00b8\u00b7\3\2\2\2\u00b8\u00b9\3\2\2\2\u00b9"+
- "\u00c3\3\2\2\2\u00ba\u00bb\7V\2\2\u00bb\u00c0\5j\66\2\u00bc\u00bd\7\5"+
- "\2\2\u00bd\u00bf\5j\66\2\u00be\u00bc\3\2\2\2\u00bf\u00c2\3\2\2\2\u00c0"+
- "\u00be\3\2\2\2\u00c0\u00c1\3\2\2\2\u00c1\u00c4\3\2\2\2\u00c2\u00c0\3\2"+
- "\2\2\u00c3\u00ba\3\2\2\2\u00c3\u00c4\3\2\2\2\u00c4\u00df\3\2\2\2\u00c5"+
- "\u00c6\7P\2\2\u00c6\u00c9\7\23\2\2\u00c7\u00c8\7\21\2\2\u00c8\u00ca\5"+
- "j\66\2\u00c9\u00c7\3\2\2\2\u00c9\u00ca\3\2\2\2\u00ca\u00ce\3\2\2\2\u00cb"+
- "\u00cc\7Q\2\2\u00cc\u00cf\5\66\34\2\u00cd\u00cf\5b\62\2\u00ce\u00cb\3"+
- "\2\2\2\u00ce\u00cd\3\2\2\2\u00ce\u00cf\3\2\2\2\u00cf\u00d1\3\2\2\2\u00d0"+
- "\u00d2\5\66\34\2\u00d1\u00d0\3\2\2\2\u00d1\u00d2\3\2\2\2\u00d2\u00df\3"+
- "\2\2\2\u00d3\u00d4\7P\2\2\u00d4\u00d9\7W\2\2\u00d5\u00d7\t\7\2\2\u00d6"+
- "\u00d5\3\2\2\2\u00d6\u00d7\3\2\2\2\u00d7\u00d8\3\2\2\2\u00d8\u00da\5h"+
- "\65\2\u00d9\u00d6\3\2\2\2\u00d9\u00da\3\2\2\2\u00da\u00df\3\2\2\2\u00db"+
- "\u00dc\7P\2\2\u00dc\u00dd\7Q\2\2\u00dd\u00df\7W\2\2\u00det\3\2\2\2\u00de"+
- "u\3\2\2\2\u00de\u0086\3\2\2\2\u00de\u0095\3\2\2\2\u00de\u009b\3\2\2\2"+
- "\u00de\u00a2\3\2\2\2\u00de\u00a7\3\2\2\2\u00de\u00ac\3\2\2\2\u00de\u00ae"+
- "\3\2\2\2\u00de\u00b0\3\2\2\2\u00de\u00c5\3\2\2\2\u00de\u00d3\3\2\2\2\u00de"+
- "\u00db\3\2\2\2\u00df\7\3\2\2\2\u00e0\u00e1\7[\2\2\u00e1\u00e6\5\34\17"+
- "\2\u00e2\u00e3\7\5\2\2\u00e3\u00e5\5\34\17\2\u00e4\u00e2\3\2\2\2\u00e5"+
- "\u00e8\3\2\2\2\u00e6\u00e4\3\2\2\2\u00e6\u00e7\3\2\2\2\u00e7\u00ea\3\2"+
- "\2\2\u00e8\u00e6\3\2\2\2\u00e9\u00e0\3\2\2\2\u00e9\u00ea\3\2\2\2\u00ea"+
- "\u00eb\3\2\2\2\u00eb\u00ec\5\n\6\2\u00ec\t\3\2\2\2\u00ed\u00f8\5\16\b"+
- "\2\u00ee\u00ef\7C\2\2\u00ef\u00f0\7\17\2\2\u00f0\u00f5\5\20\t\2\u00f1"+
- "\u00f2\7\5\2\2\u00f2\u00f4\5\20\t\2\u00f3\u00f1\3\2\2\2\u00f4\u00f7\3"+
- "\2\2\2\u00f5\u00f3\3\2\2\2\u00f5\u00f6\3\2\2\2\u00f6\u00f9\3\2\2\2\u00f7"+
- "\u00f5\3\2\2\2\u00f8\u00ee\3\2\2\2\u00f8\u00f9\3\2\2\2\u00f9\u00fb\3\2"+
- "\2\2\u00fa\u00fc\5\f\7\2\u00fb\u00fa\3\2\2\2\u00fb\u00fc\3\2\2\2\u00fc"+
- "\13\3\2\2\2\u00fd\u00fe\7\65\2\2\u00fe\u0103\t\b\2\2\u00ff\u0100\7`\2"+
- "\2\u0100\u0101\t\b\2\2\u0101\u0103\7e\2\2\u0102\u00fd\3\2\2\2\u0102\u00ff"+
- "\3\2\2\2\u0103\r\3\2\2\2\u0104\u010a\5\22\n\2\u0105\u0106\7\3\2\2\u0106"+
- "\u0107\5\n\6\2\u0107\u0108\7\4\2\2\u0108\u010a\3\2\2\2\u0109\u0104\3\2"+
- "\2\2\u0109\u0105\3\2\2\2\u010a\17\3\2\2\2\u010b\u010d\5,\27\2\u010c\u010e"+
- "\t\t\2\2\u010d\u010c\3\2\2\2\u010d\u010e\3\2\2\2\u010e\u0111\3\2\2\2\u010f"+
- "\u0110\7?\2\2\u0110\u0112\t\n\2\2\u0111\u010f\3\2\2\2\u0111\u0112\3\2"+
- "\2\2\u0112\21\3\2\2\2\u0113\u0115\7N\2\2\u0114\u0116\5\36\20\2\u0115\u0114"+
- "\3\2\2\2\u0115\u0116\3\2\2\2\u0116\u0117\3\2\2\2\u0117\u011c\5 \21\2\u0118"+
- "\u0119\7\5\2\2\u0119\u011b\5 \21\2\u011a\u0118\3\2\2\2\u011b\u011e\3\2"+
- "\2\2\u011c\u011a\3\2\2\2\u011c\u011d\3\2\2\2\u011d\u0120\3\2\2\2\u011e"+
- "\u011c\3\2\2\2\u011f\u0121\5\24\13\2\u0120\u011f\3\2\2\2\u0120\u0121\3"+
- "\2\2\2\u0121\u0124\3\2\2\2\u0122\u0123\7Z\2\2\u0123\u0125\5.\30\2\u0124"+
- "\u0122\3\2\2\2\u0124\u0125\3\2\2\2\u0125\u0129\3\2\2\2\u0126\u0127\7)"+
- "\2\2\u0127\u0128\7\17\2\2\u0128\u012a\5\26\f\2\u0129\u0126\3\2\2\2\u0129"+
- "\u012a\3\2\2\2\u012a\u012d\3\2\2\2\u012b\u012c\7*\2\2\u012c\u012e\5.\30"+
- "\2\u012d\u012b\3\2\2\2\u012d\u012e\3\2\2\2\u012e\23\3\2\2\2\u012f\u0130"+
- "\7%\2\2\u0130\u0135\5\"\22\2\u0131\u0132\7\5\2\2\u0132\u0134\5\"\22\2"+
- "\u0133\u0131\3\2\2\2\u0134\u0137\3\2\2\2\u0135\u0133\3\2\2\2\u0135\u0136"+
- "\3\2\2\2\u0136\25\3\2\2\2\u0137\u0135\3\2\2\2\u0138\u013a\5\36\20\2\u0139"+
- "\u0138\3\2\2\2\u0139\u013a\3\2\2\2\u013a\u013b\3\2\2\2\u013b\u0140\5\30"+
- "\r\2\u013c\u013d\7\5\2\2\u013d\u013f\5\30\r\2\u013e\u013c\3\2\2\2\u013f"+
- "\u0142\3\2\2\2\u0140\u013e\3\2\2\2\u0140\u0141\3\2\2\2\u0141\27\3\2\2"+
- "\2\u0142\u0140\3\2\2\2\u0143\u0144\5\32\16\2\u0144\31\3\2\2\2\u0145\u014e"+
- "\7\3\2\2\u0146\u014b\5,\27\2\u0147\u0148\7\5\2\2\u0148\u014a\5,\27\2\u0149"+
- "\u0147\3\2\2\2\u014a\u014d\3\2\2\2\u014b\u0149\3\2\2\2\u014b\u014c\3\2"+
- "\2\2\u014c\u014f\3\2\2\2\u014d\u014b\3\2\2\2\u014e\u0146\3\2\2\2\u014e"+
- "\u014f\3\2\2\2\u014f\u0150\3\2\2\2\u0150\u0153\7\4\2\2\u0151\u0153\5,"+
- "\27\2\u0152\u0145\3\2\2\2\u0152\u0151\3\2\2\2\u0153\33\3\2\2\2\u0154\u0155"+
- "\5`\61\2\u0155\u0156\7\f\2\2\u0156\u0157\7\3\2\2\u0157\u0158\5\n\6\2\u0158"+
- "\u0159\7\4\2\2\u0159\35\3\2\2\2\u015a\u015b\t\13\2\2\u015b\37\3\2\2\2"+
- "\u015c\u0161\5,\27\2\u015d\u015f\7\f\2\2\u015e\u015d\3\2\2\2\u015e\u015f"+
- "\3\2\2\2\u015f\u0160\3\2\2\2\u0160\u0162\5`\61\2\u0161\u015e\3\2\2\2\u0161"+
- "\u0162\3\2\2\2\u0162!\3\2\2\2\u0163\u0167\5*\26\2\u0164\u0166\5$\23\2"+
- "\u0165\u0164\3\2\2\2\u0166\u0169\3\2\2\2\u0167\u0165\3\2\2\2\u0167\u0168"+
- "\3\2\2\2\u0168#\3\2\2\2\u0169\u0167\3\2\2\2\u016a\u016b\5&\24\2\u016b"+
- "\u016c\7\61\2\2\u016c\u016e\5*\26\2\u016d\u016f\5(\25\2\u016e\u016d\3"+
- "\2\2\2\u016e\u016f\3\2\2\2\u016f\u0176\3\2\2\2\u0170\u0171\7<\2\2\u0171"+
- "\u0172\5&\24\2\u0172\u0173\7\61\2\2\u0173\u0174\5*\26\2\u0174\u0176\3"+
- "\2\2\2\u0175\u016a\3\2\2\2\u0175\u0170\3\2\2\2\u0176%\3\2\2\2\u0177\u0179"+
- "\7.\2\2\u0178\u0177\3\2\2\2\u0178\u0179\3\2\2\2\u0179\u0187\3\2\2\2\u017a"+
- "\u017c\7\63\2\2\u017b\u017d\7D\2\2\u017c\u017b\3\2\2\2\u017c\u017d\3\2"+
- "\2\2\u017d\u0187\3\2\2\2\u017e\u0180\7H\2\2\u017f\u0181\7D\2\2\u0180\u017f"+
- "\3\2\2\2\u0180\u0181\3\2\2\2\u0181\u0187\3\2\2\2\u0182\u0184\7&\2\2\u0183"+
- "\u0185\7D\2\2\u0184\u0183\3\2\2\2\u0184\u0185\3\2\2\2\u0185\u0187\3\2"+
- "\2\2\u0186\u0178\3\2\2\2\u0186\u017a\3\2\2\2\u0186\u017e\3\2\2\2\u0186"+
- "\u0182\3\2\2\2\u0187\'\3\2\2\2\u0188\u0189\7@\2\2\u0189\u0197\5.\30\2"+
- "\u018a\u018b\7X\2\2\u018b\u018c\7\3\2\2\u018c\u0191\5`\61\2\u018d\u018e"+
- "\7\5\2\2\u018e\u0190\5`\61\2\u018f\u018d\3\2\2\2\u0190\u0193\3\2\2\2\u0191"+
- "\u018f\3\2\2\2\u0191\u0192\3\2\2\2\u0192\u0194\3\2\2\2\u0193\u0191\3\2"+
- "\2\2\u0194\u0195\7\4\2\2\u0195\u0197\3\2\2\2\u0196\u0188\3\2\2\2\u0196"+
- "\u018a\3\2\2\2\u0197)\3\2\2\2\u0198\u019d\5b\62\2\u0199\u019b\7\f\2\2"+
- "\u019a\u0199\3\2\2\2\u019a\u019b\3\2\2\2\u019b\u019c\3\2\2\2\u019c\u019e"+
- "\5^\60\2\u019d\u019a\3\2\2\2\u019d\u019e\3\2\2\2\u019e\u01b2\3\2\2\2\u019f"+
- "\u01a0\7\3\2\2\u01a0\u01a1\5\n\6\2\u01a1\u01a6\7\4\2\2\u01a2\u01a4\7\f"+
- "\2\2\u01a3\u01a2\3\2\2\2\u01a3\u01a4\3\2\2\2\u01a4\u01a5\3\2\2\2\u01a5"+
- "\u01a7\5^\60\2\u01a6\u01a3\3\2\2\2\u01a6\u01a7\3\2\2\2\u01a7\u01b2\3\2"+
- "\2\2\u01a8\u01a9\7\3\2\2\u01a9\u01aa\5\"\22\2\u01aa\u01af\7\4\2\2\u01ab"+
- "\u01ad\7\f\2\2\u01ac\u01ab\3\2\2\2\u01ac\u01ad\3\2\2\2\u01ad\u01ae\3\2"+
- "\2\2\u01ae\u01b0\5^\60\2\u01af\u01ac\3\2\2\2\u01af\u01b0\3\2\2\2\u01b0"+
- "\u01b2\3\2\2\2\u01b1\u0198\3\2\2\2\u01b1\u019f\3\2\2\2\u01b1\u01a8\3\2"+
- "\2\2\u01b2+\3\2\2\2\u01b3\u01b4\5.\30\2\u01b4-\3\2\2\2\u01b5\u01b6\b\30"+
- "\1\2\u01b6\u01b7\7=\2\2\u01b7\u01d5\5.\30\n\u01b8\u01b9\7\37\2\2\u01b9"+
- "\u01ba\7\3\2\2\u01ba\u01bb\5\b\5\2\u01bb\u01bc\7\4\2\2\u01bc\u01d5\3\2"+
- "\2\2\u01bd\u01be\7J\2\2\u01be\u01bf\7\3\2\2\u01bf\u01c0\5j\66\2\u01c0"+
- "\u01c1\5\60\31\2\u01c1\u01c2\7\4\2\2\u01c2\u01d5\3\2\2\2\u01c3\u01c4\7"+
- "\67\2\2\u01c4\u01c5\7\3\2\2\u01c5\u01c6\5^\60\2\u01c6\u01c7\7\5\2\2\u01c7"+
- "\u01c8\5j\66\2\u01c8\u01c9\5\60\31\2\u01c9\u01ca\7\4\2\2\u01ca\u01d5\3"+
- "\2\2\2\u01cb\u01cc\7\67\2\2\u01cc\u01cd\7\3\2\2\u01cd\u01ce\5j\66\2\u01ce"+
- "\u01cf\7\5\2\2\u01cf\u01d0\5j\66\2\u01d0\u01d1\5\60\31\2\u01d1\u01d2\7"+
- "\4\2\2\u01d2\u01d5\3\2\2\2\u01d3\u01d5\5\62\32\2\u01d4\u01b5\3\2\2\2\u01d4"+
- "\u01b8\3\2\2\2\u01d4\u01bd\3\2\2\2\u01d4\u01c3\3\2\2\2\u01d4\u01cb\3\2"+
- "\2\2\u01d4\u01d3\3\2\2\2\u01d5\u01de\3\2\2\2\u01d6\u01d7\f\4\2\2\u01d7"+
- "\u01d8\7\n\2\2\u01d8\u01dd\5.\30\5\u01d9\u01da\f\3\2\2\u01da\u01db\7B"+
- "\2\2\u01db\u01dd\5.\30\4\u01dc\u01d6\3\2\2\2\u01dc\u01d9\3\2\2\2\u01dd"+
- "\u01e0\3\2\2\2\u01de\u01dc\3\2\2\2\u01de\u01df\3\2\2\2\u01df/\3\2\2\2"+
- "\u01e0\u01de\3\2\2\2\u01e1\u01e2\7\5\2\2\u01e2\u01e4\5j\66\2\u01e3\u01e1"+
- "\3\2\2\2\u01e4\u01e7\3\2\2\2\u01e5\u01e3\3\2\2\2\u01e5\u01e6\3\2\2\2\u01e6"+
- "\61\3\2\2\2\u01e7\u01e5\3\2\2\2\u01e8\u01ea\5<\37\2\u01e9\u01eb\5\64\33"+
- "\2\u01ea\u01e9\3\2\2\2\u01ea\u01eb\3\2\2\2\u01eb\63\3\2\2\2\u01ec\u01ee"+
- "\7=\2\2\u01ed\u01ec\3\2\2\2\u01ed\u01ee\3\2\2\2\u01ee\u01ef\3\2\2\2\u01ef"+
- "\u01f0\7\16\2\2\u01f0\u01f1\5<\37\2\u01f1\u01f2\7\n\2\2\u01f2\u01f3\5"+
- "<\37\2\u01f3\u021b\3\2\2\2\u01f4\u01f6\7=\2\2\u01f5\u01f4\3\2\2\2\u01f5"+
- "\u01f6\3\2\2\2\u01f6\u01f7\3\2\2\2\u01f7\u01f8\7-\2\2\u01f8\u01f9\7\3"+
- "\2\2\u01f9\u01fe\5<\37\2\u01fa\u01fb\7\5\2\2\u01fb\u01fd\5<\37\2\u01fc"+
- "\u01fa\3\2\2\2\u01fd\u0200\3\2\2\2\u01fe\u01fc\3\2\2\2\u01fe\u01ff\3\2"+
- "\2\2\u01ff\u0201\3\2\2\2\u0200\u01fe\3\2\2\2\u0201\u0202\7\4\2\2\u0202"+
- "\u021b\3\2\2\2\u0203\u0205\7=\2\2\u0204\u0203\3\2\2\2\u0204\u0205\3\2"+
- "\2\2\u0205\u0206\3\2\2\2\u0206\u0207\7-\2\2\u0207\u0208\7\3\2\2\u0208"+
- "\u0209\5\b\5\2\u0209\u020a\7\4\2\2\u020a\u021b\3\2\2\2\u020b\u020d\7="+
- "\2\2\u020c\u020b\3\2\2\2\u020c\u020d\3\2\2\2\u020d\u020e\3\2\2\2\u020e"+
- "\u020f\7\64\2\2\u020f\u021b\58\35\2\u0210\u0212\7=\2\2\u0211\u0210\3\2"+
- "\2\2\u0211\u0212\3\2\2\2\u0212\u0213\3\2\2\2\u0213\u0214\7I\2\2\u0214"+
- "\u021b\5j\66\2\u0215\u0217\7\60\2\2\u0216\u0218\7=\2\2\u0217\u0216\3\2"+
- "\2\2\u0217\u0218\3\2\2\2\u0218\u0219\3\2\2\2\u0219\u021b\7>\2\2\u021a"+
- "\u01ed\3\2\2\2\u021a\u01f5\3\2\2\2\u021a\u0204\3\2\2\2\u021a\u020c\3\2"+
- "\2\2\u021a\u0211\3\2\2\2\u021a\u0215\3\2\2\2\u021b\65\3\2\2\2\u021c\u021d"+
- "\7\64\2\2\u021d\u021e\58\35\2\u021e\67\3\2\2\2\u021f\u0221\5j\66\2\u0220"+
- "\u0222\5:\36\2\u0221\u0220\3\2\2\2\u0221\u0222\3\2\2\2\u02229\3\2\2\2"+
- "\u0223\u0224\7\35\2\2\u0224\u022a\5j\66\2\u0225\u0226\7^\2\2\u0226\u0227"+
- "\5j\66\2\u0227\u0228\7e\2\2\u0228\u022a\3\2\2\2\u0229\u0223\3\2\2\2\u0229"+
- "\u0225\3\2\2\2\u022a;\3\2\2\2\u022b\u022c\b\37\1\2\u022c\u0230\5> \2\u022d"+
- "\u022e\t\7\2\2\u022e\u0230\5<\37\6\u022f\u022b\3\2\2\2\u022f\u022d\3\2"+
- "\2\2\u0230\u023d\3\2\2\2\u0231\u0232\f\5\2\2\u0232\u0233\t\f\2\2\u0233"+
- "\u023c\5<\37\6\u0234\u0235\f\4\2\2\u0235\u0236\t\7\2\2\u0236\u023c\5<"+
- "\37\5\u0237\u0238\f\3\2\2\u0238\u0239\5T+\2\u0239\u023a\5<\37\4\u023a"+
- "\u023c\3\2\2\2\u023b\u0231\3\2\2\2\u023b\u0234\3\2\2\2\u023b\u0237\3\2"+
- "\2\2\u023c\u023f\3\2\2\2\u023d\u023b\3\2\2\2\u023d\u023e\3\2\2\2\u023e"+
- "=\3\2\2\2\u023f\u023d\3\2\2\2\u0240\u0255\5@!\2\u0241\u0255\5H%\2\u0242"+
- "\u0255\5D#\2\u0243\u0255\5R*\2\u0244\u0245\5^\60\2\u0245\u0246\7s\2\2"+
- "\u0246\u0248\3\2\2\2\u0247\u0244\3\2\2\2\u0247\u0248\3\2\2\2\u0248\u0249"+
- "\3\2\2\2\u0249\u0255\7o\2\2\u024a\u0255\5L\'\2\u024b\u024c\7\3\2\2\u024c"+
- "\u024d\5\b\5\2\u024d\u024e\7\4\2\2\u024e\u0255\3\2\2\2\u024f\u0255\5^"+
- "\60\2\u0250\u0251\7\3\2\2\u0251\u0252\5,\27\2\u0252\u0253\7\4\2\2\u0253"+
- "\u0255\3\2\2\2\u0254\u0240\3\2\2\2\u0254\u0241\3\2\2\2\u0254\u0242\3\2"+
- "\2\2\u0254\u0243\3\2\2\2\u0254\u0247\3\2\2\2\u0254\u024a\3\2\2\2\u0254"+
- "\u024b\3\2\2\2\u0254\u024f\3\2\2\2\u0254\u0250\3\2\2\2\u0255?\3\2\2\2"+
- "\u0256\u0261\5B\"\2\u0257\u0258\7_\2\2\u0258\u0259\5B\"\2\u0259\u025a"+
- "\7e\2\2\u025a\u0261\3\2\2\2\u025b\u0261\5F$\2\u025c\u025d\7_\2\2\u025d"+
- "\u025e\5F$\2\u025e\u025f\7e\2\2\u025f\u0261\3\2\2\2\u0260\u0256\3\2\2"+
- "\2\u0260\u0257\3\2\2\2\u0260\u025b\3\2\2\2\u0260\u025c\3\2\2\2\u0261A"+
- "\3\2\2\2\u0262\u0263\7\20\2\2\u0263\u0264\7\3\2\2\u0264\u0265\5,\27\2"+
- "\u0265\u0266\7\f\2\2\u0266\u0267\5\\/\2\u0267\u0268\7\4\2\2\u0268C\3\2"+
- "\2\2\u0269\u026f\7\26\2\2\u026a\u026c\7\3\2\2\u026b\u026d\7v\2\2\u026c"+
- "\u026b\3\2\2\2\u026c\u026d\3\2\2\2\u026d\u026e\3\2\2\2\u026e\u0270\7\4"+
- "\2\2\u026f\u026a\3\2\2\2\u026f\u0270\3\2\2\2\u0270E\3\2\2\2\u0271\u0272"+
- "\7\24\2\2\u0272\u0273\7\3\2\2\u0273\u0274\5,\27\2\u0274\u0275\7\5\2\2"+
- "\u0275\u0276\5\\/\2\u0276\u0277\7\4\2\2\u0277G\3\2\2\2\u0278\u027e\5J"+
- "&\2\u0279\u027a\7_\2\2\u027a\u027b\5J&\2\u027b\u027c\7e\2\2\u027c\u027e"+
- "\3\2\2\2\u027d\u0278\3\2\2\2\u027d\u0279\3\2\2\2\u027eI\3\2\2\2\u027f"+
- "\u0280\7!\2\2\u0280\u0281\7\3\2\2\u0281\u0282\5`\61\2\u0282\u0283\7%\2"+
- "\2\u0283\u0284\5<\37\2\u0284\u0285\7\4\2\2\u0285K\3\2\2\2\u0286\u028c"+
- "\5N(\2\u0287\u0288\7_\2\2\u0288\u0289\5N(\2\u0289\u028a\7e\2\2\u028a\u028c"+
- "\3\2\2\2\u028b\u0286\3\2\2\2\u028b\u0287\3\2\2\2\u028cM\3\2\2\2\u028d"+
- "\u028e\5P)\2\u028e\u029a\7\3\2\2\u028f\u0291\5\36\20\2\u0290\u028f\3\2"+
- "\2\2\u0290\u0291\3\2\2\2\u0291\u0292\3\2\2\2\u0292\u0297\5,\27\2\u0293"+
- "\u0294\7\5\2\2\u0294\u0296\5,\27\2\u0295\u0293\3\2\2\2\u0296\u0299\3\2"+
- "\2\2\u0297\u0295\3\2\2\2\u0297\u0298\3\2\2\2\u0298\u029b\3\2\2\2\u0299"+
- "\u0297\3\2\2\2\u029a\u0290\3\2\2\2\u029a\u029b\3\2\2\2\u029b\u029c\3\2"+
- "\2\2\u029c\u029d\7\4\2\2\u029dO\3\2\2\2\u029e\u02a2\7\63\2\2\u029f\u02a2"+
- "\7H\2\2\u02a0\u02a2\5`\61\2\u02a1\u029e\3\2\2\2\u02a1\u029f\3\2\2\2\u02a1"+
- "\u02a0\3\2\2\2\u02a2Q\3\2\2\2\u02a3\u02be\7>\2\2\u02a4\u02be\5X-\2\u02a5"+
- "\u02be\5h\65\2\u02a6\u02be\5V,\2\u02a7\u02a9\7u\2\2\u02a8\u02a7\3\2\2"+
- "\2\u02a9\u02aa\3\2\2\2\u02aa\u02a8\3\2\2\2\u02aa\u02ab\3\2\2\2\u02ab\u02be"+
- "\3\2\2\2\u02ac\u02be\7t\2\2\u02ad\u02ae\7a\2\2\u02ae\u02af\5j\66\2\u02af"+
- "\u02b0\7e\2\2\u02b0\u02be\3\2\2\2\u02b1\u02b2\7b\2\2\u02b2\u02b3\5j\66"+
- "\2\u02b3\u02b4\7e\2\2\u02b4\u02be\3\2\2\2\u02b5\u02b6\7c\2\2\u02b6\u02b7"+
- "\5j\66\2\u02b7\u02b8\7e\2\2\u02b8\u02be\3\2\2\2\u02b9\u02ba\7d\2\2\u02ba"+
- "\u02bb\5j\66\2\u02bb\u02bc\7e\2\2\u02bc\u02be\3\2\2\2\u02bd\u02a3\3\2"+
- "\2\2\u02bd\u02a4\3\2\2\2\u02bd\u02a5\3\2\2\2\u02bd\u02a6\3\2\2\2\u02bd"+
- "\u02a8\3\2\2\2\u02bd\u02ac\3\2\2\2\u02bd\u02ad\3\2\2\2\u02bd\u02b1\3\2"+
- "\2\2\u02bd\u02b5\3\2\2\2\u02bd\u02b9\3\2\2\2\u02beS\3\2\2\2\u02bf\u02c0"+
- "\t\r\2\2\u02c0U\3\2\2\2\u02c1\u02c2\t\16\2\2\u02c2W\3\2\2\2\u02c3\u02c5"+
- "\7/\2\2\u02c4\u02c6\t\7\2\2\u02c5\u02c4\3\2\2\2\u02c5\u02c6\3\2\2\2\u02c6"+
- "\u02c9\3\2\2\2\u02c7\u02ca\5h\65\2\u02c8\u02ca\5j\66\2\u02c9\u02c7\3\2"+
- "\2\2\u02c9\u02c8\3\2\2\2\u02ca\u02cb\3\2\2\2\u02cb\u02ce\5Z.\2\u02cc\u02cd"+
- "\7U\2\2\u02cd\u02cf\5Z.\2\u02ce\u02cc\3\2\2\2\u02ce\u02cf\3\2\2\2\u02cf"+
- "Y\3\2\2\2\u02d0\u02d1\t\17\2\2\u02d1[\3\2\2\2\u02d2\u02d3\5`\61\2\u02d3"+
- "]\3\2\2\2\u02d4\u02d5\5`\61\2\u02d5\u02d6\7s\2\2\u02d6\u02d8\3\2\2\2\u02d7"+
- "\u02d4\3\2\2\2\u02d8\u02db\3\2\2\2\u02d9\u02d7\3\2\2\2\u02d9\u02da\3\2"+
- "\2\2\u02da\u02dc\3\2\2\2\u02db\u02d9\3\2\2\2\u02dc\u02dd\5`\61\2\u02dd"+
- "_\3\2\2\2\u02de\u02e1\5d\63\2\u02df\u02e1\5f\64\2\u02e0\u02de\3\2\2\2"+
- "\u02e0\u02df\3\2\2\2\u02e1a\3\2\2\2\u02e2\u02e3\5`\61\2\u02e3\u02e4\7"+
- "\6\2\2\u02e4\u02e6\3\2\2\2\u02e5\u02e2\3\2\2\2\u02e5\u02e6\3\2\2\2\u02e6"+
- "\u02e7\3\2\2\2\u02e7\u02ef\7z\2\2\u02e8\u02e9\5`\61\2\u02e9\u02ea\7\6"+
- "\2\2\u02ea\u02ec\3\2\2\2\u02eb\u02e8\3\2\2\2\u02eb\u02ec\3\2\2\2\u02ec"+
- "\u02ed\3\2\2\2\u02ed\u02ef\5`\61\2\u02ee\u02e5\3\2\2\2\u02ee\u02eb\3\2"+
- "\2\2\u02efc\3\2\2\2\u02f0\u02f3\7{\2\2\u02f1\u02f3\7|\2\2\u02f2\u02f0"+
- "\3\2\2\2\u02f2\u02f1\3\2\2\2\u02f3e\3\2\2\2\u02f4\u02f8\7x\2\2\u02f5\u02f8"+
- "\5l\67\2\u02f6\u02f8\7y\2\2\u02f7\u02f4\3\2\2\2\u02f7\u02f5\3\2\2\2\u02f7"+
- "\u02f6\3\2\2\2\u02f8g\3\2\2\2\u02f9\u02fc\7w\2\2\u02fa\u02fc\7v\2\2\u02fb"+
- "\u02f9\3\2\2\2\u02fb\u02fa\3\2\2\2\u02fci\3\2\2\2\u02fd\u02fe\t\20\2\2"+
- "\u02fek\3\2\2\2\u02ff\u0300\t\21\2\2\u0300m\3\2\2\2h}\177\u0083\u008c"+
- "\u008e\u0092\u0099\u00a0\u00a5\u00aa\u00b4\u00b8\u00c0\u00c3\u00c9\u00ce"+
- "\u00d1\u00d6\u00d9\u00de\u00e6\u00e9\u00f5\u00f8\u00fb\u0102\u0109\u010d"+
- "\u0111\u0115\u011c\u0120\u0124\u0129\u012d\u0135\u0139\u0140\u014b\u014e"+
- "\u0152\u015e\u0161\u0167\u016e\u0175\u0178\u017c\u0180\u0184\u0186\u0191"+
- "\u0196\u019a\u019d\u01a3\u01a6\u01ac\u01af\u01b1\u01d4\u01dc\u01de\u01e5"+
- "\u01ea\u01ed\u01f5\u01fe\u0204\u020c\u0211\u0217\u021a\u0221\u0229\u022f"+
- "\u023b\u023d\u0247\u0254\u0260\u026c\u026f\u027d\u028b\u0290\u0297\u029a"+
- "\u02a1\u02aa\u02bd\u02c5\u02c9\u02ce\u02d9\u02e0\u02e5\u02eb\u02ee\u02f2"+
- "\u02f7\u02fb";
+ "\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\5\30\u01d0"+
+ "\n\30\3\30\3\30\3\30\3\30\3\30\3\30\7\30\u01d8\n\30\f\30\16\30\u01db\13"+
+ "\30\3\31\3\31\7\31\u01df\n\31\f\31\16\31\u01e2\13\31\3\32\3\32\5\32\u01e6"+
+ "\n\32\3\33\5\33\u01e9\n\33\3\33\3\33\3\33\3\33\3\33\3\33\5\33\u01f1\n"+
+ "\33\3\33\3\33\3\33\3\33\3\33\7\33\u01f8\n\33\f\33\16\33\u01fb\13\33\3"+
+ "\33\3\33\3\33\5\33\u0200\n\33\3\33\3\33\3\33\3\33\3\33\3\33\5\33\u0208"+
+ "\n\33\3\33\3\33\3\33\5\33\u020d\n\33\3\33\3\33\3\33\3\33\5\33\u0213\n"+
+ "\33\3\33\5\33\u0216\n\33\3\34\3\34\3\34\3\35\3\35\5\35\u021d\n\35\3\36"+
+ "\3\36\3\36\3\36\3\36\3\36\5\36\u0225\n\36\3\37\3\37\3\37\3\37\5\37\u022b"+
+ "\n\37\3\37\3\37\3\37\3\37\3\37\3\37\3\37\3\37\3\37\3\37\7\37\u0237\n\37"+
+ "\f\37\16\37\u023a\13\37\3 \3 \3 \3 \3 \3 \3 \5 \u0243\n \3 \3 \3 \3 \3"+
+ " \3 \3 \3 \3 \3 \3 \5 \u0250\n \3!\3!\3!\3!\3!\3!\3!\3!\3!\3!\5!\u025c"+
+ "\n!\3\"\3\"\3\"\3\"\3\"\3\"\3\"\3#\3#\3#\5#\u0268\n#\3#\5#\u026b\n#\3"+
+ "$\3$\3$\3$\3$\3$\3$\3%\3%\3%\3%\3%\5%\u0279\n%\3&\3&\3&\3&\3&\3&\3&\3"+
+ "\'\3\'\3\'\3\'\3\'\5\'\u0287\n\'\3(\3(\3(\5(\u028c\n(\3(\3(\3(\7(\u0291"+
+ "\n(\f(\16(\u0294\13(\5(\u0296\n(\3(\3(\3)\3)\3)\5)\u029d\n)\3*\3*\3*\3"+
+ "*\3*\6*\u02a4\n*\r*\16*\u02a5\3*\3*\3*\3*\3*\3*\3*\3*\3*\3*\3*\3*\3*\3"+
+ "*\3*\3*\3*\5*\u02b9\n*\3+\3+\3,\3,\3-\3-\5-\u02c1\n-\3-\3-\5-\u02c5\n"+
+ "-\3-\3-\3-\5-\u02ca\n-\3.\3.\3/\3/\3\60\3\60\3\60\7\60\u02d3\n\60\f\60"+
+ "\16\60\u02d6\13\60\3\60\3\60\3\61\3\61\5\61\u02dc\n\61\3\62\3\62\3\62"+
+ "\5\62\u02e1\n\62\3\62\3\62\3\62\3\62\5\62\u02e7\n\62\3\62\5\62\u02ea\n"+
+ "\62\3\63\3\63\5\63\u02ee\n\63\3\64\3\64\3\64\5\64\u02f3\n\64\3\65\3\65"+
+ "\5\65\u02f7\n\65\3\66\3\66\3\67\3\67\3\67\2\4.<8\2\4\6\b\n\f\16\20\22"+
+ "\24\26\30\32\34\36 \"$&(*,.\60\62\64\668:<>@BDFHJLNPRTVXZ\\^`bdfhjl\2"+
+ "\22\b\2\7\7\t\t\36\36\66\66AAEE\4\2((SS\4\2\t\tAA\4\2%%--\3\2\32\33\3"+
+ "\2mn\4\2\7\7vv\4\2\r\r\32\32\4\2##\62\62\4\2\7\7\34\34\3\2oq\3\2fl\4\2"+
+ "\"\"TT\7\2\27\30+,8;LM\\]\3\2tu\31\2\b\t\22\23\25\25\27\27\31\31\36\36"+
+ " #$&(++//\62\62\65\6688::AAEGILOPRSVWYY\\\\\u0358\2n\3\2\2\2\4q\3\2\2"+
+ "\2\6\u00d9\3\2\2\2\b\u00e4\3\2\2\2\n\u00e8\3\2\2\2\f\u00fd\3\2\2\2\16"+
+ "\u0104\3\2\2\2\20\u0106\3\2\2\2\22\u010e\3\2\2\2\24\u012a\3\2\2\2\26\u0134"+
+ "\3\2\2\2\30\u013e\3\2\2\2\32\u014d\3\2\2\2\34\u014f\3\2\2\2\36\u0155\3"+
+ "\2\2\2 \u0157\3\2\2\2\"\u015e\3\2\2\2$\u0170\3\2\2\2&\u0181\3\2\2\2(\u0191"+
+ "\3\2\2\2*\u01ac\3\2\2\2,\u01ae\3\2\2\2.\u01cf\3\2\2\2\60\u01e0\3\2\2\2"+
+ "\62\u01e3\3\2\2\2\64\u0215\3\2\2\2\66\u0217\3\2\2\28\u021a\3\2\2\2:\u0224"+
+ "\3\2\2\2<\u022a\3\2\2\2>\u024f\3\2\2\2@\u025b\3\2\2\2B\u025d\3\2\2\2D"+
+ "\u0264\3\2\2\2F\u026c\3\2\2\2H\u0278\3\2\2\2J\u027a\3\2\2\2L\u0286\3\2"+
+ "\2\2N\u0288\3\2\2\2P\u029c\3\2\2\2R\u02b8\3\2\2\2T\u02ba\3\2\2\2V\u02bc"+
+ "\3\2\2\2X\u02be\3\2\2\2Z\u02cb\3\2\2\2\\\u02cd\3\2\2\2^\u02d4\3\2\2\2"+
+ "`\u02db\3\2\2\2b\u02e9\3\2\2\2d\u02ed\3\2\2\2f\u02f2\3\2\2\2h\u02f6\3"+
+ "\2\2\2j\u02f8\3\2\2\2l\u02fa\3\2\2\2no\5\6\4\2op\7\2\2\3p\3\3\2\2\2qr"+
+ "\5,\27\2rs\7\2\2\3s\5\3\2\2\2t\u00da\5\b\5\2u\u0083\7 \2\2v\177\7\3\2"+
+ "\2wx\7G\2\2x~\t\2\2\2yz\7$\2\2z~\t\3\2\2{|\7Y\2\2|~\5V,\2}w\3\2\2\2}y"+
+ "\3\2\2\2}{\3\2\2\2~\u0081\3\2\2\2\177}\3\2\2\2\177\u0080\3\2\2\2\u0080"+
+ "\u0082\3\2\2\2\u0081\177\3\2\2\2\u0082\u0084\7\4\2\2\u0083v\3\2\2\2\u0083"+
+ "\u0084\3\2\2\2\u0084\u0085\3\2\2\2\u0085\u00da\5\6\4\2\u0086\u0092\7\31"+
+ "\2\2\u0087\u008e\7\3\2\2\u0088\u0089\7G\2\2\u0089\u008d\t\4\2\2\u008a"+
+ "\u008b\7$\2\2\u008b\u008d\t\3\2\2\u008c\u0088\3\2\2\2\u008c\u008a\3\2"+
+ "\2\2\u008d\u0090\3\2\2\2\u008e\u008c\3\2\2\2\u008e\u008f\3\2\2\2\u008f"+
+ "\u0091\3\2\2\2\u0090\u008e\3\2\2\2\u0091\u0093\7\4\2\2\u0092\u0087\3\2"+
+ "\2\2\u0092\u0093\3\2\2\2\u0093\u0094\3\2\2\2\u0094\u00da\5\6\4\2\u0095"+
+ "\u0096\7O\2\2\u0096\u0099\7R\2\2\u0097\u009a\5\66\34\2\u0098\u009a\5b"+
+ "\62\2\u0099\u0097\3\2\2\2\u0099\u0098\3\2\2\2\u0099\u009a\3\2\2\2\u009a"+
+ "\u00da\3\2\2\2\u009b\u009c\7O\2\2\u009c\u009d\7\23\2\2\u009d\u00a0\t\5"+
+ "\2\2\u009e\u00a1\5\66\34\2\u009f\u00a1\5b\62\2\u00a0\u009e\3\2\2\2\u00a0"+
+ "\u009f\3\2\2\2\u00a1\u00da\3\2\2\2\u00a2\u00a5\t\6\2\2\u00a3\u00a6\5\66"+
+ "\34\2\u00a4\u00a6\5b\62\2\u00a5\u00a3\3\2\2\2\u00a5\u00a4\3\2\2\2\u00a6"+
+ "\u00da\3\2\2\2\u00a7\u00a8\7O\2\2\u00a8\u00aa\7\'\2\2\u00a9\u00ab\5\66"+
+ "\34\2\u00aa\u00a9\3\2\2\2\u00aa\u00ab\3\2\2\2\u00ab\u00da\3\2\2\2\u00ac"+
+ "\u00ad\7O\2\2\u00ad\u00da\7K\2\2\u00ae\u00af\7P\2\2\u00af\u00b2\7R\2\2"+
+ "\u00b0\u00b1\7\21\2\2\u00b1\u00b3\5\66\34\2\u00b2\u00b0\3\2\2\2\u00b2"+
+ "\u00b3\3\2\2\2\u00b3\u00b6\3\2\2\2\u00b4\u00b7\5\66\34\2\u00b5\u00b7\5"+
+ "b\62\2\u00b6\u00b4\3\2\2\2\u00b6\u00b5\3\2\2\2\u00b6\u00b7\3\2\2\2\u00b7"+
+ "\u00c1\3\2\2\2\u00b8\u00b9\7V\2\2\u00b9\u00be\5j\66\2\u00ba\u00bb\7\5"+
+ "\2\2\u00bb\u00bd\5j\66\2\u00bc\u00ba\3\2\2\2\u00bd\u00c0\3\2\2\2\u00be"+
+ "\u00bc\3\2\2\2\u00be\u00bf\3\2\2\2\u00bf\u00c2\3\2\2\2\u00c0\u00be\3\2"+
+ "\2\2\u00c1\u00b8\3\2\2\2\u00c1\u00c2\3\2\2\2\u00c2\u00da\3\2\2\2\u00c3"+
+ "\u00c4\7P\2\2\u00c4\u00c7\7\23\2\2\u00c5\u00c6\7\21\2\2\u00c6\u00c8\5"+
+ "j\66\2\u00c7\u00c5\3\2\2\2\u00c7\u00c8\3\2\2\2\u00c8\u00cc\3\2\2\2\u00c9"+
+ "\u00ca\7Q\2\2\u00ca\u00cd\5\66\34\2\u00cb\u00cd\5b\62\2\u00cc\u00c9\3"+
+ "\2\2\2\u00cc\u00cb\3\2\2\2\u00cc\u00cd\3\2\2\2\u00cd\u00cf\3\2\2\2\u00ce"+
+ "\u00d0\5\66\34\2\u00cf\u00ce\3\2\2\2\u00cf\u00d0\3\2\2\2\u00d0\u00da\3"+
+ "\2\2\2\u00d1\u00d2\7P\2\2\u00d2\u00d7\7W\2\2\u00d3\u00d5\t\7\2\2\u00d4"+
+ "\u00d3\3\2\2\2\u00d4\u00d5\3\2\2\2\u00d5\u00d6\3\2\2\2\u00d6\u00d8\5h"+
+ "\65\2\u00d7\u00d4\3\2\2\2\u00d7\u00d8\3\2\2\2\u00d8\u00da\3\2\2\2\u00d9"+
+ "t\3\2\2\2\u00d9u\3\2\2\2\u00d9\u0086\3\2\2\2\u00d9\u0095\3\2\2\2\u00d9"+
+ "\u009b\3\2\2\2\u00d9\u00a2\3\2\2\2\u00d9\u00a7\3\2\2\2\u00d9\u00ac\3\2"+
+ "\2\2\u00d9\u00ae\3\2\2\2\u00d9\u00c3\3\2\2\2\u00d9\u00d1\3\2\2\2\u00da"+
+ "\7\3\2\2\2\u00db\u00dc\7[\2\2\u00dc\u00e1\5\34\17\2\u00dd\u00de\7\5\2"+
+ "\2\u00de\u00e0\5\34\17\2\u00df\u00dd\3\2\2\2\u00e0\u00e3\3\2\2\2\u00e1"+
+ "\u00df\3\2\2\2\u00e1\u00e2\3\2\2\2\u00e2\u00e5\3\2\2\2\u00e3\u00e1\3\2"+
+ "\2\2\u00e4\u00db\3\2\2\2\u00e4\u00e5\3\2\2\2\u00e5\u00e6\3\2\2\2\u00e6"+
+ "\u00e7\5\n\6\2\u00e7\t\3\2\2\2\u00e8\u00f3\5\16\b\2\u00e9\u00ea\7C\2\2"+
+ "\u00ea\u00eb\7\17\2\2\u00eb\u00f0\5\20\t\2\u00ec\u00ed\7\5\2\2\u00ed\u00ef"+
+ "\5\20\t\2\u00ee\u00ec\3\2\2\2\u00ef\u00f2\3\2\2\2\u00f0\u00ee\3\2\2\2"+
+ "\u00f0\u00f1\3\2\2\2\u00f1\u00f4\3\2\2\2\u00f2\u00f0\3\2\2\2\u00f3\u00e9"+
+ "\3\2\2\2\u00f3\u00f4\3\2\2\2\u00f4\u00f6\3\2\2\2\u00f5\u00f7\5\f\7\2\u00f6"+
+ "\u00f5\3\2\2\2\u00f6\u00f7\3\2\2\2\u00f7\13\3\2\2\2\u00f8\u00f9\7\65\2"+
+ "\2\u00f9\u00fe\t\b\2\2\u00fa\u00fb\7`\2\2\u00fb\u00fc\t\b\2\2\u00fc\u00fe"+
+ "\7e\2\2\u00fd\u00f8\3\2\2\2\u00fd\u00fa\3\2\2\2\u00fe\r\3\2\2\2\u00ff"+
+ "\u0105\5\22\n\2\u0100\u0101\7\3\2\2\u0101\u0102\5\n\6\2\u0102\u0103\7"+
+ "\4\2\2\u0103\u0105\3\2\2\2\u0104\u00ff\3\2\2\2\u0104\u0100\3\2\2\2\u0105"+
+ "\17\3\2\2\2\u0106\u0108\5,\27\2\u0107\u0109\t\t\2\2\u0108\u0107\3\2\2"+
+ "\2\u0108\u0109\3\2\2\2\u0109\u010c\3\2\2\2\u010a\u010b\7?\2\2\u010b\u010d"+
+ "\t\n\2\2\u010c\u010a\3\2\2\2\u010c\u010d\3\2\2\2\u010d\21\3\2\2\2\u010e"+
+ "\u0110\7N\2\2\u010f\u0111\5\36\20\2\u0110\u010f\3\2\2\2\u0110\u0111\3"+
+ "\2\2\2\u0111\u0112\3\2\2\2\u0112\u0117\5 \21\2\u0113\u0114\7\5\2\2\u0114"+
+ "\u0116\5 \21\2\u0115\u0113\3\2\2\2\u0116\u0119\3\2\2\2\u0117\u0115\3\2"+
+ "\2\2\u0117\u0118\3\2\2\2\u0118\u011b\3\2\2\2\u0119\u0117\3\2\2\2\u011a"+
+ "\u011c\5\24\13\2\u011b\u011a\3\2\2\2\u011b\u011c\3\2\2\2\u011c\u011f\3"+
+ "\2\2\2\u011d\u011e\7Z\2\2\u011e\u0120\5.\30\2\u011f\u011d\3\2\2\2\u011f"+
+ "\u0120\3\2\2\2\u0120\u0124\3\2\2\2\u0121\u0122\7)\2\2\u0122\u0123\7\17"+
+ "\2\2\u0123\u0125\5\26\f\2\u0124\u0121\3\2\2\2\u0124\u0125\3\2\2\2\u0125"+
+ "\u0128\3\2\2\2\u0126\u0127\7*\2\2\u0127\u0129\5.\30\2\u0128\u0126\3\2"+
+ "\2\2\u0128\u0129\3\2\2\2\u0129\23\3\2\2\2\u012a\u012b\7%\2\2\u012b\u0130"+
+ "\5\"\22\2\u012c\u012d\7\5\2\2\u012d\u012f\5\"\22\2\u012e\u012c\3\2\2\2"+
+ "\u012f\u0132\3\2\2\2\u0130\u012e\3\2\2\2\u0130\u0131\3\2\2\2\u0131\25"+
+ "\3\2\2\2\u0132\u0130\3\2\2\2\u0133\u0135\5\36\20\2\u0134\u0133\3\2\2\2"+
+ "\u0134\u0135\3\2\2\2\u0135\u0136\3\2\2\2\u0136\u013b\5\30\r\2\u0137\u0138"+
+ "\7\5\2\2\u0138\u013a\5\30\r\2\u0139\u0137\3\2\2\2\u013a\u013d\3\2\2\2"+
+ "\u013b\u0139\3\2\2\2\u013b\u013c\3\2\2\2\u013c\27\3\2\2\2\u013d\u013b"+
+ "\3\2\2\2\u013e\u013f\5\32\16\2\u013f\31\3\2\2\2\u0140\u0149\7\3\2\2\u0141"+
+ "\u0146\5,\27\2\u0142\u0143\7\5\2\2\u0143\u0145\5,\27\2\u0144\u0142\3\2"+
+ "\2\2\u0145\u0148\3\2\2\2\u0146\u0144\3\2\2\2\u0146\u0147\3\2\2\2\u0147"+
+ "\u014a\3\2\2\2\u0148\u0146\3\2\2\2\u0149\u0141\3\2\2\2\u0149\u014a\3\2"+
+ "\2\2\u014a\u014b\3\2\2\2\u014b\u014e\7\4\2\2\u014c\u014e\5,\27\2\u014d"+
+ "\u0140\3\2\2\2\u014d\u014c\3\2\2\2\u014e\33\3\2\2\2\u014f\u0150\5`\61"+
+ "\2\u0150\u0151\7\f\2\2\u0151\u0152\7\3\2\2\u0152\u0153\5\n\6\2\u0153\u0154"+
+ "\7\4\2\2\u0154\35\3\2\2\2\u0155\u0156\t\13\2\2\u0156\37\3\2\2\2\u0157"+
+ "\u015c\5,\27\2\u0158\u015a\7\f\2\2\u0159\u0158\3\2\2\2\u0159\u015a\3\2"+
+ "\2\2\u015a\u015b\3\2\2\2\u015b\u015d\5`\61\2\u015c\u0159\3\2\2\2\u015c"+
+ "\u015d\3\2\2\2\u015d!\3\2\2\2\u015e\u0162\5*\26\2\u015f\u0161\5$\23\2"+
+ "\u0160\u015f\3\2\2\2\u0161\u0164\3\2\2\2\u0162\u0160\3\2\2\2\u0162\u0163"+
+ "\3\2\2\2\u0163#\3\2\2\2\u0164\u0162\3\2\2\2\u0165\u0166\5&\24\2\u0166"+
+ "\u0167\7\61\2\2\u0167\u0169\5*\26\2\u0168\u016a\5(\25\2\u0169\u0168\3"+
+ "\2\2\2\u0169\u016a\3\2\2\2\u016a\u0171\3\2\2\2\u016b\u016c\7<\2\2\u016c"+
+ "\u016d\5&\24\2\u016d\u016e\7\61\2\2\u016e\u016f\5*\26\2\u016f\u0171\3"+
+ "\2\2\2\u0170\u0165\3\2\2\2\u0170\u016b\3\2\2\2\u0171%\3\2\2\2\u0172\u0174"+
+ "\7.\2\2\u0173\u0172\3\2\2\2\u0173\u0174\3\2\2\2\u0174\u0182\3\2\2\2\u0175"+
+ "\u0177\7\63\2\2\u0176\u0178\7D\2\2\u0177\u0176\3\2\2\2\u0177\u0178\3\2"+
+ "\2\2\u0178\u0182\3\2\2\2\u0179\u017b\7H\2\2\u017a\u017c\7D\2\2\u017b\u017a"+
+ "\3\2\2\2\u017b\u017c\3\2\2\2\u017c\u0182\3\2\2\2\u017d\u017f\7&\2\2\u017e"+
+ "\u0180\7D\2\2\u017f\u017e\3\2\2\2\u017f\u0180\3\2\2\2\u0180\u0182\3\2"+
+ "\2\2\u0181\u0173\3\2\2\2\u0181\u0175\3\2\2\2\u0181\u0179\3\2\2\2\u0181"+
+ "\u017d\3\2\2\2\u0182\'\3\2\2\2\u0183\u0184\7@\2\2\u0184\u0192\5.\30\2"+
+ "\u0185\u0186\7X\2\2\u0186\u0187\7\3\2\2\u0187\u018c\5`\61\2\u0188\u0189"+
+ "\7\5\2\2\u0189\u018b\5`\61\2\u018a\u0188\3\2\2\2\u018b\u018e\3\2\2\2\u018c"+
+ "\u018a\3\2\2\2\u018c\u018d\3\2\2\2\u018d\u018f\3\2\2\2\u018e\u018c\3\2"+
+ "\2\2\u018f\u0190\7\4\2\2\u0190\u0192\3\2\2\2\u0191\u0183\3\2\2\2\u0191"+
+ "\u0185\3\2\2\2\u0192)\3\2\2\2\u0193\u0198\5b\62\2\u0194\u0196\7\f\2\2"+
+ "\u0195\u0194\3\2\2\2\u0195\u0196\3\2\2\2\u0196\u0197\3\2\2\2\u0197\u0199"+
+ "\5^\60\2\u0198\u0195\3\2\2\2\u0198\u0199\3\2\2\2\u0199\u01ad\3\2\2\2\u019a"+
+ "\u019b\7\3\2\2\u019b\u019c\5\n\6\2\u019c\u01a1\7\4\2\2\u019d\u019f\7\f"+
+ "\2\2\u019e\u019d\3\2\2\2\u019e\u019f\3\2\2\2\u019f\u01a0\3\2\2\2\u01a0"+
+ "\u01a2\5^\60\2\u01a1\u019e\3\2\2\2\u01a1\u01a2\3\2\2\2\u01a2\u01ad\3\2"+
+ "\2\2\u01a3\u01a4\7\3\2\2\u01a4\u01a5\5\"\22\2\u01a5\u01aa\7\4\2\2\u01a6"+
+ "\u01a8\7\f\2\2\u01a7\u01a6\3\2\2\2\u01a7\u01a8\3\2\2\2\u01a8\u01a9\3\2"+
+ "\2\2\u01a9\u01ab\5^\60\2\u01aa\u01a7\3\2\2\2\u01aa\u01ab\3\2\2\2\u01ab"+
+ "\u01ad\3\2\2\2\u01ac\u0193\3\2\2\2\u01ac\u019a\3\2\2\2\u01ac\u01a3\3\2"+
+ "\2\2\u01ad+\3\2\2\2\u01ae\u01af\5.\30\2\u01af-\3\2\2\2\u01b0\u01b1\b\30"+
+ "\1\2\u01b1\u01b2\7=\2\2\u01b2\u01d0\5.\30\n\u01b3\u01b4\7\37\2\2\u01b4"+
+ "\u01b5\7\3\2\2\u01b5\u01b6\5\b\5\2\u01b6\u01b7\7\4\2\2\u01b7\u01d0\3\2"+
+ "\2\2\u01b8\u01b9\7J\2\2\u01b9\u01ba\7\3\2\2\u01ba\u01bb\5j\66\2\u01bb"+
+ "\u01bc\5\60\31\2\u01bc\u01bd\7\4\2\2\u01bd\u01d0\3\2\2\2\u01be\u01bf\7"+
+ "\67\2\2\u01bf\u01c0\7\3\2\2\u01c0\u01c1\5^\60\2\u01c1\u01c2\7\5\2\2\u01c2"+
+ "\u01c3\5j\66\2\u01c3\u01c4\5\60\31\2\u01c4\u01c5\7\4\2\2\u01c5\u01d0\3"+
+ "\2\2\2\u01c6\u01c7\7\67\2\2\u01c7\u01c8\7\3\2\2\u01c8\u01c9\5j\66\2\u01c9"+
+ "\u01ca\7\5\2\2\u01ca\u01cb\5j\66\2\u01cb\u01cc\5\60\31\2\u01cc\u01cd\7"+
+ "\4\2\2\u01cd\u01d0\3\2\2\2\u01ce\u01d0\5\62\32\2\u01cf\u01b0\3\2\2\2\u01cf"+
+ "\u01b3\3\2\2\2\u01cf\u01b8\3\2\2\2\u01cf\u01be\3\2\2\2\u01cf\u01c6\3\2"+
+ "\2\2\u01cf\u01ce\3\2\2\2\u01d0\u01d9\3\2\2\2\u01d1\u01d2\f\4\2\2\u01d2"+
+ "\u01d3\7\n\2\2\u01d3\u01d8\5.\30\5\u01d4\u01d5\f\3\2\2\u01d5\u01d6\7B"+
+ "\2\2\u01d6\u01d8\5.\30\4\u01d7\u01d1\3\2\2\2\u01d7\u01d4\3\2\2\2\u01d8"+
+ "\u01db\3\2\2\2\u01d9\u01d7\3\2\2\2\u01d9\u01da\3\2\2\2\u01da/\3\2\2\2"+
+ "\u01db\u01d9\3\2\2\2\u01dc\u01dd\7\5\2\2\u01dd\u01df\5j\66\2\u01de\u01dc"+
+ "\3\2\2\2\u01df\u01e2\3\2\2\2\u01e0\u01de\3\2\2\2\u01e0\u01e1\3\2\2\2\u01e1"+
+ "\61\3\2\2\2\u01e2\u01e0\3\2\2\2\u01e3\u01e5\5<\37\2\u01e4\u01e6\5\64\33"+
+ "\2\u01e5\u01e4\3\2\2\2\u01e5\u01e6\3\2\2\2\u01e6\63\3\2\2\2\u01e7\u01e9"+
+ "\7=\2\2\u01e8\u01e7\3\2\2\2\u01e8\u01e9\3\2\2\2\u01e9\u01ea\3\2\2\2\u01ea"+
+ "\u01eb\7\16\2\2\u01eb\u01ec\5<\37\2\u01ec\u01ed\7\n\2\2\u01ed\u01ee\5"+
+ "<\37\2\u01ee\u0216\3\2\2\2\u01ef\u01f1\7=\2\2\u01f0\u01ef\3\2\2\2\u01f0"+
+ "\u01f1\3\2\2\2\u01f1\u01f2\3\2\2\2\u01f2\u01f3\7-\2\2\u01f3\u01f4\7\3"+
+ "\2\2\u01f4\u01f9\5<\37\2\u01f5\u01f6\7\5\2\2\u01f6\u01f8\5<\37\2\u01f7"+
+ "\u01f5\3\2\2\2\u01f8\u01fb\3\2\2\2\u01f9\u01f7\3\2\2\2\u01f9\u01fa\3\2"+
+ "\2\2\u01fa\u01fc\3\2\2\2\u01fb\u01f9\3\2\2\2\u01fc\u01fd\7\4\2\2\u01fd"+
+ "\u0216\3\2\2\2\u01fe\u0200\7=\2\2\u01ff\u01fe\3\2\2\2\u01ff\u0200\3\2"+
+ "\2\2\u0200\u0201\3\2\2\2\u0201\u0202\7-\2\2\u0202\u0203\7\3\2\2\u0203"+
+ "\u0204\5\b\5\2\u0204\u0205\7\4\2\2\u0205\u0216\3\2\2\2\u0206\u0208\7="+
+ "\2\2\u0207\u0206\3\2\2\2\u0207\u0208\3\2\2\2\u0208\u0209\3\2\2\2\u0209"+
+ "\u020a\7\64\2\2\u020a\u0216\58\35\2\u020b\u020d\7=\2\2\u020c\u020b\3\2"+
+ "\2\2\u020c\u020d\3\2\2\2\u020d\u020e\3\2\2\2\u020e\u020f\7I\2\2\u020f"+
+ "\u0216\5j\66\2\u0210\u0212\7\60\2\2\u0211\u0213\7=\2\2\u0212\u0211\3\2"+
+ "\2\2\u0212\u0213\3\2\2\2\u0213\u0214\3\2\2\2\u0214\u0216\7>\2\2\u0215"+
+ "\u01e8\3\2\2\2\u0215\u01f0\3\2\2\2\u0215\u01ff\3\2\2\2\u0215\u0207\3\2"+
+ "\2\2\u0215\u020c\3\2\2\2\u0215\u0210\3\2\2\2\u0216\65\3\2\2\2\u0217\u0218"+
+ "\7\64\2\2\u0218\u0219\58\35\2\u0219\67\3\2\2\2\u021a\u021c\5j\66\2\u021b"+
+ "\u021d\5:\36\2\u021c\u021b\3\2\2\2\u021c\u021d\3\2\2\2\u021d9\3\2\2\2"+
+ "\u021e\u021f\7\35\2\2\u021f\u0225\5j\66\2\u0220\u0221\7^\2\2\u0221\u0222"+
+ "\5j\66\2\u0222\u0223\7e\2\2\u0223\u0225\3\2\2\2\u0224\u021e\3\2\2\2\u0224"+
+ "\u0220\3\2\2\2\u0225;\3\2\2\2\u0226\u0227\b\37\1\2\u0227\u022b\5> \2\u0228"+
+ "\u0229\t\7\2\2\u0229\u022b\5<\37\6\u022a\u0226\3\2\2\2\u022a\u0228\3\2"+
+ "\2\2\u022b\u0238\3\2\2\2\u022c\u022d\f\5\2\2\u022d\u022e\t\f\2\2\u022e"+
+ "\u0237\5<\37\6\u022f\u0230\f\4\2\2\u0230\u0231\t\7\2\2\u0231\u0237\5<"+
+ "\37\5\u0232\u0233\f\3\2\2\u0233\u0234\5T+\2\u0234\u0235\5<\37\4\u0235"+
+ "\u0237\3\2\2\2\u0236\u022c\3\2\2\2\u0236\u022f\3\2\2\2\u0236\u0232\3\2"+
+ "\2\2\u0237\u023a\3\2\2\2\u0238\u0236\3\2\2\2\u0238\u0239\3\2\2\2\u0239"+
+ "=\3\2\2\2\u023a\u0238\3\2\2\2\u023b\u0250\5@!\2\u023c\u0250\5H%\2\u023d"+
+ "\u0250\5D#\2\u023e\u0250\5R*\2\u023f\u0240\5^\60\2\u0240\u0241\7s\2\2"+
+ "\u0241\u0243\3\2\2\2\u0242\u023f\3\2\2\2\u0242\u0243\3\2\2\2\u0243\u0244"+
+ "\3\2\2\2\u0244\u0250\7o\2\2\u0245\u0250\5L\'\2\u0246\u0247\7\3\2\2\u0247"+
+ "\u0248\5\b\5\2\u0248\u0249\7\4\2\2\u0249\u0250\3\2\2\2\u024a\u0250\5^"+
+ "\60\2\u024b\u024c\7\3\2\2\u024c\u024d\5,\27\2\u024d\u024e\7\4\2\2\u024e"+
+ "\u0250\3\2\2\2\u024f\u023b\3\2\2\2\u024f\u023c\3\2\2\2\u024f\u023d\3\2"+
+ "\2\2\u024f\u023e\3\2\2\2\u024f\u0242\3\2\2\2\u024f\u0245\3\2\2\2\u024f"+
+ "\u0246\3\2\2\2\u024f\u024a\3\2\2\2\u024f\u024b\3\2\2\2\u0250?\3\2\2\2"+
+ "\u0251\u025c\5B\"\2\u0252\u0253\7_\2\2\u0253\u0254\5B\"\2\u0254\u0255"+
+ "\7e\2\2\u0255\u025c\3\2\2\2\u0256\u025c\5F$\2\u0257\u0258\7_\2\2\u0258"+
+ "\u0259\5F$\2\u0259\u025a\7e\2\2\u025a\u025c\3\2\2\2\u025b\u0251\3\2\2"+
+ "\2\u025b\u0252\3\2\2\2\u025b\u0256\3\2\2\2\u025b\u0257\3\2\2\2\u025cA"+
+ "\3\2\2\2\u025d\u025e\7\20\2\2\u025e\u025f\7\3\2\2\u025f\u0260\5,\27\2"+
+ "\u0260\u0261\7\f\2\2\u0261\u0262\5\\/\2\u0262\u0263\7\4\2\2\u0263C\3\2"+
+ "\2\2\u0264\u026a\7\26\2\2\u0265\u0267\7\3\2\2\u0266\u0268\7v\2\2\u0267"+
+ "\u0266\3\2\2\2\u0267\u0268\3\2\2\2\u0268\u0269\3\2\2\2\u0269\u026b\7\4"+
+ "\2\2\u026a\u0265\3\2\2\2\u026a\u026b\3\2\2\2\u026bE\3\2\2\2\u026c\u026d"+
+ "\7\24\2\2\u026d\u026e\7\3\2\2\u026e\u026f\5,\27\2\u026f\u0270\7\5\2\2"+
+ "\u0270\u0271\5\\/\2\u0271\u0272\7\4\2\2\u0272G\3\2\2\2\u0273\u0279\5J"+
+ "&\2\u0274\u0275\7_\2\2\u0275\u0276\5J&\2\u0276\u0277\7e\2\2\u0277\u0279"+
+ "\3\2\2\2\u0278\u0273\3\2\2\2\u0278\u0274\3\2\2\2\u0279I\3\2\2\2\u027a"+
+ "\u027b\7!\2\2\u027b\u027c\7\3\2\2\u027c\u027d\5`\61\2\u027d\u027e\7%\2"+
+ "\2\u027e\u027f\5<\37\2\u027f\u0280\7\4\2\2\u0280K\3\2\2\2\u0281\u0287"+
+ "\5N(\2\u0282\u0283\7_\2\2\u0283\u0284\5N(\2\u0284\u0285\7e\2\2\u0285\u0287"+
+ "\3\2\2\2\u0286\u0281\3\2\2\2\u0286\u0282\3\2\2\2\u0287M\3\2\2\2\u0288"+
+ "\u0289\5P)\2\u0289\u0295\7\3\2\2\u028a\u028c\5\36\20\2\u028b\u028a\3\2"+
+ "\2\2\u028b\u028c\3\2\2\2\u028c\u028d\3\2\2\2\u028d\u0292\5,\27\2\u028e"+
+ "\u028f\7\5\2\2\u028f\u0291\5,\27\2\u0290\u028e\3\2\2\2\u0291\u0294\3\2"+
+ "\2\2\u0292\u0290\3\2\2\2\u0292\u0293\3\2\2\2\u0293\u0296\3\2\2\2\u0294"+
+ "\u0292\3\2\2\2\u0295\u028b\3\2\2\2\u0295\u0296\3\2\2\2\u0296\u0297\3\2"+
+ "\2\2\u0297\u0298\7\4\2\2\u0298O\3\2\2\2\u0299\u029d\7\63\2\2\u029a\u029d"+
+ "\7H\2\2\u029b\u029d\5`\61\2\u029c\u0299\3\2\2\2\u029c\u029a\3\2\2\2\u029c"+
+ "\u029b\3\2\2\2\u029dQ\3\2\2\2\u029e\u02b9\7>\2\2\u029f\u02b9\5X-\2\u02a0"+
+ "\u02b9\5h\65\2\u02a1\u02b9\5V,\2\u02a2\u02a4\7u\2\2\u02a3\u02a2\3\2\2"+
+ "\2\u02a4\u02a5\3\2\2\2\u02a5\u02a3\3\2\2\2\u02a5\u02a6\3\2\2\2\u02a6\u02b9"+
+ "\3\2\2\2\u02a7\u02b9\7t\2\2\u02a8\u02a9\7a\2\2\u02a9\u02aa\5j\66\2\u02aa"+
+ "\u02ab\7e\2\2\u02ab\u02b9\3\2\2\2\u02ac\u02ad\7b\2\2\u02ad\u02ae\5j\66"+
+ "\2\u02ae\u02af\7e\2\2\u02af\u02b9\3\2\2\2\u02b0\u02b1\7c\2\2\u02b1\u02b2"+
+ "\5j\66\2\u02b2\u02b3\7e\2\2\u02b3\u02b9\3\2\2\2\u02b4\u02b5\7d\2\2\u02b5"+
+ "\u02b6\5j\66\2\u02b6\u02b7\7e\2\2\u02b7\u02b9\3\2\2\2\u02b8\u029e\3\2"+
+ "\2\2\u02b8\u029f\3\2\2\2\u02b8\u02a0\3\2\2\2\u02b8\u02a1\3\2\2\2\u02b8"+
+ "\u02a3\3\2\2\2\u02b8\u02a7\3\2\2\2\u02b8\u02a8\3\2\2\2\u02b8\u02ac\3\2"+
+ "\2\2\u02b8\u02b0\3\2\2\2\u02b8\u02b4\3\2\2\2\u02b9S\3\2\2\2\u02ba\u02bb"+
+ "\t\r\2\2\u02bbU\3\2\2\2\u02bc\u02bd\t\16\2\2\u02bdW\3\2\2\2\u02be\u02c0"+
+ "\7/\2\2\u02bf\u02c1\t\7\2\2\u02c0\u02bf\3\2\2\2\u02c0\u02c1\3\2\2\2\u02c1"+
+ "\u02c4\3\2\2\2\u02c2\u02c5\5h\65\2\u02c3\u02c5\5j\66\2\u02c4\u02c2\3\2"+
+ "\2\2\u02c4\u02c3\3\2\2\2\u02c5\u02c6\3\2\2\2\u02c6\u02c9\5Z.\2\u02c7\u02c8"+
+ "\7U\2\2\u02c8\u02ca\5Z.\2\u02c9\u02c7\3\2\2\2\u02c9\u02ca\3\2\2\2\u02ca"+
+ "Y\3\2\2\2\u02cb\u02cc\t\17\2\2\u02cc[\3\2\2\2\u02cd\u02ce\5`\61\2\u02ce"+
+ "]\3\2\2\2\u02cf\u02d0\5`\61\2\u02d0\u02d1\7s\2\2\u02d1\u02d3\3\2\2\2\u02d2"+
+ "\u02cf\3\2\2\2\u02d3\u02d6\3\2\2\2\u02d4\u02d2\3\2\2\2\u02d4\u02d5\3\2"+
+ "\2\2\u02d5\u02d7\3\2\2\2\u02d6\u02d4\3\2\2\2\u02d7\u02d8\5`\61\2\u02d8"+
+ "_\3\2\2\2\u02d9\u02dc\5d\63\2\u02da\u02dc\5f\64\2\u02db\u02d9\3\2\2\2"+
+ "\u02db\u02da\3\2\2\2\u02dca\3\2\2\2\u02dd\u02de\5`\61\2\u02de\u02df\7"+
+ "\6\2\2\u02df\u02e1\3\2\2\2\u02e0\u02dd\3\2\2\2\u02e0\u02e1\3\2\2\2\u02e1"+
+ "\u02e2\3\2\2\2\u02e2\u02ea\7z\2\2\u02e3\u02e4\5`\61\2\u02e4\u02e5\7\6"+
+ "\2\2\u02e5\u02e7\3\2\2\2\u02e6\u02e3\3\2\2\2\u02e6\u02e7\3\2\2\2\u02e7"+
+ "\u02e8\3\2\2\2\u02e8\u02ea\5`\61\2\u02e9\u02e0\3\2\2\2\u02e9\u02e6\3\2"+
+ "\2\2\u02eac\3\2\2\2\u02eb\u02ee\7{\2\2\u02ec\u02ee\7|\2\2\u02ed\u02eb"+
+ "\3\2\2\2\u02ed\u02ec\3\2\2\2\u02eee\3\2\2\2\u02ef\u02f3\7x\2\2\u02f0\u02f3"+
+ "\5l\67\2\u02f1\u02f3\7y\2\2\u02f2\u02ef\3\2\2\2\u02f2\u02f0\3\2\2\2\u02f2"+
+ "\u02f1\3\2\2\2\u02f3g\3\2\2\2\u02f4\u02f7\7w\2\2\u02f5\u02f7\7v\2\2\u02f6"+
+ "\u02f4\3\2\2\2\u02f6\u02f5\3\2\2\2\u02f7i\3\2\2\2\u02f8\u02f9\t\20\2\2"+
+ "\u02f9k\3\2\2\2\u02fa\u02fb\t\21\2\2\u02fbm\3\2\2\2h}\177\u0083\u008c"+
+ "\u008e\u0092\u0099\u00a0\u00a5\u00aa\u00b2\u00b6\u00be\u00c1\u00c7\u00cc"+
+ "\u00cf\u00d4\u00d7\u00d9\u00e1\u00e4\u00f0\u00f3\u00f6\u00fd\u0104\u0108"+
+ "\u010c\u0110\u0117\u011b\u011f\u0124\u0128\u0130\u0134\u013b\u0146\u0149"+
+ "\u014d\u0159\u015c\u0162\u0169\u0170\u0173\u0177\u017b\u017f\u0181\u018c"+
+ "\u0191\u0195\u0198\u019e\u01a1\u01a7\u01aa\u01ac\u01cf\u01d7\u01d9\u01e0"+
+ "\u01e5\u01e8\u01f0\u01f9\u01ff\u0207\u020c\u0212\u0215\u021c\u0224\u022a"+
+ "\u0236\u0238\u0242\u024f\u025b\u0267\u026a\u0278\u0286\u028b\u0292\u0295"+
+ "\u029c\u02a5\u02b8\u02c0\u02c4\u02c9\u02d4\u02db\u02e0\u02e6\u02e9\u02ed"+
+ "\u02f2\u02f6";
public static final ATN _ATN =
new ATNDeserializer().deserialize(_serializedATN.toCharArray());
static {
diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseVisitor.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseVisitor.java
index 1575e310c1469..ed64045191be0 100644
--- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseVisitor.java
+++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseVisitor.java
@@ -71,13 +71,6 @@ interface SqlBaseVisitor extends ParseTreeVisitor {
* @return the visitor result
*/
T visitShowSchemas(SqlBaseParser.ShowSchemasContext ctx);
- /**
- * Visit a parse tree produced by the {@code sysCatalogs}
- * labeled alternative in {@link SqlBaseParser#statement}.
- * @param ctx the parse tree
- * @return the visitor result
- */
- T visitSysCatalogs(SqlBaseParser.SysCatalogsContext ctx);
/**
* Visit a parse tree produced by the {@code sysTables}
* labeled alternative in {@link SqlBaseParser#statement}.
@@ -99,13 +92,6 @@ interface SqlBaseVisitor extends ParseTreeVisitor {
* @return the visitor result
*/
T visitSysTypes(SqlBaseParser.SysTypesContext ctx);
- /**
- * Visit a parse tree produced by the {@code sysTableTypes}
- * labeled alternative in {@link SqlBaseParser#statement}.
- * @param ctx the parse tree
- * @return the visitor result
- */
- T visitSysTableTypes(SqlBaseParser.SysTableTypesContext ctx);
/**
* Visit a parse tree produced by {@link SqlBaseParser#query}.
* @param ctx the parse tree
diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysCatalogs.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysCatalogs.java
deleted file mode 100644
index a98b1bdc1b7a7..0000000000000
--- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysCatalogs.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * 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.sql.plan.logical.command.sys;
-
-import org.elasticsearch.action.ActionListener;
-import org.elasticsearch.xpack.sql.expression.Attribute;
-import org.elasticsearch.xpack.sql.plan.logical.command.Command;
-import org.elasticsearch.xpack.sql.session.Rows;
-import org.elasticsearch.xpack.sql.session.SchemaRowSet;
-import org.elasticsearch.xpack.sql.session.SqlSession;
-import org.elasticsearch.xpack.sql.tree.Source;
-import org.elasticsearch.xpack.sql.tree.NodeInfo;
-
-import java.util.List;
-
-import static java.util.Collections.singletonList;
-
-/**
- * System command returning the catalogs (clusters) available.
- * Currently returns only the current cluster name.
- */
-public class SysCatalogs extends Command {
-
- public SysCatalogs(Source source) {
- super(source);
- }
-
- @Override
- protected NodeInfo info() {
- return NodeInfo.create(this);
- }
-
- @Override
- public List output() {
- return singletonList(keyword("TABLE_CAT"));
- }
-
- @Override
- public final void execute(SqlSession session, ActionListener listener) {
- String cluster = session.indexResolver().clusterName();
- listener.onResponse(Rows.of(output(), singletonList(singletonList(cluster))));
- }
-
- @Override
- public int hashCode() {
- return getClass().hashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
-
- if (obj == null || getClass() != obj.getClass()) {
- return false;
- }
-
- return true;
- }
-}
\ No newline at end of file
diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTableTypes.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTableTypes.java
deleted file mode 100644
index fef6171b95d22..0000000000000
--- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTableTypes.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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.sql.plan.logical.command.sys;
-
-import org.elasticsearch.action.ActionListener;
-import org.elasticsearch.xpack.sql.analysis.index.IndexResolver.IndexType;
-import org.elasticsearch.xpack.sql.expression.Attribute;
-import org.elasticsearch.xpack.sql.plan.logical.command.Command;
-import org.elasticsearch.xpack.sql.session.Rows;
-import org.elasticsearch.xpack.sql.session.SchemaRowSet;
-import org.elasticsearch.xpack.sql.session.SqlSession;
-import org.elasticsearch.xpack.sql.tree.Source;
-import org.elasticsearch.xpack.sql.tree.NodeInfo;
-
-import java.util.Comparator;
-import java.util.List;
-
-import static java.util.Collections.singletonList;
-import static java.util.stream.Collectors.toList;
-
-/**
- * System command returning the types of tables supported,
- * index and alias.
- */
-public class SysTableTypes extends Command {
-
- public SysTableTypes(Source source) {
- super(source);
- }
-
- @Override
- protected NodeInfo info() {
- return NodeInfo.create(this);
- }
-
- @Override
- public List output() {
- return singletonList(keyword("TABLE_TYPE"));
- }
-
- @Override
- public final void execute(SqlSession session, ActionListener listener) {
- listener.onResponse(Rows.of(output(), IndexType.VALID.stream()
- // *DBC requires ascending order
- .sorted(Comparator.comparing(t -> t.toSql()))
- .map(t -> singletonList(t.toSql()))
- .collect(toList())));
- }
-
- @Override
- public int hashCode() {
- return getClass().hashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
-
- if (obj == null || getClass() != obj.getClass()) {
- return false;
- }
-
- return true;
- }
-}
\ No newline at end of file
diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysCatalogsTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysCatalogsTests.java
deleted file mode 100644
index 757c7ed010834..0000000000000
--- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysCatalogsTests.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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.sql.plan.logical.command.sys;
-
-import org.elasticsearch.action.ActionListener;
-import org.elasticsearch.common.collect.Tuple;
-import org.elasticsearch.test.ESTestCase;
-import org.elasticsearch.xpack.sql.TestUtils;
-import org.elasticsearch.xpack.sql.analysis.analyzer.Analyzer;
-import org.elasticsearch.xpack.sql.analysis.analyzer.Verifier;
-import org.elasticsearch.xpack.sql.analysis.index.EsIndex;
-import org.elasticsearch.xpack.sql.analysis.index.IndexResolution;
-import org.elasticsearch.xpack.sql.analysis.index.IndexResolver;
-import org.elasticsearch.xpack.sql.expression.function.FunctionRegistry;
-import org.elasticsearch.xpack.sql.parser.SqlParser;
-import org.elasticsearch.xpack.sql.plan.logical.command.Command;
-import org.elasticsearch.xpack.sql.session.SqlSession;
-import org.elasticsearch.xpack.sql.stats.Metrics;
-import org.elasticsearch.xpack.sql.type.TypesTests;
-
-import static java.util.Collections.singletonList;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class SysCatalogsTests extends ESTestCase {
-
- private final SqlParser parser = new SqlParser();
-
- @SuppressWarnings({ "rawtypes", "unchecked" })
- private Tuple sql(String sql) {
- EsIndex test = new EsIndex("test", TypesTests.loadMapping("mapping-multi-field-with-nested.json", true));
- Analyzer analyzer = new Analyzer(TestUtils.TEST_CFG, new FunctionRegistry(), IndexResolution.valid(test),
- new Verifier(new Metrics()));
- Command cmd = (Command) analyzer.analyze(parser.createStatement(sql), true);
-
- IndexResolver resolver = mock(IndexResolver.class);
- when(resolver.clusterName()).thenReturn("cluster");
-
- doAnswer(invocation -> {
- ((ActionListener) invocation.getArguments()[2]).onResponse(singletonList(test));
- return Void.TYPE;
- }).when(resolver).resolveAsSeparateMappings(any(), any(), any());
-
- SqlSession session = new SqlSession(null, null, null, resolver, null, null, null, null);
- return new Tuple<>(cmd, session);
- }
-
- public void testSysCatalogs() throws Exception {
- Tuple sql = sql("SYS CATALOGS");
-
- sql.v1().execute(sql.v2(), ActionListener.wrap(r -> {
- assertEquals(1, r.size());
- assertEquals("cluster", r.column(0));
- }, ex -> fail(ex.getMessage())));
- }
-}
\ No newline at end of file
diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTableTypesTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTableTypesTests.java
deleted file mode 100644
index 2458a3f34eb39..0000000000000
--- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTableTypesTests.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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.sql.plan.logical.command.sys;
-
-import org.elasticsearch.action.ActionListener;
-import org.elasticsearch.common.collect.Tuple;
-import org.elasticsearch.test.ESTestCase;
-import org.elasticsearch.xpack.sql.TestUtils;
-import org.elasticsearch.xpack.sql.analysis.analyzer.Analyzer;
-import org.elasticsearch.xpack.sql.analysis.analyzer.Verifier;
-import org.elasticsearch.xpack.sql.analysis.index.EsIndex;
-import org.elasticsearch.xpack.sql.analysis.index.IndexResolution;
-import org.elasticsearch.xpack.sql.analysis.index.IndexResolver;
-import org.elasticsearch.xpack.sql.expression.function.FunctionRegistry;
-import org.elasticsearch.xpack.sql.parser.SqlParser;
-import org.elasticsearch.xpack.sql.plan.logical.command.Command;
-import org.elasticsearch.xpack.sql.session.SqlSession;
-import org.elasticsearch.xpack.sql.stats.Metrics;
-import org.elasticsearch.xpack.sql.type.TypesTests;
-
-import static org.mockito.Mockito.mock;
-
-public class SysTableTypesTests extends ESTestCase {
-
- private final SqlParser parser = new SqlParser();
-
- private Tuple sql(String sql) {
- EsIndex test = new EsIndex("test", TypesTests.loadMapping("mapping-multi-field-with-nested.json", true));
- Analyzer analyzer = new Analyzer(TestUtils.TEST_CFG, new FunctionRegistry(), IndexResolution.valid(test),
- new Verifier(new Metrics()));
- Command cmd = (Command) analyzer.analyze(parser.createStatement(sql), true);
-
- IndexResolver resolver = mock(IndexResolver.class);
- SqlSession session = new SqlSession(null, null, null, resolver, null, null, null, null);
- return new Tuple<>(cmd, session);
- }
-
- public void testSysTableTypes() throws Exception {
- Tuple sql = sql("SYS TABLE TYPES");
-
- sql.v1().execute(sql.v2(), ActionListener.wrap(r -> {
- assertEquals(2, r.size());
- assertEquals("ALIAS", r.column(0));
- assertTrue(r.advanceRow());
- assertEquals("BASE TABLE", r.column(0));
- }, ex -> fail(ex.getMessage())));
- }
-
-}
\ No newline at end of file
diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/stats/VerifierMetricsTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/stats/VerifierMetricsTests.java
index 1db0d4383e756..3f50915f98637 100644
--- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/stats/VerifierMetricsTests.java
+++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/stats/VerifierMetricsTests.java
@@ -93,8 +93,7 @@ public void testOrderByQuery() {
public void testCommand() {
Counters c = sql(randomFrom("SHOW FUNCTIONS", "SHOW COLUMNS FROM library", "SHOW SCHEMAS",
- "SHOW TABLES", "SYS CATALOGS", "SYS COLUMNS LIKE '%name'",
- "SYS TABLES", "SYS TYPES"));
+ "SHOW TABLES", "SYS COLUMNS LIKE '%name'", "SYS TABLES", "SYS TYPES"));
assertEquals(0, where(c));
assertEquals(0, limit(c));
assertEquals(0, groupby(c));
From 6d99e790b3ee78f99f4810ff79b38488e43a146d Mon Sep 17 00:00:00 2001
From: Tim Vernum
Date: Wed, 16 Jan 2019 21:52:17 +1100
Subject: [PATCH 05/16] Add SSL Configuration Library (#37287)
This introduces a new ssl-config library that can parse
and validate SSL/TLS settings and files.
It supports the standard configuration settings as used in the
Elastic Stack such as "ssl.verification_mode" and
"ssl.certificate_authorities" as well as all file formats used
in other parts of Elasticsearch security (such as PEM, JKS,
PKCS#12, PKCS#8, et al).
---
libs/ssl-config/build.gradle | 42 ++
.../common/ssl/DefaultJdkTrustConfig.java | 128 ++++
.../elasticsearch/common/ssl/DerParser.java | 297 +++++++++
.../common/ssl/EmptyKeyConfig.java | 52 ++
.../common/ssl/KeyStoreUtil.java | 163 +++++
.../common/ssl/PemKeyConfig.java | 122 ++++
.../common/ssl/PemTrustConfig.java | 122 ++++
.../elasticsearch/common/ssl/PemUtils.java | 613 ++++++++++++++++++
.../ssl/SslClientAuthenticationMode.java | 101 +++
.../common/ssl/SslConfigException.java | 33 +
.../common/ssl/SslConfiguration.java | 164 +++++
.../common/ssl/SslConfigurationKeys.java | 181 ++++++
.../common/ssl/SslConfigurationLoader.java | 371 +++++++++++
.../common/ssl/SslKeyConfig.java | 46 ++
.../common/ssl/SslTrustConfig.java | 46 ++
.../common/ssl/SslVerificationMode.java | 104 +++
.../common/ssl/StoreKeyConfig.java | 106 +++
.../common/ssl/StoreTrustConfig.java | 90 +++
.../common/ssl/TrustEverythingConfig.java | 92 +++
.../ssl/DefaultJdkTrustConfigTests.java | 79 +++
.../common/ssl/PemKeyConfigTests.java | 148 +++++
.../common/ssl/PemTrustConfigTests.java | 150 +++++
.../common/ssl/PemUtilsTests.java | 219 +++++++
.../ssl/SslConfigurationLoaderTests.java | 220 +++++++
.../common/ssl/SslConfigurationTests.java | 140 ++++
.../common/ssl/StoreKeyConfigTests.java | 215 ++++++
.../common/ssl/StoreTrustConfigTests.java | 169 +++++
.../src/test/resources/certs/README.txt | 75 +++
.../src/test/resources/certs/ca-all/ca.jks | Bin 0 -> 2460 bytes
.../src/test/resources/certs/ca-all/ca.p12 | Bin 0 -> 2818 bytes
.../src/test/resources/certs/ca1/ca.crt | 19 +
.../src/test/resources/certs/ca1/ca.key | 27 +
.../src/test/resources/certs/ca1/ca.p12 | Bin 0 -> 1066 bytes
.../src/test/resources/certs/ca2/ca.crt | 19 +
.../src/test/resources/certs/ca2/ca.key | 27 +
.../src/test/resources/certs/ca2/ca.p12 | Bin 0 -> 1066 bytes
.../src/test/resources/certs/ca3/ca.crt | 19 +
.../src/test/resources/certs/ca3/ca.key | 27 +
.../src/test/resources/certs/ca3/ca.p12 | Bin 0 -> 1066 bytes
.../test/resources/certs/cert-all/certs.jks | Bin 0 -> 4268 bytes
.../test/resources/certs/cert-all/certs.p12 | Bin 0 -> 4757 bytes
.../src/test/resources/certs/cert1/cert1.crt | 19 +
.../src/test/resources/certs/cert1/cert1.key | 27 +
.../src/test/resources/certs/cert1/cert1.p12 | Bin 0 -> 2456 bytes
.../src/test/resources/certs/cert2/cert2.crt | 19 +
.../src/test/resources/certs/cert2/cert2.key | 30 +
.../src/test/resources/certs/cert2/cert2.p12 | Bin 0 -> 2456 bytes
.../resources/certs/pem-utils/README.asciidoc | 149 +++++
.../pem-utils/corrupted_key_pkcs8_plain.pem | 24 +
.../pem-utils/dsa_key_openssl_encrypted.pem | 15 +
.../certs/pem-utils/dsa_key_openssl_plain.pem | 12 +
.../dsa_key_openssl_plain_with_params.pem | 18 +
.../certs/pem-utils/dsa_key_pkcs8_plain.pem | 9 +
.../pem-utils/ec_key_openssl_encrypted.pem | 7 +
.../certs/pem-utils/ec_key_openssl_plain.pem | 4 +
.../ec_key_openssl_plain_with_params.pem | 7 +
.../certs/pem-utils/ec_key_pkcs8_plain.pem | 4 +
.../test/resources/certs/pem-utils/empty.pem | 0
.../certs/pem-utils/key_pkcs8_encrypted.pem | 29 +
.../certs/pem-utils/key_unsupported.pem | 7 +
.../certs/pem-utils/rsa_key_pkcs8_plain.pem | 28 +
.../certs/pem-utils/testnode-aes128.pem | 30 +
.../certs/pem-utils/testnode-aes192.pem | 30 +
.../certs/pem-utils/testnode-aes256.pem | 30 +
.../certs/pem-utils/testnode-unprotected.pem | 27 +
.../resources/certs/pem-utils/testnode.crt | 23 +
.../resources/certs/pem-utils/testnode.jks | Bin 0 -> 9360 bytes
.../resources/certs/pem-utils/testnode.pem | 30 +
.../pem-utils/testnode_with_bagattrs.pem | 32 +
settings.gradle | 9 +-
70 files changed, 5044 insertions(+), 1 deletion(-)
create mode 100644 libs/ssl-config/build.gradle
create mode 100644 libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/DefaultJdkTrustConfig.java
create mode 100644 libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/DerParser.java
create mode 100644 libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/EmptyKeyConfig.java
create mode 100644 libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/KeyStoreUtil.java
create mode 100644 libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemKeyConfig.java
create mode 100644 libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemTrustConfig.java
create mode 100644 libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemUtils.java
create mode 100644 libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslClientAuthenticationMode.java
create mode 100644 libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslConfigException.java
create mode 100644 libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslConfiguration.java
create mode 100644 libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslConfigurationKeys.java
create mode 100644 libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslConfigurationLoader.java
create mode 100644 libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslKeyConfig.java
create mode 100644 libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslTrustConfig.java
create mode 100644 libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslVerificationMode.java
create mode 100644 libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/StoreKeyConfig.java
create mode 100644 libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/StoreTrustConfig.java
create mode 100644 libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/TrustEverythingConfig.java
create mode 100644 libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/DefaultJdkTrustConfigTests.java
create mode 100644 libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/PemKeyConfigTests.java
create mode 100644 libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/PemTrustConfigTests.java
create mode 100644 libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/PemUtilsTests.java
create mode 100644 libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/SslConfigurationLoaderTests.java
create mode 100644 libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/SslConfigurationTests.java
create mode 100644 libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/StoreKeyConfigTests.java
create mode 100644 libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/StoreTrustConfigTests.java
create mode 100644 libs/ssl-config/src/test/resources/certs/README.txt
create mode 100644 libs/ssl-config/src/test/resources/certs/ca-all/ca.jks
create mode 100644 libs/ssl-config/src/test/resources/certs/ca-all/ca.p12
create mode 100644 libs/ssl-config/src/test/resources/certs/ca1/ca.crt
create mode 100644 libs/ssl-config/src/test/resources/certs/ca1/ca.key
create mode 100644 libs/ssl-config/src/test/resources/certs/ca1/ca.p12
create mode 100644 libs/ssl-config/src/test/resources/certs/ca2/ca.crt
create mode 100644 libs/ssl-config/src/test/resources/certs/ca2/ca.key
create mode 100644 libs/ssl-config/src/test/resources/certs/ca2/ca.p12
create mode 100644 libs/ssl-config/src/test/resources/certs/ca3/ca.crt
create mode 100644 libs/ssl-config/src/test/resources/certs/ca3/ca.key
create mode 100644 libs/ssl-config/src/test/resources/certs/ca3/ca.p12
create mode 100644 libs/ssl-config/src/test/resources/certs/cert-all/certs.jks
create mode 100644 libs/ssl-config/src/test/resources/certs/cert-all/certs.p12
create mode 100644 libs/ssl-config/src/test/resources/certs/cert1/cert1.crt
create mode 100644 libs/ssl-config/src/test/resources/certs/cert1/cert1.key
create mode 100644 libs/ssl-config/src/test/resources/certs/cert1/cert1.p12
create mode 100644 libs/ssl-config/src/test/resources/certs/cert2/cert2.crt
create mode 100644 libs/ssl-config/src/test/resources/certs/cert2/cert2.key
create mode 100644 libs/ssl-config/src/test/resources/certs/cert2/cert2.p12
create mode 100644 libs/ssl-config/src/test/resources/certs/pem-utils/README.asciidoc
create mode 100644 libs/ssl-config/src/test/resources/certs/pem-utils/corrupted_key_pkcs8_plain.pem
create mode 100644 libs/ssl-config/src/test/resources/certs/pem-utils/dsa_key_openssl_encrypted.pem
create mode 100644 libs/ssl-config/src/test/resources/certs/pem-utils/dsa_key_openssl_plain.pem
create mode 100644 libs/ssl-config/src/test/resources/certs/pem-utils/dsa_key_openssl_plain_with_params.pem
create mode 100644 libs/ssl-config/src/test/resources/certs/pem-utils/dsa_key_pkcs8_plain.pem
create mode 100644 libs/ssl-config/src/test/resources/certs/pem-utils/ec_key_openssl_encrypted.pem
create mode 100644 libs/ssl-config/src/test/resources/certs/pem-utils/ec_key_openssl_plain.pem
create mode 100644 libs/ssl-config/src/test/resources/certs/pem-utils/ec_key_openssl_plain_with_params.pem
create mode 100644 libs/ssl-config/src/test/resources/certs/pem-utils/ec_key_pkcs8_plain.pem
create mode 100644 libs/ssl-config/src/test/resources/certs/pem-utils/empty.pem
create mode 100644 libs/ssl-config/src/test/resources/certs/pem-utils/key_pkcs8_encrypted.pem
create mode 100644 libs/ssl-config/src/test/resources/certs/pem-utils/key_unsupported.pem
create mode 100644 libs/ssl-config/src/test/resources/certs/pem-utils/rsa_key_pkcs8_plain.pem
create mode 100644 libs/ssl-config/src/test/resources/certs/pem-utils/testnode-aes128.pem
create mode 100644 libs/ssl-config/src/test/resources/certs/pem-utils/testnode-aes192.pem
create mode 100644 libs/ssl-config/src/test/resources/certs/pem-utils/testnode-aes256.pem
create mode 100644 libs/ssl-config/src/test/resources/certs/pem-utils/testnode-unprotected.pem
create mode 100644 libs/ssl-config/src/test/resources/certs/pem-utils/testnode.crt
create mode 100644 libs/ssl-config/src/test/resources/certs/pem-utils/testnode.jks
create mode 100644 libs/ssl-config/src/test/resources/certs/pem-utils/testnode.pem
create mode 100644 libs/ssl-config/src/test/resources/certs/pem-utils/testnode_with_bagattrs.pem
diff --git a/libs/ssl-config/build.gradle b/libs/ssl-config/build.gradle
new file mode 100644
index 0000000000000..8d5b1d18b8c04
--- /dev/null
+++ b/libs/ssl-config/build.gradle
@@ -0,0 +1,42 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+dependencies {
+ compile "org.elasticsearch:elasticsearch-core:${version}"
+
+ if (isEclipse == false || project.path == ":libs:ssl-config-tests") {
+ testCompile("org.elasticsearch.test:framework:${version}") {
+ exclude group: 'org.elasticsearch', module: 'elasticsearch-ssl-config'
+ }
+ }
+
+ testCompile "com.carrotsearch.randomizedtesting:randomizedtesting-runner:${versions.randomizedrunner}"
+ testCompile "junit:junit:${versions.junit}"
+ testCompile "org.hamcrest:hamcrest-all:${versions.hamcrest}"
+}
+
+forbiddenApisMain {
+ replaceSignatureFiles 'jdk-signatures'
+}
+forbiddenPatterns {
+ exclude '**/*.key'
+ exclude '**/*.pem'
+ exclude '**/*.p12'
+ exclude '**/*.jks'
+}
diff --git a/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/DefaultJdkTrustConfig.java b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/DefaultJdkTrustConfig.java
new file mode 100644
index 0000000000000..5a1fbe72c3f49
--- /dev/null
+++ b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/DefaultJdkTrustConfig.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.common.ssl;
+
+import org.elasticsearch.common.Nullable;
+
+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.KeyStore;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+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 {
+
+ private final BiFunction systemProperties;
+ private final char[] trustStorePassword;
+
+ /**
+ * Create a trust config that uses System properties to determine the TrustStore type, and the relevant password.
+ */
+ DefaultJdkTrustConfig() {
+ this(System::getProperty);
+ }
+
+ /**
+ * Create a trust config that uses supplied {@link BiFunction} to determine the TrustStore type, and the relevant password.
+ */
+ DefaultJdkTrustConfig(BiFunction systemProperties) {
+ this(systemProperties, isPkcs11Truststore(systemProperties) ? getSystemTrustStorePassword(systemProperties) : null);
+ }
+
+ /**
+ * @param trustStorePassword the password for the truststore. It applies only when PKCS#11 tokens are used, is null otherwise
+ */
+ DefaultJdkTrustConfig(BiFunction systemProperties, @Nullable char[] trustStorePassword) {
+ this.systemProperties = systemProperties;
+ this.trustStorePassword = trustStorePassword;
+ }
+
+ @Override
+ public X509ExtendedTrustManager createTrustManager() {
+ try {
+ return KeyStoreUtil.createTrustManager(getSystemTrustStore(), TrustManagerFactory.getDefaultAlgorithm());
+ } catch (GeneralSecurityException e) {
+ throw new SslConfigException("failed to initialize a TrustManager for the system keystore", e);
+ }
+ }
+
+ /**
+ * When a PKCS#11 token is used as the system default keystore/truststore, we need to pass the keystore
+ * password when loading, even for reading certificates only ( as opposed to i.e. JKS keystores where
+ * we only need to pass the password for reading Private Key entries ).
+ *
+ * @return the KeyStore used as truststore for PKCS#11 initialized with the password, null otherwise
+ */
+ private KeyStore getSystemTrustStore() {
+ if (isPkcs11Truststore(systemProperties) && trustStorePassword != null) {
+ try {
+ KeyStore keyStore = KeyStore.getInstance("PKCS11");
+ keyStore.load(null, trustStorePassword);
+ return keyStore;
+ } catch (GeneralSecurityException | IOException e) {
+ throw new SslConfigException("failed to load the system PKCS#11 truststore", e);
+ }
+ }
+ return null;
+ }
+
+ private static boolean isPkcs11Truststore(BiFunction systemProperties) {
+ return systemProperties.apply("javax.net.ssl.trustStoreType", "").equalsIgnoreCase("PKCS11");
+ }
+
+ private static char[] getSystemTrustStorePassword(BiFunction systemProperties) {
+ return systemProperties.apply("javax.net.ssl.trustStorePassword", "").toCharArray();
+ }
+
+ @Override
+ public Collection getDependentFiles() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public String toString() {
+ return "JDK-trusted-certs";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final DefaultJdkTrustConfig that = (DefaultJdkTrustConfig) o;
+ return Arrays.equals(this.trustStorePassword, that.trustStorePassword);
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(trustStorePassword);
+ }
+}
diff --git a/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/DerParser.java b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/DerParser.java
new file mode 100644
index 0000000000000..da650369d508c
--- /dev/null
+++ b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/DerParser.java
@@ -0,0 +1,297 @@
+/*
+ Copyright (c) 1998-2010 AOL Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ */
+
+package org.elasticsearch.common.ssl;
+
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.util.Objects;
+
+/**
+ * A bare-minimum ASN.1 DER decoder, just having enough functions to
+ * decode PKCS#1 private keys in order to remain JCE/JVM agnostic.
+ *
+ * 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 {
+ // Constructed Flag
+ private static final int CONSTRUCTED = 0x20;
+
+ // Tag and data types
+ private static final int INTEGER = 0x02;
+ private static final int OCTET_STRING = 0x04;
+ private static final int OBJECT_OID = 0x06;
+ private static final int NUMERIC_STRING = 0x12;
+ private static final int PRINTABLE_STRING = 0x13;
+ private static final int VIDEOTEX_STRING = 0x15;
+ private static final int IA5_STRING = 0x16;
+ private static final int GRAPHIC_STRING = 0x19;
+ private static final int ISO646_STRING = 0x1A;
+ private static final int GENERAL_STRING = 0x1B;
+
+ private static final int UTF8_STRING = 0x0C;
+ private static final int UNIVERSAL_STRING = 0x1C;
+ private static final int BMP_STRING = 0x1E;
+
+
+ private InputStream derInputStream;
+ private int maxAsnObjectLength;
+
+ DerParser(byte[] bytes) {
+ this.derInputStream = new ByteArrayInputStream(bytes);
+ this.maxAsnObjectLength = bytes.length;
+ }
+
+ Asn1Object readAsn1Object() throws IOException {
+ int tag = derInputStream.read();
+ if (tag == -1) {
+ throw new IOException("Invalid DER: stream too short, missing tag");
+ }
+ int length = getLength();
+ // getLength() can return any 32 bit integer, so ensure that a corrupted encoding won't
+ // force us into allocating a very large array
+ if (length > maxAsnObjectLength) {
+ throw new IOException("Invalid DER: size of ASN.1 object to be parsed appears to be larger than the size of the key file " +
+ "itself.");
+ }
+ byte[] value = new byte[length];
+ int n = derInputStream.read(value);
+ if (n < length) {
+ throw new IOException("Invalid DER: stream too short, missing value. " +
+ "Could only read " + n + " out of " + length + " bytes");
+ }
+ return new Asn1Object(tag, length, value);
+
+ }
+
+ /**
+ * Decode the length of the field. Can only support length
+ * encoding up to 4 octets.
+ *
+ * In BER/DER encoding, length can be encoded in 2 forms:
+ *
+ *
+ * Short form. One octet. Bit 8 has value "0" and bits 7-1
+ * give the length.
+ *
+ * Long form. Two to 127 octets (only 4 is supported here).
+ * Bit 8 of first octet has value "1" and bits 7-1 give the
+ * number of additional length octets. Second and following
+ * octets give the length, base 256, most significant digit first.
+ *
+ *
+ *
+ * @return The length as integer
+ */
+ private int getLength() throws IOException {
+
+ int i = derInputStream.read();
+ if (i == -1)
+ throw new IOException("Invalid DER: length missing");
+
+ // A single byte short length
+ if ((i & ~0x7F) == 0)
+ return i;
+
+ int num = i & 0x7F;
+
+ // We can't handle length longer than 4 bytes
+ if (i >= 0xFF || num > 4)
+ throw new IOException("Invalid DER: length field too big ("
+ + i + ")"); //$NON-NLS-1$
+
+ byte[] bytes = new byte[num];
+ int n = derInputStream.read(bytes);
+ if (n < num)
+ throw new IOException("Invalid DER: length too short");
+
+ return new BigInteger(1, bytes).intValue();
+ }
+
+
+ /**
+ * An ASN.1 TLV. The object is not parsed. It can
+ * only handle integers.
+ *
+ * @author zhang
+ */
+ static class Asn1Object {
+
+ protected final int type;
+ protected final int length;
+ protected final byte[] value;
+ protected final int tag;
+
+ /**
+ * Construct a ASN.1 TLV. The TLV could be either a
+ * constructed or primitive entity.
+ *
+ * The first byte in DER encoding is made of following fields:
+ *
+ *
+ * -------------------------------------------------
+ * |Bit 8|Bit 7|Bit 6|Bit 5|Bit 4|Bit 3|Bit 2|Bit 1|
+ * -------------------------------------------------
+ * | Class | CF | + Type |
+ * -------------------------------------------------
+ *
+ *
+ * Class: Universal, Application, Context or Private
+ * CF: Constructed flag. If 1, the field is constructed.
+ * Type: This is actually called tag in ASN.1. It
+ * indicates data type (Integer, String) or a construct
+ * (sequence, choice, set).
+ *
+ *
+ * @param tag Tag or Identifier
+ * @param length Length of the field
+ * @param value Encoded octet string for the field.
+ */
+ Asn1Object(int tag, int length, byte[] value) {
+ this.tag = tag;
+ this.type = tag & 0x1F;
+ this.length = length;
+ this.value = value;
+ }
+
+ public int getType() {
+ return type;
+ }
+
+ public int getLength() {
+ return length;
+ }
+
+ public byte[] getValue() {
+ return value;
+ }
+
+ public boolean isConstructed() {
+ return (tag & DerParser.CONSTRUCTED) == DerParser.CONSTRUCTED;
+ }
+
+ /**
+ * For constructed field, return a parser for its content.
+ *
+ * @return A parser for the construct.
+ */
+ public DerParser getParser() throws IOException {
+ if (!isConstructed())
+ throw new IOException("Invalid DER: can't parse primitive entity"); //$NON-NLS-1$
+
+ return new DerParser(value);
+ }
+
+ /**
+ * Get the value as integer
+ *
+ * @return BigInteger
+ */
+ public BigInteger getInteger() throws IOException {
+ if (type != DerParser.INTEGER)
+ throw new IOException("Invalid DER: object is not integer"); //$NON-NLS-1$
+
+ return new BigInteger(value);
+ }
+
+ public String getString() throws IOException {
+
+ String encoding;
+
+ switch (type) {
+ case DerParser.OCTET_STRING:
+ // octet string is basically a byte array
+ return toHexString(value);
+ case DerParser.NUMERIC_STRING:
+ case DerParser.PRINTABLE_STRING:
+ case DerParser.VIDEOTEX_STRING:
+ case DerParser.IA5_STRING:
+ case DerParser.GRAPHIC_STRING:
+ case DerParser.ISO646_STRING:
+ case DerParser.GENERAL_STRING:
+ encoding = "ISO-8859-1"; //$NON-NLS-1$
+ break;
+
+ case DerParser.BMP_STRING:
+ encoding = "UTF-16BE"; //$NON-NLS-1$
+ break;
+
+ case DerParser.UTF8_STRING:
+ encoding = "UTF-8"; //$NON-NLS-1$
+ break;
+
+ case DerParser.UNIVERSAL_STRING:
+ throw new IOException("Invalid DER: can't handle UCS-4 string"); //$NON-NLS-1$
+
+ default:
+ throw new IOException("Invalid DER: object is not a string"); //$NON-NLS-1$
+ }
+
+ return new String(value, encoding);
+ }
+
+ public String getOid() throws IOException {
+
+ if (type != DerParser.OBJECT_OID) {
+ throw new IOException("Ivalid DER: object is not object OID");
+ }
+ StringBuilder sb = new StringBuilder(64);
+ switch (value[0] / 40) {
+ case 0:
+ sb.append('0');
+ break;
+ case 1:
+ sb.append('1');
+ value[0] -= 40;
+ break;
+ default:
+ sb.append('2');
+ value[0] -= 80;
+ break;
+ }
+ int oidPart = 0;
+ for (int i = 0; i < length; i++) {
+ oidPart = (oidPart << 7) + (value[i] & 0x7F);
+ if ((value[i] & 0x80) == 0) {
+ sb.append('.');
+ sb.append(oidPart);
+ oidPart = 0;
+ }
+ }
+
+ return sb.toString();
+ }
+ }
+
+ private static final char[] HEX_DIGITS = "0123456789abcdef".toCharArray();
+ private static String toHexString(byte[] bytes) {
+ Objects.requireNonNull(bytes);
+ StringBuilder sb = new StringBuilder(2 * bytes.length);
+
+ for (int i = 0; i < bytes.length; i++) {
+ byte b = bytes[i];
+ sb.append(HEX_DIGITS[b >> 4 & 0xf]).append(HEX_DIGITS[b & 0xf]);
+ }
+
+ return sb.toString();
+ }
+
+}
diff --git a/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/EmptyKeyConfig.java b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/EmptyKeyConfig.java
new file mode 100644
index 0000000000000..0844ffb7ee952
--- /dev/null
+++ b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/EmptyKeyConfig.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.common.ssl;
+
+import javax.net.ssl.X509ExtendedKeyManager;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * A {@link SslKeyConfig} that does nothing (provides a null key manager)
+ */
+final class EmptyKeyConfig implements SslKeyConfig {
+
+ static final EmptyKeyConfig INSTANCE = new EmptyKeyConfig();
+
+ private EmptyKeyConfig() {
+ // Enforce a single instance
+ }
+
+ @Override
+ public Collection getDependentFiles() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public X509ExtendedKeyManager createKeyManager() {
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return "empty-key-config";
+ }
+}
diff --git a/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/KeyStoreUtil.java b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/KeyStoreUtil.java
new file mode 100644
index 0000000000000..0a2526c7f7cfa
--- /dev/null
+++ b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/KeyStoreUtil.java
@@ -0,0 +1,163 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.common.ssl;
+
+import org.elasticsearch.common.Nullable;
+
+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.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import java.util.Collection;
+import java.util.Locale;
+
+/**
+ * A variety of utility methods for working with or constructing {@link KeyStore} instances.
+ */
+final class KeyStoreUtil {
+
+ private KeyStoreUtil() {
+ throw new IllegalStateException("Utility class should not be instantiated");
+ }
+
+ /**
+ * Make a best guess about the "type" (see {@link KeyStore#getType()}) of the keystore file located at the given {@code Path}.
+ * This method only references the file name of the keystore, it does not look at its contents.
+ */
+ static String inferKeyStoreType(Path path) {
+ String name = path == null ? "" : path.toString().toLowerCase(Locale.ROOT);
+ if (name.endsWith(".p12") || name.endsWith(".pfx") || name.endsWith(".pkcs12")) {
+ return "PKCS12";
+ } else {
+ return "jks";
+ }
+ }
+
+ /**
+ * Read the given keystore file.
+ *
+ * @throws SslConfigException If there is a problem reading from the provided path
+ * @throws GeneralSecurityException If there is a problem with the keystore contents
+ */
+ static KeyStore readKeyStore(Path path, String type, char[] password) throws GeneralSecurityException {
+ if (Files.notExists(path)) {
+ throw new SslConfigException("cannot read a [" + type + "] keystore from [" + path.toAbsolutePath()
+ + "] because the file does not exist");
+ }
+ try {
+ KeyStore keyStore = KeyStore.getInstance(type);
+ try (InputStream in = Files.newInputStream(path)) {
+ keyStore.load(in, password);
+ }
+ return keyStore;
+ } catch (IOException e) {
+ throw new SslConfigException("cannot read a [" + type + "] keystore from [" + path.toAbsolutePath() + "] - " + e.getMessage(),
+ e);
+ }
+ }
+
+ /**
+ * Construct an in-memory keystore with a single key entry.
+ * @param certificateChain A certificate chain (ordered from subject to issuer)
+ * @param privateKey The private key that corresponds to the subject certificate (index 0 of {@code certificateChain})
+ * @param password The password for the private key
+ *
+ * @throws GeneralSecurityException If there is a problem with the provided certificates/key
+ */
+ static KeyStore buildKeyStore(Collection certificateChain, PrivateKey privateKey, char[] password)
+ throws GeneralSecurityException {
+ KeyStore keyStore = buildNewKeyStore();
+ keyStore.setKeyEntry("key", privateKey, password, certificateChain.toArray(new Certificate[0]));
+ return keyStore;
+ }
+
+ /**
+ * Construct an in-memory keystore with multiple trusted cert entries.
+ * @param certificates The root certificates to trust
+ */
+ static KeyStore buildTrustStore(Iterable certificates) throws GeneralSecurityException {
+ assert certificates != null : "Cannot create keystore with null certificates";
+ KeyStore store = buildNewKeyStore();
+ int counter = 0;
+ for (Certificate certificate : certificates) {
+ store.setCertificateEntry("cert-" + counter, certificate);
+ counter++;
+ }
+ return store;
+ }
+
+ private static KeyStore buildNewKeyStore() throws GeneralSecurityException {
+ KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+ try {
+ keyStore.load(null, null);
+ } catch (IOException e) {
+ // This should never happen so callers really shouldn't be forced to deal with it themselves.
+ throw new SslConfigException("Unexpected error initializing a new in-memory keystore", e);
+ }
+ return keyStore;
+ }
+
+ /**
+ * Creates a {@link X509ExtendedKeyManager} based on the key material in the provided {@link KeyStore}
+ */
+ static X509ExtendedKeyManager createKeyManager(KeyStore keyStore, char[] password, String algorithm) throws GeneralSecurityException {
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
+ kmf.init(keyStore, password);
+ KeyManager[] keyManagers = kmf.getKeyManagers();
+ for (KeyManager keyManager : keyManagers) {
+ if (keyManager instanceof X509ExtendedKeyManager) {
+ return (X509ExtendedKeyManager) keyManager;
+ }
+ }
+ throw new SslConfigException("failed to find a X509ExtendedKeyManager in the key manager factory for [" + algorithm
+ + "] and keystore [" + keyStore + "]");
+ }
+
+ /**
+ * Creates a {@link X509ExtendedTrustManager} based on the trust material in the provided {@link KeyStore}
+ */
+ static X509ExtendedTrustManager createTrustManager(@Nullable KeyStore trustStore, String algorithm)
+ throws NoSuchAlgorithmException, KeyStoreException {
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
+ tmf.init(trustStore);
+ TrustManager[] trustManagers = tmf.getTrustManagers();
+ for (TrustManager trustManager : trustManagers) {
+ if (trustManager instanceof X509ExtendedTrustManager) {
+ return (X509ExtendedTrustManager) trustManager;
+ }
+ }
+ throw new SslConfigException("failed to find a X509ExtendedTrustManager in the trust manager factory for [" + algorithm
+ + "] and truststore [" + trustStore + "]");
+ }
+
+
+}
diff --git a/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemKeyConfig.java b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemKeyConfig.java
new file mode 100644
index 0000000000000..dd091e0a22218
--- /dev/null
+++ b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemKeyConfig.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.common.ssl;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.X509ExtendedKeyManager;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A {@link SslKeyConfig} that reads from PEM formatted paths.
+ */
+public final class PemKeyConfig implements SslKeyConfig {
+ private final Path certificate;
+ private final Path key;
+ private final char[] keyPassword;
+
+ public PemKeyConfig(Path certificate, Path key, char[] keyPassword) {
+ this.certificate = Objects.requireNonNull(certificate, "Certificate cannot be null");
+ this.key = Objects.requireNonNull(key, "Key cannot be null");
+ this.keyPassword = Objects.requireNonNull(keyPassword, "Key password cannot be null (but may be empty)");
+ }
+
+ @Override
+ public Collection getDependentFiles() {
+ return Arrays.asList(certificate, key);
+ }
+
+ @Override
+ public X509ExtendedKeyManager createKeyManager() {
+ PrivateKey privateKey = getPrivateKey();
+ List certificates = getCertificates();
+ try {
+ final KeyStore keyStore = KeyStoreUtil.buildKeyStore(certificates, privateKey, keyPassword);
+ return KeyStoreUtil.createKeyManager(keyStore, keyPassword, KeyManagerFactory.getDefaultAlgorithm());
+ } catch (GeneralSecurityException e) {
+ throw new SslConfigException("failed to load a KeyManager for certificate/key pair [" + certificate + "], [" + key + "]", e);
+ }
+ }
+
+ private PrivateKey getPrivateKey() {
+ try {
+ final PrivateKey privateKey = PemUtils.readPrivateKey(key, () -> keyPassword);
+ if (privateKey == null) {
+ throw new SslConfigException("could not load ssl private key file [" + key + "]");
+ }
+ return privateKey;
+ } catch (FileNotFoundException | NoSuchFileException e) {
+ throw new SslConfigException("the configured ssl private key file [" + key.toAbsolutePath() + "] does not exist", e);
+ } catch (IOException e) {
+ throw new SslConfigException("the configured ssl private key file [" + key.toAbsolutePath() + "] cannot be read", e);
+ } catch (GeneralSecurityException e) {
+ throw new SslConfigException("cannot load ssl private key file [" + key.toAbsolutePath() + "]", e);
+ }
+ }
+
+ private List getCertificates() {
+ try {
+ return PemUtils.readCertificates(Collections.singleton(certificate));
+ } catch (FileNotFoundException | NoSuchFileException e) {
+ throw new SslConfigException("the configured ssl certificate file [" + certificate.toAbsolutePath() + "] does not exist", e);
+ } catch (IOException e) {
+ throw new SslConfigException("the configured ssl certificate file [" + certificate .toAbsolutePath()+ "] cannot be read", e);
+ } catch (GeneralSecurityException e) {
+ throw new SslConfigException("cannot load ssl certificate from [" + certificate.toAbsolutePath() + "]", e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "PEM-key-config{cert=" + certificate.toAbsolutePath() + " key=" + key.toAbsolutePath() + "}";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final PemKeyConfig that = (PemKeyConfig) o;
+ return Objects.equals(this.certificate, that.certificate) &&
+ Objects.equals(this.key, that.key) &&
+ Arrays.equals(this.keyPassword, that.keyPassword);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hash(certificate, key);
+ result = 31 * result + Arrays.hashCode(keyPassword);
+ return result;
+ }
+}
diff --git a/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemTrustConfig.java b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemTrustConfig.java
new file mode 100644
index 0000000000000..f3cf8cd8bd7aa
--- /dev/null
+++ b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemTrustConfig.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.common.ssl;
+
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509ExtendedTrustManager;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * A {@link org.elasticsearch.common.ssl.SslTrustConfig} that reads a list of PEM encoded trusted certificates (CAs) from the file
+ * system.
+ * Strictly speaking, this class does not require PEM certificates, and will load any file that can be read by
+ * {@link java.security.cert.CertificateFactory#generateCertificate(InputStream)}.
+ */
+public final class PemTrustConfig implements SslTrustConfig {
+ private final List certificateAuthorities;
+
+ /**
+ * Construct a new trust config for the provided paths.
+ * The paths are stored as-is, and are not read until {@link #createTrustManager()} is called.
+ * This means that
+ *
+ * validation of the file (contents and accessibility) is deferred, and this constructor will not fail on missing
+ * of invalid files.
+ *
+ * if the contents of the files are modified, then subsequent calls {@link #createTrustManager()} will return a new trust
+ * manager that trust a different set of CAs.
+ *
+ *
+ */
+ public PemTrustConfig(List certificateAuthorities) {
+ this.certificateAuthorities = Collections.unmodifiableList(certificateAuthorities);
+ }
+
+ @Override
+ public Collection getDependentFiles() {
+ return certificateAuthorities;
+ }
+
+ @Override
+ public X509ExtendedTrustManager createTrustManager() {
+ try {
+ final List certificates = loadCertificates();
+ KeyStore store = KeyStoreUtil.buildTrustStore(certificates);
+ return KeyStoreUtil.createTrustManager(store, TrustManagerFactory.getDefaultAlgorithm());
+ } catch (GeneralSecurityException e) {
+ throw new SslConfigException("cannot create trust using PEM certificates [" + caPathsAsString() + "]", e);
+ }
+ }
+
+ private List loadCertificates() throws CertificateException {
+ try {
+ return PemUtils.readCertificates(this.certificateAuthorities);
+ } catch (FileNotFoundException | NoSuchFileException e) {
+ throw new SslConfigException("cannot configure trust using PEM certificates [" + caPathsAsString()
+ + "] because one or more files do not exist", e);
+ } catch (IOException e) {
+ throw new SslConfigException("cannot configure trust using PEM certificates [" + caPathsAsString()
+ + "] because one or more files cannot be read", e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "PEM-trust{" + caPathsAsString() + "}";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final PemTrustConfig that = (PemTrustConfig) o;
+ return Objects.equals(this.certificateAuthorities, that.certificateAuthorities);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(certificateAuthorities);
+ }
+
+ private String caPathsAsString() {
+ return certificateAuthorities.stream()
+ .map(Path::toAbsolutePath)
+ .map(Object::toString)
+ .collect(Collectors.joining(","));
+ }
+
+}
diff --git a/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemUtils.java b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemUtils.java
new file mode 100644
index 0000000000000..aca7ba56b2ae9
--- /dev/null
+++ b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemUtils.java
@@ -0,0 +1,613 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.common.ssl;
+
+import org.elasticsearch.common.CharArrays;
+
+import javax.crypto.Cipher;
+import javax.crypto.EncryptedPrivateKeyInfo;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.interfaces.ECKey;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.DSAPrivateKeySpec;
+import java.security.spec.ECGenParameterSpec;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPrivateKeySpec;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.RSAPrivateCrtKeySpec;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
+
+final class PemUtils {
+
+ private static final String PKCS1_HEADER = "-----BEGIN RSA PRIVATE KEY-----";
+ private static final String PKCS1_FOOTER = "-----END RSA PRIVATE KEY-----";
+ private static final String OPENSSL_DSA_HEADER = "-----BEGIN DSA PRIVATE KEY-----";
+ private static final String OPENSSL_DSA_FOOTER = "-----END DSA PRIVATE KEY-----";
+ private static final String OPENSSL_DSA_PARAMS_HEADER ="-----BEGIN DSA PARAMETERS-----";
+ private static final String OPENSSL_DSA_PARAMS_FOOTER ="-----END DSA PARAMETERS-----";
+ private static final String PKCS8_HEADER = "-----BEGIN PRIVATE KEY-----";
+ private static final String PKCS8_FOOTER = "-----END PRIVATE KEY-----";
+ private static final String PKCS8_ENCRYPTED_HEADER = "-----BEGIN ENCRYPTED PRIVATE KEY-----";
+ private static final String PKCS8_ENCRYPTED_FOOTER = "-----END ENCRYPTED PRIVATE KEY-----";
+ private static final String OPENSSL_EC_HEADER = "-----BEGIN EC PRIVATE KEY-----";
+ private static final String OPENSSL_EC_FOOTER = "-----END EC PRIVATE KEY-----";
+ private static final String OPENSSL_EC_PARAMS_HEADER = "-----BEGIN EC PARAMETERS-----";
+ private static final String OPENSSL_EC_PARAMS_FOOTER = "-----END EC PARAMETERS-----";
+ private static final String HEADER = "-----BEGIN";
+
+ private PemUtils() {
+ throw new IllegalStateException("Utility class should not be instantiated");
+ }
+
+ /**
+ * Creates a {@link PrivateKey} from the contents of a file. Supports PKCS#1, PKCS#8
+ * encoded formats of encrypted and plaintext RSA, DSA and EC(secp256r1) keys
+ *
+ * @param keyPath the path for the key file
+ * @param passwordSupplier A password supplier for the potentially encrypted (password protected) key
+ * @return a private key from the contents of the file
+ */
+ public static PrivateKey readPrivateKey(Path keyPath, Supplier passwordSupplier) throws IOException, GeneralSecurityException {
+ try (BufferedReader bReader = Files.newBufferedReader(keyPath, StandardCharsets.UTF_8)) {
+ String line = bReader.readLine();
+ while (null != line && line.startsWith(HEADER) == false) {
+ line = bReader.readLine();
+ }
+ if (null == line) {
+ throw new SslConfigException("Error parsing Private Key [" + keyPath.toAbsolutePath() + "], file is empty");
+ }
+ if (PKCS8_ENCRYPTED_HEADER.equals(line.trim())) {
+ char[] password = passwordSupplier.get();
+ if (password == null) {
+ throw new SslConfigException("cannot read encrypted key [" + keyPath.toAbsolutePath() + "] without a password");
+ }
+ return parsePKCS8Encrypted(bReader, password);
+ } else if (PKCS8_HEADER.equals(line.trim())) {
+ return parsePKCS8(bReader);
+ } else if (PKCS1_HEADER.equals(line.trim())) {
+ return parsePKCS1Rsa(bReader, passwordSupplier);
+ } else if (OPENSSL_DSA_HEADER.equals(line.trim())) {
+ return parseOpenSslDsa(bReader, passwordSupplier);
+ } else if (OPENSSL_DSA_PARAMS_HEADER.equals(line.trim())) {
+ return parseOpenSslDsa(removeDsaHeaders(bReader), passwordSupplier);
+ } else if (OPENSSL_EC_HEADER.equals(line.trim())) {
+ return parseOpenSslEC(bReader, passwordSupplier);
+ } else if (OPENSSL_EC_PARAMS_HEADER.equals(line.trim())) {
+ return parseOpenSslEC(removeECHeaders(bReader), passwordSupplier);
+ } else {
+ throw new SslConfigException("error parsing Private Key [" + keyPath.toAbsolutePath()
+ + "], file does not contain a supported key format");
+ }
+ } catch (FileNotFoundException | NoSuchFileException e) {
+ throw new SslConfigException("private key file [" + keyPath.toAbsolutePath() + "] does not exist", e);
+ } catch (IOException | GeneralSecurityException e) {
+ throw new SslConfigException("private key file [" + keyPath.toAbsolutePath() + "] cannot be parsed", e);
+ }
+ }
+
+ /**
+ * Removes the EC Headers that OpenSSL adds to EC private keys as the information in them
+ * is redundant
+ *
+ * @throws IOException if the EC Parameter footer is missing
+ */
+ private static BufferedReader removeECHeaders(BufferedReader bReader) throws IOException {
+ String line = bReader.readLine();
+ while (line != null) {
+ if (OPENSSL_EC_PARAMS_FOOTER.equals(line.trim())) {
+ break;
+ }
+ line = bReader.readLine();
+ }
+ if (null == line || OPENSSL_EC_PARAMS_FOOTER.equals(line.trim()) == false) {
+ throw new IOException("Malformed PEM file, EC Parameters footer is missing");
+ }
+ // Verify that the key starts with the correct header before passing it to parseOpenSslEC
+ if (OPENSSL_EC_HEADER.equals(bReader.readLine()) == false) {
+ throw new IOException("Malformed PEM file, EC Key header is missing");
+ }
+ return bReader;
+ }
+
+ /**
+ * Removes the DSA Params Headers that OpenSSL adds to DSA private keys as the information in them
+ * is redundant
+ *
+ * @throws IOException if the EC Parameter footer is missing
+ */
+ private static BufferedReader removeDsaHeaders(BufferedReader bReader) throws IOException {
+ String line = bReader.readLine();
+ while (line != null) {
+ if (OPENSSL_DSA_PARAMS_FOOTER.equals(line.trim())) {
+ break;
+ }
+ line = bReader.readLine();
+ }
+ if (null == line || OPENSSL_DSA_PARAMS_FOOTER.equals(line.trim()) == false) {
+ throw new IOException("Malformed PEM file, DSA Parameters footer is missing");
+ }
+ // Verify that the key starts with the correct header before passing it to parseOpenSslDsa
+ if (OPENSSL_DSA_HEADER.equals(bReader.readLine()) == false) {
+ throw new IOException("Malformed PEM file, DSA Key header is missing");
+ }
+ return bReader;
+ }
+
+ /**
+ * Creates a {@link PrivateKey} from the contents of {@code bReader} that contains an plaintext private key encoded in
+ * PKCS#8
+ *
+ * @param bReader the {@link BufferedReader} containing the key file contents
+ * @return {@link PrivateKey}
+ * @throws IOException if the file can't be read
+ * @throws GeneralSecurityException if the private key can't be generated from the {@link PKCS8EncodedKeySpec}
+ */
+ private static PrivateKey parsePKCS8(BufferedReader bReader) throws IOException, GeneralSecurityException {
+ StringBuilder sb = new StringBuilder();
+ String line = bReader.readLine();
+ while (line != null) {
+ if (PKCS8_FOOTER.equals(line.trim())) {
+ break;
+ }
+ sb.append(line.trim());
+ line = bReader.readLine();
+ }
+ if (null == line || PKCS8_FOOTER.equals(line.trim()) == false) {
+ throw new IOException("Malformed PEM file, PEM footer is invalid or missing");
+ }
+ byte[] keyBytes = Base64.getDecoder().decode(sb.toString());
+ String keyAlgo = getKeyAlgorithmIdentifier(keyBytes);
+ KeyFactory keyFactory = KeyFactory.getInstance(keyAlgo);
+ return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(keyBytes));
+ }
+
+ /**
+ * Creates a {@link PrivateKey} from the contents of {@code bReader} that contains an EC private key encoded in
+ * OpenSSL traditional format.
+ *
+ * @param bReader the {@link BufferedReader} containing the key file contents
+ * @param passwordSupplier A password supplier for the potentially encrypted (password protected) key
+ * @return {@link PrivateKey}
+ * @throws IOException if the file can't be read
+ * @throws GeneralSecurityException if the private key can't be generated from the {@link ECPrivateKeySpec}
+ */
+ private static PrivateKey parseOpenSslEC(BufferedReader bReader, Supplier passwordSupplier) throws IOException,
+ GeneralSecurityException {
+ StringBuilder sb = new StringBuilder();
+ String line = bReader.readLine();
+ Map pemHeaders = new HashMap<>();
+ while (line != null) {
+ if (OPENSSL_EC_FOOTER.equals(line.trim())) {
+ break;
+ }
+ // Parse PEM headers according to https://www.ietf.org/rfc/rfc1421.txt
+ if (line.contains(":")) {
+ String[] header = line.split(":");
+ pemHeaders.put(header[0].trim(), header[1].trim());
+ } else {
+ sb.append(line.trim());
+ }
+ line = bReader.readLine();
+ }
+ if (null == line || OPENSSL_EC_FOOTER.equals(line.trim()) == false) {
+ throw new IOException("Malformed PEM file, PEM footer is invalid or missing");
+ }
+ byte[] keyBytes = possiblyDecryptPKCS1Key(pemHeaders, sb.toString(), passwordSupplier);
+ KeyFactory keyFactory = KeyFactory.getInstance("EC");
+ ECPrivateKeySpec ecSpec = parseEcDer(keyBytes);
+ return keyFactory.generatePrivate(ecSpec);
+ }
+
+ /**
+ * Creates a {@link PrivateKey} from the contents of {@code bReader} that contains an RSA private key encoded in
+ * OpenSSL traditional format.
+ *
+ * @param bReader the {@link BufferedReader} containing the key file contents
+ * @param passwordSupplier A password supplier for the potentially encrypted (password protected) key
+ * @return {@link PrivateKey}
+ * @throws IOException if the file can't be read
+ * @throws GeneralSecurityException if the private key can't be generated from the {@link RSAPrivateCrtKeySpec}
+ */
+ private static PrivateKey parsePKCS1Rsa(BufferedReader bReader, Supplier passwordSupplier) throws IOException,
+ GeneralSecurityException {
+ StringBuilder sb = new StringBuilder();
+ String line = bReader.readLine();
+ Map pemHeaders = new HashMap<>();
+
+ while (line != null) {
+ if (PKCS1_FOOTER.equals(line.trim())) {
+ // Unencrypted
+ break;
+ }
+ // Parse PEM headers according to https://www.ietf.org/rfc/rfc1421.txt
+ if (line.contains(":")) {
+ String[] header = line.split(":");
+ pemHeaders.put(header[0].trim(), header[1].trim());
+ } else {
+ sb.append(line.trim());
+ }
+ line = bReader.readLine();
+ }
+ if (null == line || PKCS1_FOOTER.equals(line.trim()) == false) {
+ throw new IOException("Malformed PEM file, PEM footer is invalid or missing");
+ }
+ byte[] keyBytes = possiblyDecryptPKCS1Key(pemHeaders, sb.toString(), passwordSupplier);
+ RSAPrivateCrtKeySpec spec = parseRsaDer(keyBytes);
+ KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+ return keyFactory.generatePrivate(spec);
+ }
+
+ /**
+ * Creates a {@link PrivateKey} from the contents of {@code bReader} that contains an DSA private key encoded in
+ * OpenSSL traditional format.
+ *
+ * @param bReader the {@link BufferedReader} containing the key file contents
+ * @param passwordSupplier A password supplier for the potentially encrypted (password protected) key
+ * @return {@link PrivateKey}
+ * @throws IOException if the file can't be read
+ * @throws GeneralSecurityException if the private key can't be generated from the {@link DSAPrivateKeySpec}
+ */
+ private static PrivateKey parseOpenSslDsa(BufferedReader bReader, Supplier passwordSupplier) throws IOException,
+ GeneralSecurityException {
+ StringBuilder sb = new StringBuilder();
+ String line = bReader.readLine();
+ Map pemHeaders = new HashMap<>();
+
+ while (line != null) {
+ if (OPENSSL_DSA_FOOTER.equals(line.trim())) {
+ // Unencrypted
+ break;
+ }
+ // Parse PEM headers according to https://www.ietf.org/rfc/rfc1421.txt
+ if (line.contains(":")) {
+ String[] header = line.split(":");
+ pemHeaders.put(header[0].trim(), header[1].trim());
+ } else {
+ sb.append(line.trim());
+ }
+ line = bReader.readLine();
+ }
+ if (null == line || OPENSSL_DSA_FOOTER.equals(line.trim()) == false) {
+ throw new IOException("Malformed PEM file, PEM footer is invalid or missing");
+ }
+ byte[] keyBytes = possiblyDecryptPKCS1Key(pemHeaders, sb.toString(), passwordSupplier);
+ DSAPrivateKeySpec spec = parseDsaDer(keyBytes);
+ KeyFactory keyFactory = KeyFactory.getInstance("DSA");
+ return keyFactory.generatePrivate(spec);
+ }
+
+ /**
+ * Creates a {@link PrivateKey} from the contents of {@code bReader} that contains an encrypted private key encoded in
+ * PKCS#8
+ *
+ * @param bReader the {@link BufferedReader} containing the key file contents
+ * @param keyPassword The password for the encrypted (password protected) key
+ * @return {@link PrivateKey}
+ * @throws IOException if the file can't be read
+ * @throws GeneralSecurityException if the private key can't be generated from the {@link PKCS8EncodedKeySpec}
+ */
+ private static PrivateKey parsePKCS8Encrypted(BufferedReader bReader, char[] keyPassword) throws IOException,
+ GeneralSecurityException {
+ StringBuilder sb = new StringBuilder();
+ String line = bReader.readLine();
+ while (line != null) {
+ if (PKCS8_ENCRYPTED_FOOTER.equals(line.trim())) {
+ break;
+ }
+ sb.append(line.trim());
+ line = bReader.readLine();
+ }
+ if (null == line || PKCS8_ENCRYPTED_FOOTER.equals(line.trim()) == false) {
+ throw new IOException("Malformed PEM file, PEM footer is invalid or missing");
+ }
+ byte[] keyBytes = Base64.getDecoder().decode(sb.toString());
+
+ EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(keyBytes);
+ SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(encryptedPrivateKeyInfo.getAlgName());
+ SecretKey secretKey = secretKeyFactory.generateSecret(new PBEKeySpec(keyPassword));
+ Arrays.fill(keyPassword, '\u0000');
+ Cipher cipher = Cipher.getInstance(encryptedPrivateKeyInfo.getAlgName());
+ cipher.init(Cipher.DECRYPT_MODE, secretKey, encryptedPrivateKeyInfo.getAlgParameters());
+ PKCS8EncodedKeySpec keySpec = encryptedPrivateKeyInfo.getKeySpec(cipher);
+ String keyAlgo = getKeyAlgorithmIdentifier(keySpec.getEncoded());
+ KeyFactory keyFactory = KeyFactory.getInstance(keyAlgo);
+ return keyFactory.generatePrivate(keySpec);
+ }
+
+ /**
+ * Decrypts the password protected contents using the algorithm and IV that is specified in the PEM Headers of the file
+ *
+ * @param pemHeaders The Proc-Type and DEK-Info PEM headers that have been extracted from the key file
+ * @param keyContents The key as a base64 encoded String
+ * @param passwordSupplier A password supplier for the encrypted (password protected) key
+ * @return the decrypted key bytes
+ * @throws GeneralSecurityException if the key can't be decrypted
+ * @throws IOException if the PEM headers are missing or malformed
+ */
+ private static byte[] possiblyDecryptPKCS1Key(Map pemHeaders, String keyContents, Supplier passwordSupplier)
+ throws GeneralSecurityException, IOException {
+ byte[] keyBytes = Base64.getDecoder().decode(keyContents);
+ String procType = pemHeaders.get("Proc-Type");
+ if ("4,ENCRYPTED".equals(procType)) {
+ //We only handle PEM encryption
+ String encryptionParameters = pemHeaders.get("DEK-Info");
+ if (null == encryptionParameters) {
+ //malformed pem
+ throw new IOException("Malformed PEM File, DEK-Info header is missing");
+ }
+ char[] password = passwordSupplier.get();
+ if (password == null) {
+ throw new IOException("cannot read encrypted key without a password");
+ }
+ Cipher cipher = getCipherFromParameters(encryptionParameters, password);
+ byte[] decryptedKeyBytes = cipher.doFinal(keyBytes);
+ return decryptedKeyBytes;
+ }
+ return keyBytes;
+ }
+
+ /**
+ * Creates a {@link Cipher} from the contents of the DEK-Info header of a PEM file. RFC 1421 indicates that supported algorithms are
+ * defined in RFC 1423. RFC 1423 only defines DES-CBS and triple DES (EDE) in CBC mode. AES in CBC mode is also widely used though ( 3
+ * different variants of 128, 192, 256 bit keys )
+ *
+ * @param dekHeaderValue The value of the the DEK-Info PEM header
+ * @param password The password with which the key is encrypted
+ * @return a cipher of the appropriate algorithm and parameters to be used for decryption
+ * @throws GeneralSecurityException if the algorithm is not available in the used security provider, or if the key is inappropriate
+ * for the cipher
+ * @throws IOException if the DEK-Info PEM header is invalid
+ */
+ private static Cipher getCipherFromParameters(String dekHeaderValue, char[] password) throws
+ GeneralSecurityException, IOException {
+ final String padding = "PKCS5Padding";
+ final SecretKey encryptionKey;
+ final String[] valueTokens = dekHeaderValue.split(",");
+ if (valueTokens.length != 2) {
+ throw new IOException("Malformed PEM file, DEK-Info PEM header is invalid");
+ }
+ final String algorithm = valueTokens[0];
+ final String ivString = valueTokens[1];
+ final byte[] iv;
+ try {
+ iv = hexStringToByteArray(ivString);
+ } catch (IllegalArgumentException e) {
+ throw new IOException("Malformed PEM file, DEK-Info IV is invalid", e);
+ }
+ if ("DES-CBC".equals(algorithm)) {
+ byte[] key = generateOpenSslKey(password, iv, 8);
+ encryptionKey = new SecretKeySpec(key, "DES");
+ } else if ("DES-EDE3-CBC".equals(algorithm)) {
+ byte[] key = generateOpenSslKey(password, iv, 24);
+ encryptionKey = new SecretKeySpec(key, "DESede");
+ } else if ("AES-128-CBC".equals(algorithm)) {
+ byte[] key = generateOpenSslKey(password, iv, 16);
+ encryptionKey = new SecretKeySpec(key, "AES");
+ } else if ("AES-192-CBC".equals(algorithm)) {
+ byte[] key = generateOpenSslKey(password, iv, 24);
+ encryptionKey = new SecretKeySpec(key, "AES");
+ } else if ("AES-256-CBC".equals(algorithm)) {
+ byte[] key = generateOpenSslKey(password, iv, 32);
+ encryptionKey = new SecretKeySpec(key, "AES");
+ } else {
+ throw new GeneralSecurityException("Private Key encrypted with unsupported algorithm [" + algorithm + "]");
+ }
+ String transformation = encryptionKey.getAlgorithm() + "/" + "CBC" + "/" + padding;
+ Cipher cipher = Cipher.getInstance(transformation);
+ cipher.init(Cipher.DECRYPT_MODE, encryptionKey, new IvParameterSpec(iv));
+ return cipher;
+ }
+
+ /**
+ * Performs key stretching in the same manner that OpenSSL does. This is basically a KDF
+ * that uses n rounds of salted MD5 (as many times as needed to get the necessary number of key bytes)
+ *
+ * https://www.openssl.org/docs/man1.1.0/crypto/PEM_write_bio_PrivateKey_traditional.html
+ */
+ private static byte[] generateOpenSslKey(char[] password, byte[] salt, int keyLength) {
+ byte[] passwordBytes = CharArrays.toUtf8Bytes(password);
+ MessageDigest md5 = messageDigest("md5");
+ byte[] key = new byte[keyLength];
+ int copied = 0;
+ int remaining;
+ while (copied < keyLength) {
+ remaining = keyLength - copied;
+ md5.update(passwordBytes, 0, passwordBytes.length);
+ md5.update(salt, 0, 8);// AES IV (salt) is longer but we only need 8 bytes
+ byte[] tempDigest = md5.digest();
+ int bytesToCopy = (remaining > 16) ? 16 : remaining; // MD5 digests are 16 bytes
+ System.arraycopy(tempDigest, 0, key, copied, bytesToCopy);
+ copied += bytesToCopy;
+ if (remaining == 0) {
+ break;
+ }
+ md5.update(tempDigest, 0, 16); // use previous round digest as IV
+ }
+ Arrays.fill(passwordBytes, (byte) 0);
+ return key;
+ }
+
+ /**
+ * Converts a hexadecimal string to a byte array
+ */
+ private static byte[] hexStringToByteArray(String hexString) {
+ int len = hexString.length();
+ if (len % 2 == 0) {
+ byte[] data = new byte[len / 2];
+ for (int i = 0; i < len; i += 2) {
+ final int k = Character.digit(hexString.charAt(i), 16);
+ final int l = Character.digit(hexString.charAt(i + 1), 16);
+ if (k == -1 || l == -1) {
+ throw new IllegalStateException("String [" + hexString + "] is not hexadecimal");
+ }
+ data[i / 2] = (byte) ((k << 4) + l);
+ }
+ return data;
+ } else {
+ throw new IllegalStateException("Hexadecimal string [" + hexString +
+ "] has odd length and cannot be converted to a byte array");
+ }
+ }
+
+ /**
+ * Parses a DER encoded EC key to an {@link ECPrivateKeySpec} using a minimal {@link DerParser}
+ *
+ * @param keyBytes the private key raw bytes
+ * @return {@link ECPrivateKeySpec}
+ * @throws IOException if the DER encoded key can't be parsed
+ */
+ private static ECPrivateKeySpec parseEcDer(byte[] keyBytes) throws IOException,
+ GeneralSecurityException {
+ DerParser parser = new DerParser(keyBytes);
+ DerParser.Asn1Object sequence = parser.readAsn1Object();
+ parser = sequence.getParser();
+ parser.readAsn1Object().getInteger(); // version
+ String keyHex = parser.readAsn1Object().getString();
+ BigInteger privateKeyInt = new BigInteger(keyHex, 16);
+ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
+ AlgorithmParameterSpec prime256v1ParamSpec = new ECGenParameterSpec("secp256r1");
+ keyPairGenerator.initialize(prime256v1ParamSpec);
+ ECParameterSpec parameterSpec = ((ECKey) keyPairGenerator.generateKeyPair().getPrivate()).getParams();
+ return new ECPrivateKeySpec(privateKeyInt, parameterSpec);
+ }
+
+ /**
+ * Parses a DER encoded RSA key to a {@link RSAPrivateCrtKeySpec} using a minimal {@link DerParser}
+ *
+ * @param keyBytes the private key raw bytes
+ * @return {@link RSAPrivateCrtKeySpec}
+ * @throws IOException if the DER encoded key can't be parsed
+ */
+ private static RSAPrivateCrtKeySpec parseRsaDer(byte[] keyBytes) throws IOException {
+ DerParser parser = new DerParser(keyBytes);
+ DerParser.Asn1Object sequence = parser.readAsn1Object();
+ parser = sequence.getParser();
+ parser.readAsn1Object().getInteger(); // (version) We don't need it but must read to get to modulus
+ BigInteger modulus = parser.readAsn1Object().getInteger();
+ BigInteger publicExponent = parser.readAsn1Object().getInteger();
+ BigInteger privateExponent = parser.readAsn1Object().getInteger();
+ BigInteger prime1 = parser.readAsn1Object().getInteger();
+ BigInteger prime2 = parser.readAsn1Object().getInteger();
+ BigInteger exponent1 = parser.readAsn1Object().getInteger();
+ BigInteger exponent2 = parser.readAsn1Object().getInteger();
+ BigInteger coefficient = parser.readAsn1Object().getInteger();
+ return new RSAPrivateCrtKeySpec(modulus, publicExponent, privateExponent, prime1, prime2, exponent1, exponent2, coefficient);
+ }
+
+ /**
+ * Parses a DER encoded DSA key to a {@link DSAPrivateKeySpec} using a minimal {@link DerParser}
+ *
+ * @param keyBytes the private key raw bytes
+ * @return {@link DSAPrivateKeySpec}
+ * @throws IOException if the DER encoded key can't be parsed
+ */
+ private static DSAPrivateKeySpec parseDsaDer(byte[] keyBytes) throws IOException {
+ DerParser parser = new DerParser(keyBytes);
+ DerParser.Asn1Object sequence = parser.readAsn1Object();
+ parser = sequence.getParser();
+ parser.readAsn1Object().getInteger(); // (version) We don't need it but must read to get to p
+ BigInteger p = parser.readAsn1Object().getInteger();
+ BigInteger q = parser.readAsn1Object().getInteger();
+ BigInteger g = parser.readAsn1Object().getInteger();
+ parser.readAsn1Object().getInteger(); // we don't need x
+ BigInteger x = parser.readAsn1Object().getInteger();
+ return new DSAPrivateKeySpec(x, p, q, g);
+ }
+
+ /**
+ * Parses a DER encoded private key and reads its algorithm identifier Object OID.
+ *
+ * @param keyBytes the private key raw bytes
+ * @return A string identifier for the key algorithm (RSA, DSA, or EC)
+ * @throws GeneralSecurityException if the algorithm oid that is parsed from ASN.1 is unknown
+ * @throws IOException if the DER encoded key can't be parsed
+ */
+ private static String getKeyAlgorithmIdentifier(byte[] keyBytes) throws IOException, GeneralSecurityException {
+ DerParser parser = new DerParser(keyBytes);
+ DerParser.Asn1Object sequence = parser.readAsn1Object();
+ parser = sequence.getParser();
+ parser.readAsn1Object().getInteger(); // version
+ DerParser.Asn1Object algSequence = parser.readAsn1Object();
+ parser = algSequence.getParser();
+ String oidString = parser.readAsn1Object().getOid();
+ switch (oidString) {
+ case "1.2.840.10040.4.1":
+ return "DSA";
+ case "1.2.840.113549.1.1.1":
+ return "RSA";
+ case "1.2.840.10045.2.1":
+ return "EC";
+ }
+ throw new GeneralSecurityException("Error parsing key algorithm identifier. Algorithm with OID [" + oidString +
+ "] is not żsupported");
+ }
+
+ static List readCertificates(Collection certPaths) throws CertificateException, IOException {
+ CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+ List certificates = new ArrayList<>(certPaths.size());
+ for (Path path : certPaths) {
+ try (InputStream input = Files.newInputStream(path)) {
+ final Collection extends Certificate> parsed = certFactory.generateCertificates(input);
+ if (parsed.isEmpty()) {
+ throw new SslConfigException("failed to parse any certificates from [" + path.toAbsolutePath() + "]");
+ }
+ certificates.addAll(parsed);
+ }
+ }
+ return certificates;
+ }
+
+ private static MessageDigest messageDigest(String digestAlgorithm) {
+ try {
+ return MessageDigest.getInstance(digestAlgorithm);
+ } catch (NoSuchAlgorithmException e) {
+ throw new SslConfigException("unexpected exception creating MessageDigest instance for [" + digestAlgorithm + "]", e);
+ }
+ }
+}
diff --git a/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslClientAuthenticationMode.java b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslClientAuthenticationMode.java
new file mode 100644
index 0000000000000..8a972b6c78826
--- /dev/null
+++ b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslClientAuthenticationMode.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.elasticsearch.common.ssl;
+
+import javax.net.ssl.SSLParameters;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * The client authentication mode that is used for SSL servers.
+ */
+public enum SslClientAuthenticationMode {
+
+ /**
+ * Never request a client certificate.
+ */
+ NONE() {
+ public boolean enabled() {
+ return false;
+ }
+
+ public void configure(SSLParameters sslParameters) {
+ // nothing to do here
+ assert !sslParameters.getWantClientAuth();
+ assert !sslParameters.getNeedClientAuth();
+ }
+ },
+ /**
+ * Request a client certificate, but do not enforce that one is provided.
+ */
+ OPTIONAL() {
+ public boolean enabled() {
+ return true;
+ }
+
+ public void configure(SSLParameters sslParameters) {
+ sslParameters.setWantClientAuth(true);
+ }
+ },
+ /**
+ * Request and require a client certificate.
+ */
+ REQUIRED() {
+ public boolean enabled() {
+ return true;
+ }
+
+ public void configure(SSLParameters sslParameters) {
+ sslParameters.setNeedClientAuth(true);
+ }
+ };
+
+ /**
+ * @return true if client authentication is enabled
+ */
+ public abstract boolean enabled();
+
+ /**
+ * Configure client authentication of the provided {@link SSLParameters}
+ */
+ public abstract void configure(SSLParameters sslParameters);
+
+ private static final Map LOOKUP = Collections.unmodifiableMap(buildLookup());
+
+ static Map buildLookup() {
+ final Map map = new LinkedHashMap<>(3);
+ map.put("none", NONE);
+ map.put("optional", OPTIONAL);
+ map.put("required", REQUIRED);
+ return map;
+ }
+
+ public static SslClientAuthenticationMode parse(String value) {
+ final SslClientAuthenticationMode mode = LOOKUP.get(value.toLowerCase(Locale.ROOT));
+ if (mode == null) {
+ final String allowedValues = LOOKUP.keySet().stream().collect(Collectors.joining(","));
+ throw new SslConfigException("could not resolve ssl client authentication, unknown value ["
+ + value + "], recognised values are [" + allowedValues + "]");
+ }
+ return mode;
+ }
+}
diff --git a/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslConfigException.java b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslConfigException.java
new file mode 100644
index 0000000000000..ae5d332e0a1e7
--- /dev/null
+++ b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslConfigException.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.common.ssl;
+
+/**
+ * A base exception for problems that occur while trying to configure SSL.
+ */
+public class SslConfigException extends RuntimeException {
+ public SslConfigException(String message, Exception cause) {
+ super(message, cause);
+ }
+
+ public SslConfigException(String message) {
+ super(message);
+ }
+}
diff --git a/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslConfiguration.java b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslConfiguration.java
new file mode 100644
index 0000000000000..146ba916b6b07
--- /dev/null
+++ b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslConfiguration.java
@@ -0,0 +1,164 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.common.ssl;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.X509ExtendedKeyManager;
+import javax.net.ssl.X509ExtendedTrustManager;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * A object encapsulating all necessary configuration for an SSL context (client or server).
+ * The configuration itself is immutable, but the {@link #getKeyConfig() key config} and
+ * {@link #getTrustConfig() trust config} may depend on reading key and certificate material
+ * from files (see {@link #getDependentFiles()}, and the content of those files may change.
+ */
+public class SslConfiguration {
+
+ private final SslTrustConfig trustConfig;
+ private final SslKeyConfig keyConfig;
+ private final SslVerificationMode verificationMode;
+ private final SslClientAuthenticationMode clientAuth;
+ private final List ciphers;
+ private final List supportedProtocols;
+
+ public SslConfiguration(SslTrustConfig trustConfig, SslKeyConfig keyConfig, SslVerificationMode verificationMode,
+ SslClientAuthenticationMode clientAuth, List ciphers, List supportedProtocols) {
+ if (ciphers == null || ciphers.isEmpty()) {
+ throw new SslConfigException("cannot configure SSL/TLS without any supported cipher suites");
+ }
+ if (supportedProtocols == null || supportedProtocols.isEmpty()) {
+ throw new SslConfigException("cannot configure SSL/TLS without any supported protocols");
+ }
+ this.trustConfig = Objects.requireNonNull(trustConfig, "trust config cannot be null");
+ this.keyConfig = Objects.requireNonNull(keyConfig, "key config cannot be null");
+ this.verificationMode = Objects.requireNonNull(verificationMode, "verification mode cannot be null");
+ this.clientAuth = Objects.requireNonNull(clientAuth, "client authentication cannot be null");
+ this.ciphers = Collections.unmodifiableList(ciphers);
+ this.supportedProtocols = Collections.unmodifiableList(supportedProtocols);
+ }
+
+ public SslTrustConfig getTrustConfig() {
+ return trustConfig;
+ }
+
+ public SslKeyConfig getKeyConfig() {
+ return keyConfig;
+ }
+
+ public SslVerificationMode getVerificationMode() {
+ return verificationMode;
+ }
+
+ public SslClientAuthenticationMode getClientAuth() {
+ return clientAuth;
+ }
+
+ public List getCipherSuites() {
+ return ciphers;
+ }
+
+ public List getSupportedProtocols() {
+ return supportedProtocols;
+ }
+
+ /**
+ * @return A collection of files that are used by this SSL configuration. If the contents of these files change, then any
+ * subsequent call to {@link #createSslContext()} (or similar methods) may create a context with different behaviour.
+ * It is recommended that these files be monitored for changes, and a new ssl-context is created whenever any of the files are modified.
+ */
+ public Collection getDependentFiles() {
+ Set paths = new HashSet<>(keyConfig.getDependentFiles());
+ paths.addAll(trustConfig.getDependentFiles());
+ return paths;
+ }
+
+ /**
+ * Dynamically create a new SSL context based on the current state of the configuration.
+ * Because the {@link #getKeyConfig() key config} and {@link #getTrustConfig() trust config} may change based on the
+ * contents of their referenced files (see {@link #getDependentFiles()}, consecutive calls to this method may
+ * return ssl-contexts with different configurations.
+ */
+ public SSLContext createSslContext() {
+ final X509ExtendedKeyManager keyManager = keyConfig.createKeyManager();
+ final X509ExtendedTrustManager trustManager = trustConfig.createTrustManager();
+ try {
+ SSLContext sslContext = SSLContext.getInstance(contextProtocol());
+ sslContext.init(new X509ExtendedKeyManager[] { keyManager }, new X509ExtendedTrustManager[] { trustManager }, null);
+ return sslContext;
+ } catch (GeneralSecurityException e) {
+ throw new SslConfigException("cannot create ssl context", e);
+ }
+ }
+
+ /**
+ * Picks the best (highest security / most recent standard) SSL/TLS protocol (/version) that is supported by the
+ * {@link #getSupportedProtocols() configured protocols}.
+ */
+ private String contextProtocol() {
+ if (supportedProtocols.isEmpty()) {
+ throw new SslConfigException("no SSL/TLS protocols have been configured");
+ }
+ for (String tryProtocol : Arrays.asList("TLSv1.2", "TLSv1.1", "TLSv1", "SSLv3")) {
+ if (supportedProtocols.contains(tryProtocol)) {
+ return tryProtocol;
+ }
+ }
+ return "SSL";
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + '{' +
+ "trustConfig=" + trustConfig +
+ ", keyConfig=" + keyConfig +
+ ", verificationMode=" + verificationMode +
+ ", clientAuth=" + clientAuth +
+ ", ciphers=" + ciphers +
+ ", supportedProtocols=" + supportedProtocols +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ final SslConfiguration that = (SslConfiguration) o;
+ return Objects.equals(this.trustConfig, that.trustConfig) &&
+ Objects.equals(this.keyConfig, that.keyConfig) &&
+ this.verificationMode == that.verificationMode &&
+ this.clientAuth == that.clientAuth &&
+ Objects.equals(this.ciphers, that.ciphers) &&
+ Objects.equals(this.supportedProtocols, that.supportedProtocols);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(trustConfig, keyConfig, verificationMode, clientAuth, ciphers, supportedProtocols);
+ }
+}
diff --git a/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslConfigurationKeys.java b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslConfigurationKeys.java
new file mode 100644
index 0000000000000..6e717f1c4cd11
--- /dev/null
+++ b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslConfigurationKeys.java
@@ -0,0 +1,181 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.common.ssl;
+
+import javax.net.ssl.TrustManagerFactory;
+import java.security.KeyStore;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Utility class for handling the standard setting keys for use in SSL configuration.
+ *
+ * @see SslConfiguration
+ * @see SslConfigurationLoader
+ */
+public class SslConfigurationKeys {
+ /**
+ * The SSL/TLS protocols (i.e. versions) that should be used
+ */
+ public static final String PROTOCOLS = "supported_protocols";
+
+ /**
+ * The SSL/TLS cipher suites that should be used
+ */
+ public static final String CIPHERS = "cipher_suites";
+
+ /**
+ * Whether certificate and/or hostname verification should be used
+ */
+ public static final String VERIFICATION_MODE = "verification_mode";
+
+ /**
+ * When operating as a server, whether to request/require client certificates
+ */
+ public static final String CLIENT_AUTH = "client_authentication";
+
+ // Trust
+ /**
+ * A list of paths to PEM formatted certificates that should be trusted as CAs
+ */
+ public static final String CERTIFICATE_AUTHORITIES = "certificate_authorities";
+ /**
+ * The path to a KeyStore file (in a format supported by this JRE) that should be used as a trust-store
+ */
+ public static final String TRUSTSTORE_PATH = "truststore.path";
+ /**
+ * The password for the file configured in {@link #TRUSTSTORE_PATH}, as a secure setting.
+ */
+ public static final String TRUSTSTORE_SECURE_PASSWORD = "truststore.secure_password";
+ /**
+ * The password for the file configured in {@link #TRUSTSTORE_PATH}, as a non-secure setting.
+ * The use of this setting {@link #isDeprecated(String) is deprecated}.
+ */
+ public static final String TRUSTSTORE_LEGACY_PASSWORD = "truststore.password";
+ /**
+ * The {@link KeyStore#getType() keystore type} for the file configured in {@link #TRUSTSTORE_PATH}.
+ */
+ public static final String TRUSTSTORE_TYPE = "truststore.type";
+ /**
+ * The {@link TrustManagerFactory#getAlgorithm() trust management algorithm} to use when configuring trust
+ * with a {@link #TRUSTSTORE_PATH truststore}.
+ */
+ public static final String TRUSTSTORE_ALGORITHM = "truststore.algorithm";
+
+ // Key Management
+ // -- Keystore
+ /**
+ * The path to a KeyStore file (in a format supported by this JRE) that should be used for key management
+ */
+ public static final String KEYSTORE_PATH = "keystore.path";
+ /**
+ * The password for the file configured in {@link #KEYSTORE_PATH}, as a secure setting.
+ */
+ public static final String KEYSTORE_SECURE_PASSWORD = "keystore.secure_password";
+ /**
+ * The password for the file configured in {@link #KEYSTORE_PATH}, as a non-secure setting.
+ * The use of this setting {@link #isDeprecated(String) is deprecated}.
+ */
+ public static final String KEYSTORE_LEGACY_PASSWORD = "keystore.password";
+ /**
+ * The password for the key within the {@link #KEYSTORE_PATH configured keystore}, as a secure setting.
+ * If no key password is specified, it will default to the keystore password.
+ */
+ public static final String KEYSTORE_SECURE_KEY_PASSWORD = "keystore.secure_key_password";
+ /**
+ * The password for the key within the {@link #KEYSTORE_PATH configured keystore}, as a non-secure setting.
+ * The use of this setting {@link #isDeprecated(String) is deprecated}.
+ * If no key password is specified, it will default to the keystore password.
+ */
+ public static final String KEYSTORE_LEGACY_KEY_PASSWORD = "keystore.key_password";
+ /**
+ * The {@link KeyStore#getType() keystore type} for the file configured in {@link #KEYSTORE_PATH}.
+ */
+ public static final String KEYSTORE_TYPE = "keystore.type";
+ /**
+ * The {@link javax.net.ssl.KeyManagerFactory#getAlgorithm() key management algorithm} to use when
+ * connstructing a Key manager from a {@link #KEYSTORE_PATH keystore}.
+ */
+ public static final String KEYSTORE_ALGORITHM = "keystore.algorithm";
+ // -- PEM
+ /**
+ * The path to a PEM formatted file that contains the certificate to be used as part of key management
+ */
+ public static final String CERTIFICATE = "certificate";
+ /**
+ * The path to a PEM formatted file that contains the private key for the configured {@link #CERTIFICATE}.
+ */
+ public static final String KEY = "key";
+ /**
+ * The password to read the configured {@link #KEY}, as a secure setting.
+ * This (or the {@link #KEY_LEGACY_PASSPHRASE legacy fallback}) is required if the key file is encrypted.
+ */
+ public static final String KEY_SECURE_PASSPHRASE = "secure_key_passphrase";
+ /**
+ * The password to read the configured {@link #KEY}, as a non-secure setting.
+ * The use of this setting {@link #isDeprecated(String) is deprecated}.
+ */
+ public static final String KEY_LEGACY_PASSPHRASE = "key_passphrase";
+
+ private static final Set DEPRECATED_KEYS = new HashSet<>(
+ Arrays.asList(TRUSTSTORE_LEGACY_PASSWORD, KEYSTORE_LEGACY_PASSWORD, KEYSTORE_LEGACY_KEY_PASSWORD, KEY_LEGACY_PASSPHRASE)
+ );
+
+ private SslConfigurationKeys() {
+ throw new IllegalStateException("Utility class should not be instantiated");
+ }
+
+ /**
+ * The list of keys that are used to load a non-secure, non-list setting
+ */
+ public static List getStringKeys() {
+ return Arrays.asList(
+ VERIFICATION_MODE, CLIENT_AUTH,
+ TRUSTSTORE_PATH, TRUSTSTORE_LEGACY_PASSWORD, TRUSTSTORE_TYPE, TRUSTSTORE_TYPE,
+ KEYSTORE_PATH, KEYSTORE_LEGACY_PASSWORD, KEYSTORE_LEGACY_KEY_PASSWORD, KEYSTORE_TYPE, KEYSTORE_ALGORITHM,
+ CERTIFICATE, KEY, KEY_LEGACY_PASSPHRASE
+ );
+ }
+
+ /**
+ * The list of keys that are used to load a non-secure, list setting
+ */
+ public static List getListKeys() {
+ return Arrays.asList(PROTOCOLS, CIPHERS, CERTIFICATE_AUTHORITIES);
+ }
+
+ /**
+ * The list of keys that are used to load a secure setting (such as a password) that would typically be stored in the elasticsearch
+ * keystore.
+ */
+ public static List getSecureStringKeys() {
+ return Arrays.asList(TRUSTSTORE_SECURE_PASSWORD, KEYSTORE_SECURE_PASSWORD, KEYSTORE_SECURE_KEY_PASSWORD, KEY_SECURE_PASSPHRASE);
+ }
+
+ /**
+ * @return {@code true} if the provided key is a deprecated setting
+ */
+ public static boolean isDeprecated(String key) {
+ return DEPRECATED_KEYS.contains(key);
+ }
+
+}
diff --git a/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslConfigurationLoader.java b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslConfigurationLoader.java
new file mode 100644
index 0000000000000..186d20b1ea858
--- /dev/null
+++ b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslConfigurationLoader.java
@@ -0,0 +1,371 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.common.ssl;
+
+import javax.crypto.Cipher;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.TrustManagerFactory;
+import java.nio.file.Path;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import static org.elasticsearch.common.ssl.KeyStoreUtil.inferKeyStoreType;
+import static org.elasticsearch.common.ssl.SslConfigurationKeys.CERTIFICATE;
+import static org.elasticsearch.common.ssl.SslConfigurationKeys.CERTIFICATE_AUTHORITIES;
+import static org.elasticsearch.common.ssl.SslConfigurationKeys.CIPHERS;
+import static org.elasticsearch.common.ssl.SslConfigurationKeys.CLIENT_AUTH;
+import static org.elasticsearch.common.ssl.SslConfigurationKeys.KEY;
+import static org.elasticsearch.common.ssl.SslConfigurationKeys.KEYSTORE_ALGORITHM;
+import static org.elasticsearch.common.ssl.SslConfigurationKeys.KEYSTORE_LEGACY_KEY_PASSWORD;
+import static org.elasticsearch.common.ssl.SslConfigurationKeys.KEYSTORE_LEGACY_PASSWORD;
+import static org.elasticsearch.common.ssl.SslConfigurationKeys.KEYSTORE_PATH;
+import static org.elasticsearch.common.ssl.SslConfigurationKeys.KEYSTORE_SECURE_KEY_PASSWORD;
+import static org.elasticsearch.common.ssl.SslConfigurationKeys.KEYSTORE_SECURE_PASSWORD;
+import static org.elasticsearch.common.ssl.SslConfigurationKeys.KEYSTORE_TYPE;
+import static org.elasticsearch.common.ssl.SslConfigurationKeys.KEY_LEGACY_PASSPHRASE;
+import static org.elasticsearch.common.ssl.SslConfigurationKeys.KEY_SECURE_PASSPHRASE;
+import static org.elasticsearch.common.ssl.SslConfigurationKeys.PROTOCOLS;
+import static org.elasticsearch.common.ssl.SslConfigurationKeys.TRUSTSTORE_ALGORITHM;
+import static org.elasticsearch.common.ssl.SslConfigurationKeys.TRUSTSTORE_LEGACY_PASSWORD;
+import static org.elasticsearch.common.ssl.SslConfigurationKeys.TRUSTSTORE_PATH;
+import static org.elasticsearch.common.ssl.SslConfigurationKeys.TRUSTSTORE_SECURE_PASSWORD;
+import static org.elasticsearch.common.ssl.SslConfigurationKeys.TRUSTSTORE_TYPE;
+import static org.elasticsearch.common.ssl.SslConfigurationKeys.VERIFICATION_MODE;
+
+/**
+ * Loads {@link SslConfiguration} from settings.
+ * This class handles the logic of interpreting the various "ssl.*" configuration settings and their interactions
+ * (as well as being aware of dependencies and conflicts between different settings).
+ * The constructed {@code SslConfiguration} has sensible defaults for any settings that are not explicitly configured,
+ * and these defaults can be overridden through the various {@code setDefaultXyz} methods.
+ * It is {@code abstract} because this library has minimal dependencies, so the extraction of the setting values from
+ * the underlying setting source must be handled by the code that makes use of this class.
+ *
+ * @see SslConfiguration
+ * @see SslConfigurationKeys
+ */
+public abstract class SslConfigurationLoader {
+
+ static final List DEFAULT_PROTOCOLS = Arrays.asList("TLSv1.2", "TLSv1.1", "TLSv1");
+ static final List DEFAULT_CIPHERS = loadDefaultCiphers();
+ private static final char[] EMPTY_PASSWORD = new char[0];
+
+ private final String settingPrefix;
+
+ private SslTrustConfig defaultTrustConfig;
+ private SslKeyConfig defaultKeyConfig;
+ private SslVerificationMode defaultVerificationMode;
+ private SslClientAuthenticationMode defaultClientAuth;
+ private List defaultCiphers;
+ private List defaultProtocols;
+
+ /**
+ * Construct a new loader with the "standard" default values.
+ *
+ * @param settingPrefix The prefix to apply to all settings that are loaded. It may be the empty string, otherwise it
+ * must end in a "." (period). For example, if the prefix is {@code "reindex.ssl."} then the keys that are
+ * passed to methods like {@link #getSettingAsString(String)} will be in the form
+ * {@code "reindex.ssl.verification_mode"}, and those same keys will be reported in error messages (via
+ * {@link SslConfigException}).
+ */
+ public SslConfigurationLoader(String settingPrefix) {
+ this.settingPrefix = settingPrefix == null ? "" : settingPrefix;
+ if (this.settingPrefix.isEmpty() == false && this.settingPrefix.endsWith(".") == false) {
+ throw new IllegalArgumentException("Setting prefix [" + settingPrefix + "] must be blank or end in '.'");
+ }
+ this.defaultTrustConfig = new DefaultJdkTrustConfig();
+ this.defaultKeyConfig = EmptyKeyConfig.INSTANCE;
+ this.defaultVerificationMode = SslVerificationMode.FULL;
+ this.defaultClientAuth = SslClientAuthenticationMode.OPTIONAL;
+ this.defaultProtocols = DEFAULT_PROTOCOLS;
+ this.defaultCiphers = DEFAULT_CIPHERS;
+ }
+
+ /**
+ * Change the default trust config.
+ * The initial trust config is {@link DefaultJdkTrustConfig}, which trusts the JDK's default CA certs
+ */
+ public void setDefaultTrustConfig(SslTrustConfig defaultTrustConfig) {
+ this.defaultTrustConfig = defaultTrustConfig;
+ }
+
+ /**
+ * Change the default key config.
+ * The initial key config is {@link EmptyKeyConfig}, which does not provide any keys
+ */
+ public void setDefaultKeyConfig(SslKeyConfig defaultKeyConfig) {
+ this.defaultKeyConfig = defaultKeyConfig;
+ }
+
+ /**
+ * Change the default verification mode.
+ * The initial verification mode is {@link SslVerificationMode#FULL}.
+ */
+ public void setDefaultVerificationMode(SslVerificationMode defaultVerificationMode) {
+ this.defaultVerificationMode = defaultVerificationMode;
+ }
+
+ /**
+ * Change the default client authentication mode.
+ * The initial client auth mode is {@link SslClientAuthenticationMode#OPTIONAL}.
+ */
+ public void setDefaultClientAuth(SslClientAuthenticationMode defaultClientAuth) {
+ this.defaultClientAuth = defaultClientAuth;
+ }
+
+ /**
+ * Change the default supported ciphers.
+ * The initial cipher list depends on the availability of {@link #has256BitAES() 256 bit AES}.
+ *
+ * @see #loadDefaultCiphers()
+ */
+ public void setDefaultCiphers(List defaultCiphers) {
+ this.defaultCiphers = defaultCiphers;
+ }
+
+ /**
+ * Change the default SSL/TLS protocol list.
+ * The initial protocol list is defined by {@link #DEFAULT_PROTOCOLS}
+ */
+ public void setDefaultProtocols(List defaultProtocols) {
+ this.defaultProtocols = defaultProtocols;
+ }
+
+ /**
+ * Clients of this class should implement this method to load a fully-qualified key from the preferred settings source.
+ * This method will be called for basic string settings (see {@link SslConfigurationKeys#getStringKeys()}).
+ *
+ * The setting should be returned as a string, and this class will convert it to the relevant type.
+ *
+ * @throws Exception If a {@link RuntimeException} is thrown, it will be rethrown unwrapped. All checked exceptions are wrapped in
+ * {@link SslConfigException} before being rethrown.
+ */
+ protected abstract String getSettingAsString(String key) throws Exception;
+
+ /**
+ * Clients of this class should implement this method to load a fully-qualified key from the preferred secure settings source.
+ * This method will be called for any setting keys that are marked as being
+ * {@link SslConfigurationKeys#getSecureStringKeys() secure} settings.
+ *
+ * @throws Exception If a {@link RuntimeException} is thrown, it will be rethrown unwrapped. All checked exceptions are wrapped in
+ * {@link SslConfigException} before being rethrown.
+ */
+ protected abstract char[] getSecureSetting(String key) throws Exception;
+
+ /**
+ * Clients of this class should implement this method to load a fully-qualified key from the preferred settings source.
+ * This method will be called for list settings (see {@link SslConfigurationKeys#getListKeys()}).
+ *
+ * The setting should be returned as a list of strings, and this class will convert the values to the relevant type.
+ *
+ * @throws Exception If a {@link RuntimeException} is thrown, it will be rethrown unwrapped. All checked exceptions are wrapped in
+ * {@link SslConfigException} before being rethrown.
+ */
+ protected abstract List getSettingAsList(String key) throws Exception;
+
+ /**
+ * Resolve all necessary configuration settings, and load a {@link SslConfiguration}.
+ *
+ * @param basePath The base path to use for any settings that represent file paths. Typically points to the Elasticsearch
+ * configuration directory.
+ * @throws SslConfigException For any problems with the configuration, or with loading the required SSL classes.
+ */
+ public SslConfiguration load(Path basePath) {
+ Objects.requireNonNull(basePath, "Base Path cannot be null");
+ final List protocols = resolveListSetting(PROTOCOLS, Function.identity(), defaultProtocols);
+ final List ciphers = resolveListSetting(CIPHERS, Function.identity(), defaultCiphers);
+ final SslVerificationMode verificationMode = resolveSetting(VERIFICATION_MODE, SslVerificationMode::parse, defaultVerificationMode);
+ final SslClientAuthenticationMode clientAuth = resolveSetting(CLIENT_AUTH, SslClientAuthenticationMode::parse, defaultClientAuth);
+
+ final SslTrustConfig trustConfig = buildTrustConfig(basePath, verificationMode);
+ final SslKeyConfig keyConfig = buildKeyConfig(basePath);
+
+ if (protocols == null || protocols.isEmpty()) {
+ throw new SslConfigException("no protocols configured in [" + settingPrefix + PROTOCOLS + "]");
+ }
+ if (ciphers == null || ciphers.isEmpty()) {
+ throw new SslConfigException("no cipher suites configured in [" + settingPrefix + CIPHERS + "]");
+ }
+ return new SslConfiguration(trustConfig, keyConfig, verificationMode, clientAuth, ciphers, protocols);
+ }
+
+ private SslTrustConfig buildTrustConfig(Path basePath, SslVerificationMode verificationMode) {
+ final List certificateAuthorities = resolveListSetting(CERTIFICATE_AUTHORITIES, basePath::resolve, null);
+ final Path trustStorePath = resolveSetting(TRUSTSTORE_PATH, basePath::resolve, null);
+
+ if (certificateAuthorities != null && trustStorePath != null) {
+ throw new SslConfigException("cannot specify both [" + settingPrefix + CERTIFICATE_AUTHORITIES + "] and [" +
+ settingPrefix + TRUSTSTORE_PATH + "]");
+ }
+ if (verificationMode.isCertificateVerificationEnabled() == false) {
+ return TrustEverythingConfig.TRUST_EVERYTHING;
+ }
+ if (certificateAuthorities != null) {
+ return new PemTrustConfig(certificateAuthorities);
+ }
+ if (trustStorePath != null) {
+ final char[] password = resolvePasswordSetting(TRUSTSTORE_SECURE_PASSWORD, TRUSTSTORE_LEGACY_PASSWORD);
+ final String storeType = resolveSetting(TRUSTSTORE_TYPE, Function.identity(), inferKeyStoreType(trustStorePath));
+ final String algorithm = resolveSetting(TRUSTSTORE_ALGORITHM, Function.identity(), TrustManagerFactory.getDefaultAlgorithm());
+ return new StoreTrustConfig(trustStorePath, password, storeType, algorithm);
+ }
+ return defaultTrustConfig;
+ }
+
+ private SslKeyConfig buildKeyConfig(Path basePath) {
+ final Path certificatePath = resolveSetting(CERTIFICATE, basePath::resolve, null);
+ final Path keyPath = resolveSetting(KEY, basePath::resolve, null);
+ final Path keyStorePath = resolveSetting(KEYSTORE_PATH, basePath::resolve, null);
+
+ if (certificatePath != null && keyStorePath != null) {
+ throw new SslConfigException("cannot specify both [" + settingPrefix + CERTIFICATE + "] and [" +
+ settingPrefix + KEYSTORE_PATH + "]");
+ }
+
+ if (certificatePath != null || keyPath != null) {
+ if (keyPath == null) {
+ throw new SslConfigException("cannot specify [" + settingPrefix + CERTIFICATE + "] without also setting [" +
+ settingPrefix + KEY + "]");
+ }
+ if (certificatePath == null) {
+ throw new SslConfigException("cannot specify [" + settingPrefix + KEYSTORE_PATH + "] without also setting [" +
+ settingPrefix + CERTIFICATE + "]");
+ }
+ final char[] password = resolvePasswordSetting(KEY_SECURE_PASSPHRASE, KEY_LEGACY_PASSPHRASE);
+ return new PemKeyConfig(certificatePath, keyPath, password);
+ }
+
+ if (keyStorePath != null) {
+ final char[] storePassword = resolvePasswordSetting(KEYSTORE_SECURE_PASSWORD, KEYSTORE_LEGACY_PASSWORD);
+ char[] keyPassword = resolvePasswordSetting(KEYSTORE_SECURE_KEY_PASSWORD, KEYSTORE_LEGACY_KEY_PASSWORD);
+ if (keyPassword.length == 0) {
+ keyPassword = storePassword;
+ }
+ final String storeType = resolveSetting(KEYSTORE_TYPE, Function.identity(), inferKeyStoreType(keyStorePath));
+ final String algorithm = resolveSetting(KEYSTORE_ALGORITHM, Function.identity(), KeyManagerFactory.getDefaultAlgorithm());
+ return new StoreKeyConfig(keyStorePath, storePassword, storeType, keyPassword, algorithm);
+ }
+
+ return defaultKeyConfig;
+ }
+
+ private char[] resolvePasswordSetting(String secureSettingKey, String legacySettingKey) {
+ final char[] securePassword = resolveSecureSetting(secureSettingKey, null);
+ final String legacyPassword = resolveSetting(legacySettingKey, Function.identity(), null);
+ if (securePassword == null) {
+ if (legacyPassword == null) {
+ return EMPTY_PASSWORD;
+ } else {
+ return legacyPassword.toCharArray();
+ }
+ } else {
+ if (legacyPassword != null) {
+ throw new SslConfigException("cannot specify both [" + settingPrefix + secureSettingKey + "] and ["
+ + settingPrefix + legacySettingKey + "]");
+ } else {
+ return securePassword;
+ }
+ }
+ }
+
+ private V resolveSetting(String key, Function parser, V defaultValue) {
+ try {
+ String setting = getSettingAsString(settingPrefix + key);
+ if (setting == null || setting.isEmpty()) {
+ return defaultValue;
+ }
+ return parser.apply(setting);
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new SslConfigException("cannot retrieve setting [" + settingPrefix + key + "]", e);
+ }
+ }
+
+ private char[] resolveSecureSetting(String key, char[] defaultValue) {
+ try {
+ char[] setting = getSecureSetting(settingPrefix + key);
+ if (setting == null || setting.length == 0) {
+ return defaultValue;
+ }
+ return setting;
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new SslConfigException("cannot retrieve secure setting [" + settingPrefix + key + "]", e);
+ }
+
+ }
+
+ private List resolveListSetting(String key, Function parser, List defaultValue) {
+ try {
+ final List list = getSettingAsList(settingPrefix + key);
+ if (list == null || list.isEmpty()) {
+ return defaultValue;
+ }
+ return list.stream().map(parser).collect(Collectors.toList());
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new SslConfigException("cannot retrieve setting [" + settingPrefix + key + "]", e);
+ }
+ }
+
+ private static List loadDefaultCiphers() {
+ final List ciphers128 = Arrays.asList(
+ "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
+ "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
+ "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
+ "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
+ "TLS_RSA_WITH_AES_128_CBC_SHA256",
+ "TLS_RSA_WITH_AES_128_CBC_SHA"
+ );
+ final List ciphers256 = Arrays.asList(
+ "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
+ "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
+ "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
+ "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
+ "TLS_RSA_WITH_AES_256_CBC_SHA256",
+ "TLS_RSA_WITH_AES_256_CBC_SHA"
+ );
+ if (has256BitAES()) {
+ List ciphers = new ArrayList<>(ciphers256.size() + ciphers128.size());
+ ciphers.addAll(ciphers256);
+ ciphers.addAll(ciphers128);
+ return ciphers;
+ } else {
+ return ciphers128;
+ }
+ }
+
+ private static boolean has256BitAES() {
+ try {
+ return Cipher.getMaxAllowedKeyLength("AES") > 128;
+ } catch (NoSuchAlgorithmException e) {
+ // No AES? Things are going to be very weird, but technically that means we don't have 256 bit AES, so ...
+ return false;
+ }
+ }
+}
diff --git a/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslKeyConfig.java b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslKeyConfig.java
new file mode 100644
index 0000000000000..4f5e6b8669310
--- /dev/null
+++ b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslKeyConfig.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.common.ssl;
+
+import javax.net.ssl.X509ExtendedKeyManager;
+import java.nio.file.Path;
+import java.util.Collection;
+
+/**
+ * An interface for building a key manager at runtime.
+ * The method for constructing the key manager is implementation dependent.
+ */
+public interface SslKeyConfig {
+
+ /**
+ * @return A collection of files that are read by this config object.
+ * The {@link #createKeyManager()} method will read these files dynamically, so the behaviour of this key config may change whenever
+ * any of these files are modified.
+ */
+ Collection getDependentFiles();
+
+ /**
+ * @return A new {@link X509ExtendedKeyManager}.
+ * @throws SslConfigException if there is a problem configuring the key manager.
+ */
+ X509ExtendedKeyManager createKeyManager();
+
+}
+
diff --git a/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslTrustConfig.java b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslTrustConfig.java
new file mode 100644
index 0000000000000..2cd61218e6bb7
--- /dev/null
+++ b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslTrustConfig.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.common.ssl;
+
+import javax.net.ssl.X509ExtendedTrustManager;
+import java.nio.file.Path;
+import java.util.Collection;
+
+/**
+ * An interface for building a trust manager at runtime.
+ * The method for constructing the trust manager is implementation dependent.
+ */
+public interface SslTrustConfig {
+
+ /**
+ * @return A collection of files that are read by this config object.
+ * The {@link #createTrustManager()} method will read these files dynamically, so the behaviour of this trust config may change if
+ * any of these files are modified.
+ */
+ Collection getDependentFiles();
+
+ /**
+ * @return A new {@link X509ExtendedTrustManager}.
+ * @throws SslConfigException if there is a problem configuring the trust manager.
+ */
+ X509ExtendedTrustManager createTrustManager();
+
+}
+
diff --git a/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslVerificationMode.java b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslVerificationMode.java
new file mode 100644
index 0000000000000..eee6e9cb2d6a1
--- /dev/null
+++ b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslVerificationMode.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.elasticsearch.common.ssl;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * Represents the verification mode to be used for SSL connections.
+ */
+public enum SslVerificationMode {
+ /**
+ * Verify neither the hostname, nor the provided certificate.
+ */
+ NONE {
+ @Override
+ public boolean isHostnameVerificationEnabled() {
+ return false;
+ }
+
+ @Override
+ public boolean isCertificateVerificationEnabled() {
+ return false;
+ }
+ },
+ /**
+ * Verify the provided certificate against the trust chain, but do not verify the hostname.
+ */
+ CERTIFICATE {
+ @Override
+ public boolean isHostnameVerificationEnabled() {
+ return false;
+ }
+
+ @Override
+ public boolean isCertificateVerificationEnabled() {
+ return true;
+ }
+ },
+ /**
+ * Verify the provided certificate against the trust chain, and also verify that the hostname to which this client is connected
+ * matches one of the Subject-Alternative-Names in the certificate.
+ */
+ FULL {
+ @Override
+ public boolean isHostnameVerificationEnabled() {
+ return true;
+ }
+
+ @Override
+ public boolean isCertificateVerificationEnabled() {
+ return true;
+ }
+ };
+
+ /**
+ * @return true if hostname verification is enabled
+ */
+ public abstract boolean isHostnameVerificationEnabled();
+
+ /**
+ * @return true if certificate verification is enabled
+ */
+ public abstract boolean isCertificateVerificationEnabled();
+
+ private static final Map LOOKUP = Collections.unmodifiableMap(buildLookup());
+
+ private static Map buildLookup() {
+ Map map = new LinkedHashMap<>(3);
+ map.put("none", NONE);
+ map.put("certificate", CERTIFICATE);
+ map.put("full", FULL);
+ return map;
+ }
+
+ public static SslVerificationMode parse(String value) {
+ final SslVerificationMode mode = LOOKUP.get(value.toLowerCase(Locale.ROOT));
+ if (mode == null) {
+ final String allowedValues = LOOKUP.keySet().stream().collect(Collectors.joining(","));
+ throw new SslConfigException("could not resolve ssl client verification mode, unknown value ["
+ + value + "], recognised values are [" + allowedValues + "]");
+ }
+ return mode;
+ }
+}
diff --git a/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/StoreKeyConfig.java b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/StoreKeyConfig.java
new file mode 100644
index 0000000000000..683aaaaa06cb4
--- /dev/null
+++ b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/StoreKeyConfig.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.common.ssl;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.X509ExtendedKeyManager;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.UnrecoverableKeyException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+
+/**
+ * A {@link SslKeyConfig} that builds a Key Manager from a keystore file.
+ */
+public class StoreKeyConfig implements SslKeyConfig {
+ private final Path path;
+ private final char[] storePassword;
+ private final String type;
+ private final char[] keyPassword;
+ private final String algorithm;
+
+ /**
+ * @param path The path to the keystore file
+ * @param storePassword The password for the keystore
+ * @param type The {@link KeyStore#getType() type} of the keystore (typically "PKCS12" or "jks").
+ * See {@link KeyStoreUtil#inferKeyStoreType(Path)}.
+ * @param keyPassword The password for the key(s) within the keystore
+ * (see {@link javax.net.ssl.KeyManagerFactory#init(KeyStore, char[])}).
+ * @param algorithm The algorithm to use for the Key Manager (see {@link KeyManagerFactory#getAlgorithm()}).
+ */
+ StoreKeyConfig(Path path, char[] storePassword, String type, char[] keyPassword, String algorithm) {
+ this.path = path;
+ this.storePassword = storePassword;
+ this.type = type;
+ this.keyPassword = keyPassword;
+ this.algorithm = algorithm;
+ }
+
+ @Override
+ public Collection getDependentFiles() {
+ return Collections.singleton(path);
+ }
+
+ @Override
+ public X509ExtendedKeyManager createKeyManager() {
+ try {
+ final KeyStore keyStore = KeyStoreUtil.readKeyStore(path, type, storePassword);
+ checkKeyStore(keyStore);
+ return KeyStoreUtil.createKeyManager(keyStore, keyPassword, algorithm);
+ } catch (UnrecoverableKeyException e) {
+ String message = "failed to load a KeyManager for keystore [" + path.toAbsolutePath()
+ + "], this is usually caused by an incorrect key-password";
+ if (keyPassword.length == 0) {
+ message += " (no key-password was provided)";
+ } else if (Arrays.equals(storePassword, keyPassword)) {
+ message += " (we tried to access the key using the same password as the keystore)";
+ }
+ throw new SslConfigException(message, e);
+ } catch (GeneralSecurityException e) {
+ throw new SslConfigException("failed to load a KeyManager for keystore [" + path + "] of type [" + type + "]", e);
+ }
+ }
+
+ /**
+ * Verifies that the keystore contains at least 1 private key entry.
+ */
+ private void checkKeyStore(KeyStore keyStore) throws KeyStoreException {
+ Enumeration aliases = keyStore.aliases();
+ while (aliases.hasMoreElements()) {
+ String alias = aliases.nextElement();
+ if (keyStore.isKeyEntry(alias)) {
+ return;
+ }
+ }
+ final String message;
+ if (path != null) {
+ message = "the keystore [" + path + "] does not contain a private key entry";
+ } else {
+ message = "the configured PKCS#11 token does not contain a private key entry";
+ }
+ throw new SslConfigException(message);
+ }
+
+}
diff --git a/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/StoreTrustConfig.java b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/StoreTrustConfig.java
new file mode 100644
index 0000000000000..0dc0a3818c8ed
--- /dev/null
+++ b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/StoreTrustConfig.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.common.ssl;
+
+import javax.net.ssl.X509ExtendedTrustManager;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+
+/**
+ * A {@link SslTrustConfig} that builds a Trust Manager from a keystore file.
+ */
+final class StoreTrustConfig implements SslTrustConfig {
+ private final Path path;
+ private final char[] password;
+ private final String type;
+ private final String algorithm;
+
+ /**
+ * @param path The path to the keystore file
+ * @param password The password for the keystore
+ * @param type The {@link KeyStore#getType() type} of the keystore (typically "PKCS12" or "jks").
+ * See {@link KeyStoreUtil#inferKeyStoreType(Path)}.
+ * @param algorithm The algorithm to use for the Trust Manager (see {@link javax.net.ssl.TrustManagerFactory#getAlgorithm()}).
+ */
+ StoreTrustConfig(Path path, char[] password, String type, String algorithm) {
+ this.path = path;
+ this.type = type;
+ this.algorithm = algorithm;
+ this.password = password;
+ }
+
+ @Override
+ public Collection getDependentFiles() {
+ return Collections.singleton(path);
+ }
+
+ @Override
+ public X509ExtendedTrustManager createTrustManager() {
+ try {
+ final KeyStore store = KeyStoreUtil.readKeyStore(path, type, password);
+ checkTrustStore(store);
+ return KeyStoreUtil.createTrustManager(store, algorithm);
+ } catch (GeneralSecurityException e) {
+ throw new SslConfigException("cannot create trust manager for path=[" + (path == null ? null : path.toAbsolutePath())
+ + "] type=[" + type + "] password=[" + (password.length == 0 ? "" : "") + "]", e);
+ }
+ }
+
+ /**
+ * Verifies that the keystore contains at least 1 trusted certificate entry.
+ */
+ private void checkTrustStore(KeyStore store) throws GeneralSecurityException {
+ Enumeration aliases = store.aliases();
+ while (aliases.hasMoreElements()) {
+ String alias = aliases.nextElement();
+ if (store.isCertificateEntry(alias)) {
+ return;
+ }
+ }
+ final String message;
+ if (path != null) {
+ message = "the truststore [" + path + "] does not contain any trusted certificate entries";
+ } else {
+ message = "the configured PKCS#11 token does not contain any trusted certificate entries";
+ }
+ throw new SslConfigException(message);
+ }
+
+}
diff --git a/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/TrustEverythingConfig.java b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/TrustEverythingConfig.java
new file mode 100644
index 0000000000000..f3ed83a7e7d7b
--- /dev/null
+++ b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/TrustEverythingConfig.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.common.ssl;
+
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.X509ExtendedTrustManager;
+import java.net.Socket;
+import java.nio.file.Path;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * A {@link SslTrustConfig} that trusts all certificates. Used when {@link SslVerificationMode#isCertificateVerificationEnabled()} is
+ * {@code false}.
+ * This class cannot be used on FIPS-140 JVM as it has its own trust manager implementation.
+ */
+final class TrustEverythingConfig implements SslTrustConfig {
+
+ static final TrustEverythingConfig TRUST_EVERYTHING = new TrustEverythingConfig();
+
+ private TrustEverythingConfig() {
+ // single instances
+ }
+
+ /**
+ * The {@link X509ExtendedTrustManager} that will trust all certificates.
+ * All methods are implemented as a no-op and do not throw exceptions regardless of the certificate presented.
+ */
+ private static final X509ExtendedTrustManager TRUST_MANAGER = new X509ExtendedTrustManager() {
+ @Override
+ public void checkClientTrusted(X509Certificate[] x509Certificates, String s, Socket socket) {
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] x509Certificates, String s, Socket socket) {
+ }
+
+ @Override
+ public void checkClientTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) {
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) {
+ }
+
+ @Override
+ public void checkClientTrusted(X509Certificate[] x509Certificates, String s) {
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] x509Certificates, String s) {
+ }
+
+ @Override
+ public X509Certificate[] getAcceptedIssuers() {
+ return new X509Certificate[0];
+ }
+ };
+
+ @Override
+ public Collection getDependentFiles() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public X509ExtendedTrustManager createTrustManager() {
+ return TRUST_MANAGER;
+ }
+
+ @Override
+ public String toString() {
+ return "trust everything";
+ }
+}
diff --git a/libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/DefaultJdkTrustConfigTests.java b/libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/DefaultJdkTrustConfigTests.java
new file mode 100644
index 0000000000000..b1aad439e47e1
--- /dev/null
+++ b/libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/DefaultJdkTrustConfigTests.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.common.ssl;
+
+import org.elasticsearch.test.ESTestCase;
+import org.junit.Assert;
+
+import javax.net.ssl.X509ExtendedTrustManager;
+import java.security.cert.X509Certificate;
+import java.util.Locale;
+import java.util.Optional;
+import java.util.function.BiFunction;
+import java.util.stream.Stream;
+
+import static org.hamcrest.Matchers.emptyArray;
+import static org.hamcrest.Matchers.emptyIterable;
+import static org.hamcrest.Matchers.not;
+
+public class DefaultJdkTrustConfigTests extends ESTestCase {
+
+ private static final BiFunction EMPTY_SYSTEM_PROPERTIES = (key, defaultValue) -> defaultValue;
+
+ public void testGetSystemTrustStoreWithNoSystemProperties() throws Exception {
+ final DefaultJdkTrustConfig trustConfig = new DefaultJdkTrustConfig((key, defaultValue) -> defaultValue);
+ assertThat(trustConfig.getDependentFiles(), emptyIterable());
+ final X509ExtendedTrustManager trustManager = trustConfig.createTrustManager();
+ assertStandardIssuers(trustManager);
+ }
+
+ public void testGetNonPKCS11TrustStoreWithPasswordSet() throws Exception {
+ final DefaultJdkTrustConfig trustConfig = new DefaultJdkTrustConfig(EMPTY_SYSTEM_PROPERTIES, "fakepassword".toCharArray());
+ assertThat(trustConfig.getDependentFiles(), emptyIterable());
+ final X509ExtendedTrustManager trustManager = trustConfig.createTrustManager();
+ assertStandardIssuers(trustManager);
+ }
+
+ private void assertStandardIssuers(X509ExtendedTrustManager trustManager) {
+ assertThat(trustManager.getAcceptedIssuers(), not(emptyArray()));
+ // This is a sample of the CAs that we expect on every JRE.
+ // We can safely change this list if the JRE's issuer list changes, but we want to assert something useful.
+ assertHasTrustedIssuer(trustManager, "VeriSign");
+ assertHasTrustedIssuer(trustManager, "GeoTrust");
+ assertHasTrustedIssuer(trustManager, "DigiCert");
+ assertHasTrustedIssuer(trustManager, "thawte");
+ assertHasTrustedIssuer(trustManager, "COMODO");
+ }
+
+ private void assertHasTrustedIssuer(X509ExtendedTrustManager trustManager, String name) {
+ final String lowerName = name.toLowerCase(Locale.ROOT);
+ final Optional ca = Stream.of(trustManager.getAcceptedIssuers())
+ .filter(cert -> cert.getSubjectDN().getName().toLowerCase(Locale.ROOT).contains(lowerName))
+ .findAny();
+ if (ca.isPresent() == false) {
+ logger.info("Failed to find issuer [{}] in trust manager, but did find ...", lowerName);
+ for (X509Certificate cert : trustManager.getAcceptedIssuers()) {
+ logger.info(" - {}", cert.getSubjectDN().getName().replaceFirst("^\\w+=([^,]+),.*", "$1"));
+ }
+ Assert.fail("Cannot find trusted issuer with name [" + name + "].");
+ }
+ }
+
+}
diff --git a/libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/PemKeyConfigTests.java b/libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/PemKeyConfigTests.java
new file mode 100644
index 0000000000000..8a5bb469e3c2c
--- /dev/null
+++ b/libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/PemKeyConfigTests.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.common.ssl;
+
+import org.elasticsearch.test.ESTestCase;
+import org.hamcrest.Matchers;
+
+import javax.net.ssl.X509ExtendedKeyManager;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.security.GeneralSecurityException;
+import java.security.PrivateKey;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+
+import static org.hamcrest.Matchers.arrayWithSize;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.iterableWithSize;
+import static org.hamcrest.Matchers.notNullValue;
+
+public class PemKeyConfigTests extends ESTestCase {
+ private static final int IP_NAME = 7;
+ private static final int DNS_NAME = 2;
+
+ public void testBuildKeyConfigFromPemFilesWithoutPassword() throws Exception {
+ final Path cert = getDataPath("/certs/cert1/cert1.crt");
+ final Path key = getDataPath("/certs/cert1/cert1.key");
+ final PemKeyConfig keyConfig = new PemKeyConfig(cert, key, new char[0]);
+ assertThat(keyConfig.getDependentFiles(), Matchers.containsInAnyOrder(cert, key));
+ assertCertificateAndKey(keyConfig, "CN=cert1");
+ }
+
+ public void testBuildKeyConfigFromPemFilesWithPassword() throws Exception {
+ final Path cert = getDataPath("/certs/cert2/cert2.crt");
+ final Path key = getDataPath("/certs/cert2/cert2.key");
+ final PemKeyConfig keyConfig = new PemKeyConfig(cert, key, "c2-pass".toCharArray());
+ assertThat(keyConfig.getDependentFiles(), Matchers.containsInAnyOrder(cert, key));
+ assertCertificateAndKey(keyConfig, "CN=cert2");
+ }
+
+ public void testKeyManagerFailsWithIncorrectPassword() throws Exception {
+ final Path cert = getDataPath("/certs/cert2/cert2.crt");
+ final Path key = getDataPath("/certs/cert2/cert2.key");
+ final PemKeyConfig keyConfig = new PemKeyConfig(cert, key, "wrong-password".toCharArray());
+ assertPasswordIsIncorrect(keyConfig, key);
+ }
+
+ public void testMissingCertificateFailsWithMeaningfulMessage() throws Exception {
+ final Path key = getDataPath("/certs/cert1/cert1.key");
+ final Path cert = key.getParent().resolve("dne.crt");
+
+ final PemKeyConfig keyConfig = new PemKeyConfig(cert, key, new char[0]);
+ assertFileNotFound(keyConfig, "certificate", cert);
+ }
+
+ public void testMissingKeyFailsWithMeaningfulMessage() throws Exception {
+ final Path cert = getDataPath("/certs/cert1/cert1.crt");
+ final Path key = cert.getParent().resolve("dne.key");
+
+ final PemKeyConfig keyConfig = new PemKeyConfig(cert, key, new char[0]);
+ assertFileNotFound(keyConfig, "private key", key);
+ }
+
+ public void testKeyConfigReloadsFileContents() throws Exception {
+ final Path cert1 = getDataPath("/certs/cert1/cert1.crt");
+ final Path key1 = getDataPath("/certs/cert1/cert1.key");
+ final Path cert2 = getDataPath("/certs/cert2/cert2.crt");
+ final Path key2 = getDataPath("/certs/cert2/cert2.key");
+ final Path cert = createTempFile("cert", ".crt");
+ final Path key = createTempFile("cert", ".key");
+
+ final PemKeyConfig keyConfig = new PemKeyConfig(cert, key, new char[0]);
+
+ Files.copy(cert1, cert, StandardCopyOption.REPLACE_EXISTING);
+ Files.copy(key1, key, StandardCopyOption.REPLACE_EXISTING);
+ assertCertificateAndKey(keyConfig, "CN=cert1");
+
+ Files.copy(cert2, cert, StandardCopyOption.REPLACE_EXISTING);
+ Files.copy(key2, key, StandardCopyOption.REPLACE_EXISTING);
+ assertPasswordIsIncorrect(keyConfig, key);
+
+ Files.copy(cert1, cert, StandardCopyOption.REPLACE_EXISTING);
+ Files.copy(key1, key, StandardCopyOption.REPLACE_EXISTING);
+ assertCertificateAndKey(keyConfig, "CN=cert1");
+
+ Files.delete(cert);
+ assertFileNotFound(keyConfig, "certificate", cert);
+ }
+
+ private void assertCertificateAndKey(PemKeyConfig keyConfig, String expectedDN) throws CertificateParsingException {
+ final X509ExtendedKeyManager keyManager = keyConfig.createKeyManager();
+ assertThat(keyManager, notNullValue());
+
+ final PrivateKey privateKey = keyManager.getPrivateKey("key");
+ assertThat(privateKey, notNullValue());
+ assertThat(privateKey.getAlgorithm(), is("RSA"));
+
+ final X509Certificate[] chain = keyManager.getCertificateChain("key");
+ assertThat(chain, notNullValue());
+ assertThat(chain, arrayWithSize(1));
+ final X509Certificate certificate = chain[0];
+ assertThat(certificate.getIssuerDN().getName(), is("CN=Test CA 1"));
+ assertThat(certificate.getSubjectDN().getName(), is(expectedDN));
+ assertThat(certificate.getSubjectAlternativeNames(), iterableWithSize(2));
+ assertThat(certificate.getSubjectAlternativeNames(), containsInAnyOrder(
+ Arrays.asList(DNS_NAME, "localhost"),
+ Arrays.asList(IP_NAME, "127.0.0.1")
+ ));
+ }
+
+ private void assertPasswordIsIncorrect(PemKeyConfig keyConfig, Path key) {
+ final SslConfigException exception = expectThrows(SslConfigException.class, keyConfig::createKeyManager);
+ assertThat(exception.getMessage(), containsString("private key file"));
+ assertThat(exception.getMessage(), containsString(key.toAbsolutePath().toString()));
+ assertThat(exception.getCause(), instanceOf(GeneralSecurityException.class));
+ }
+
+ private void assertFileNotFound(PemKeyConfig keyConfig, String type, Path file) {
+ final SslConfigException exception = expectThrows(SslConfigException.class, keyConfig::createKeyManager);
+ assertThat(exception.getMessage(), containsString(type + " file"));
+ assertThat(exception.getMessage(), containsString(file.toAbsolutePath().toString()));
+ assertThat(exception.getMessage(), containsString("does not exist"));
+ assertThat(exception.getCause(), instanceOf(NoSuchFileException.class));
+ }
+}
diff --git a/libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/PemTrustConfigTests.java b/libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/PemTrustConfigTests.java
new file mode 100644
index 0000000000000..3d78976e1e83b
--- /dev/null
+++ b/libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/PemTrustConfigTests.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.common.ssl;
+
+import org.elasticsearch.test.ESTestCase;
+import org.hamcrest.Matchers;
+
+import javax.net.ssl.X509ExtendedTrustManager;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.StandardOpenOption;
+import java.security.GeneralSecurityException;
+import java.security.Principal;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class PemTrustConfigTests extends ESTestCase {
+
+ public void testBuildTrustConfigFromSinglePemFile() throws Exception {
+ final Path cert = getDataPath("/certs/ca1/ca.crt");
+ final PemTrustConfig trustConfig = new PemTrustConfig(Collections.singletonList(cert));
+ assertThat(trustConfig.getDependentFiles(), Matchers.containsInAnyOrder(cert));
+ assertCertificateChain(trustConfig, "CN=Test CA 1");
+ }
+
+ public void testBuildTrustConfigFromMultiplePemFiles() throws Exception {
+ final Path cert1 = getDataPath("/certs/ca1/ca.crt");
+ final Path cert2 = getDataPath("/certs/ca2/ca.crt");
+ final Path cert3 = getDataPath("/certs/ca3/ca.crt");
+ final PemTrustConfig trustConfig = new PemTrustConfig(Arrays.asList(cert1, cert2, cert3));
+ assertThat(trustConfig.getDependentFiles(), Matchers.containsInAnyOrder(cert1, cert2, cert3));
+ assertCertificateChain(trustConfig, "CN=Test CA 1", "CN=Test CA 2", "CN=Test CA 3");
+ }
+
+ public void testBadFileFormatFails() throws Exception {
+ final Path ca = createTempFile("ca", ".crt");
+ Files.write(ca, randomByteArrayOfLength(128), StandardOpenOption.APPEND);
+ final PemTrustConfig trustConfig = new PemTrustConfig(Collections.singletonList(ca));
+ assertThat(trustConfig.getDependentFiles(), Matchers.containsInAnyOrder(ca));
+ assertInvalidFileFormat(trustConfig, ca);
+ }
+
+ public void testEmptyFileFails() throws Exception {
+ final Path ca = createTempFile("ca", ".crt");
+ final PemTrustConfig trustConfig = new PemTrustConfig(Collections.singletonList(ca));
+ assertThat(trustConfig.getDependentFiles(), Matchers.containsInAnyOrder(ca));
+ assertEmptyFile(trustConfig, ca);
+ }
+
+ public void testMissingFileFailsWithMeaningfulMessage() throws Exception {
+ final Path cert = getDataPath("/certs/ca1/ca.crt").getParent().resolve("dne.crt");
+ final PemTrustConfig trustConfig = new PemTrustConfig(Collections.singletonList(cert));
+ assertThat(trustConfig.getDependentFiles(), Matchers.containsInAnyOrder(cert));
+ assertFileNotFound(trustConfig, cert);
+ }
+
+ public void testOneMissingFileFailsWithMeaningfulMessageEvenIfOtherFileExist() throws Exception {
+ final Path cert1 = getDataPath("/certs/ca1/ca.crt");
+ final Path cert2 = getDataPath("/certs/ca2/ca.crt").getParent().resolve("dne.crt");
+ final Path cert3 = getDataPath("/certs/ca3/ca.crt");
+ final PemTrustConfig trustConfig = new PemTrustConfig(Arrays.asList(cert1, cert2, cert3));
+ assertThat(trustConfig.getDependentFiles(), Matchers.containsInAnyOrder(cert1, cert2, cert3));
+ assertFileNotFound(trustConfig, cert2);
+ }
+
+ public void testTrustConfigReloadsFileContents() throws Exception {
+ final Path cert1 = getDataPath("/certs/ca1/ca.crt");
+ final Path cert2 = getDataPath("/certs/ca2/ca.crt");
+ final Path cert3 = getDataPath("/certs/ca3/ca.crt");
+
+ final Path ca1 = createTempFile("ca1", ".crt");
+ final Path ca2 = createTempFile("ca2", ".crt");
+
+ final PemTrustConfig trustConfig = new PemTrustConfig(Arrays.asList(ca1, ca2));
+
+ Files.copy(cert1, ca1, StandardCopyOption.REPLACE_EXISTING);
+ Files.copy(cert2, ca2, StandardCopyOption.REPLACE_EXISTING);
+ assertCertificateChain(trustConfig, "CN=Test CA 1", "CN=Test CA 2");
+
+ Files.copy(cert3, ca2, StandardCopyOption.REPLACE_EXISTING);
+ assertCertificateChain(trustConfig, "CN=Test CA 1", "CN=Test CA 3");
+
+ Files.delete(ca1);
+ assertFileNotFound(trustConfig, ca1);
+
+ Files.write(ca1, randomByteArrayOfLength(128), StandardOpenOption.CREATE);
+ assertInvalidFileFormat(trustConfig, ca1);
+ }
+
+ private void assertCertificateChain(PemTrustConfig trustConfig, String... caNames) {
+ final X509ExtendedTrustManager trustManager = trustConfig.createTrustManager();
+ final X509Certificate[] issuers = trustManager.getAcceptedIssuers();
+ final Set issuerNames = Stream.of(issuers)
+ .map(X509Certificate::getSubjectDN)
+ .map(Principal::getName)
+ .collect(Collectors.toSet());
+
+ assertThat(issuerNames, Matchers.containsInAnyOrder(caNames));
+ }
+
+ private void assertEmptyFile(PemTrustConfig trustConfig, Path file) {
+ final SslConfigException exception = expectThrows(SslConfigException.class, trustConfig::createTrustManager);
+ assertThat(exception.getMessage(), Matchers.containsString(file.toAbsolutePath().toString()));
+ assertThat(exception.getMessage(), Matchers.containsString("failed to parse any certificates"));
+ }
+
+ private void assertInvalidFileFormat(PemTrustConfig trustConfig, Path file) {
+ if (inFipsJvm()) {
+ // When running on BC-FIPS, an invalid file format behaves like an empty file
+ assertEmptyFile(trustConfig, file);
+ return;
+ }
+ final SslConfigException exception = expectThrows(SslConfigException.class, trustConfig::createTrustManager);
+ assertThat(exception.getMessage(), Matchers.containsString(file.toAbsolutePath().toString()));
+ assertThat(exception.getMessage(), Matchers.containsString("cannot create trust"));
+ assertThat(exception.getMessage(), Matchers.containsString("PEM"));
+ assertThat(exception.getCause(), Matchers.instanceOf(GeneralSecurityException.class));
+ }
+
+ private void assertFileNotFound(PemTrustConfig trustConfig, Path file) {
+ final SslConfigException exception = expectThrows(SslConfigException.class, trustConfig::createTrustManager);
+ assertThat(exception.getMessage(), Matchers.containsString("files do not exist"));
+ assertThat(exception.getMessage(), Matchers.containsString("PEM"));
+ assertThat(exception.getMessage(), Matchers.containsString(file.toAbsolutePath().toString()));
+ assertThat(exception.getCause(), Matchers.instanceOf(NoSuchFileException.class));
+ }
+}
diff --git a/libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/PemUtilsTests.java b/libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/PemUtilsTests.java
new file mode 100644
index 0000000000000..60f0cd168ce1e
--- /dev/null
+++ b/libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/PemUtilsTests.java
@@ -0,0 +1,219 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.common.ssl;
+
+import org.elasticsearch.test.ESTestCase;
+
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.Key;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.util.function.Supplier;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.core.StringContains.containsString;
+
+public class PemUtilsTests extends ESTestCase {
+
+ private static final Supplier EMPTY_PASSWORD = () -> new char[0];
+ private static final Supplier TESTNODE_PASSWORD = "testnode"::toCharArray;
+
+ public void testReadPKCS8RsaKey() throws Exception {
+ Key key = getKeyFromKeystore("RSA");
+ assertThat(key, notNullValue());
+ assertThat(key, instanceOf(PrivateKey.class));
+ PrivateKey privateKey = PemUtils.readPrivateKey(getDataPath("/certs/pem-utils/rsa_key_pkcs8_plain.pem"), EMPTY_PASSWORD);
+ assertThat(privateKey, notNullValue());
+ assertThat(privateKey, equalTo(key));
+ }
+
+ public void testReadPKCS8RsaKeyWithBagAttrs() throws Exception {
+ Key key = getKeyFromKeystore("RSA");
+ assertThat(key, notNullValue());
+ assertThat(key, instanceOf(PrivateKey.class));
+ PrivateKey privateKey = PemUtils.readPrivateKey(getDataPath("/certs/pem-utils/testnode_with_bagattrs.pem"), EMPTY_PASSWORD);
+ assertThat(privateKey, notNullValue());
+ assertThat(privateKey, equalTo(key));
+ }
+
+ public void testReadPKCS8DsaKey() throws Exception {
+ Key key = getKeyFromKeystore("DSA");
+ assertThat(key, notNullValue());
+ assertThat(key, instanceOf(PrivateKey.class));
+ PrivateKey privateKey = PemUtils.readPrivateKey(getDataPath("/certs/pem-utils/dsa_key_pkcs8_plain.pem"), EMPTY_PASSWORD);
+ assertThat(privateKey, notNullValue());
+ assertThat(privateKey, equalTo(key));
+ }
+
+ public void testReadPKCS8EcKey() throws Exception {
+ Key key = getKeyFromKeystore("EC");
+ assertThat(key, notNullValue());
+ assertThat(key, instanceOf(PrivateKey.class));
+ PrivateKey privateKey = PemUtils.readPrivateKey(getDataPath("/certs/pem-utils/ec_key_pkcs8_plain.pem"), EMPTY_PASSWORD);
+ assertThat(privateKey, notNullValue());
+ assertThat(privateKey, equalTo(key));
+ }
+
+ public void testReadEncryptedPKCS8Key() throws Exception {
+ assumeFalse("Can't run in a FIPS JVM, PBE KeySpec is not available", inFipsJvm());
+ Key key = getKeyFromKeystore("RSA");
+ assertThat(key, notNullValue());
+ assertThat(key, instanceOf(PrivateKey.class));
+ PrivateKey privateKey = PemUtils.readPrivateKey(getDataPath
+ ("/certs/pem-utils/key_pkcs8_encrypted.pem"), TESTNODE_PASSWORD);
+ assertThat(privateKey, notNullValue());
+ assertThat(privateKey, equalTo(key));
+ }
+
+ public void testReadDESEncryptedPKCS1Key() throws Exception {
+ Key key = getKeyFromKeystore("RSA");
+ assertThat(key, notNullValue());
+ assertThat(key, instanceOf(PrivateKey.class));
+ PrivateKey privateKey = PemUtils.readPrivateKey(getDataPath("/certs/pem-utils/testnode.pem"), TESTNODE_PASSWORD);
+ assertThat(privateKey, notNullValue());
+ assertThat(privateKey, equalTo(key));
+ }
+
+ public void testReadAESEncryptedPKCS1Key() throws Exception {
+ Key key = getKeyFromKeystore("RSA");
+ assertThat(key, notNullValue());
+ assertThat(key, instanceOf(PrivateKey.class));
+ String bits = randomFrom("128", "192", "256");
+ PrivateKey privateKey = PemUtils.readPrivateKey(getDataPath("/certs/pem-utils/testnode-aes" + bits + ".pem"), TESTNODE_PASSWORD);
+
+ assertThat(privateKey, notNullValue());
+ assertThat(privateKey, equalTo(key));
+ }
+
+ public void testReadPKCS1RsaKey() throws Exception {
+ Key key = getKeyFromKeystore("RSA");
+ assertThat(key, notNullValue());
+ assertThat(key, instanceOf(PrivateKey.class));
+ PrivateKey privateKey = PemUtils.readPrivateKey(getDataPath("/certs/pem-utils/testnode-unprotected.pem"), TESTNODE_PASSWORD);
+
+ assertThat(privateKey, notNullValue());
+ assertThat(privateKey, equalTo(key));
+ }
+
+ public void testReadOpenSslDsaKey() throws Exception {
+ Key key = getKeyFromKeystore("DSA");
+ assertThat(key, notNullValue());
+ assertThat(key, instanceOf(PrivateKey.class));
+ PrivateKey privateKey = PemUtils.readPrivateKey(getDataPath("/certs/pem-utils/dsa_key_openssl_plain.pem"), EMPTY_PASSWORD);
+
+ assertThat(privateKey, notNullValue());
+ assertThat(privateKey, equalTo(key));
+ }
+
+ public void testReadOpenSslDsaKeyWithParams() throws Exception {
+ Key key = getKeyFromKeystore("DSA");
+ assertThat(key, notNullValue());
+ assertThat(key, instanceOf(PrivateKey.class));
+ PrivateKey privateKey = PemUtils.readPrivateKey(getDataPath("/certs/pem-utils/dsa_key_openssl_plain_with_params.pem"),
+ EMPTY_PASSWORD);
+
+ assertThat(privateKey, notNullValue());
+ assertThat(privateKey, equalTo(key));
+ }
+
+ public void testReadEncryptedOpenSslDsaKey() throws Exception {
+ Key key = getKeyFromKeystore("DSA");
+ assertThat(key, notNullValue());
+ assertThat(key, instanceOf(PrivateKey.class));
+ PrivateKey privateKey = PemUtils.readPrivateKey(getDataPath("/certs/pem-utils/dsa_key_openssl_encrypted.pem"), TESTNODE_PASSWORD);
+
+ assertThat(privateKey, notNullValue());
+ assertThat(privateKey, equalTo(key));
+ }
+
+ public void testReadOpenSslEcKey() throws Exception {
+ Key key = getKeyFromKeystore("EC");
+ assertThat(key, notNullValue());
+ assertThat(key, instanceOf(PrivateKey.class));
+ PrivateKey privateKey = PemUtils.readPrivateKey(getDataPath("/certs/pem-utils/ec_key_openssl_plain.pem"), EMPTY_PASSWORD);
+
+ assertThat(privateKey, notNullValue());
+ assertThat(privateKey, equalTo(key));
+ }
+
+ public void testReadOpenSslEcKeyWithParams() throws Exception {
+ Key key = getKeyFromKeystore("EC");
+ assertThat(key, notNullValue());
+ assertThat(key, instanceOf(PrivateKey.class));
+ PrivateKey privateKey = PemUtils.readPrivateKey(getDataPath("/certs/pem-utils/ec_key_openssl_plain_with_params.pem"),
+ EMPTY_PASSWORD);
+
+ assertThat(privateKey, notNullValue());
+ assertThat(privateKey, equalTo(key));
+ }
+
+ public void testReadEncryptedOpenSslEcKey() throws Exception {
+ Key key = getKeyFromKeystore("EC");
+ assertThat(key, notNullValue());
+ assertThat(key, instanceOf(PrivateKey.class));
+ PrivateKey privateKey = PemUtils.readPrivateKey(getDataPath("/certs/pem-utils/ec_key_openssl_encrypted.pem"), TESTNODE_PASSWORD);
+
+ assertThat(privateKey, notNullValue());
+ assertThat(privateKey, equalTo(key));
+ }
+
+ public void testReadUnsupportedKey() {
+ final Path path = getDataPath("/certs/pem-utils/key_unsupported.pem");
+ SslConfigException e = expectThrows(SslConfigException.class, () -> PemUtils.readPrivateKey(path, TESTNODE_PASSWORD));
+ assertThat(e.getMessage(), containsString("file does not contain a supported key format"));
+ assertThat(e.getMessage(), containsString(path.toAbsolutePath().toString()));
+ }
+
+ public void testReadPemCertificateAsKey() {
+ final Path path = getDataPath("/certs/pem-utils/testnode.crt");
+ SslConfigException e = expectThrows(SslConfigException.class, () -> PemUtils.readPrivateKey(path, TESTNODE_PASSWORD));
+ assertThat(e.getMessage(), containsString("file does not contain a supported key format"));
+ assertThat(e.getMessage(), containsString(path.toAbsolutePath().toString()));
+ }
+
+ public void testReadCorruptedKey() {
+ final Path path = getDataPath("/certs/pem-utils/corrupted_key_pkcs8_plain.pem");
+ SslConfigException e = expectThrows(SslConfigException.class, () -> PemUtils.readPrivateKey(path, TESTNODE_PASSWORD));
+ assertThat(e.getMessage(), containsString("private key"));
+ assertThat(e.getMessage(), containsString("cannot be parsed"));
+ assertThat(e.getMessage(), containsString(path.toAbsolutePath().toString()));
+ assertThat(e.getCause().getMessage(), containsString("PEM footer is invalid or missing"));
+ }
+
+ public void testReadEmptyFile() {
+ final Path path = getDataPath("/certs/pem-utils/empty.pem");
+ SslConfigException e = expectThrows(SslConfigException.class, () -> PemUtils.readPrivateKey(path, TESTNODE_PASSWORD));
+ assertThat(e.getMessage(), containsString("file is empty"));
+ assertThat(e.getMessage(), containsString(path.toAbsolutePath().toString()));
+ }
+
+ private Key getKeyFromKeystore(String algo) throws Exception {
+ Path keystorePath = getDataPath("/certs/pem-utils/testnode.jks");
+ try (InputStream in = Files.newInputStream(keystorePath)) {
+ KeyStore keyStore = KeyStore.getInstance("jks");
+ keyStore.load(in, "testnode".toCharArray());
+ return keyStore.getKey("testnode_" + algo, "testnode".toCharArray());
+ }
+ }
+}
diff --git a/libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/SslConfigurationLoaderTests.java b/libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/SslConfigurationLoaderTests.java
new file mode 100644
index 0000000000000..20a161b78fd5f
--- /dev/null
+++ b/libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/SslConfigurationLoaderTests.java
@@ -0,0 +1,220 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.common.ssl;
+
+import org.elasticsearch.common.settings.MockSecureSettings;
+import org.elasticsearch.common.settings.SecureString;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.test.ESTestCase;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.TrustManagerFactory;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+
+public class SslConfigurationLoaderTests extends ESTestCase {
+
+ private final Path certRoot = getDataPath("/certs/ca1/ca.crt").getParent().getParent();
+
+ private Settings settings;
+ private MockSecureSettings secureSettings = new MockSecureSettings();
+ private SslConfigurationLoader loader = new SslConfigurationLoader("test.ssl.") {
+ @Override
+ protected String getSettingAsString(String key) throws Exception {
+ return settings.get(key);
+ }
+
+ @Override
+ protected char[] getSecureSetting(String key) throws Exception {
+ final SecureString secStr = secureSettings.getString(key);
+ return secStr == null ? null : secStr.getChars();
+ }
+
+ @Override
+ protected List getSettingAsList(String key) throws Exception {
+ return settings.getAsList(key);
+ }
+ };
+
+ /**
+ * A test for non-trust, non-key configurations.
+ * These are straight forward and can all be tested together
+ */
+ public void testBasicConfigurationOptions() {
+ final SslVerificationMode verificationMode = randomFrom(SslVerificationMode.values());
+ final SslClientAuthenticationMode clientAuth = randomFrom(SslClientAuthenticationMode.values());
+ final String[] ciphers = generateRandomStringArray(8, 12, false, false);
+ final String[] protocols = generateRandomStringArray(4, 5, false, false);
+ settings = Settings.builder()
+ .put("test.ssl.verification_mode", verificationMode.name().toLowerCase(Locale.ROOT))
+ .put("test.ssl.client_authentication", clientAuth.name().toLowerCase(Locale.ROOT))
+ .putList("test.ssl.cipher_suites", ciphers)
+ .putList("test.ssl.supported_protocols", protocols)
+ .build();
+ final SslConfiguration configuration = loader.load(certRoot);
+ assertThat(configuration.getClientAuth(), is(clientAuth));
+ assertThat(configuration.getVerificationMode(), is(verificationMode));
+ assertThat(configuration.getCipherSuites(), equalTo(Arrays.asList(ciphers)));
+ assertThat(configuration.getSupportedProtocols(), equalTo(Arrays.asList(protocols)));
+ if (verificationMode == SslVerificationMode.NONE) {
+ final SslTrustConfig trustConfig = configuration.getTrustConfig();
+ assertThat(trustConfig, instanceOf(TrustEverythingConfig.class));
+ }
+ }
+
+ public void testLoadTrustFromPemCAs() {
+ settings = Settings.builder()
+ .putList("test.ssl.certificate_authorities", "ca1/ca.crt", "ca2/ca.crt", "ca3/ca.crt")
+ .build();
+ final SslConfiguration configuration = loader.load(certRoot);
+ final SslTrustConfig trustConfig = configuration.getTrustConfig();
+ assertThat(trustConfig, instanceOf(PemTrustConfig.class));
+ assertThat(trustConfig.getDependentFiles(),
+ containsInAnyOrder(getDataPath("/certs/ca1/ca.crt"), getDataPath("/certs/ca2/ca.crt"), getDataPath("/certs/ca3/ca.crt")));
+ assertThat(trustConfig.createTrustManager(), notNullValue());
+ }
+
+ public void testLoadTrustFromPkcs12() {
+ final Settings.Builder builder = Settings.builder().put("test.ssl.truststore.path", "ca-all/ca.p12");
+ if (randomBoolean()) {
+ builder.put("test.ssl.truststore.password", "p12-pass");
+ } else {
+ secureSettings.setString("test.ssl.truststore.secure_password", "p12-pass");
+ }
+ if (randomBoolean()) {
+ // If this is not set, the loader will guess from the extension
+ builder.put("test.ssl.truststore.type", "PKCS12");
+ }
+ if (randomBoolean()) {
+ builder.put("test.ssl.truststore.algorithm", TrustManagerFactory.getDefaultAlgorithm());
+ }
+ settings = builder.build();
+ final SslConfiguration configuration = loader.load(certRoot);
+ final SslTrustConfig trustConfig = configuration.getTrustConfig();
+ assertThat(trustConfig, instanceOf(StoreTrustConfig.class));
+ assertThat(trustConfig.getDependentFiles(), containsInAnyOrder(getDataPath("/certs/ca-all/ca.p12")));
+ assertThat(trustConfig.createTrustManager(), notNullValue());
+ }
+
+ public void testLoadTrustFromJKS() {
+ final Settings.Builder builder = Settings.builder().put("test.ssl.truststore.path", "ca-all/ca.jks");
+ if (randomBoolean()) {
+ builder.put("test.ssl.truststore.password", "jks-pass");
+ } else {
+ secureSettings.setString("test.ssl.truststore.secure_password", "jks-pass");
+ }
+ if (randomBoolean()) {
+ // If this is not set, the loader will guess from the extension
+ builder.put("test.ssl.truststore.type", "jks");
+ }
+ if (randomBoolean()) {
+ builder.put("test.ssl.truststore.algorithm", TrustManagerFactory.getDefaultAlgorithm());
+ }
+ settings = builder.build();
+ final SslConfiguration configuration = loader.load(certRoot);
+ final SslTrustConfig trustConfig = configuration.getTrustConfig();
+ assertThat(trustConfig, instanceOf(StoreTrustConfig.class));
+ assertThat(trustConfig.getDependentFiles(), containsInAnyOrder(getDataPath("/certs/ca-all/ca.jks")));
+ assertThat(trustConfig.createTrustManager(), notNullValue());
+ }
+
+ public void testLoadKeysFromPemFiles() {
+ final boolean usePassword = randomBoolean();
+ final boolean useLegacyPassword = usePassword && randomBoolean();
+ final String certName = usePassword ? "cert2" : "cert1";
+ final Settings.Builder builder = Settings.builder()
+ .put("test.ssl.certificate", certName + "/" + certName + ".crt")
+ .put("test.ssl.key", certName + "/" + certName + ".key");
+ if (usePassword) {
+ if (useLegacyPassword) {
+ builder.put("test.ssl.key_passphrase", "c2-pass");
+ } else {
+ secureSettings.setString("test.ssl.secure_key_passphrase", "c2-pass");
+ }
+ }
+ settings = builder.build();
+ final SslConfiguration configuration = loader.load(certRoot);
+ final SslKeyConfig keyConfig = configuration.getKeyConfig();
+ assertThat(keyConfig, instanceOf(PemKeyConfig.class));
+ assertThat(keyConfig.getDependentFiles(), containsInAnyOrder(
+ getDataPath("/certs/" + certName + "/" + certName + ".crt"), getDataPath("/certs/" + certName + "/" + certName + ".key")));
+ assertThat(keyConfig.createKeyManager(), notNullValue());
+ }
+
+ public void testLoadKeysFromPKCS12() {
+ final Settings.Builder builder = Settings.builder()
+ .put("test.ssl.keystore.path", "cert-all/certs.p12");
+ if (randomBoolean()) {
+ builder.put("test.ssl.keystore.password", "p12-pass");
+ } else {
+ secureSettings.setString("test.ssl.keystore.secure_password", "p12-pass");
+ }
+ if (randomBoolean()) {
+ // If this is not set, the loader will guess from the extension
+ builder.put("test.ssl.keystore.type", "PKCS12");
+ }
+ if (randomBoolean()) {
+ builder.put("test.ssl.keystore.algorithm", KeyManagerFactory.getDefaultAlgorithm());
+ }
+ settings = builder.build();
+ final SslConfiguration configuration = loader.load(certRoot);
+ final SslKeyConfig keyConfig = configuration.getKeyConfig();
+ assertThat(keyConfig, instanceOf(StoreKeyConfig.class));
+ assertThat(keyConfig.getDependentFiles(), containsInAnyOrder(getDataPath("/certs/cert-all/certs.p12")));
+ assertThat(keyConfig.createKeyManager(), notNullValue());
+ }
+
+ public void testLoadKeysFromJKS() {
+ assumeFalse("Can't use JKS/PKCS12 keystores in a FIPS JVM", inFipsJvm());
+ final Settings.Builder builder = Settings.builder()
+ .put("test.ssl.keystore.path", "cert-all/certs.jks");
+ if (randomBoolean()) {
+ builder.put("test.ssl.keystore.password", "jks-pass");
+ } else {
+ secureSettings.setString("test.ssl.keystore.secure_password", "jks-pass");
+ }
+ if (randomBoolean()) {
+ builder.put("test.ssl.keystore.key_password", "key-pass");
+ } else {
+ secureSettings.setString("test.ssl.keystore.secure_key_password", "key-pass");
+ }
+ if (randomBoolean()) {
+ // If this is not set, the loader will guess from the extension
+ builder.put("test.ssl.keystore.type", "jks");
+ }
+ if (randomBoolean()) {
+ builder.put("test.ssl.keystore.algorithm", KeyManagerFactory.getDefaultAlgorithm());
+ }
+ settings = builder.build();
+ final SslConfiguration configuration = loader.load(certRoot);
+ final SslKeyConfig keyConfig = configuration.getKeyConfig();
+ assertThat(keyConfig, instanceOf(StoreKeyConfig.class));
+ assertThat(keyConfig.getDependentFiles(), containsInAnyOrder(getDataPath("/certs/cert-all/certs.jks")));
+ assertThat(keyConfig.createKeyManager(), notNullValue());
+ }
+}
diff --git a/libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/SslConfigurationTests.java b/libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/SslConfigurationTests.java
new file mode 100644
index 0000000000000..b8986462ebe47
--- /dev/null
+++ b/libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/SslConfigurationTests.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.common.ssl;
+
+import org.elasticsearch.test.ESTestCase;
+import org.elasticsearch.test.EqualsHashCodeTestUtils;
+import org.hamcrest.Matchers;
+import org.mockito.Mockito;
+
+import javax.net.ssl.SSLContext;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.elasticsearch.common.ssl.SslConfigurationLoader.DEFAULT_CIPHERS;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+
+public class SslConfigurationTests extends ESTestCase {
+
+ static final String[] VALID_PROTOCOLS = { "TLSv1.2", "TLSv1.1", "TLSv1", "SSLv3", "SSLv2Hello", "SSLv2" };
+
+ public void testBasicConstruction() {
+ final SslTrustConfig trustConfig = Mockito.mock(SslTrustConfig.class);
+ Mockito.when(trustConfig.toString()).thenReturn("TEST-TRUST");
+ final SslKeyConfig keyConfig = Mockito.mock(SslKeyConfig.class);
+ Mockito.when(keyConfig.toString()).thenReturn("TEST-KEY");
+ final SslVerificationMode verificationMode = randomFrom(SslVerificationMode.values());
+ final SslClientAuthenticationMode clientAuth = randomFrom(SslClientAuthenticationMode.values());
+ final List ciphers = randomSubsetOf(randomIntBetween(1, DEFAULT_CIPHERS.size()), DEFAULT_CIPHERS);
+ final List protocols = randomSubsetOf(randomIntBetween(1, 4), VALID_PROTOCOLS);
+ final SslConfiguration configuration =
+ new SslConfiguration(trustConfig, keyConfig, verificationMode, clientAuth, ciphers, protocols);
+
+ assertThat(configuration.getTrustConfig(), is(trustConfig));
+ assertThat(configuration.getKeyConfig(), is(keyConfig));
+ assertThat(configuration.getVerificationMode(), is(verificationMode));
+ assertThat(configuration.getClientAuth(), is(clientAuth));
+ assertThat(configuration.getCipherSuites(), is(ciphers));
+ assertThat(configuration.getSupportedProtocols(), is(protocols));
+
+ assertThat(configuration.toString(), containsString("TEST-TRUST"));
+ assertThat(configuration.toString(), containsString("TEST-KEY"));
+ assertThat(configuration.toString(), containsString(verificationMode.toString()));
+ assertThat(configuration.toString(), containsString(clientAuth.toString()));
+ assertThat(configuration.toString(), containsString(randomFrom(ciphers)));
+ assertThat(configuration.toString(), containsString(randomFrom(protocols)));
+ }
+
+ public void testEqualsAndHashCode() {
+ final SslTrustConfig trustConfig = Mockito.mock(SslTrustConfig.class);
+ final SslKeyConfig keyConfig = Mockito.mock(SslKeyConfig.class);
+ final SslVerificationMode verificationMode = randomFrom(SslVerificationMode.values());
+ final SslClientAuthenticationMode clientAuth = randomFrom(SslClientAuthenticationMode.values());
+ final List ciphers = randomSubsetOf(randomIntBetween(1, DEFAULT_CIPHERS.size() - 1), DEFAULT_CIPHERS);
+ final List protocols = randomSubsetOf(randomIntBetween(1, VALID_PROTOCOLS.length - 1), VALID_PROTOCOLS);
+ final SslConfiguration configuration =
+ new SslConfiguration(trustConfig, keyConfig, verificationMode, clientAuth, ciphers, protocols);
+
+ EqualsHashCodeTestUtils.checkEqualsAndHashCode(configuration,
+ orig -> new SslConfiguration(orig.getTrustConfig(), orig.getKeyConfig(), orig.getVerificationMode(), orig.getClientAuth(),
+ orig.getCipherSuites(), orig.getSupportedProtocols()),
+ orig -> {
+ switch (randomIntBetween(1, 4)) {
+ case 1:
+ return new SslConfiguration(orig.getTrustConfig(), orig.getKeyConfig(),
+ randomValueOtherThan(orig.getVerificationMode(), () -> randomFrom(SslVerificationMode.values())),
+ orig.getClientAuth(), orig.getCipherSuites(), orig.getSupportedProtocols());
+ case 2:
+ return new SslConfiguration(orig.getTrustConfig(), orig.getKeyConfig(), orig.getVerificationMode(),
+ randomValueOtherThan(orig.getClientAuth(), () -> randomFrom(SslClientAuthenticationMode.values())),
+ orig.getCipherSuites(), orig.getSupportedProtocols());
+ case 3:
+ return new SslConfiguration(orig.getTrustConfig(), orig.getKeyConfig(),
+ orig.getVerificationMode(), orig.getClientAuth(), DEFAULT_CIPHERS, orig.getSupportedProtocols());
+ case 4:
+ default:
+ return new SslConfiguration(orig.getTrustConfig(), orig.getKeyConfig(), orig.getVerificationMode(),
+ orig.getClientAuth(), orig.getCipherSuites(), Arrays.asList(VALID_PROTOCOLS));
+ }
+ });
+ }
+
+ public void testDependentFiles() {
+ final SslTrustConfig trustConfig = Mockito.mock(SslTrustConfig.class);
+ final SslKeyConfig keyConfig = Mockito.mock(SslKeyConfig.class);
+ final SslConfiguration configuration = new SslConfiguration(trustConfig, keyConfig,
+ randomFrom(SslVerificationMode.values()), randomFrom(SslClientAuthenticationMode.values()),
+ DEFAULT_CIPHERS, SslConfigurationLoader.DEFAULT_PROTOCOLS);
+
+ final Path dir = createTempDir();
+ final Path file1 = dir.resolve(randomAlphaOfLength(1) + ".pem");
+ final Path file2 = dir.resolve(randomAlphaOfLength(2) + ".pem");
+ final Path file3 = dir.resolve(randomAlphaOfLength(3) + ".pem");
+ final Path file4 = dir.resolve(randomAlphaOfLength(4) + ".pem");
+ final Path file5 = dir.resolve(randomAlphaOfLength(5) + ".pem");
+
+ Mockito.when(trustConfig.getDependentFiles()).thenReturn(Arrays.asList(file1, file2));
+ Mockito.when(keyConfig.getDependentFiles()).thenReturn(Arrays.asList(file3, file4, file5));
+ assertThat(configuration.getDependentFiles(), Matchers.containsInAnyOrder(file1, file2, file3, file4, file5));
+ }
+
+ public void testBuildSslContext() {
+ final SslTrustConfig trustConfig = Mockito.mock(SslTrustConfig.class);
+ final SslKeyConfig keyConfig = Mockito.mock(SslKeyConfig.class);
+ final String protocol = randomFrom(SslConfigurationLoader.DEFAULT_PROTOCOLS);
+ final SslConfiguration configuration = new SslConfiguration(trustConfig, keyConfig,
+ randomFrom(SslVerificationMode.values()), randomFrom(SslClientAuthenticationMode.values()),
+ DEFAULT_CIPHERS, Collections.singletonList(protocol));
+
+ Mockito.when(trustConfig.createTrustManager()).thenReturn(null);
+ Mockito.when(keyConfig.createKeyManager()).thenReturn(null);
+ final SSLContext sslContext = configuration.createSslContext();
+ assertThat(sslContext.getProtocol(), equalTo(protocol));
+
+ Mockito.verify(trustConfig).createTrustManager();
+ Mockito.verify(keyConfig).createKeyManager();
+ Mockito.verifyNoMoreInteractions(trustConfig, keyConfig);
+ }
+
+}
diff --git a/libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/StoreKeyConfigTests.java b/libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/StoreKeyConfigTests.java
new file mode 100644
index 0000000000000..f5fcc16c6a023
--- /dev/null
+++ b/libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/StoreKeyConfigTests.java
@@ -0,0 +1,215 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.common.ssl;
+
+import org.elasticsearch.test.ESTestCase;
+import org.hamcrest.Matchers;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.X509ExtendedKeyManager;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.security.GeneralSecurityException;
+import java.security.PrivateKey;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+
+import static org.hamcrest.Matchers.arrayWithSize;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.iterableWithSize;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
+
+public class StoreKeyConfigTests extends ESTestCase {
+
+ private static final int IP_NAME = 7;
+ private static final int DNS_NAME = 2;
+
+ private static final char[] P12_PASS = "p12-pass".toCharArray();
+ private static final char[] JKS_PASS = "jks-pass".toCharArray();
+
+ public void testLoadSingleKeyPKCS12() throws Exception {
+ assumeFalse("Can't use JKS/PKCS12 keystores in a FIPS JVM", inFipsJvm());
+ final Path p12 = getDataPath("/certs/cert1/cert1.p12");
+ final StoreKeyConfig keyConfig = new StoreKeyConfig(p12, P12_PASS, "PKCS12", P12_PASS, KeyManagerFactory.getDefaultAlgorithm());
+ assertThat(keyConfig.getDependentFiles(), Matchers.containsInAnyOrder(p12));
+ assertKeysLoaded(keyConfig, "cert1");
+ }
+
+ public void testLoadMultipleKeyPKCS12() throws Exception {
+ assumeFalse("Can't use JKS/PKCS12 keystores in a FIPS JVM", inFipsJvm());
+ final Path p12 = getDataPath("/certs/cert-all/certs.p12");
+ final StoreKeyConfig keyConfig = new StoreKeyConfig(p12, P12_PASS, "PKCS12", P12_PASS, KeyManagerFactory.getDefaultAlgorithm());
+ assertThat(keyConfig.getDependentFiles(), Matchers.containsInAnyOrder(p12));
+ assertKeysLoaded(keyConfig, "cert1", "cert2");
+ }
+
+ public void testLoadMultipleKeyJksWithSeparateKeyPassword() throws Exception {
+ assumeFalse("Can't use JKS/PKCS12 keystores in a FIPS JVM", inFipsJvm());
+ final Path jks = getDataPath("/certs/cert-all/certs.jks");
+ final StoreKeyConfig keyConfig = new StoreKeyConfig(jks, JKS_PASS, "jks", "key-pass".toCharArray(),
+ KeyManagerFactory.getDefaultAlgorithm());
+ assertThat(keyConfig.getDependentFiles(), Matchers.containsInAnyOrder(jks));
+ assertKeysLoaded(keyConfig, "cert1", "cert2");
+ }
+
+ public void testKeyManagerFailsWithIncorrectStorePassword() throws Exception {
+ assumeFalse("Can't use JKS/PKCS12 keystores in a FIPS JVM", inFipsJvm());
+ final Path jks = getDataPath("/certs/cert-all/certs.jks");
+ final StoreKeyConfig keyConfig = new StoreKeyConfig(jks, P12_PASS, "jks", "key-pass".toCharArray(),
+ KeyManagerFactory.getDefaultAlgorithm());
+ assertThat(keyConfig.getDependentFiles(), Matchers.containsInAnyOrder(jks));
+ assertPasswordIsIncorrect(keyConfig, jks);
+ }
+
+ public void testKeyManagerFailsWithIncorrectKeyPassword() throws Exception {
+ assumeFalse("Can't use JKS/PKCS12 keystores in a FIPS JVM", inFipsJvm());
+ final Path jks = getDataPath("/certs/cert-all/certs.jks");
+ final StoreKeyConfig keyConfig = new StoreKeyConfig(jks, JKS_PASS, "jks", JKS_PASS, KeyManagerFactory.getDefaultAlgorithm());
+ assertThat(keyConfig.getDependentFiles(), Matchers.containsInAnyOrder(jks));
+ assertPasswordIsIncorrect(keyConfig, jks);
+ }
+
+ public void testKeyManagerFailsWithMissingKeystoreFile() throws Exception {
+ assumeFalse("Can't use JKS/PKCS12 keystores in a FIPS JVM", inFipsJvm());
+ final Path path = getDataPath("/certs/cert-all/certs.jks").getParent().resolve("dne.jks");
+ final StoreKeyConfig keyConfig = new StoreKeyConfig(path, JKS_PASS, "jks", JKS_PASS, KeyManagerFactory.getDefaultAlgorithm());
+ assertThat(keyConfig.getDependentFiles(), Matchers.containsInAnyOrder(path));
+ assertFileNotFound(keyConfig, path);
+ }
+
+ public void testMissingKeyEntriesFailsWithMeaningfulMessage() throws Exception {
+ assumeFalse("Can't use JKS/PKCS12 keystores in a FIPS JVM", inFipsJvm());
+ final Path ks;
+ final char[] password;
+ final String type;
+ if (randomBoolean()) {
+ type = "PKCS12";
+ ks = getDataPath("/certs/ca-all/ca.p12");
+ password = P12_PASS;
+ } else {
+ type = "jks";
+ ks = getDataPath("/certs/ca-all/ca.jks");
+ password = JKS_PASS;
+ }
+ final StoreKeyConfig keyConfig = new StoreKeyConfig(ks, password, type, password, KeyManagerFactory.getDefaultAlgorithm());
+ assertThat(keyConfig.getDependentFiles(), Matchers.containsInAnyOrder(ks));
+ assertNoPrivateKeyEntries(keyConfig, ks);
+ }
+
+ public void testKeyConfigReloadsFileContents() throws Exception {
+ assumeFalse("Can't use JKS/PKCS12 keystores in a FIPS JVM", inFipsJvm());
+ final Path cert1 = getDataPath("/certs/cert1/cert1.p12");
+ final Path cert2 = getDataPath("/certs/cert2/cert2.p12");
+ final Path jks = getDataPath("/certs/cert-all/certs.jks");
+
+ final Path p12 = createTempFile("cert", ".p12");
+
+ final StoreKeyConfig keyConfig = new StoreKeyConfig(p12, P12_PASS, "PKCS12", P12_PASS, KeyManagerFactory.getDefaultAlgorithm());
+
+ Files.copy(cert1, p12, StandardCopyOption.REPLACE_EXISTING);
+ assertKeysLoaded(keyConfig, "cert1");
+ assertKeysNotLoaded(keyConfig, "cert2");
+
+ Files.copy(jks, p12, StandardCopyOption.REPLACE_EXISTING);
+ // Because (a) cannot load a JKS as a PKCS12 & (b) the password is wrong.
+ assertBadKeyStore(keyConfig, p12);
+
+ Files.copy(cert2, p12, StandardCopyOption.REPLACE_EXISTING);
+ assertKeysLoaded(keyConfig, "cert2");
+ assertKeysNotLoaded(keyConfig, "cert1");
+
+ Files.delete(p12);
+ assertFileNotFound(keyConfig, p12);
+ }
+
+ private void assertKeysLoaded(StoreKeyConfig keyConfig, String... names) throws CertificateParsingException {
+ final X509ExtendedKeyManager keyManager = keyConfig.createKeyManager();
+ assertThat(keyManager, notNullValue());
+
+ for (String name : names) {
+ final PrivateKey privateKey = keyManager.getPrivateKey(name);
+ assertThat(privateKey, notNullValue());
+ assertThat(privateKey.getAlgorithm(), is("RSA"));
+
+ final X509Certificate[] chain = keyManager.getCertificateChain(name);
+ assertThat(chain, notNullValue());
+ assertThat(chain, arrayWithSize(1));
+ final X509Certificate certificate = chain[0];
+ assertThat(certificate.getIssuerDN().getName(), is("CN=Test CA 1"));
+ assertThat(certificate.getSubjectDN().getName(), is("CN=" + name));
+ assertThat(certificate.getSubjectAlternativeNames(), iterableWithSize(2));
+ assertThat(certificate.getSubjectAlternativeNames(), containsInAnyOrder(
+ Arrays.asList(DNS_NAME, "localhost"),
+ Arrays.asList(IP_NAME, "127.0.0.1")
+ ));
+ }
+ }
+
+ private void assertKeysNotLoaded(StoreKeyConfig keyConfig, String... names) throws CertificateParsingException {
+ final X509ExtendedKeyManager keyManager = keyConfig.createKeyManager();
+ assertThat(keyManager, notNullValue());
+
+ for (String name : names) {
+ final PrivateKey privateKey = keyManager.getPrivateKey(name);
+ assertThat(privateKey, nullValue());
+ }
+ }
+
+ private void assertPasswordIsIncorrect(StoreKeyConfig keyConfig, Path key) {
+ final SslConfigException exception = expectThrows(SslConfigException.class, keyConfig::createKeyManager);
+ assertThat(exception.getMessage(), containsString("keystore"));
+ assertThat(exception.getMessage(), containsString(key.toAbsolutePath().toString()));
+ if (exception.getCause() instanceof GeneralSecurityException) {
+ assertThat(exception.getMessage(), containsString("password"));
+ } else {
+ assertThat(exception.getCause(), instanceOf(IOException.class));
+ assertThat(exception.getCause().getMessage(), containsString("password"));
+ }
+ }
+
+ private void assertBadKeyStore(StoreKeyConfig keyConfig, Path key) {
+ final SslConfigException exception = expectThrows(SslConfigException.class, keyConfig::createKeyManager);
+ assertThat(exception.getMessage(), containsString("keystore"));
+ assertThat(exception.getMessage(), containsString(key.toAbsolutePath().toString()));
+ assertThat(exception.getCause(), instanceOf(IOException.class));
+ }
+
+ private void assertFileNotFound(StoreKeyConfig keyConfig, Path file) {
+ final SslConfigException exception = expectThrows(SslConfigException.class, keyConfig::createKeyManager);
+ assertThat(exception.getMessage(), containsString("keystore"));
+ assertThat(exception.getMessage(), containsString(file.toAbsolutePath().toString()));
+ assertThat(exception.getMessage(), containsString("does not exist"));
+ assertThat(exception.getCause(), nullValue());
+ }
+
+ private void assertNoPrivateKeyEntries(StoreKeyConfig keyConfig, Path file) {
+ final SslConfigException exception = expectThrows(SslConfigException.class, keyConfig::createKeyManager);
+ assertThat(exception.getMessage(), containsString("keystore"));
+ assertThat(exception.getMessage(), containsString(file.toAbsolutePath().toString()));
+ assertThat(exception.getMessage(), containsString("does not contain a private key entry"));
+ }
+}
diff --git a/libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/StoreTrustConfigTests.java b/libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/StoreTrustConfigTests.java
new file mode 100644
index 0000000000000..207bf9179415f
--- /dev/null
+++ b/libs/ssl-config/src/test/java/org/elasticsearch/common/ssl/StoreTrustConfigTests.java
@@ -0,0 +1,169 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.common.ssl;
+
+import org.elasticsearch.test.ESTestCase;
+import org.hamcrest.Matchers;
+
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509ExtendedTrustManager;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.StandardOpenOption;
+import java.security.Principal;
+import java.security.cert.X509Certificate;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.nullValue;
+
+public class StoreTrustConfigTests extends ESTestCase {
+
+ private static final char[] P12_PASS = "p12-pass".toCharArray();
+ private static final char[] JKS_PASS = "jks-pass".toCharArray();
+ private static final String DEFAULT_ALGORITHM = TrustManagerFactory.getDefaultAlgorithm();
+
+ public void testBuildTrustConfigFromPKCS12() throws Exception {
+ assumeFalse("Can't use JKS/PKCS12 keystores in a FIPS JVM", inFipsJvm());
+ final Path ks = getDataPath("/certs/ca1/ca.p12");
+ final StoreTrustConfig trustConfig = new StoreTrustConfig(ks, P12_PASS, "PKCS12", DEFAULT_ALGORITHM);
+ assertThat(trustConfig.getDependentFiles(), Matchers.containsInAnyOrder(ks));
+ assertCertificateChain(trustConfig, "CN=Test CA 1");
+ }
+
+ public void testBuildTrustConfigFromJKS() throws Exception {
+ assumeFalse("Can't use JKS/PKCS12 keystores in a FIPS JVM", inFipsJvm());
+ final Path ks = getDataPath("/certs/ca-all/ca.jks");
+ final StoreTrustConfig trustConfig = new StoreTrustConfig(ks, JKS_PASS, "jks", DEFAULT_ALGORITHM);
+ assertThat(trustConfig.getDependentFiles(), Matchers.containsInAnyOrder(ks));
+ assertCertificateChain(trustConfig, "CN=Test CA 1", "CN=Test CA 2", "CN=Test CA 3");
+ }
+
+ public void testBadKeyStoreFormatFails() throws Exception {
+ assumeFalse("Can't use JKS/PKCS12 keystores in a FIPS JVM", inFipsJvm());
+ final Path ks = createTempFile("ca", ".p12");
+ Files.write(ks, randomByteArrayOfLength(128), StandardOpenOption.APPEND);
+ final StoreTrustConfig trustConfig = new StoreTrustConfig(ks, new char[0], randomFrom("PKCS12", "jks"), DEFAULT_ALGORITHM);
+ assertThat(trustConfig.getDependentFiles(), Matchers.containsInAnyOrder(ks));
+ assertInvalidFileFormat(trustConfig, ks);
+ }
+
+ public void testMissingKeyStoreFailsWithMeaningfulMessage() throws Exception {
+ assumeFalse("Can't use JKS/PKCS12 keystores in a FIPS JVM", inFipsJvm());
+ final Path ks = getDataPath("/certs/ca-all/ca.p12").getParent().resolve("keystore.dne");
+ final StoreTrustConfig trustConfig = new StoreTrustConfig(ks, new char[0], randomFrom("PKCS12", "jks"), DEFAULT_ALGORITHM);
+ assertThat(trustConfig.getDependentFiles(), Matchers.containsInAnyOrder(ks));
+ assertFileNotFound(trustConfig, ks);
+ }
+
+ public void testIncorrectPasswordFailsWithMeaningfulMessage() throws Exception {
+ final Path ks = getDataPath("/certs/ca1/ca.p12");
+ final StoreTrustConfig trustConfig = new StoreTrustConfig(ks, new char[0], "PKCS12", DEFAULT_ALGORITHM);
+ assertThat(trustConfig.getDependentFiles(), Matchers.containsInAnyOrder(ks));
+ assertPasswordIsIncorrect(trustConfig, ks);
+ }
+
+ public void testMissingTrustEntriesFailsWithMeaningfulMessage() throws Exception {
+ assumeFalse("Can't use JKS/PKCS12 keystores in a FIPS JVM", inFipsJvm());
+ final Path ks;
+ final char[] password;
+ final String type;
+ if (randomBoolean()) {
+ type = "PKCS12";
+ ks = getDataPath("/certs/cert-all/certs.p12");
+ password = P12_PASS;
+ } else {
+ type = "jks";
+ ks = getDataPath("/certs/cert-all/certs.jks");
+ password = JKS_PASS;
+ }
+ final StoreTrustConfig trustConfig = new StoreTrustConfig(ks, password, type, DEFAULT_ALGORITHM);
+ assertThat(trustConfig.getDependentFiles(), Matchers.containsInAnyOrder(ks));
+ assertNoCertificateEntries(trustConfig, ks);
+ }
+
+ public void testTrustConfigReloadsKeysStoreContents() throws Exception {
+ assumeFalse("Can't use JKS/PKCS12 keystores in a FIPS JVM", inFipsJvm());
+ final Path ks1 = getDataPath("/certs/ca1/ca.p12");
+ final Path ksAll = getDataPath("/certs/ca-all/ca.p12");
+
+ final Path ks = createTempFile("ca", "p12");
+
+ final StoreTrustConfig trustConfig = new StoreTrustConfig(ks, P12_PASS, "PKCS12", DEFAULT_ALGORITHM);
+
+ Files.copy(ks1, ks, StandardCopyOption.REPLACE_EXISTING);
+ assertCertificateChain(trustConfig, "CN=Test CA 1");
+
+ Files.delete(ks);
+ assertFileNotFound(trustConfig, ks);
+
+ Files.write(ks, randomByteArrayOfLength(128), StandardOpenOption.CREATE);
+ assertInvalidFileFormat(trustConfig, ks);
+
+ Files.copy(ksAll, ks, StandardCopyOption.REPLACE_EXISTING);
+ assertCertificateChain(trustConfig, "CN=Test CA 1", "CN=Test CA 2", "CN=Test CA 3");
+ }
+
+ private void assertCertificateChain(StoreTrustConfig trustConfig, String... caNames) {
+ final X509ExtendedTrustManager trustManager = trustConfig.createTrustManager();
+ final X509Certificate[] issuers = trustManager.getAcceptedIssuers();
+ final Set issuerNames = Stream.of(issuers)
+ .map(X509Certificate::getSubjectDN)
+ .map(Principal::getName)
+ .collect(Collectors.toSet());
+
+ assertThat(issuerNames, Matchers.containsInAnyOrder(caNames));
+ }
+
+ private void assertInvalidFileFormat(StoreTrustConfig trustConfig, Path file) {
+ final SslConfigException exception = expectThrows(SslConfigException.class, trustConfig::createTrustManager);
+ assertThat(exception.getMessage(), Matchers.containsString("cannot read"));
+ assertThat(exception.getMessage(), Matchers.containsString("keystore"));
+ assertThat(exception.getMessage(), Matchers.containsString(file.toAbsolutePath().toString()));
+ assertThat(exception.getCause(), Matchers.instanceOf(IOException.class));
+ }
+
+ private void assertFileNotFound(StoreTrustConfig trustConfig, Path file) {
+ final SslConfigException exception = expectThrows(SslConfigException.class, trustConfig::createTrustManager);
+ assertThat(exception.getMessage(), Matchers.containsString("file does not exist"));
+ assertThat(exception.getMessage(), Matchers.containsString("keystore"));
+ assertThat(exception.getMessage(), Matchers.containsString(file.toAbsolutePath().toString()));
+ assertThat(exception.getCause(), nullValue());
+ }
+
+ private void assertPasswordIsIncorrect(StoreTrustConfig trustConfig, Path key) {
+ final SslConfigException exception = expectThrows(SslConfigException.class, trustConfig::createTrustManager);
+ assertThat(exception.getMessage(), containsString("keystore"));
+ assertThat(exception.getMessage(), containsString(key.toAbsolutePath().toString()));
+ assertThat(exception.getMessage(), containsString("password"));
+ }
+
+ private void assertNoCertificateEntries(StoreTrustConfig trustConfig, Path file) {
+ final SslConfigException exception = expectThrows(SslConfigException.class, trustConfig::createTrustManager);
+ assertThat(exception.getMessage(), Matchers.containsString("does not contain any trusted certificate entries"));
+ assertThat(exception.getMessage(), Matchers.containsString("truststore"));
+ assertThat(exception.getMessage(), Matchers.containsString(file.toAbsolutePath().toString()));
+ }
+
+}
diff --git a/libs/ssl-config/src/test/resources/certs/README.txt b/libs/ssl-config/src/test/resources/certs/README.txt
new file mode 100644
index 0000000000000..a04a31011b4dd
--- /dev/null
+++ b/libs/ssl-config/src/test/resources/certs/README.txt
@@ -0,0 +1,75 @@
+#!/bin/bash
+#
+# This is README describes how the certificates in this directory were created.
+# This file can also be executed as a script
+#
+
+# 1. Create first CA PEM ("ca1")
+
+elasticsearch-certutil ca --pem --out ca1.zip --days 9999 --ca-dn "CN=Test CA 1"
+unzip ca1.zip
+mv ca ca1
+
+# 2. Create first CA PEM ("ca2")
+
+elasticsearch-certutil ca --pem --out ca2.zip --days 9999 --ca-dn "CN=Test CA 2"
+unzip ca2.zip
+mv ca ca2
+
+# 3. Create first CA PEM ("ca3")
+
+elasticsearch-certutil ca --pem --out ca3.zip --days 9999 --ca-dn "CN=Test CA 3"
+unzip ca3.zip
+mv ca ca3
+
+# 4. Create "cert1" PEM
+
+elasticsearch-certutil cert --pem --out cert1.zip --name cert1 --ip 127.0.0.1 --dns localhost --days 9999 --ca-key ca1/ca.key --ca-cert ca1/ca.crt
+unzip cert1.zip
+
+# 5. Create "cert2" PEM (same as cert1, but with a password)
+
+elasticsearch-certutil cert --pem --out cert2.zip --name cert2 --ip 127.0.0.1 --dns localhost --days 9999 --ca-key ca1/ca.key --ca-cert ca1/ca.crt --pass "c2-pass"
+unzip cert2.zip
+
+# 6. Convert CAs to PKCS#12
+
+for n in 1 2 3
+do
+ keytool -importcert -file ca${n}/ca.crt -alias ca -keystore ca${n}/ca.p12 -storetype PKCS12 -storepass p12-pass -v
+ keytool -importcert -file ca${n}/ca.crt -alias ca${n} -keystore ca-all/ca.p12 -storetype PKCS12 -storepass p12-pass -v
+done
+
+# 7. Convert CAs to JKS
+
+for n in 1 2 3
+do
+ keytool -importcert -file ca${n}/ca.crt -alias ca${n} -keystore ca-all/ca.jks -storetype jks -storepass jks-pass -v
+done
+
+# 8. Convert Certs to PKCS#12
+
+for Cert in cert1 cert2
+do
+ openssl pkcs12 -export -out $Cert/$Cert.p12 -inkey $Cert/$Cert.key -in $Cert/$Cert.crt -name $Cert -passout pass:p12-pass
+done
+
+# 9. Import Certs into single PKCS#12 keystore
+
+for Cert in cert1 cert2
+do
+ keytool -importkeystore -noprompt \
+ -srckeystore $Cert/$Cert.p12 -srcstoretype PKCS12 -srcstorepass p12-pass \
+ -destkeystore cert-all/certs.p12 -deststoretype PKCS12 -deststorepass p12-pass
+done
+
+# 10. Import Certs into single JKS keystore with separate key-password
+
+for Cert in cert1 cert2
+do
+ keytool -importkeystore -noprompt \
+ -srckeystore $Cert/$Cert.p12 -srcstoretype PKCS12 -srcstorepass p12-pass \
+ -destkeystore cert-all/certs.jks -deststoretype jks -deststorepass jks-pass
+ keytool -keypasswd -keystore cert-all/certs.jks -alias $Cert -keypass p12-pass -new key-pass -storepass jks-pass
+done
+
diff --git a/libs/ssl-config/src/test/resources/certs/ca-all/ca.jks b/libs/ssl-config/src/test/resources/certs/ca-all/ca.jks
new file mode 100644
index 0000000000000000000000000000000000000000..0c00ff13d906ff3cb4e584dea41e702d31eab693
GIT binary patch
literal 2460
zcma*oc{tST9suz9%|cTRV$3AQQjx9cXQzX5tu;7@>{~<;5o1Y0V{Buo7^5MMov|A_
zWhavCU~S0`5QGh01=QG_LV;u$5+nnQIWQ;yg`xw`v{}1nq>IMi**Irc@ry+KwMWxToDw-q7*bh1yBi8Q_@gT
zw8Ns5RY4U+@ZaGNR*`}ITVD?#ATTn(3)%dlolr6WK(Zz}l6{tFb~G2T!_KoKrzpxl
zVvoN>0Zr`$Wdhw&ToZmRQd@(M@sjeeXi5iO9QlMdEPi#(E?mCH{0%2ed!(0MV#K8^
z;fK}c5>q{Xn=96Vj%XsJ*9S1wrl=cfpR}&DC!$`T&8%r&m=aAm-{j$sl6LU-Vmv^8sg$T$9g7<`Tne-b+h7?;vzcH$`OWy5-53XtZ(3EBNp-
z@>HKIl*o;M6;`ah)pXFU_9I$=~>s$DfK-tc%8^ek2{SSUQr^qr)5^&Nq%zA{Rc^5d|&?Nxct^vFwNLYaP6`6E}o
z*FZi81wWO(**Z$8Zf8knWHKdl?bH|O@{p-@ViS*VfE7Niuz9AS_dPpoy2-cUj&Pp$
zDf3uQ;6%k+5-L28eNHH->cR3#Ls`@(QT#3V{CIFI*IINds(4s;d=+?ZXjIuvAZ7&U
ziQ7tBx%*3$jPXBl8xM{3Y7!@H_$#<~m{|EV)Pzjf5Qesc5_@UVwY>yBCuq!n+4~_w
zU`;EK{vDKU6MhDYEx1%=)hKuEfwjZRtQDclcBtEG4!u
z`yK6?D-*nLzlF<8*~c_X-ami7K;1;yBR*NE(a;N}uSu~dw5h&*?I)Re^Kfi5WKz&m
zE=vu!@Jb)QX7gg08TSa^K1DjSb1ckdfFwxW<>2|7*|YhqJ90jC-IKP9eHoM1mCn1Q
zqlb@KEInu>^6!bWZPNr9t9R+`GNe+SnIXBr{!@7&y3@EPijqwfmfqlWm^7_wM82c;
zFeB@_{5vLows*l1vZA+MV{-%3vVj1hqjZU-zwGE84AW{*8mCv?Q<9V+KiPT;A`{;SHQc+kP8zy%yQi&H4P
zrV9jlePY`~?kfXa%G5)E?C#oiVMq3*PWH=
zGGgiFXbdGFrrEXM9bo{-73zMU+z~Ty38&OS5Ue)3it#f4S?|{(_xF+
z9!dR_H_5qN=A0aQn)J(3g&>!zu%%rsm8)0bk4@=LbMv?-sG`3*jZaW6x+4W
zIo;%NZz6v(Z*lR-88tux%I&h<55CKbq;pCH;*%*J1RxK#A8j?ps9^=^kv?wNR4uBu
zz#wb4X2)A{)vU!x0N(zrrF;QDV^BI`6dwXP$N_gY7HjUV?RYe;De`R7<7`?K;u9ua
zr5&bE?ru2+%COiFz3RjD{i^Giag%Tj0vfdpoDo)_Dm2FRS`D!Lcs
zdON8K%x@=NSt180d6Tv$oLla<98yvOQ~8KTV(^OQ&~dNp#9@ZRjHwmD
zu<%ooxx}oN3>e;AE>g=7Mjvx%zA{&eC$8R&;mTJB+q~!mO`M&GG6WQJ6wdtzpUE|?
z^Cw8G@C0N$D^`kAkX|3{oZ%^6b`aK&Rq+$o8;bgqDa6407YoIjd7VSc&QT_wO#6bV
z$~~EBu+b8cO+GqgOiBI>(5(n9VQWsJ`8k-h@RIS`;2J)&rnn+hN-%Ke*(g(7TZYRcn-A>Rc0NzRI9Y(ol^5Y9U;TJ0;{A{m6DC9e9l1n-*-G4?GfH}J=W{5b|G8O
z0~V}EJLEdZ5vs^RB-;4=QEVq^r-`21cdu?gLt;~q&dACep$7YTuNqY->LBwbz9zbF
zvc~kauu5Xc@Mey_Nk7IqNw+@BXNaOhruPpLr%m9B+uW~DO`3)pcgq*FYWf>T?-8q}
zMSj|4&oLjOHv6W@*ZArE7g73)!K>#g0Mc%bVN02F)V3<32*_B0jtC1mIqw|ml>`ud
zxZfd&Uw~(zih6WBb>KM^GYrObt@4;NkTnmC2v~?}{-m7Xx}8Cxzbo^Dq61q)@d|~h
z!@IEN6VXle1j<>e2o6L3g2q+-vu}=#0>}45hQ8lPoFf*5&DbMvj*Js=Zg-Z7bwvrB
zcfIBzJ9A0DFmE$3g_dlcl=O=*jp5lW4Rgz702DNMJsQ%M$enHr0lF
zpX%A58UuOC5h8;?s>XfwODsw_q}Nx~ULQo+$q0UlnH6W@F^%O_n!i#Megd>8@j&pG
zgD3~`VPRQ}nE?4zXEOWgc%~RRnz!d~L;9+2u%x!Ih4LPvpV*yej4B&pF_u8TLwy6H
z-WYx8?Y&d{!z|Q!k(aa0cXzUJ>ZhI_t$dRgG?JdOLt^%640a7J8w*WHtv^|E^|J-WT()L+0$jR0YGpV1Li!Z24v)N)hLYy9nI&Nz
zscOgiH6FVOsz)#q%_l;{7$wYn{l@fxPWt7;Opmom(;uSLmzF&5BX((|@Wq98BF6>Ps;tdY4Qg}Pyf8D&50bh+g
zhg4jW9|WT*Rr$2cr_Qw_=O>ZVGv#sQ}0B
zXvbLzIGek!*a(C&=DSMhyPI3N+vsbpJ&>1eX#dDy2&-Pl%~uMrIALzUZOqJ}-_
ze+CH>^Xo3`g~ywL0FG3hhwp`)!z$xEuP*pV%eIq`w#*z7kh0
z6;<{nXp>rEn2w4an!LgWvi28HgR8+32$p-GgnH{1QijAVWN1sLNwH)jcX#}#HW5Eh
zLUR)^vSAS3YQzeEfK(LCGEfmlubbjY`r|j2S_`Me8i7Y~4dsO^*QQu^Kcr=b6rv>m
zh|aWv2Ei)%G$Opr#}50T7U06s-3|cAnI)2rL}Nf&$I_~OQM~h|v?&n`RKGa@H`A6U
z_qaH2A)_R{hpR(aBo>YitFW%ags5F}Y8UqCY<8hXMnY^YNV@*J;A_13KWwfg3cGIC
z1+fDnKSEV}b158Oibg#sAnOJwoX^OQCSAw_s||Bd2vWv9Be3Jq-N547M=6rGADyRZ
z%VeWbD7l+2E@>#4zgQpJ2t$^%1d$`jj=*}}0b$3_!w4A!XU=}mM7*Bk>;Z$~WCvlD
zC2<6;ELmrR?Q8--#@yMSR3}@K?nPkJof?1Qjw<
zi(S8eGPg_q=>kEPu_$h%Evt?=)-@?f4c*^~R=+O6;lfqq4HAiZq22%CNns__iI`r`H_
zVuUFdSW`7SUAx@I=2OtK>!d9~+-V@i5mQekSmW%9oc~AhQ#*bk4r
z&_iJixZ`+w8?7oaIKUhsW5~YMQOCT@RCpi$Y)?!T-cl1dbjMCVc($8ld2=F>x{md
UbeXLd#TYiI>bWM60s{etpl{Y(Qvd(}
literal 0
HcmV?d00001
diff --git a/libs/ssl-config/src/test/resources/certs/ca1/ca.crt b/libs/ssl-config/src/test/resources/certs/ca1/ca.crt
new file mode 100644
index 0000000000000..08e351d886c2f
--- /dev/null
+++ b/libs/ssl-config/src/test/resources/certs/ca1/ca.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDCTCCAfGgAwIBAgIUZ0xcthORO/ye5P1Ia/IarOGvwHYwDQYJKoZIhvcNAQEL
+BQAwFDESMBAGA1UEAxMJVGVzdCBDQSAxMB4XDTE5MDEwMzA3MzgyNloXDTQ2MDUy
+MDA3MzgyNlowFDESMBAGA1UEAxMJVGVzdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEArrEcyCaTpx0JCZdNAhb/nGROBRNPl2QdKuFM1pLRMoKl
+1XAMYRy88B1jSuteP18O+pk83F6jV7byYyp8f616nTHqoFfzl4rN/iM02b63/oQd
+qSSn4oPyiPfsS4I49taSJnH+8slbNg9iyiwoFnywcVaj1X9t+DAQXDFxNczpuIiq
+Q8apxoORiJz4U/sC9dNOV4y8DnB0Vi6Ypb3npMvt/H3mvHC6tRuibVNSh2oBSa3o
+gA1+ovxmGXavxfX2uquE+R4umgTr3HiHBviFvw2o1EPc9wHbR0iuyUtym3REIFko
+VmdzIanZEtdk3HyHa7wggP9zMWfETVZuurJ64VhL0wIDAQABo1MwUTAdBgNVHQ4E
+FgQUh3fjY8KpBOoVTBJ5bcenE/g9dZcwHwYDVR0jBBgwFoAUh3fjY8KpBOoVTBJ5
+bcenE/g9dZcwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAWTDz
+r1a7K41KefdsjMM75z6Vxm5nXeGDItmUWaerVVUmRkeln+bbY0uReoHELuTA76uR
+TMt9fOAmXpmfhbssRKv9TffOg5nb5IAvjDDRkCIXCJvBHcNLuYsTkjC7beuQEfSg
+3ayoRWfaF4EliTk96pEsnGNz0szWkhx/oWvZ8pvY/HA80xxiAbIFDgh4MJhwiCeh
+1PwbvUx+i6VYfM/6eIGk1WIY5wJR56Dj8afVsOED+hn2Rs5oWFXY0Eu6XNHJfAFg
+RyaTrL3+X9SK08yUJwQMFnW/k4IUKWi3JyWb3PwGOqIjUXIfNH/WkwMZSErkyNln
+mHWm8kQbx9OLpi5BlQ==
+-----END CERTIFICATE-----
diff --git a/libs/ssl-config/src/test/resources/certs/ca1/ca.key b/libs/ssl-config/src/test/resources/certs/ca1/ca.key
new file mode 100644
index 0000000000000..990ad2d005d34
--- /dev/null
+++ b/libs/ssl-config/src/test/resources/certs/ca1/ca.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEArrEcyCaTpx0JCZdNAhb/nGROBRNPl2QdKuFM1pLRMoKl1XAM
+YRy88B1jSuteP18O+pk83F6jV7byYyp8f616nTHqoFfzl4rN/iM02b63/oQdqSSn
+4oPyiPfsS4I49taSJnH+8slbNg9iyiwoFnywcVaj1X9t+DAQXDFxNczpuIiqQ8ap
+xoORiJz4U/sC9dNOV4y8DnB0Vi6Ypb3npMvt/H3mvHC6tRuibVNSh2oBSa3ogA1+
+ovxmGXavxfX2uquE+R4umgTr3HiHBviFvw2o1EPc9wHbR0iuyUtym3REIFkoVmdz
+IanZEtdk3HyHa7wggP9zMWfETVZuurJ64VhL0wIDAQABAoIBAG6T1fAr2xLRIkNb
+7ncAL9TC+U/lJWBjEsNt0cGRNbKPWIF+Z5ehJUeoko195y6d8VFXZlrn3OVM/Kkg
+36XCHfca/bV5dsvaJQJVLsMWIkmNP2kttsd/Viq1JHG3gG9e6yxCxGrSYlYZ7yKi
+SM3TJ6zWduZRvz52ziRNd6fiiZ8wc/wwWMXvXoMJ39EhGdZKKTowvKU59oV4ieg8
+vEK1V2ec2D6RN6m0CHzm4erKZSIJoMJAhKV1D42+z73XhJuv/gbIlUlXrG1l4XW9
+Sd59dU9v4NGCv6BpqQqC//fVwW7KfTGwmMtL7AP5vria/dzf9EyZxPk+HLgC6bHK
+Y3TU2WECgYEA9x0A/aB/4CepYAGUxIlc8r+ylhYTWmUafjvBJ8fPQs2PQi5JgE2s
+HE4yRcnPIZPaDMYDUPluXoSkirX5jYqQAVeOhTut8tTwV1FMf57P470FPVMgV5X6
+axsiMBIcRvYAc1cq7n7k45Ix8YNKfp5WjG1r7XLR9Xa5Q0Bno9WxII8CgYEAtPlg
+NuJnSnrka9jZQQTvzp6ULP448MWdjHmYmj93lUIC9XL3hkPqu+cZjZT5C2xf/36w
+5wEAHSNVO8SUjJ1bKgjyfyvaxosonbrDv3TWl1Ib8NFmumXjMzCw6LfDmrJbZc8X
+VA7VG1HClgPmojDc61s/F7unRbRUcIowP1dVOn0CgYA/u/xQbf/tSW129JFxK1iM
+x4KBEUqGiwMNQc4su20qdqgXUqbkb6QPXN+8fjNtHpwjpUKftOWRfTaPDCZEKlO/
+9NwuYtkXg3JFoxNO6yAFRfA/A9yYmncO/t2PdmxSpQoytW2+O34/b6pv9wPUqnP6
+HhKzGGUsoSVhQhA5All/4wKBgA9Nv0sk3iM4PTS5g7Wx2y2Xz2P2o44IyAfnCHaS
+w2QFzwY+kJv0BleZdVm5rU2//mY2qnL+bKoKIN0LBJzXeawWUZtbdAayId8kugTo
+tnTZZq94pb1BfHMJvQwQ7iOYzY3Qc2KSVocW5OOWtNwmUag9cRpqrfyBAVr69JWG
+pxhpAoGAUYkzE88ay83byRR+I/bVLDyI/OLs9mSfPJ1BbntDnmzv/ReUJCr9cZvo
+18/iQyhICA1IO+V0JgQPRY7cz44cBTb9QiQJvtRtFyyKLBC9roYZ7Y8CfTAcX73H
+7yY5UdS82r/Quu2Kp9EUbrTqmev7h8k5/kjXkvIdLv7soLMGJh8=
+-----END RSA PRIVATE KEY-----
diff --git a/libs/ssl-config/src/test/resources/certs/ca1/ca.p12 b/libs/ssl-config/src/test/resources/certs/ca1/ca.p12
new file mode 100644
index 0000000000000000000000000000000000000000..0fd23865b718dbe636d5858e2df48de3de29f2c3
GIT binary patch
literal 1066
zcmV+_1l9X6f&?Z40Ru3C1K$P-Duzgg_YDCD0ic2d&;)`5%rJrj$S{Hf#0Ci}hDe6@
z4FLxRpn?OnFoFZI0s#Opf&;1s2`Yw2hW8Bt2LUiC1_~;MNQUt@QkvZlv?-zL
zNggUd=Re|zDkEVabP2V;Msf?oB-y<7Pc8o4(pOWQkuf5{aShe6GSeDmnhlWDp%)i9c!&h|PADoo3D|nb$bXX%5aLOfl9dcVRPg@AL|JoO_`?
zsF>Wvv`v!}A~}dC)8s3}9PY21*vSc8!_dGj_OV_gvz5d$5&Md+S}R}CF#bJg>YAkB
z)?S0?;8Yn?7N3SsUzb2Dc#vD3><*6{D8*B@k3{6|CwAtra0R#jnhj)WcMUh$bb*
zs;UlSz4Ac7a|%fUL8@>EnvmvN;igvOk(Iso(Kp`+oklQg5*~KJ6{d1g`NOctRsv^P
zlR}xx1xE4XF&-TcUJ9SzyK!ZHk-hI!Fh0iC4??b^Mw$Cb^&dwt$69iBB(nbCf^JQ{
z5VkWz=MyE8_9n{glUkbl7VEZEENUhJ2n7;W?lE*S`<#9>#0I=-%Q>ylRlI(S7ocXb7O_Yk
z?k68~slnmrnK69u)Z_&TM)xe0o)9vi54QjzUKC{Tam}{N3`Xi2D$)erTvRrw-Az$a
z-ExCr6RtSEx#aOM$I8hOwz3Vr(|aHyUq$|V1~c*!%(P7NZjli5
z)I%;!I9Ms$9?Z=`WY~x;9kN8$#9ub^h4nFUW_b2PQ6RrXAMp+CFZ%;mC5`=?ROpAk
zD3pM@qrbiSRpTgzhF2)HlI)`cj>l4_qU_Fv#`%M4_ltg2PZNtvH+zN*I!li9H1f|*
zldfBjrXQ`TAp#!tr{Uqmb(H_ON1`Yb%yIvnt$2N_V%il!&nNc)59BqrS1do>`|#nq
zsUMlE=I$En*Eh>$>U?A8#8G!TGeDetUe25zz=FNe$#hpu-`DG|H{5i)PHKn4y#-^&
zq=c!UdjwswgS2^G@$x^w0$M?Bc`!aOAutIB1uG5%0vZJX1QadxGm=y)=j6x{eY~3m
ked4onjVlBc>;V73IoP;Q@`&Z{sh*?z8@$}-9Zc8*3>YK0_UA8AEsf13{}
z8Y%i;0zW}_8xrfYu$sXepN}%OcIvsI)Vgf7Lr3Lsb$~ZgtxbpDdax<6RlTC0|hGj_IO
zAWC+x3`xzH-RSb10=2b&e%7lIA2@pceQe%3!c{Yi(`MBmvd&iPxaZEy|C*;q{KRVclj*>aG$>Stbk#p-02Op)DTlQh)h?U4
z
ztNa0U!Y-BK)+IUM>)m|E)6e}k0OD_09@Q&xve{38er
z+p^0XgF5*S=r^c@~w>{u{tjkhW5`a$}X{;
zcZZ<&P-dDrWas%FzVN|qB^f$NWEHEVTD+n8miuE}fHt0%%UV?FD_2lYQzN3f5&Y$T
zDwO|J#RAY{bdiY)9!@Y&59ZP~^{acj*Iu!Y(e!G^mW?;!s~FC;Y(An>Bf~5Ik(tS2Zd3}M0_d16)%g|Tm2NsG1w26
kQK^!Ii8llkx=mLVA{>|L9`%vqgfb~E0s{etpw}7kAOHXW
literal 0
HcmV?d00001
diff --git a/libs/ssl-config/src/test/resources/certs/ca3/ca.crt b/libs/ssl-config/src/test/resources/certs/ca3/ca.crt
new file mode 100644
index 0000000000000..3f408375378d4
--- /dev/null
+++ b/libs/ssl-config/src/test/resources/certs/ca3/ca.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDCjCCAfKgAwIBAgIVAJMN07EKmD5RR6PmEZDjqSWBpRY9MA0GCSqGSIb3DQEB
+CwUAMBQxEjAQBgNVBAMTCVRlc3QgQ0EgMzAeFw0xOTAxMDMwNzQwMTlaFw00NjA1
+MjAwNzQwMTlaMBQxEjAQBgNVBAMTCVRlc3QgQ0EgMzCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBAJEPRYQiGOmfaHdOiCWPqzWgmHgbdw32yaQFKUOqqAQn
+OhkujPg2jJdqMjp7aFrZjLjVi2WyHdAPe3ibuUHT6PyTmcgtDxe+xAUJCG6vJKpy
+vrDNXVq9rotts+gEKeRbhr0lw2lppDhNNGiKkRm7R2sNRTyIW0vBNujicSWcli2H
+hQDjFWornjcd8OBFLvhY3tSwic8uwuSAYgrZfWHOgBWSC1xqUFHa6561K264yeNZ
+8cyb41euYAsohdQ/VPPFu3ISEYlZwTwwp/e2l8IpgKpB59Mrdc1TonpD5h3XMiW8
+iPJE0eZ9KJSv1SzEty2nno4s7LAu6cvwXQD2RP2vY4cCAwEAAaNTMFEwHQYDVR0O
+BBYEFAQZfDT971m21ReFP+rO5pTFrLLIMB8GA1UdIwQYMBaAFAQZfDT971m21ReF
+P+rO5pTFrLLIMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAEHK
+JublAXM3FprLQRU+MAIQyZp4VpSanxL/JE6UkGv3P0aQUQZt7BR+uv7xWc1Ygrk9
+gOK7pZh9vXZSUc6NFEa05O9lXUvYy9UN+iFxlWPe3niPHt8lW/6oUqMfnE2ePIG6
+Iaoaks29qwcbKYnl4ZcurdbcCfCoc7GAQYE1zKO4fJqnRogFaoPEwHJ5+5nmd4Xl
+8TP/v4ISHzB/cyCIiA9O8EssRXLBxwd7zgY0kicBKgy3/Rtd/HfGLunZdBBun6Hk
+T5L+SVLGLIoFFahA7NQUQPKzrx8KMgh6SiskWblc8pk4/Q1Z2q9E1L69z4SDl1oq
+HDYgZBcLjn4QXONgz8Y=
+-----END CERTIFICATE-----
diff --git a/libs/ssl-config/src/test/resources/certs/ca3/ca.key b/libs/ssl-config/src/test/resources/certs/ca3/ca.key
new file mode 100644
index 0000000000000..3626d71ba2032
--- /dev/null
+++ b/libs/ssl-config/src/test/resources/certs/ca3/ca.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEAkQ9FhCIY6Z9od06IJY+rNaCYeBt3DfbJpAUpQ6qoBCc6GS6M
++DaMl2oyOntoWtmMuNWLZbId0A97eJu5QdPo/JOZyC0PF77EBQkIbq8kqnK+sM1d
+Wr2ui22z6AQp5FuGvSXDaWmkOE00aIqRGbtHaw1FPIhbS8E26OJxJZyWLYeFAOMV
+aiueNx3w4EUu+Fje1LCJzy7C5IBiCtl9Yc6AFZILXGpQUdrrnrUrbrjJ41nxzJvj
+V65gCyiF1D9U88W7chIRiVnBPDCn97aXwimAqkHn0yt1zVOiekPmHdcyJbyI8kTR
+5n0olK/VLMS3LaeejizssC7py/BdAPZE/a9jhwIDAQABAoIBADld7sIIsg2Ca0/z
+kMg5/x2gO2wUgIrXNHtXRzBphzTNRp662Ck5eXRQHTkfoO985bgbS5uWS1ADL3NN
+MoCkC5oHzWNq3nMnkGHlZp5PSZLW+i71qJvANA0T/3gcXWzf/XNEQfmoO7fAYJ+P
+XT7t35qojt8XlfNpoAuNse2L9aBfQ8knEP7Aym/G3ko9mD1dvm2YKYYkzpLKYOoE
+5MpCNCAcdn/va3bqEEgzq8ogwO9gNwerLnXCbId6xpInwfoTE6I15UySmeDQg+8P
+nDiVWd+/xCEjFVBNwxDG1BjG0j2AlUZoDFuBeRmn48SjgRTc02PRdGKdC0Mys+1o
+ZMIEpqECgYEA39/9vfGAQpz4ssLFL+AqKKRmgJwt/ZRkHtUK5w+c3dZBi1aRhWMY
+5mrQt6USSm5YovJR6bxvjlD/nVwnENprLYdlrwAIEbT9jpVFtGHZqQumrH3JmGtu
+Uz3mjZuHXblgoGUfAL6X20qK891iJoQ5VUtWYrP0ndd1f8CyE7/qzWkCgYEApeAD
+aCBUMFBtmmXXrrQWBTu9tTgn26jMmFk/p5NBeSKGs2GCUB1sRgPUc35NCdtIetjs
+frQlsEOKTD2PAnnTKL2ZFuk/mxFwQIXJVjAnhMICm92J8UL4GjWH1EuaLRQqks57
+n52dd9mDZFFE1lAfAihq9BEdLfwgFKlgoyl8W28CgYAzmwt/tGKveEWv10vjDFZL
+hhIGxXmogYNOxCc+OhAb5t63At6Kk9xSiP7RxmBf/e26qgcNzR0d/jfeCzcKIH8i
+QJrE60nw4vqr2mb1/LRSzle+XUSSOPl2gMdbjyV2ClxmvMiXwFd6+kTrj/WnEUWy
+Dqq8F+VkWR1BtKaX/N5gOQKBgHsBNqWFq8i0K8LeGOYFx3qUBacYAH6kmyuyq0CC
+M4A3uTnWakMsvnjhKC+JDmnrwcDPkfiXcIdYXnsQ/zbvzkWc66SQzUkZ0msWiuou
+BXAuSq74xu0xIziUT6h/c9JP7Q42rnf78qTImOXQWkKu4X/BJybcdg3+tG999xqn
+jf9jAoGAG4hzKaKqvqKOfDGr3Eebu2/uS73xtUTxiUheqIqoTJHO4jjWOcjKI1my
+91pnfC/t3fGLB2ObqDTw+H3jRf7gqIkMbUAswjpMX3GpOm2+eI5U9B8y5dV5PoUt
+RV32fzc/xu9Ib2i3Q29vuHrZczb4lxpi8UVWCLcrPuvC2lM8QTY=
+-----END RSA PRIVATE KEY-----
diff --git a/libs/ssl-config/src/test/resources/certs/ca3/ca.p12 b/libs/ssl-config/src/test/resources/certs/ca3/ca.p12
new file mode 100644
index 0000000000000000000000000000000000000000..1331cec05d77efa8874e1488cdd01ff1d86fc940
GIT binary patch
literal 1066
zcmV+_1l9X6f&?Z40Ru3C1K$P-Duzgg_YDCD0ic2d&;)`5%rJrj$S{Hf#0Ci}hDe6@
z4FLxRpn?OnFoFZI0s#Opf&;1s2`Yw2hW8Bt2LUiC1_~;MNQU0?F}#D_;xm*aa6sc0s{cUP=JC1aJD5(nerH+Wxf+5Jk;DOZ#buyM*_;P(}c9T
z>PYG!7k*@G(StzDGwDGN<2a1!jLVC_p!+=s>ub9P15zr*-ZU
zj`PaouF>j$iL?*Z^%g&?FL2wVG=I!hof?U*O(;IFD+}1ic+?kCmus*Fzg!4>U53*J
z!E6?v0@UI48njrBewaRg(4ky3+3_9rHiVP1v}ga83x>cQ&XWX`E7qImcD;>};!Xq6LFFS$D`mRWga
zE`Rs5p_4}-Tsm11roRflM%S!o2%@AO4s}h`f9!8_Q>c@YTAmr94l#X8r9Y4r
zoO=vWkPF~Ax;j>W-ojK@(G8qN_JPjO3-lkGZFf!3iS~CQN!}e
zUZYS+-<_F`=$)Q7)r{UnlYv@
zSogpwNka{+*?i*&aruM>$*z2Ly#KLC4!rTe#4Cp}PR)v!z2Xo}V|RMtS7EIqtX$`o
zFi4`oAahbksnxL56N)+Y`1}=R7dC=Pm=CKDYhnEI%s{g_A(I&|+wFUHii7+xAqLQu
zO#J0woy}intVTB)czqha`k~AO0U|XJ^?2SPk9{I)0iVzLA;J)QM!CTQNa=NzfX}1w
zjLw%7FBmw>sqI7F6RebL7Eng`*BFc2B-lZpl9Sy%g^Onk)N*RNY3N~bS49^75<5)2
zC5;TUw&ac!7(!EAuq-_HbtL`KoeS4}tx<%tU)1ZBx8^cqY~*@;iUvEp=$(oGQStY@
zJIOT8+rN}HJMoaGtFI)OvCb9`pc9*Ddh2HcD4L)yXQHZLNGvPF%--ph>LcKF{)BTy
zIIlR4#{?{rx0W!OQ60ali=Q-Gf=6{uEyL3}JyF2Iz}lTNs#TMkv;Z-$S_9)Ore?Hq
zz;$$d(b09y3ey^%$=0SqHGlG8WiUQ4AutIB1uG5%0vZJX1QZZtwI7X?*GEMVo|vk1
kaJiE^Q7Z%#z<}{rL?7Te8a4k~6kM7xQr#1h0s{etp!Xs75&!@I
literal 0
HcmV?d00001
diff --git a/libs/ssl-config/src/test/resources/certs/cert-all/certs.jks b/libs/ssl-config/src/test/resources/certs/cert-all/certs.jks
new file mode 100644
index 0000000000000000000000000000000000000000..4cf839ce8d046fa5e18de403210a79844870d002
GIT binary patch
literal 4268
zcmcJRWmFVgx5uaGhLM&~ItPXn5EzH<1|_6ZYDgIxB$O1CW?*S&*~tcZRSKmbLg^Ru(1MHeX{3PwxG|#-$PJ?o`>|_uCW{s
zfx5s>6o}~LJruXW_#4_9fywxQ#hi!_Dv>o3=v*OJ@-1*f5`XemcP<_}TVE?m^;{E)
zzEUCDD4&|YRrv&%A=HMwlZy)en*O^9M;@+NU45(Zi``3yRZ22%k1jceIIA(yw@urR
zkD|@$=c?ABbv1e67f#+b_HVT07oLnf?$>0|t09)Na%7fbT&X2Z_eGt|sh-K1vs;dQ
zQoMWhhL78C)HYO3Kd9R{mQ~C^ngQmN(|BBy_SfK{k`AF%cY1YR|6GQE<5)*(uL*pQ4Xagr@j>o`!;o!b+Nmw
zq#TFaQ)@u)h3dvKkadL|>k7wy-+HX8O7UD5DBe4)x#sn5@+Lf4hm~#0DAEVbJB~O(
zuPVs9(Z!S^)#{qO-ufyAov4@!54f1EG0&&x7v?AgE}^aJktv+_7bUk$i9Zu;Xh0H%
zb01hfch95IZw8o
zGTEIHHr{Z$uX-$;yfA8M!`F-fQVDj>$G|)#F&DgEacahY;&aj
zL}F^XM)QT=x#`BnGV0VJN)_>n5a%J4nb)Rrx9elkQA>LI(GF#0leYJsHPFaxpsF=9
zf>FS|GWwRCVYQBWaFQ=lipTAT6lyO(bY8+rO$#wX8$l7blhGG+e^~DW(OAggNMcJC
z%a*0@oOpP+Hjqx`u+
zQRj|G*-Q|m{;U}G&lB(M3jlq@b&?OTEC~W?$M!Qi?MGTgOc#S1~j^t<@XmTWt2u-%?
zYwh%6yZ{4t+^=XirH9qF?}iya*KD?&GGv#t6b2K?q~x=9+vi&6)jIF5>&uuFdh<7|lf_VX^=K
zm>m`cW`RY4z!)$H2m(QcTvFF$I|@cy$Ge(A*7pss6qBi76oe!KPt>3MrT_xT2mmms
z7%hyN5NrYg(~%fEqWn1SDRYRy*cd3pWMN`3I80hXOj7bb1BHYXOi~>7?+`{UMhPST
zd)ME#42uHZ`sdNtP7IC$QUR{#NkLISAYk~Bb?wZRN8Lp5=knWf{0{h|qZF;a4beb$
z5`x)zolZJFGs{g^zGKaY)MR&gZo#<$#-TZ$vG@>{UrglC)ML1S7f69TA6W`bF+h
z{vy+v;9Nu8l29vgJ(EJlC&8AZ@8E>%+QDwF+wOvS0a`zO7us)1S^0d|P2mxFYnnTq
zH!`Nfv#X8^EJ|v;n|YO63FpzzO8C^$^odiEBx-o+!u+{rp@``Fd$S4F?V^B8typ)%
zmxC1q1A}xVG%X=y4iJ`iXIK}h2^|-}u%`lM3I(FR`bj*KKsSM~1kkc{_S&CsV!!s{
z<5lW84@`T1(3mJvpp|41;z#_HL?AE_0K~e&oMAVw@9icfgdPHIyCe_`BNq0zsa@A;;I}vc!5)EkI#vnmX
z+;-%gl}t$pF*{bxfFq@Rs_MttlsBeAF5z^1XhEjbzGMk)*=_US3ssDfmqtvXW1Q%L
zGNt!V8RQz_NGxu>ymG)S4c;(2wr%UjhPACrWUpG&5)elt9@xgF->axxrMB;g-Qaf0
zZZ{@LkPi12J(A8ZD+J}lwcWgZsOBd2Dp1a6)@%M9RksTf&7Fpb%m*o<;!7+4}hQL-_{+z;`^KEFEZPp!gt#unbEQs-XW6OUl
zie=t2Z)#btUTv=Yv+DWfA<7^CZj}_#=oi2uL{)LJvyC7i=|g1CJZcpkH{j54^XZlQ
z0(7Btl(qGhQleMQM7$SDuJx{`%^0L4ze-!NwVRwEGNCJ>DojnFVMt-%s^YtAHslDn
zmwc%91$Jo|zY>YK-08Qmm$Fxv+Ed!8+(S*tepAVF1P8+22tncJAYN9
zzC*T}dKu0&w=90h#Q6=Sm@M3ls5sO@PX*PdDl
z(g@!i5Hj9t#8WdvR%>?W>XdmEhXSGbExmYCsEAZdFkclba^0yk@?h=#nh3GX1kuuz
zf*Y}gkJ*MKV@GVRUaY!=vqe#4@-a7cn`DpwyPPZ4C9S01ZTe=!nw|~4#KgYBc-d&N
z3&&wfm-7Arl%w>CSAwxO4!o7RK9g;tPfX{P|FKNIt?s-X>vlyqzfA#ljf-NucD3H=pSjWsNR3*4Ai`k>Neu{MHi=W9s8
zRV%U1VjxkqHHQe(E&&5=r5(LEl61I`G*^Pokulu?Nk;|^UZ)2*M>`V`BZ
zG*h}tv_Zvrn`%tx2YT%0#hiDeG(806X72x>O-kJ3O4(xdL|2Ldx-D%m+{sClG-0Lr
zu`bL`A5lFz6vtK}4l=1HoW8RPT8kP*l55&lqo?p>W);+lWnZ&LcY4u1;kIx2SLcZ&
z*^ygM0~6it+DuYEPJiR*8PNBg!`}oEIRl(7BgOvHE%1r7m5}9$cJ}#=VH`mQGn=
zhtx=5AABf2?;KJBt=s)Ih>j}~?vbxFZVFaqp)<`S=!%rrFX-vxU3r=qW|+6+s1h
za?<;dp^9%VQiS`-a~fsT$VBnMS{6Cwg1zC|Yev!-fVIw(vo=yT?3_?OOUj_ztI2HR
zo|WuZtsDI;r37DfjD5CA_d^Q?lbi!{dBPVpjv60U6tPQK0Ab|aVP$%qvKc`Q4LVi?
z)i-ngXNVw)9vH)MknPdS9%kdFxY^u#5p4hEbNN3&1WZEwpAhjU{BIB;_P;{Ji=kpV
z3Rws3<^rNnl3u;$THD!-FS?Ji#$&o9LvGz0A%tF%5KW58bIcFb3mz`bbZcQd7Oht1dFfs%M%vjm!^YJ4XFiGUa8Jnvc#1`_ZTXf)Bl-
z%Zc6*sS7}}6EAR{t7UmJ;SOS;5fwH3(IWM(@XiP&i?J`@HtQyvUezh!jl1&XPQla6
za;uK(U!pW3%=1l>`IKfHAIiDbE)54!c?1jois-pnSdgRI1=ps^>NBQ
z=v_x=#ZcI)#n(aZtcmJ?oIEO0Gt%?ku4(Mo4M~xLNLqZip$Eh??q1}U902H1ZeBps
zV>apcUBl^)&)kov%y-3u`26BGY%ruB4HWiV(%pk=Uh964(KnEC_VP3bZAqQroRNZs
z`kxh1phXsBN!XKCsKk$kFg-I-XOMd7=|jf*EwKbTZ)86!V?1q~!pfY@nLB(L&@PK%XW=TvtEz#{lT^fy2W%ZK
oA{e*&IeKkn7Q)OmUv+aq;Cq<(2t5AN0di&a7x%PT&SK5K0P87#`~Uy|
literal 0
HcmV?d00001
diff --git a/libs/ssl-config/src/test/resources/certs/cert-all/certs.p12 b/libs/ssl-config/src/test/resources/certs/cert-all/certs.p12
new file mode 100644
index 0000000000000000000000000000000000000000..b971a1e39c83baeea8e4fab3cf6b76804047ee48
GIT binary patch
literal 4757
zcmb7`Wl$RmmxhCr0L9%36e-r=R$PJvC{_v-N{SR|a0?J5m*P%wY0yHE;#RasDQ*RF
zarYJv6x;63?)~QbvpYNI$C-KFIp^myFBnCYgo}d*Mp0=K0{O!=!?9#IL^x;^l^i~b
zO6m_51EWAq{}zEt@ll|nKNt;05kLt^{`-m)goBGl;a`JM_*n2`LeT%ZQW|>v
zOXt{p;c9YKJ;i=Gq{^s&)aJN~Bx|d%*htydiR;8`<+R#p-DyH8?=MB&${HemTXdcoy~(7gxEmoNzq>rA
z&z^TBA{y{hos(C!rD~-B(PjRIqs0W7x!=*sm&@}nnr$@9
z2l>`k`c265nbygglsTeLGIxL0Ses~uT|}(8-Ajm$l(e2gtU+x;dT*O`^rCV0Sp6f)
zoXxn5I_C&2Vz9CBN)~e=^1}9<#ay^rZu8LFF-N#C^ufX`mqdI5
zl|@<&=aa_L;O1?It&b!DC^Ug9YdoTHsXetK^%2R}Po0{kP;LkN=YV=Q8c;gcCkCdi
z!kR|5CD$n1Ad$QZ%fN5eHhE~NAq0}09_j#H(Q
z=)f|k5pZ9PcFpZw9ryCKzhl)p$K*BA&lN-A*nLnzwDZ*HeVFVW_w-K((^5UFXOca+
z&L1PfHaxF)-#{LoUS(UL$C`K^Bmsi$n^ol@DYLe1m-+R!AA9ArEd`H+VKjQzlvHyJ
zy=w5m4?J0G>Pw%w&ms}~u1dNrWkpY_A9BfNaaeZhKnGXdpXrrP0`9V=SoA(>5xu2e
zy~QK*sO+`-E0?YO*J{q#w)XhfOu#O=IyAt%p}`xeLg-Gd^(6t;%GZg`hiPMzS14dkpE_Fr(Kk|=1
zSZY>f1d5w#=InNi%3sIrBLvt~&v8zer5Q17&`@%8`hrQHa8M3+Qe{tao|NqBqF(PE
zr4?z})YkLnhO3297TGVYA6^j-S+S;bD&sKAP%;og3ixZa@m=M-&v@$rM9NV7vp2+xGE4IQv|C5;g3v
zkVjo5(NtPB;C>?bV}y7Jij5%KxH{Vj!)LkUQG)lbeAFa(`8*mp*ntyHsH=~VR8M&;
z@-*+`ZZhP4cYALc*6H65p;&)J($8oEnK-uYO8mJoIc=0uFGd7}ajD4`^RdgyUVwFx
zo;jY5ErU}QMn};XSp*hs&k+&q&*qp<;7*@qv9?NGxs0V9L*^gzop%%pvKiFPK2eAi
z&@zfBfEoW$C6Gphlnn&10oVaN0A2tQFvq_lS`iw2Dq}~u-9r%xaY+ejQ7~BK&-_1p
zdMrsQ%Vb%YV9R17WYp1D`x?o?{Ett8A-_tO@CzRi#XjV*I&pAICZyByVc(LbyV`?M
zA;asJQL`rJl*E^;lcuNNGIVOo09`;Kp8ETWI3MY_7*q4>60qw+xIdY?{*+K&WlHqW
z#v&g&*e3rzK<}i-;sfqrommiNA#&+FIjJM)L$;S8E=0|
z^n>{wYL}R17htxyXB>s|4cwP8s~6wDSYQoz&<_p)!~48+EJY(FMYWG&2JmA^F%
zt1WzAlL*dS3kYLt_=y
z$442qmu_R{(9&p}G^cQ->NQ<*W3FsAi}oNUAvTbS#AU)7H{Rz4U8<2VyIhANq5ICg
zo9PNj#wVW53j$}Iak)g0K;xsxyhA*PK1y_VY}!pYg_@gbr{FxjpYP5p@#i2bk%`QQ
zGQ5mB9k^ffq^N=hU*g+frhT(oM#r?qW^V(7dXQPywEWgLdzL%_Q5p`&5Au@f}(L!97
zwIlYDbJFYU^AwJ|siFtF-%s6kKj^q3u^pe*~21pKmlL6fSTBC;6
z&|N21nU+L;8y6UFP|=9dL8w05b#@{i899xM)S^{$~$rr78P4&B%nI;SZy
zhsk=z^C{;GWhs5X)GAxwu0sy)@4wKTZb4e^s8wQZW0ZmPVjm(BLSFe<#K$Kb)=UY`
zUUei+t9Y7h;^P~@YsP4Ij|K1`!2p>5L=Xd`b4GU>8peL+dj=P5h}sVr;v?S3hNUQ<8K3ZZtB)!`Z8m0Lc~k2tmBFm7}D37#uv;)opy
z2heYp7`AMI0l{a_f_;lLW%_nmj78*^N*gABUj4KSqmpyEtvM-S`jzFf)YH7YV
zwfdK*V(i(;#Jkcl5Ctx;Q8_FUXlRLjSw+jr
zGka=V3^pFZ*cY2-<|lXMuanoN9$c~iCVu(kk4=CKDv#NoPwa9^W4UTOT7B;+tT0=~(UcpeOMp0q?hcy+>h
zDf7urN>1gMGn_l_C_?J&g+2goY?C}_X6F6xTm-gwU+abS8$O}Emb@|IvLSQJT{tNwA02M$zst=7+FC?s_lX!VVrkbV`fzMP?-Dbs2{u{
z<$T~9*8eYm?xP1U2W1tZemM>BC7$GTF
z%@5SVX|!7$6YlUUh$s!cfq!TuowxCF>ymM)JwvbW4%?0b;k+K&oXNVMV3cwgMPNKFaL4{tz`umkj#SmY-nJ2+K
zquLnH9qzc7P9DZiDVG`A7hbg@G+gNmr~5OM$aoGb>QnIJzBmxH*Mpp@8e
z7Z6Kt$V%w8MEBNt%WCxeQ0;ROE5;Ksy<2TV7}$P(jr`0E>MX|hgxA1;xJre%Gjz|)CwU9;(-``QM=?P*Vf%6He-fX
z;Y+GqzS|R(Wj58kg@!55_rpZKGDCXI_hjosU0|L|OE$IBymoFy8&JzH`sg2>nKiM+
zMLAb1q951|@bRj@WPRJP#4*T{FgqLPCp5%g=nU<@ais*;8NH#<|N_K$WV<
z26=5jV_-qj4|?k;JXqP3fcGZ{GIau`xix@i+oq^X$C1RgJ{)OdR;k@a?#xi?=2)%z
zT!N6aA!nbexXz`n!zW6sen!#6;RC049iYH2OsV-P3WA0Dne@JpoUE1lOEt!~({6M!jqEn-7_BU`Qa$V5<
zL<-~pp)QRmE$VRD+W}EU87HtIW*aMx3dVxBiB0bYCW#BhdIfJoBGX77Wzq3MBRXj#
zldqnYC)jih497XpwV<+@LMZQ@$NeT}Pb@U~6@hp|jGV}(lt@P_wF3!v
zo!F$}sy;Zc^Sz0GI@GtyDBC@C%;?%syv|wxhe7kOtc8iAyOg%&KCz@~yo@s*PE9w-Hg-91-n-jPPHt
z!sn?I#is3ybg;XJM(2N}(!+j^-4fI3PDDK|e?Koh1pY}2_nvoq8+$+Q{btJ&C1rQ;
z)JaI?c`XE=>pK-_)((z2T&Htk)=R41DSmQO%e3nF4kBuZ7#n*`YAw*gTBC;(pT*dV
z^BDWUrPgovO?^kHwL7Dmc=dRpZ{W8O3U|-75_xd(6p}9#q7)@k1gsszT3m`{ybZgS
z<{qD1qRGY{C^Bn2^ZrRt&H$yMHn4mw^wcJYuu-NxFgCqX@Io6X6kT)FQ=Jt_eSZ9x
zesfN&r5_6=K@^dTzikjOn`M27wXTaYUpEu&WE6^;1aqx*8zP6bR{fRt-Za`cKzlII
zP=(Zma|DDG-0{n{kf=TctN`W!0|^NP9^m0J5diRM>LA_>ql5Rofm|L{s#SVZ-A`_)
g@M(HC31Qq>ap_mpB^BEC+2M7+VpwtUaKh350Zkq9zW@LL
literal 0
HcmV?d00001
diff --git a/libs/ssl-config/src/test/resources/certs/cert1/cert1.crt b/libs/ssl-config/src/test/resources/certs/cert1/cert1.crt
new file mode 100644
index 0000000000000..51f39295f62e3
--- /dev/null
+++ b/libs/ssl-config/src/test/resources/certs/cert1/cert1.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDGzCCAgOgAwIBAgIUCYPL1cogC+8WOfSZytklHmrHQh4wDQYJKoZIhvcNAQEL
+BQAwFDESMBAGA1UEAxMJVGVzdCBDQSAxMB4XDTE5MDEwMzA3NDA0MloXDTQ2MDUy
+MDA3NDA0MlowEDEOMAwGA1UEAxMFY2VydDEwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQCWz6ITDTlkTLueB30Jx0+7sWHdlM5ObZjWhMQ1eyJD0gYU/gkH
+2C88IN/PtSv04tzFS6PA4KPDLIyaAhczPlGElSansiui//CpieCI4tt5c2BgVo3X
+dJaylYoW3CRILUrlSBOMUmJCQEokverxMrz8DeppNxRfj99pQkoxUkmFMZj/C7XN
+VYrTttdF1li5FUtWJxw234OUfum3PQIzz6YTmoPtLrJ2fB8I4CH8R5hwGcryhBSA
+qq8pgy61aTPCgEBZ1c4Dvl65X8dG2QEVPjwMZnnbGjvlZefOgkmAWJ1VjihA3GVg
+O2mx4tB4D2x5K/OAxh2foZkDVhqJfBkOblLnAgMBAAGjaTBnMB0GA1UdDgQWBBRM
+RZ6Qlozj5hWTqf3+oTznFyZTsDAfBgNVHSMEGDAWgBSHd+NjwqkE6hVMEnltx6cT
++D11lzAaBgNVHREEEzARgglsb2NhbGhvc3SHBH8AAAEwCQYDVR0TBAIwADANBgkq
+hkiG9w0BAQsFAAOCAQEAOTUJ64T6kO2H51j2bKIof4ij4yoDD86gLmUF7qXB2Wt4
+tMDCqs9+5VnRzSWY1652mpwPClcK/MfE26PR6DUunoES+8VSbARWh0OB6zsAAWyp
+WJ4RxlfYdNpJZjpx3umLGj4yeCh0iOhfoArBUT3vaJJrea+rTro4UFE2Z29uWALr
+NvjKZ0Qrn1DMP3N9b7y81dR9RMlzeqk5tlPhAqhHzQM0hDdFKA5uIFn71QQpd5SI
+y8MpllWFGGq/+5m7FD0t71GQ/m5xCyfUiqQU31Nj3ThU21SPHBqZIZvQ/na/OaAf
+GySn+0ZHAvyNRTL2y2Fk/YAY68kgx2E44H5YSqbFJA==
+-----END CERTIFICATE-----
diff --git a/libs/ssl-config/src/test/resources/certs/cert1/cert1.key b/libs/ssl-config/src/test/resources/certs/cert1/cert1.key
new file mode 100644
index 0000000000000..461ae5ee31ba7
--- /dev/null
+++ b/libs/ssl-config/src/test/resources/certs/cert1/cert1.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEAls+iEw05ZEy7ngd9CcdPu7Fh3ZTOTm2Y1oTENXsiQ9IGFP4J
+B9gvPCDfz7Ur9OLcxUujwOCjwyyMmgIXMz5RhJUmp7Irov/wqYngiOLbeXNgYFaN
+13SWspWKFtwkSC1K5UgTjFJiQkBKJL3q8TK8/A3qaTcUX4/faUJKMVJJhTGY/wu1
+zVWK07bXRdZYuRVLViccNt+DlH7ptz0CM8+mE5qD7S6ydnwfCOAh/EeYcBnK8oQU
+gKqvKYMutWkzwoBAWdXOA75euV/HRtkBFT48DGZ52xo75WXnzoJJgFidVY4oQNxl
+YDtpseLQeA9seSvzgMYdn6GZA1YaiXwZDm5S5wIDAQABAoIBABCilJEfa045/JQA
+5XT3rD7a4R2s9VjHVA2NlYsEqxHqD8uu/dYEraknQyjJJjEb+Rg2MLjszoOP3W57
+fo2jeSBzx1DGIXQYYTaCQ+c1htoNtPrLcVfrv1exkQrWe5YOkO1blvRqffYq20LU
+RB8Y5qmy60Fx1uh3mUAmFML9/agYVJo4yCxnNDrMg9UjF4bn/39uOf6C7mEVJRTl
+7ET5wcbdl10EOWW2m60hJOQLSOY9N1eafFEO+V2Xb80PC2t3Mqt5+T7n0CKCx/p9
+4F7QAz+hsfksY3oTUUXwL0KoJTLdJrjCoG4mWJ/Re3qEKJqmMfT4XpJKrF7HfgcK
+RCyH06kCgYEA/5TVQK4G1Dc4LnSCmCb+ECQvmGRBtK6Alh3Txb4IwGHDGMfC8W4O
+gt03A8ZE92pjITHd1+cLykKBsmaVmEtiyD7YL5G3mumR1YdMFEBSZdxOTeD95+aL
+YxTofPsDIUIPSFecRWwri7TyYcvUGyDchL0vDc6Gp95/ZFFgt26uxAsCgYEAlw7e
+g2McHws9cSAfouULbKbf6jXzy21t6CeqJGID/kjdUws63prcQvmFtFHWrv3rKO09
+hgb3Kd3gUz8t9tAD3F718bSZwzLASwO5ujPHZQVRTotutgCGeKPgXqzyVWeo45ji
+4DfQl53jG0aA1DzoZSA3/owcuX6CVGPLzQnhehUCgYAHe9UuuqnKhv9nJNQ6HlIs
+KNMX9D+USdPMEX2E+caJ05MB47+KkD1uiYm125VjZUMX0rz7OHG472+ayLQyrGpt
+EKIF6o9kwtgZV4fbw/Jltyi30RG+O5rzQMZ5+mOiEqwd4yrZQYyY36iFQpGoZbLv
+VBbPoa+BtNsoFdXuKRiG9wKBgBjJhdXFc53ceE6R2N8f+onvsBp8k+6znC9WIuMp
+ekJFrpur4hMZEj+jNj9qlnHMlMP4efn+NpyWHfNLEL3JUHje1Di/S+Pt9gPZLqbR
+TEzVXIwo8RfIakhti6m9c150ThBazA/C2OWoMNYO8aDiBbhiWw3X6/a8PaKfZZfV
+oTwpAoGASCTx55uThl9rN+XDKFXN500K4r+Q9OBOEkfDuDUERpBohUfoy8dO5eiT
+mGiqx5P0hoxEC70vnw5fJz4ZpSJ7LcpCfq2TezknJP3MZKwTdBM/pODSPMU5YCW4
+T9ocEQui5PKOTLlVo1QKrG8w6f/YMfZuGa9zP/LmTLZEado9nuk=
+-----END RSA PRIVATE KEY-----
diff --git a/libs/ssl-config/src/test/resources/certs/cert1/cert1.p12 b/libs/ssl-config/src/test/resources/certs/cert1/cert1.p12
new file mode 100644
index 0000000000000000000000000000000000000000..2635a887bc87e4b0c7eab4a4609caf53fa701a27
GIT binary patch
literal 2456
zcmY+Ec{CJ?9><3nlNpRPVyumtm|G3TI%tH*K4VSDb}fa>l-)%`b|FTVWU^$POoT!r
zOV)JlGDz8%iEPDVZugw`-hJH>aWM4X4cu&CGz`SKqj5$QF}a6e
zXXgY&k%8G2CVt&@Tzz-%O>VLZvUb($?Y!d`jb8hTdCRnIr7HWg&N2s+qOO|&iMxpf
z@Y&thuj(%;es0L?g`2!&M!yg-vAunbD&YVrH)x9ySRGcsTMxTw&OJaRMD_frPFD}g
zHmzuu)8LnHFZ^9+a4&dMq{i06ObBFfvhvOzb&R~=yzB;R>Z_Bsq%R#i?aSRqYLxk9
z9c@88l85B~q%Jb&qqrHo+cKdKE-&pS2J?wFJJkYDB_*ZKVD-O$BivvQc$79oyVwCt
zJIK6Y(Sz=saU|q*JdTF@wC*9?wwg=gt^K#^mt^r9SE3clQf05i_)=JWi+F4Qjq1L!
z%AoETsMjt$YW$42k*59Zq@A4NX;)L0ExV>?n%11l$Zb@f?-kMP{A`V(tqebskL$&-
zgV-Zz`4h|sceO&V&kGvDb@9cC*N0qbkse8`+9(`=;k@>_A?H0OMw@pmE`Ktj&5}u2
zUDh)@W;s44B~7hX8Fl+Wih$S~!4r_J?arbsO;CP`wnU)7k$Jf@KG^1ZZUuGJNN#R?
zft*0KHByP38F_~VuLmpFPS{vO30gtVB!5PSmZ(ARyS-L^IOemFnT
z^OfVjV_llW!?Z5Ej}%smIH%8j>wT-~nJP+t#;4rBwLg2T-1IQg2!~;i
zevIk~{i)8CZ$;nElR!6k>APvHu;Nds_4Rk?)z|ICs%O3(#r~@1GihP$#iwD8r1V^#Pw3RSSp_Mrn
zAKzD1=P|rkuPL!nV)*FlDXV+(0s<_rnfxtfS)o8ak!fm=D5($rFINO66@jx$L;sKu
z4A}2`z9~~Tu6DhuvaqkdbckqCFPr?(eM687-#e_OM3YVCTV@_pbS=a&XF?~qW}QTO
zF60KeXQJLDHdD6+B#|-;jV{vfz!B3FeqP17He?M}KCntbHnB}>wdutf4CM^gjOAHQ?8gY#Y$CkGD;GnY%Uz$6
zPr?Qx=ra9g8P2(Cf>tghaMT(jHk27Sv~t)GmMeUjz2n=-FG8OQUecYI0Hd9orbPEj
zlE$=0NP_)h0&~<^C&FPIrL!|!YW)!t62C!3a&kTPf-wMA0?}aUg_+`-ny@RuBQ)tj
z=`7CEF1osM9eT8m4rmMC2Nr&`OZ(LMLhN&?w;v?l$aoc*PfIw@3mr@AQhpx2FwFs)
z4dT*2sa{)Zu;@Jiwl;QseQW)Y+1jVSsKykH=f4U
zGJYDRhnD?~Jwr5I%q|V;wwDzUT*d3BEssA7StqFj3vcxzHEHv`8Nre
z#8u#1v$61X?33!ybP=*Zqg=CF3o-MBTJcjJfNwLM%nWNDUmSEM3*k18a
z?7S~-tNKZFnY6or8%G$FAlh^&2EwS$>AX`Fa6EPvr-kUp+1%avguwM(9Z7noT6rmf
z&%@m>$m>qvPSESY7TyQDP`gHJ0`9(R+N-bmL|?1B9j
zC=NY-lwLQYNQkr?S|8!NT?|I`Bc3DZ>-3b>u}nhf)aW$uBB`lg&kTfG-+jy?U9j)m
za{p5^gZL4K^*pGg!Y!+NY36_Oiu_W+#T4T&NE?!xeBoyEV_#Hq3+yyA05&xi*3c?^E?Aq7(XKt>+
zMn^vQasb4ocn)BJRzS<3Az%&^49G6V0RRcvcJo)$1BSSjczMLKzR^|JfdQf*&JL{~
MetwyX^uGu6FKEGTvH$=8
literal 0
HcmV?d00001
diff --git a/libs/ssl-config/src/test/resources/certs/cert2/cert2.crt b/libs/ssl-config/src/test/resources/certs/cert2/cert2.crt
new file mode 100644
index 0000000000000..3b7ca01e48bae
--- /dev/null
+++ b/libs/ssl-config/src/test/resources/certs/cert2/cert2.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDGzCCAgOgAwIBAgIULGiQ5jnAntO91sS7Al5aUv8/jg8wDQYJKoZIhvcNAQEL
+BQAwFDESMBAGA1UEAxMJVGVzdCBDQSAxMB4XDTE5MDEwMzA3NDE1NVoXDTQ2MDUy
+MDA3NDE1NVowEDEOMAwGA1UEAxMFY2VydDIwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQDQbV6x3P9sstd5zKkjOylk+/X1j0vI6C93HwkF3d9NwhMoV1zq
+aSj2SmAQCz4mIjMlAFR9mm6F+3sb8xkMFJD2Mypc5dW6TS5krhbhJdMpoVbceZtS
+yuIsvQi1GT2Uwyu89doDiUNBIANqaexrK5x2S/Fy4L8dNl1x2k6PJi6zVpvbnNLV
+TSbuSMp3oY23PpX/m6wzJlCYicO8ucMhPwmC0OL9WJNKny4vuEPdiV6/LwCVS4Vr
+UpfNqgXLzRMJEbx7C2QEG7T6o2g2101oANBuPaDZcwIQ//EI3IkT10JcABIbwsdj
+/Oqj0cf7iEW1IfJWx+kRVT8NfEA5QjL1KQ4HAgMBAAGjaTBnMB0GA1UdDgQWBBS+
+/gUxdwfGB0XcPHsbM6Qw50S2OTAfBgNVHSMEGDAWgBSHd+NjwqkE6hVMEnltx6cT
++D11lzAaBgNVHREEEzARgglsb2NhbGhvc3SHBH8AAAEwCQYDVR0TBAIwADANBgkq
+hkiG9w0BAQsFAAOCAQEAiztFGa3uZVb6Fs8evN4CU+hPFYdhF57lEfT6Xa1OUD1B
+5e5rDOZfVloy4gzLdtNCS9lieTgB7Yc3wDUCm0cS48JCMxykSRTI6M0Fmofsgd5d
+OKR7CB+jR1Egj6nZren62XCgqjuJ+dbP4DinY6TifFzFX1vOD4RTb0mEn2WHL/JB
+DnDxOETmBtKFyueprMtXkTO23dXsYXQeo2Gyih+t5ksqMnxCW2GFkkOqrOUQY8CF
+6CVmmb9UCYk3f3Av9TedqJ8Cmoe+HSP0R2oxpnc7cd1v37QPxWgHETro9zS4FBrt
+6KgNTP99b+aLxeeuMKJuRVR+TCZPuyYbBzUfp2ZZOg==
+-----END CERTIFICATE-----
diff --git a/libs/ssl-config/src/test/resources/certs/cert2/cert2.key b/libs/ssl-config/src/test/resources/certs/cert2/cert2.key
new file mode 100644
index 0000000000000..1384b2daf309a
--- /dev/null
+++ b/libs/ssl-config/src/test/resources/certs/cert2/cert2.key
@@ -0,0 +1,30 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,68F4EFD94D4D9BDF
+
+TL01d1JqaN0P1sb8284jOGpLn42cubkLY7JPj+bmpL2PEH9cT2xo7MM5cwvNSbPX
+nuZ1CMFSFqAyxulh/rfZwU1BumKLiwM+ep0A/lWwKJEor+ancCBdkIHWfSg3Jc0i
++nHhL3e7+W+XRfSlafQQkhFkeOXs0hSa4WYX6tymvMAf8OLAenKB/0MOvKjd6EOu
+ZIvnBxAjAaDoRr4X4izIVlNmsIbvYARW9WjjcK2FZhNn+WLLTkfwEkl0glRl440L
+ET9qAjoH7j5KL5yKw5h2HhALSLaXdiAfCGvhpU4K0Afpb225mvR/uL3HLpJEGKKb
+DuoK99zvjHqReq1YndIR688ioc0O4dUvujZKgn8OR2JvGJVSc+mgaIWadfs2aADz
+CJIlqnSJa1EW6qm6knLJwieEBoNHbeFszrCKrYdy7uicys9PR1XsoMrOly+HhVnR
+PChA3gV6AVIjyMAUkFLg4NAHjgDbb81lu8ENFmlJcjgVeXDMykrpBmhzmhSPtOEb
+6bdQCKuA7zXIpJCmP66ZSuNHxikrfqLJjXW3PojLuCx5nfO0akpxjSeyLHzv/YXV
+YxnpLJxMybG1KUHyDHRmDrd+UPzLnh52O/g9NoiAlluUcblH+BKer18dJdGSl95G
+P+Ted08S0yP2niNz6XHv5KbztzsP73n0w82akCQB8ZAsGJ0CNw7S7N6tWam/UkMN
+tvM9nFCjvwah1funu8nj9QyWrzac6ngPP0s8tcKG0ahLEJYn7Hx2Oqn7K39fbMkX
+UOJVNEBQP2Anf5dJMfYPEI0xihv3ec1RxpipOm9DKQ878jxnqxgZxa+Pab9j/JxQ
+j+BGaoOnYj2jHBnt4nCP75F0BEZaGxsZX2MjJySK+5jy2WW3JRC/E0qPkL6oBvT2
+3e37fep4XKSjqR9lSYjW+AlAVw2uPkwxDp5sD7XFGsH3SM1qj54NWwljpKXnOTbQ
+Xws18VEiWsQ3kD7ft11w8/67Bb27TsWEJRo1vCC4KsYBCyjEOrp13aJxpSiNI24W
+oSmrQTsCco1Yrxfs0noNu0FzfJHV6qmHR7ps0fj+AWJyquYbIEK1762+r2uutgSg
+ZVaPWLkm9uq5K5rNXsj3w1L1CK4YcDre0yt+Kg4Pt3OQR2lF8CpABguA3gR9kO2g
+9N8hAfiOq5MDliU+9r6Cr/dPkdzV0Eo971HdfaLD7S/pjSP37fcTcOpDhwNt3UcK
+amhR/Zm8Ll414zc/iNiVTcu6+chzSY9Yhwfa8A/XuIfoqMrTiaBMPlxe0tNKaKVs
+09d6U5JoTQJcnei/4ODkIIYOzsmMtHgKtmeg86AB8yeLNgqWCrJi+Fxnha+cJ9No
++STMQP4vS8qgRe5XkRGaAZBHyPAwxOou1Iu167LjlFFl0YSYUZTChha5XBG2Uhm8
+FeIH1Ip+83fxMoyiYROOdeMuLXtQIndA3fmjduBEO0kPtAwQ2xfH4g59XnnSL97S
+9AslnPnUWzDR0zBr4GQBNAKLaMIFGzhDZEzaooYetoeDYSczil/Rf1D6wyM6Cgjq
+BK7c0kNum9uXaDbYCh6spzYua0j1noqsBTm9V25178lNbkQ6yAPdeYCxRmUXHtXk
+-----END RSA PRIVATE KEY-----
diff --git a/libs/ssl-config/src/test/resources/certs/cert2/cert2.p12 b/libs/ssl-config/src/test/resources/certs/cert2/cert2.p12
new file mode 100644
index 0000000000000000000000000000000000000000..269e855f7709843933975c58237541d8cec052b3
GIT binary patch
literal 2456
zcmY+Ec{CJ^8pelN80%OX+eA#Zu?^Wve8^7L>^o&F8A)XuW(pPAVvLeRSz_!lmI>J=
zjj}|xK1+k_TOmH&zH{!q-@Sjl=RN27o%8(nd(Z@E7BdqNO@O*Upr}NX#63B8vl+B&;;Q0KL}Dl01o_)=*&!j->>^0!h}XZ*#6zX!305bfmk<1=CtE^n1Yy@
zSpo3`AW83s$S_;38S{pdZ6Ev{f}1ZZ&~Mf=5*TKCVa|X2%+h^5`e_;K8Vkg39;Tvo
zF>KV8k4*K+4-P?Ks8lgC?aP=w9{@s?AI`LqT`$%LmeT7@vCWy)sqUmpv|f5|#uw9f
z_RUuXm!o4x>qUhKnO`HB=Ply3a4+PhoD*;_ed036(E{tW!PD?|AcNU(eP3
z{kB2xxcfS!im;N{nC2aMH>;&@a*mZL{E1hW1+a=;g6YIyZrAw_UIrKnPM_iJvfd)g
zH({P3Ge=)&h2z{$vn9&5*c=csQp3p+{W;(W5q9YciO(wO{JFO$hEG=2P3Vpm-|S6Q
zJb(qU7(*rCk;JJWxuS{FcB{JYC7Oho@z*>rhl6QT4^uR4XlH8lm
zCB|PzlQbTujqCb&LJFd!t34`31sPr~0k&&7@Z(s+>&r%wcwHYzPA@xJUdp|O?c>AO
zXSR%ye-3Qf>G=RAMWE~sr2Phm^|D>d@oZ?9n|f`S_k{eAjPL+n8P@1<^m+YQKh)8I
zSpLaY+__T!k0_&N!2nGV<(^(kLL*mP{6NIENurz`DtFKe*BMpzG1_`>(zhGfJ4#5`
zlXf!E$V9&B24?Oj+mUrH*5693%XKLa)8~y?iZZh;
z)>SApy!I$2WD!3=lJ2>Y+AB^5&w3>|?5NG80qlwsSvkXH5GL=&=*{(0L~d=XG22$5
zbNd0;Ha-rPkI^q`aKE_Q^*AFf?k2!P;ymXlmFO7zSr+E-_H6SGxQ>OPLoowot&_fk
z1@&68QFMQN!vgsYehOv}dC=1=f!ClMVlAA&f=lWFqZ@$Dp66?A1D>-+>|;31)v5;f
z?shii{t7p28Z)$OJ@!_F>B+-0rx-6+Dw(EoX$*^YT4LYzEobY^hZVHI-W|7@Qn0+8
z^suXaKLim~;K+O0bEc9X@*uyRv#He7{!X4l&f?px%qhndP*FJjR5iQ7E~4I(Zw`Tr%sC3nOR(*zJErE+Bv{K?Jbr
z@A%U1`vb!MAFbJ#m1{yUiTp>C&G#n-zV37xz9em#n`$auM#5F?|$}e8L~zB2rdba
zcx!ll^0j)dnh8sf4Vk9*S3--c=8}4&jYFSg6FuLr1eyA)=+YpG72!Hp2Y7(;?4<)<8v<`7aSDyUepg5Uj1Eb_L@
ztC&P`K$p!e;g6PTaO^#AaPdHNQAM5LklcjYwb?7wXQS*xkPO!oHvf6P$ezv^m=(0o
zQ@54Y5j(k&E>RJb%6XEc-GQR;5a}|KlTtj_+B752OcUEioqrw~E;ln_8Dr7H{txb_
z!K8|x8{c;9eKln+zoMj?J~_|4M9jdaUws{yYIp;ynbgfl0loimcbUU$jANP4M}re~
zg`x0N$S`o*i_o;p61^V^OAn3|bx&&}Dd}&nYm9L~A+p?LRf}+NtLIR8#=9V1|I
zfpndN^ydzk%uEyO;Yxbws5HZ;G~Y$_3th67o>8L~o+0<`Q_6+VhGhc5b!N=IPi1=8
z?V9v}8Fz#i
zi@7r5;NGQg&uWMGvb8|rBd~MHw{=gN2X=H#7gw6e(|)?ZGDL*jwoS?gt|vdP^ZK;y
zSBjz+_DTnpM92uEoGH)wjj1j8J)m2boQWruZ(MXZ-1?-o8ha
z4aQdbL}ZbW`tD@hESJFU)9Z~W%)*IZo9b0;$zSqZ8B@w%z?ur29F~*DcauPfcL}ar
z_x#YvaSQpenhu?jbEfO1@dNfAT3b?(O#Q_tpGGo@mZ~;%#CIVP)_x|jWn9)xO<_Zz
z$RD#%dt5yqb^IasJc^={i6oSjzx4hE#_55E)>gRgD_REZhOrAvginLQdz5UHfK_h~
zBFbPcOsb5!AmGL4hfX-S{!h(WD}tXvoySA=tSTMRn~+}7CPM?)c|T31!xG1KxWIaQ
z(BQPx%8`c_k2K=^u1sBsvC?9C4YG4u3}s03FK=Igb;dJb&q3afDmz4rfyyGyvo1Bo
zT!7k6!zotNtb1R97ekSQf|dslIFiN`4O&q1^47Nk{g<3leLktjJuc*pg!lL2@bf18
zqdpELx@;TrqH6kJ!O#diwxMm+UrYR4rQ8toE?1^`ShGmk5)?cqmSg+9`+)kzJzlq>
zhDVA9KhAkv#y-3)m#|_F4#6EHqi`(MtO1e;^(yZxPq3mHzM*k<6}GUi1$Hi<_EuCk
zC2U2f#9IX{)EyTxwU)N`7(r+)u=7bn{5oG+I=pLDRCkLf`r}{WWK*_`emb9jzY5Ps
zb;ug<{#af;-~i*@N>kIq?LMy<*CXeL*)C7xVWg7c0=xXwK1s>!UiA+zXsi3kZ9+G}
zD*~nJVsUY7GQ(9_4=wcHb%gRObBMA5d;$J|8-SYt6*Tf6bV?Zx;ums@I}D!mOI99P
z6(T0`&T^bOxrkOqi=&|supAeVSr`le@h^r~f%Vd8(uH}--#=7cD5XGjk3g(b)tykr
Kqh$NP2lQWnTyQ1;
literal 0
HcmV?d00001
diff --git a/libs/ssl-config/src/test/resources/certs/pem-utils/README.asciidoc b/libs/ssl-config/src/test/resources/certs/pem-utils/README.asciidoc
new file mode 100644
index 0000000000000..0136e967106e1
--- /dev/null
+++ b/libs/ssl-config/src/test/resources/certs/pem-utils/README.asciidoc
@@ -0,0 +1,149 @@
+= Keystore Details
+This document details the steps used to create the certificate and keystore files in this directory.
+
+== Instructions on generating self-signed certificates
+The certificates in this directory have been generated using the following openssl configuration and commands.
+
+OpenSSL Configuration File is located in this directory as `openssl_config.cnf`.
+
+NOTE: The `alt_names` section provides the Subject Alternative Names for each certificate. This is necessary for testing
+with hostname verification enabled.
+
+[source,shell]
+-----------------------------------------------------------------------------------------------------------
+openssl req -new -x509 -extensions v3_req -out .cert -keyout .pem -days 1460 -config config.cnf
+-----------------------------------------------------------------------------------------------------------
+
+When prompted the password is always set to the value of .
+
+Because we intend to import these certificates into a Java Keystore file, they certificate and private key must be combined
+in a PKCS12 certificate.
+
+[source,shell]
+-----------------------------------------------------------------------------------------------------------
+openssl pkcs12 -export -name -in .cert -inkey .pem -out .p12
+-----------------------------------------------------------------------------------------------------------
+
+== Creating the Keystore
+We need to create a keystore from the created PKCS12 certificate.
+
+[source,shell]
+-----------------------------------------------------------------------------------------------------------
+keytool -importkeystore -destkeystore .jks -srckeystore .p12 -srcstoretype pkcs12 -alias
+-----------------------------------------------------------------------------------------------------------
+
+The keystore is now created and has the private/public key pair. You can import additional trusted certificates using
+`keytool -importcert`. When doing so make sure to specify an alias so that others can recreate the keystore if necessary.
+
+=== Changes and additions for removing Bouncy Castle Dependency
+
+`testnode-unprotected.pem` is simply the decrypted `testnode.pem`
+------
+openssl rsa -in testnode.pem -out testnode-unprotected.pem
+------
+
+`rsa_key_pkcs8_plain.pem` is the same plaintext key encoded in `PKCS#8`
+------
+openssl pkcs8 -topk8 -inform PEM -outform PEM -in testnode-unprotected.pem -out rsa_key_pkcs8_plain.pem -nocrypt
+------
+
+`testnode-aes{128,192,256}.pem` is the testnode.pem private key, encrypted with `AES-128`, `AES-192` and `AES-256`
+respectively, encoded in `PKCS#1`
+[source,shell]
+------
+openssl rsa -aes128 -in testnode-unprotected.pem -out testnode-aes128.pem
+------
+[source,shell]
+------
+openssl rsa -aes192 -in testnode-unprotected.pem -out testnode-aes192.pem
+------
+[source,shell]
+------
+openssl rsa -aes256 -in testnode-unprotected.pem -out testnode-aes256.pem
+------
+
+Adding `DSA` and `EC` Keys to the Keystore
+
+[source,shell]
+------
+keytool -genkeypair -keyalg DSA -alias testnode_dsa -keystore testnode.jks -storepass testnode \
+ -keypass testnode -validity 10000 -keysize 1024 -dname "CN=Elasticsearch Test Node" \
+ -ext SAN=dns:localhost,dns:localhost.localdomain,dns:localhost4,dns:localhost4.localdomain4,dns:localhost6,dns:localhost6.localdomain6,ip:127.0.0.1,ip:0:0:0:0:0:0:0:1
+------
+[source,shell]
+------
+keytool -genkeypair -keyalg EC -alias testnode_ec -keystore testnode.jks -storepass testnode \
+ -keypass testnode -validity 10000 -keysize 256 -dname "CN=Elasticsearch Test Node" \
+ -ext SAN=dns:localhost,dns:localhost.localdomain,dns:localhost4,dns:localhost4.localdomain4,dns:localhost6,dns:localhost6.localdomain6,ip:127.0.0.1,ip:0:0:0:0:0:0:0:1
+------
+
+Exporting the `DSA` and `EC` private keys from the keystore
+
+[source,shell]
+----
+keytool -importkeystore -srckeystore testnode.jks -destkeystore dsa.p12 -deststoretype PKCS12 \
+ -srcalias testnode_dsa -deststorepass testnode -destkeypass testnode
+----
+[source,shell]
+----
+openssl pkcs12 -in dsa.p12 -nodes -nocerts | openssl pkcs8 -topk8 -nocrypt -outform pem \
+ -out dsa_key_pkcs8_plain.pem
+----
+[source,shell]
+----
+keytool -importkeystore -srckeystore testnode.jks -destkeystore ec.p12 -deststoretype PKCS12 \
+ -srcalias testnode_ec -deststorepass testnode -destkeypass testnode
+----
+[source,shell]
+----
+openssl pkcs12 -in ec.p12 -nodes -nocerts | openssl pkcs8 -topk8 -nocrypt -outform pem \
+ -out ec_key_pkcs8_plain.pem
+----
+
+
+
+Create `PKCS#8` encrypted key from the encrypted `PKCS#1` encoded `testnode.pem`
+[source,shell]
+-----
+openssl pkcs8 -topk8 -inform PEM -outform PEM -in testnode.pem -out key_pkcs8_encrypted.pem
+-----
+[source,shell]
+-----
+ssh-keygen -t ed25519 -f key_unsupported.pem
+-----
+
+
+Convert `prime256v1-key-noparam.pem` to `PKCS#8` format
+-----
+openssl pkcs8 -topk8 -in prime256v1-key-noparam.pem -nocrypt -out prime256v1-key-noparam-pkcs8.pem
+-----
+
+Generate the keys and self-signed certificates in `nodes/self/` :
+
+------
+openssl req -newkey rsa:2048 -keyout n1.c1.key -x509 -days 3650 -subj "/CN=n1.c1" -reqexts SAN \
+ -extensions SAN -config <(cat /etc/ssl/openssl.cnf \
+ <(printf "[SAN]\nsubjectAltName=otherName.1:2.5.4.3;UTF8:node1.cluster1")) -out n1.c1.crt
+------
+
+
+Create a `CA` keypair for testing
+[source,shell]
+-----
+openssl req -newkey rsa:2048 -nodes -keyout ca.key -x509 -subj "/CN=certAuth" -days 10000 -out ca.crt
+-----
+
+Generate Certificates signed with our CA for testing
+[source,shell]
+------
+Â openssl req -new -newkey rsa:2048 -keyout n2.c2.key -reqexts SAN -extensions SAN \
+ -config <(cat /etc/ssl/openssl.cnf <(printf "[SAN]\nsubjectAltName=otherName.1:2.5.4.3;UTF8:node2.cluster2"))\
+ -out n2.c2.csr
+------
+
+[source,shell]
+------
+openssl x509 -req -in n2.c2.csr -extensions SAN -CA ca.crt -CAkey ca.key -CAcreateserial \
+ -extfile <(cat /etc/ssl/openssl.cnf <(printf "[SAN]\nsubjectAltName=otherName.1:2.5.4.3;UTF8:node2.cluster2"))\
+ -out n2.c2.crt -days 10000
+------
diff --git a/libs/ssl-config/src/test/resources/certs/pem-utils/corrupted_key_pkcs8_plain.pem b/libs/ssl-config/src/test/resources/certs/pem-utils/corrupted_key_pkcs8_plain.pem
new file mode 100644
index 0000000000000..4b2271a6f3971
--- /dev/null
+++ b/libs/ssl-config/src/test/resources/certs/pem-utils/corrupted_key_pkcs8_plain.pem
@@ -0,0 +1,24 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDesZnVBuxbT4y7
+KtIuYx8MUq0sGQgVbxXSBG66sWDU9Qoo1HUyra0xXCONgRMBT9RjSIpk7OOC9g8q
+ENNgFO179YdHVkrgJhW/tNBf+C0VAb+B79zu7SwtyH2nt9t378dmItL+sERkMiiG
++BS/O+cDz44hifDiS7Eqj/mJugAhLjWSUyD+UBObxXvUsxjryKeG3vX9mRCgAcqB
+xH3PjI1i9DVaoobwMbwpE5eW2WXexOspuXnMmGfrrR6z/VmdHqe/C3rGdJOX+Y0c
+yOR+/Vuzisn+nLeo/GJx2hIif8rKiNRyAdUXfx+4DLYJBN2NUbl9aP2LP6ZC8ubf
+6qwhhB0XAgMBAAECggEBAKuzP6qSNfaJNTayY2/EmRHFRSP1ANiV17sgE8f6L3DC
+pdypQtuaMSkXo4nc9SxTwqvyKFJ8m0ZENZj3dCJmwFyNCIqmLAD7HFW9MdRs40WJ
+HYEv0aaeUyvRo6CHD74/r/w96XTZr0GZssmtyUFRDGNRyoJter7gIW9xprLcKHFr
+YTmdaAXbOm5W/K3844EBouTYzYnZYWQjB3jT/g5dIic3AtLb5YfGlpaXXb74xTOU
+BqY1uKonGiDCh0aXXRl2Ucyre6FWslNNy4cAAXm6/5GT6iMo7wDXQftvtyK2IszP
+IFcOG6xcAaJjgZ5wvM3ch0qNhQi4vL7c4Bm5JS9meoECgYEA88ItaVrfm2osX/6/
+fA8wYxxYU5RQRyOgLuzBXoRkISynLJaLVj2gFOQxVQeUK++xK6R182RQatOJcWFT
+WwmIL3CchCwnnXgPvMc51iFKY94DbdvrRatP8c5sSk7IQlpS3aVa7f7DCqexggr5
+3PYysuiLirL+n9I1oZiUxpsS6/cCgYEA6eCcDshQzb7UQfWy//BRMp7u6DDuq+54
+38kJIFsPX0/CGyWsiFYEac8VH7jaGof99j7Zuebeb50TX57ZCBEK2LaHe474ggkY
+GGSoo3VWBn44A1P5ADaRGRwJ4/u79qAg0ldnyxFHWtW+Wbn11DoOg40rl+DOnFBJ
+W+bWJn4az+ECgYEAzWduDt5lmLfiRs4LG4ZNFudWwq8y6o9ptsEIvRXArnfLM3Z0
+Waq6T4Bu1aD6Sf/EAuul/QAmB67TnbgOnqMsoBU7vuDaTQZT9JbI9Ni+r+Lwbs2n
+tuCCEFgKxp8Wf1tPgriJJA3O2xauLNAE9x57YGk21Ry6FYD0coR5sdYRHscCgYEA
+lGQM4Fw82K5RoqAwOK/T9RheYTha1v/x9ZtqjPr53/GNKQhYVhCtsCzSLFRvHhJX
+EpyCLK/NRmgVWMBC2BloFmSJxd3K00bN4PxM+5mBQZFoHMR04qu8mH/vzpV0h2DG
+Mm9+zZti+MFRi0CwNz2248T4ed8LeKaARS1LhxTQEkECgYBFsPNkfGWyP4zsgzFs
diff --git a/libs/ssl-config/src/test/resources/certs/pem-utils/dsa_key_openssl_encrypted.pem b/libs/ssl-config/src/test/resources/certs/pem-utils/dsa_key_openssl_encrypted.pem
new file mode 100644
index 0000000000000..a251de23f4879
--- /dev/null
+++ b/libs/ssl-config/src/test/resources/certs/pem-utils/dsa_key_openssl_encrypted.pem
@@ -0,0 +1,15 @@
+-----BEGIN DSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,BE9A0B63873F6B7A
+
+lGSpJkwN0J9p+2Wm58706EYz6mmjgz7okjMtsR87GMIiK/wVwjKmyUa73QTVVs15
+N/EOySftBk3VUSPx9G1ZMxKpp3l/hvkIcsDDfCPAZFqwdQQJ8BEeF9jDd5ZoI6Yz
+Yus1+X8A1OpX1O7PCZ08e2fLeVuEWg62/JQcNukuvL7AKm+qa1sda5/ktquv2eMZ
+nbTiOE3Xe+uDsgABQdy1h4EsMEaMdE6QrWdxLGWDGcdzSzfltvnhmmsK2CQsV4e1
+huQeb8ylShJuIr+mgtKgUlIlJwSd7ka8hIdmGt1LO9+NZOPUGN04daQkETtfwsmu
+YIYkh66CuLbT4nZny64Spa7AeINSmf9GA72/QtRSo3M7Khlw/95Lz24iKAy7/Lbt
+AKYenSQeJtlNgWzPcDIeUrIzXXmAXHN5YGMg/7X0h7EGu5BxYbLydkBRvSkV9gzU
+Ms6JD5aON10DQhjIUwUcBnhSnwPPpIVa2xf9mqytkcg+zDgr57ygZ9n4D+iv4jiC
+ZJuFCFrgeqHrCEKRphWRckyhPo25ix9XXv7FmUw8jxb/3uTk93CS4Wv5LK4JkK6Z
+AyF99S2kDqsE1u71qHJU2w==
+-----END DSA PRIVATE KEY-----
diff --git a/libs/ssl-config/src/test/resources/certs/pem-utils/dsa_key_openssl_plain.pem b/libs/ssl-config/src/test/resources/certs/pem-utils/dsa_key_openssl_plain.pem
new file mode 100644
index 0000000000000..a64642fc9ab0c
--- /dev/null
+++ b/libs/ssl-config/src/test/resources/certs/pem-utils/dsa_key_openssl_plain.pem
@@ -0,0 +1,12 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIBuwIBAAKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADDHj+AtlEmaUVdQCJR
++1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gEexAiwk+7qdf+t8Yb
++DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/IiAxmd0UgBxwIVAJdg
+UI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jrqgvlX
+TAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCj
+rh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQB
+TDv+z0kqAoGAd0xuuUUSAXsXaQ/dp9ThBTVzdVhGk6VAcWb403uMXUyXKsnCIAST
+m6bVWKjNxO1EsP3Slyd5CwbqIRUBK5NjzdQP/hHGtEIbqtYKY1VZI7T91Lk8/Dc/
+p9Vgh27bPR8Yq8wPKU3EIJzYi0Nw8AxZf10yK+5tQ6pPUa3dH6lXt5oCFF1LyfuB
+qBYh7hyIsfkb+cZoQ57t
+-----END DSA PRIVATE KEY-----
diff --git a/libs/ssl-config/src/test/resources/certs/pem-utils/dsa_key_openssl_plain_with_params.pem b/libs/ssl-config/src/test/resources/certs/pem-utils/dsa_key_openssl_plain_with_params.pem
new file mode 100644
index 0000000000000..0a2ea861b9b66
--- /dev/null
+++ b/libs/ssl-config/src/test/resources/certs/pem-utils/dsa_key_openssl_plain_with_params.pem
@@ -0,0 +1,18 @@
+-----BEGIN DSA PARAMETERS-----
+ThisisnotvalidabutwedontparseiteitherwaykFJyVA+0q1vAej5iQVmUvu1y
+fuA5axTA5IT86U7acP0KV8eawbDXVhqiP0zcSeP731coxJaUHC6XB0FVMhYi4fZn
+fexykg9Kxe/QBfDtcj3CEJNH/xoptJQVx3hi+0BPPK8+eUXTjwkQerGMwUD7UQak
+xuUS/22GakHZV5G/kCc=
+-----END DSA PARAMETERS-----
+-----BEGIN DSA PRIVATE KEY-----
+MIIBuwIBAAKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADDHj+AtlEmaUVdQCJR
++1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gEexAiwk+7qdf+t8Yb
++DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/IiAxmd0UgBxwIVAJdg
+UI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jrqgvlX
+TAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCj
+rh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQB
+TDv+z0kqAoGAd0xuuUUSAXsXaQ/dp9ThBTVzdVhGk6VAcWb403uMXUyXKsnCIAST
+m6bVWKjNxO1EsP3Slyd5CwbqIRUBK5NjzdQP/hHGtEIbqtYKY1VZI7T91Lk8/Dc/
+p9Vgh27bPR8Yq8wPKU3EIJzYi0Nw8AxZf10yK+5tQ6pPUa3dH6lXt5oCFF1LyfuB
+qBYh7hyIsfkb+cZoQ57t
+-----END DSA PRIVATE KEY-----
diff --git a/libs/ssl-config/src/test/resources/certs/pem-utils/dsa_key_pkcs8_plain.pem b/libs/ssl-config/src/test/resources/certs/pem-utils/dsa_key_pkcs8_plain.pem
new file mode 100644
index 0000000000000..fc5f17ce89897
--- /dev/null
+++ b/libs/ssl-config/src/test/resources/certs/pem-utils/dsa_key_pkcs8_plain.pem
@@ -0,0 +1,9 @@
+-----BEGIN PRIVATE KEY-----
+MIIBSwIBADCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdS
+PO9EAMMeP4C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVCl
+pJ+f6AR7ECLCT7up1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith
+1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7L
+vKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3
+zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImo
+g9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoEFgIUXUvJ+4GoFiHuHIix+Rv5xmhDnu0=
+-----END PRIVATE KEY-----
diff --git a/libs/ssl-config/src/test/resources/certs/pem-utils/ec_key_openssl_encrypted.pem b/libs/ssl-config/src/test/resources/certs/pem-utils/ec_key_openssl_encrypted.pem
new file mode 100644
index 0000000000000..69dfde4b3c502
--- /dev/null
+++ b/libs/ssl-config/src/test/resources/certs/pem-utils/ec_key_openssl_encrypted.pem
@@ -0,0 +1,7 @@
+-----BEGIN EC PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,692E4272CB077E56A0D4772B323EFB14
+
+BXvDiK0ulUFKw1fDq5TMVb9gAXCeWCGUGOg/+A65aaxd1zU+aR2dxhCGXjsiLzRn
+YFSZR2J/L7YP1qvWC7f0NQ==
+-----END EC PRIVATE KEY-----
diff --git a/libs/ssl-config/src/test/resources/certs/pem-utils/ec_key_openssl_plain.pem b/libs/ssl-config/src/test/resources/certs/pem-utils/ec_key_openssl_plain.pem
new file mode 100644
index 0000000000000..e1d0a6a8319c0
--- /dev/null
+++ b/libs/ssl-config/src/test/resources/certs/pem-utils/ec_key_openssl_plain.pem
@@ -0,0 +1,4 @@
+-----BEGIN EC PRIVATE KEY-----
+MDECAQEEILEXCgqp9wZqKVmG6HTESPeCyx2O4TDoFqyILz7OGocEoAoGCCqGSM49
+AwEH
+-----END EC PRIVATE KEY-----
diff --git a/libs/ssl-config/src/test/resources/certs/pem-utils/ec_key_openssl_plain_with_params.pem b/libs/ssl-config/src/test/resources/certs/pem-utils/ec_key_openssl_plain_with_params.pem
new file mode 100644
index 0000000000000..2ad57473236b3
--- /dev/null
+++ b/libs/ssl-config/src/test/resources/certs/pem-utils/ec_key_openssl_plain_with_params.pem
@@ -0,0 +1,7 @@
+-----BEGIN EC PARAMETERS-----
+Notvalidbutnotparsed
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MDECAQEEILEXCgqp9wZqKVmG6HTESPeCyx2O4TDoFqyILz7OGocEoAoGCCqGSM49
+AwEH
+-----END EC PRIVATE KEY-----
diff --git a/libs/ssl-config/src/test/resources/certs/pem-utils/ec_key_pkcs8_plain.pem b/libs/ssl-config/src/test/resources/certs/pem-utils/ec_key_pkcs8_plain.pem
new file mode 100644
index 0000000000000..7e6de54424702
--- /dev/null
+++ b/libs/ssl-config/src/test/resources/certs/pem-utils/ec_key_pkcs8_plain.pem
@@ -0,0 +1,4 @@
+-----BEGIN PRIVATE KEY-----
+MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCCxFwoKqfcGailZhuh0
+xEj3gssdjuEw6BasiC8+zhqHBA==
+-----END PRIVATE KEY-----
diff --git a/libs/ssl-config/src/test/resources/certs/pem-utils/empty.pem b/libs/ssl-config/src/test/resources/certs/pem-utils/empty.pem
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/libs/ssl-config/src/test/resources/certs/pem-utils/key_pkcs8_encrypted.pem b/libs/ssl-config/src/test/resources/certs/pem-utils/key_pkcs8_encrypted.pem
new file mode 100644
index 0000000000000..28059d5a2266d
--- /dev/null
+++ b/libs/ssl-config/src/test/resources/certs/pem-utils/key_pkcs8_encrypted.pem
@@ -0,0 +1,29 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIE6TAbBgkqhkiG9w0BBQMwDgQI2jwlFL0XId0CAggABIIEyMujZbpG6zKb2pVu
+soamTaoLcZwNofS9ncGIEH1nbI8UpPY81VeOIBm4mneDt8RU5bIOXP4IZEZY9uU+
+pugKQ3hT8vBQjJujjuctUPaFxB0kGEeITOInY2jn2BFDbUgy5Z7EVD4G2K06SDDK
+oD+twbzZo9x34VizwpHHb8wE+DFyYc+sp+Re2Qk3FReKgjdJezfcRHbKrrlx2rJ+
+k/YAPmzcFYVbuUiB6HY6BGzSJO1JxT8iNJE+Hmk3ZLXG590hp0vuGSkY/ihbeix4
+1rQs7u4riqXJ+DJBmXt/wXUij0/k6s4igACNsT2MkZkGEDkzqzE+kj2VYOHSX+Wd
+5W0WCfftcsIQ8eow4ACec/Ns9ionLjx1xnbTjRMkpGgbVsreupU9AQ4MhLNNgwyl
+six/cxUxTvH8Modd0/4KQFkeo352A6+DKCaPZ87SoF2Rge1otcJaZVcX1gBvIztB
+/xzYwyUydQEwblU0kCYWRgxlKP9jxFoke2RX1BodRfAMNDxS0XyYrA/JzB7ZRsS7
+QGYPy/PPb014U3KhpJdjwbNu2VaCVdGryYA9+BTP+Vzwcp8MZoMPnnjnBh1YyVAj
+c7oyzKU5e5SVsYni1Kt/536YxQgFCAUHv/g+zQqqGEvyiMXhsCwVpoy7UcFYgmlw
+40g3+ejwvlO3YA67gQQKebEv6/Laz1hVNiXT0m3okAXWxXgF/g2eBO5NTRdtaWn3
+VNH5ub+LOr6cMhk9BAtKgRG+xeh8/2SqH12UbwtlmxqnBAfHtqlE6yJ1ViMDHxF9
+101xJlEONmC3fcEAjShK6LEbFwPJns3WbGc0ds36CzXWtO29XGssr+YoiF9e3Eus
+/XQjmjOJxRoWkNEYsxlJ3IRH2vUcdCoAp8IlD7JYxx8UBCSJDBo7/0QKU6INeWjo
+5+aNbaLAJULSKo1LTZjjANm+G+KcSnbn5Ed8fmY+D61A5/7WMIVxq/uDLFvxCnRG
+QcLbtqbPztiWwWZOuTwNTA3bfAhEG0ZzNr+0z33jW5T9ChvdutgxJMf3Khazx9cx
+mWyCpJwtSv7hSbp4nCS2fmHCum2yIrOnou8TSOlQFERZ3UEZMgLpWeupH/W5C3Ad
+rOspFrK6K8a/iNSs5OdYUIK2iHddTs5u7AEZ9I5MTuYnccuGuXfQTTA06TJvJTax
+c2oDbXMnXs4pHLiiSRp34IHIYubdrj8X5vTODC5djl8h1167ToXo5zGdXqT1om+u
+4ndNLbbI1vld5G7KAL6TlTETg+N7S8v3KYoBEWzykwgqqppWnWTqPWQxM8Iph5ly
+AQlzz7feERi/h/s57RZ5ksoVAdbtk2U6wgHnLrWhKZ7+ZOAfpNAjGHwWyXTzylXo
+zQ9A8Kmd0jBMsru4fsGpldf4lTsqO/abUSWrAAREGnlz/ZjEb944Yox7JUhWC15C
+WxXK2rFbiF3S0HtEvU17rdn4HCsZBilnY+hTpHj1MA6O451/A3ghxGXFKz/9LUcS
+YBRQJaSM3hTqC3WoTVBeVc5nCFOpu4F89JqhEgXOLKweueMbTMRSNm93tXWT13s3
+Q/o0pNJv/K6+bIQwsX/oDafMXcW7STxQJObbAleRbcn8/rGS2eEnVZ6907faUR/L
+7eu9vgAa/jh9FHpZ0Q==
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/libs/ssl-config/src/test/resources/certs/pem-utils/key_unsupported.pem b/libs/ssl-config/src/test/resources/certs/pem-utils/key_unsupported.pem
new file mode 100644
index 0000000000000..96f95848d099f
--- /dev/null
+++ b/libs/ssl-config/src/test/resources/certs/pem-utils/key_unsupported.pem
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACBqIPMG94HL7zedFzsvi45mHS8ZuyLQXqvHpHobcdNCJAAAAJimRM7VpkTO
+1QAAAAtzc2gtZWQyNTUxOQAAACBqIPMG94HL7zedFzsvi45mHS8ZuyLQXqvHpHobcdNCJA
+AAAEBvVc8FVPGUs3LZ1o+LnjW4uUlEnk/5LQQ9yO2eiI3SFGog8wb3gcvvN50XOy+LjmYd
+Lxm7ItBeq8ekehtx00IkAAAAEWlvYW5uaXNAc2VjdXJlYm94AQIDBA==
+-----END OPENSSH PRIVATE KEY-----
diff --git a/libs/ssl-config/src/test/resources/certs/pem-utils/rsa_key_pkcs8_plain.pem b/libs/ssl-config/src/test/resources/certs/pem-utils/rsa_key_pkcs8_plain.pem
new file mode 100644
index 0000000000000..dd1675957f69f
--- /dev/null
+++ b/libs/ssl-config/src/test/resources/certs/pem-utils/rsa_key_pkcs8_plain.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDesZnVBuxbT4y7
+KtIuYx8MUq0sGQgVbxXSBG66sWDU9Qoo1HUyra0xXCONgRMBT9RjSIpk7OOC9g8q
+ENNgFO179YdHVkrgJhW/tNBf+C0VAb+B79zu7SwtyH2nt9t378dmItL+sERkMiiG
++BS/O+cDz44hifDiS7Eqj/mJugAhLjWSUyD+UBObxXvUsxjryKeG3vX9mRCgAcqB
+xH3PjI1i9DVaoobwMbwpE5eW2WXexOspuXnMmGfrrR6z/VmdHqe/C3rGdJOX+Y0c
+yOR+/Vuzisn+nLeo/GJx2hIif8rKiNRyAdUXfx+4DLYJBN2NUbl9aP2LP6ZC8ubf
+6qwhhB0XAgMBAAECggEBAKuzP6qSNfaJNTayY2/EmRHFRSP1ANiV17sgE8f6L3DC
+pdypQtuaMSkXo4nc9SxTwqvyKFJ8m0ZENZj3dCJmwFyNCIqmLAD7HFW9MdRs40WJ
+HYEv0aaeUyvRo6CHD74/r/w96XTZr0GZssmtyUFRDGNRyoJter7gIW9xprLcKHFr
+YTmdaAXbOm5W/K3844EBouTYzYnZYWQjB3jT/g5dIic3AtLb5YfGlpaXXb74xTOU
+BqY1uKonGiDCh0aXXRl2Ucyre6FWslNNy4cAAXm6/5GT6iMo7wDXQftvtyK2IszP
+IFcOG6xcAaJjgZ5wvM3ch0qNhQi4vL7c4Bm5JS9meoECgYEA88ItaVrfm2osX/6/
+fA8wYxxYU5RQRyOgLuzBXoRkISynLJaLVj2gFOQxVQeUK++xK6R182RQatOJcWFT
+WwmIL3CchCwnnXgPvMc51iFKY94DbdvrRatP8c5sSk7IQlpS3aVa7f7DCqexggr5
+3PYysuiLirL+n9I1oZiUxpsS6/cCgYEA6eCcDshQzb7UQfWy//BRMp7u6DDuq+54
+38kJIFsPX0/CGyWsiFYEac8VH7jaGof99j7Zuebeb50TX57ZCBEK2LaHe474ggkY
+GGSoo3VWBn44A1P5ADaRGRwJ4/u79qAg0ldnyxFHWtW+Wbn11DoOg40rl+DOnFBJ
+W+bWJn4az+ECgYEAzWduDt5lmLfiRs4LG4ZNFudWwq8y6o9ptsEIvRXArnfLM3Z0
+Waq6T4Bu1aD6Sf/EAuul/QAmB67TnbgOnqMsoBU7vuDaTQZT9JbI9Ni+r+Lwbs2n
+tuCCEFgKxp8Wf1tPgriJJA3O2xauLNAE9x57YGk21Ry6FYD0coR5sdYRHscCgYEA
+lGQM4Fw82K5RoqAwOK/T9RheYTha1v/x9ZtqjPr53/GNKQhYVhCtsCzSLFRvHhJX
+EpyCLK/NRmgVWMBC2BloFmSJxd3K00bN4PxM+5mBQZFoHMR04qu8mH/vzpV0h2DG
+Mm9+zZti+MFRi0CwNz2248T4ed8LeKaARS1LhxTQEkECgYBFsPNkfGWyP4zsgzFs
+3tMgXnIgl3Lh+vnEIzVakASf3RZrSucJhA713u5L9YB64wPdVJp4YZIoEmHebP9J
+Jt1f9ghcWk6ffUVBQJPmWuRbB/BU8SI+kgtf50Jnizbfm5qoQEt2UdGUbwU3P1+t
+z4SnBvIZ3b2inN+Hwdm5onOBlw==
+-----END PRIVATE KEY-----
diff --git a/libs/ssl-config/src/test/resources/certs/pem-utils/testnode-aes128.pem b/libs/ssl-config/src/test/resources/certs/pem-utils/testnode-aes128.pem
new file mode 100644
index 0000000000000..b4448ec8afaf3
--- /dev/null
+++ b/libs/ssl-config/src/test/resources/certs/pem-utils/testnode-aes128.pem
@@ -0,0 +1,30 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,AD45A956510B909DCCACCE07DE3BA1C2
+
+Vk+KErTbsSdjNO5vaCpik/OLkaOQ4Fm3rNIUrQPMEBiK/TXnHMvC/X1DZenSwA8W
+yHuSpoAAg/HjQv5UskRtn6Rt74ALViM4hO6BleNxr/8lIBZAeLNjqoGwf62MyExV
+rraRhXvYepiTnVSQDYuTafxdIXqzg7O5kYcR46gpphXTjMWDMLxsEiKQ1u51lPFU
+SzxSMGMKiJL3PAXuWyoKgUihw6sv+mVPzq4MVcZKTrlcNRGRFQWUhVzqNd5Qdx/v
+vBUFbWVcMXx4tSsx/WtIOiUwZTbmLk4dpXysb0+Tp6lb+7AQ2RR+9tkBWEdBPUx9
+qkBfFdAvfnA5vKR0SwAZU0dFaDWlQD2ktCJv4hwPN0XYMIv5WW9HoA+R88y+dhHT
+sYgM3eEusQv9byC+XCzxPNg40yC8X9TG2z2deMUl6ippsrTULPx1WaoLf12x1Yl3
+vZ7MFB2hvJmWYofjTVz7Xa3FMH1dhJgBTwpUY//EgPhSaTrEMGwrXJQk40nam/LX
+KjK/acvYmZHZZZJ+E0Pv481tFiiWVlXqfI9Tw1ffi4EzezhQTtzz2EBHaanHNEFa
+C+7XQnxmBoNPpwOBh4Lh9oLcDN9uOGBLb+dIzn2cNQZXhBCKI8IV14YtZGZYhRHg
+D+q7V6I/lEd1WNerHZRNI9o4ZBTJl+7GlJ1gveDTdcx28hCdC5oae6ZwIzSZPuDA
+JPF3vr2yci7JsUpBqnaSnxpz5eKYbng3WjqweBXNgRWLhF8HT8fmWNJyvYjWpg+x
+c8vh/FEM1HY3jsxE8NtIAlObJDMm/K/k8keVbbGm8c58oKdO4kdM+Z6aLe53nFo8
+ISwxsps//eak25Rx2H0bNvO4LVhqNHPXyYQ2nqtx7UpEgndrggHP7n3vcjtdE1f3
+N83gSm6SIVIeQJom16Z5Cjm4PRvJltIf2njpLTeP43eMoYNNVSCr2iZyrSNXnEes
+TI47HidjCNkCp5ahPnuzzyeBCo9L9x2odTNOrga8sBii7VQBE3cGhAFkaUf0E6os
+gpPqUWHkXE9Nb6H6EBR4gwbdpUqcgrm+kp6Ei5N/z7gSfV91WO45WmMLpCPmlPDQ
+An+drt25y+AhaIEmoczUGAiz3jOdyd6Xqw+dVXGb9WPxXL4YnXgr4mSC2am9Vad6
+/MgIqYfqA/AOW1wY2dhoqfAGG2ITadFh82W6cqMhmeDQtDFb6/s96O4e46zev+fP
+Nhro3k+JnL3InC9qAvkxEa/FpbL205X3X3FTXM6xK9ZDvq8+hbPxCjg73mXQfbbG
+0/M8hE5hDgILTPiHhHFzGVNjYTAvjNnttg1n7+A52WGs+Hfwlf2x10p8Y2YwyOon
+qfEMM3G1C3sDzEYmo+w0IZ+pesMWejMPOFiHYRCWVl8r5jx9lTSvbB3Xj+0Ygyo9
+15iLEGyr623I8LDBegqpNntlhX+AeHuJcthPRB6Jl2S0Q1xikD4fW1Ge29/l9Ndi
+7TvZoSGh1jfA71pE1Ay2RyH5PMNj8KJvTGZPFEuIdzDUKlJkC3xUEvl6Q0prU171
+d/ka98AxLR9jUur0ARqxsckd5IXDTlZqsRs8W/gk5FP9RibiN7upiJcKgwYddiJx
+-----END RSA PRIVATE KEY-----
diff --git a/libs/ssl-config/src/test/resources/certs/pem-utils/testnode-aes192.pem b/libs/ssl-config/src/test/resources/certs/pem-utils/testnode-aes192.pem
new file mode 100644
index 0000000000000..4696e77bd3f64
--- /dev/null
+++ b/libs/ssl-config/src/test/resources/certs/pem-utils/testnode-aes192.pem
@@ -0,0 +1,30 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-192-CBC,FACBF3734C8DD3C53F31E294D7E8DC16
+
+9g2VpXQljNeeag2/jh0b1aKE+xcbkNKfIMeUljhiOxULegO53Apn/THshhJhtgPG
+VYRlmk1ImCnwbWiy4C7WVXbOh1yGbYMPLipbtjEI7dr7OPbRX+GYn2Sln6iW9K61
+A019xPz1dLJ4bciNf5gcq5Wf/Qxj8R33ZPqANIHyMeZDSdGqFu+BQyQuQtJqFLkv
+nokev80VIRuxinfmV3RSdUHo3g7iXRNq10bwxV+fns5fyzm5eq4q8Ac0M2NbhWds
+wVl2gft73W41nXFqgS17Xo7cuAIdE07EGXVOq7UGKwLvAkgRWhZEt0BJZKB3XQAs
+GfApMSOfIfTIS0YFjmkbGMKfprc8cgqPyDafKLDAGwViTWfM5oO2duium7OjV80g
+eaL6iAImxFzfg3n8hsHg31iisM5p6d9VegXlY7YacdkFR11LN47nXoFU9l9vtKPG
+TSouB4/0Dw4eCxmfbmJiO4pe8jn4pk4XhMszqc0Q+fRkHXeEigQgFsI4SSkuNk7r
+EPSMqPSHpB5SkLyccfvd/wSBv1DvfdMIA5+CUUj3qAT7pm6tvtj0ZnXXnUVexlfp
+9+mPMrP0oJ8fSX5kQksCbw+a4C+1ffCzU4S1CUVKboopHzbU2LG80XvjPqXGj2OL
+++ghD7OjcD7DqWkO81FQPadrHqWMa8gf2rHmuamZh58LIpattu99lIHVHfFJhYlg
+s8EEJQRLa7V4/1Mx9uZGKNmjHNzw/QGW5VqZ3MoVTuXQ3uKmfsXdUTpGRszkJzU1
+zpIOGOMWctWcCmTXpYEhYfiNcPK/WyHntlQJpUgutX/Pho4Q9dP0U1fgsHiKTcRJ
+IAg3/pdCiv48K3Wx8Ib+J09mx4wP0rYnaT6f3LSTV+O8u+D8swjngDJ9vYOnyBQt
+Z5nYrCpQcvaTGhWAQdz9OqAmPwjY7aLn3hbT0Jf3aFxH3uiWJi0UE3ahLhNWiDTU
+PT1VtQ1fSt/ZpJM6KduR1aBFYcEyPIE/MQq9Y2jcYKrIyc4OqkZBwVOFZtRx8cQ7
+tsy1iY3FJjKllp1VdDKRtPs1oKqyX3k446iYryjZs3cDbWV+H5MSwxh7yqw+j5qE
+XfvhaImoDFAEisep+w2i7nu80D5uNhFr9bHC/MnRCVlzO1HfrNNns1Oncey1ebJL
+PSmpYAiArym6m6fIM9EtTtUrkNUmU0LeqfAaDUmGgtufmmExOtH7/pEuOfbCzoO9
+ZX+TMBRMlOGg55Wc+J597AyEg9mqGKqgoPF8Si2qEElOFYVlaZ88YGPaXKLKI2DA
+T7LXYlf+njThf948xsgM41JxE2VG6Ibo3ucHXFEF+QVk+Arrv8jQEGNc1n6cv4Ep
+ICoWwHAWN4gvACBi6V0C8Mb5V9cRL6hkCsVZUyOGOKm580qiakxmUe+xGHuMW7Cs
+208L5Lsgnn4ynRKLT0yfup73XdQzut/Bkws4ECdDSoSH45VNMR7bdjoGsWkCn5n/
+gbU8PWTPYL907KLpwRBx8fvmOgP2lLBj2gmwyJeowRlzc1MLtsUnH/7H2YSQJgbX
+0ZKIRHASwjpnlL4uhp1QMn9Nj9H++MiJ59q7kUmZBJstlbyAw11HkP4cwCIccNO4
+-----END RSA PRIVATE KEY-----
diff --git a/libs/ssl-config/src/test/resources/certs/pem-utils/testnode-aes256.pem b/libs/ssl-config/src/test/resources/certs/pem-utils/testnode-aes256.pem
new file mode 100644
index 0000000000000..64c765456a1ff
--- /dev/null
+++ b/libs/ssl-config/src/test/resources/certs/pem-utils/testnode-aes256.pem
@@ -0,0 +1,30 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-256-CBC,134008CB231A5AD0F27EB8F6FB18A873
+
+aJRdAed/XZ+Rl6/s/TwOw8rj+sw2ficvnKjCVJj5wt0+qD2NumPpkXmK9J0+SP21
+Mzzm8H0pQRWrI78vwfFXUxUmQMAuavB9k8HuvZtj1b4GvfHrT/BBbs5wS0RPbE6N
+xZuvTvr5UMYFsP85lotcooau3CLtkVXz9ucMQv9v1r2dBvq/7owzl3M+AxhS1oU2
+f8qc3Q411RhVQl29tZha9gidfzBvOO2HH8AqjHxWMHw448oo/b+fXVrpezD1LkmP
+0JxP+kJDt1KCiwXj7oRAMaHbHemA2HS713TK+6HammQroF0PCB33Dasy6zaPmP5G
+HiJAHvBiblc+vCT7D1lUQCmbjRmeoSESq/P3l8Jhag+wT8SSm5nGaiX7aYHqc00Q
+17Gw5e8/iWOU+c3DjCH5qXZFxVrpJgSvVBrrnF3y4sQCG41QpPC7X3mWYWLHZ5vX
+GxcI4f1aJ+jECDTvdpE9KL6ncZ05p3A3wr+FqrDPJTb+S1mpD0f6lRhnKILXK83N
++EbRVRTCH5QIx5ZepX28ykuZQa6vHGtnL9WXLX4ZgAIe2abMA5hNs72Hi47LUrss
+lA3gMdydKA/WtoimBLqb9brEy9qFsP/2YatKnyXYkjeCgtTQ5LELWSqnFkzQ51wk
+VPhT8SqXcPIe9rrNmf7xwJvcZ0IS4tEkT+TovFAs1lo86bCx7VKfWfxcWG/FvW1L
+5/1tU4uhpXLOjhOvWOx56zqxt9RORMw3SEh3At4vVHqT2xQAStT1d9QU0/QiM3EE
+pBf9uQjRfzlwXph6Gs8XmQYLjSwHurT8hrkoa4/czhE4v7BTst+q6fB3gtxOgV6z
+GVBsRK0Lz0ldd56UvnzyChUpE8EFE/Kv6P7T8cgTPnTcGchO4hcKyC31doAFn0pU
+LURMC5szvRUEHbPriz9/9qeHBLFMAmGkCfXpwjNoynsKA7/VlAd/44CP82Ljd8Fb
+PdwXjz8JNAL+gg3q8Xz4S+z6ZNXVJ1U9GDxjesp7QRbhl1J/ynsGyqIADUmPKjyk
+8yFihQYBiZdgiYaOBl9F2X0SINUKaANmVO7HJG+WbPs68fcObfFHRWugC7FljY+b
+Az6tNhkKVerCXBEMsZ9XNY05SsyAvcKsWcJbxon9ecIeu7/N8k9eseUL0xQg1oQX
+L6wjgmS2ckpPnKVFPXhujZb45PtYEA2ObGd6fPV+82cSgfFM6sPorAmmFhThBXa+
+nE8o72MPVvdUFas3Fs7YugxeFTh9jO4zp/3XA+fFfpxPQbwWjnjxS87OAB+AF6iy
+Ul/jZP46kDOnyLdMLvSf5Oq8A73bdGa/09ODsoWjrXlyYmfUZPKPGQ5Hbs5cUSvs
+GciJvb3o3OYfSjkn6DVF95f53TiJ9pbGY+zG85f/F3BwbqpRmNYLyxvl4ZzjLs+U
+PN24gC78ROzgvHAhY0Ta6PQw8SN5FEoQGmOQT2otZc+Apu1J1Z85mpxd0dYPh29m
+kWvx13gZSGxCvNttqfqcRQJTOerQ4PRIyMDJG/sou8hDU51X9USAfjs1spuE6X/a
+PIGNpM2TIOaqU/IIFJrGx01vVBhYGvYF8D/q+wwwnjJGYQl7Hscc+JdFmhWE0T2R
+-----END RSA PRIVATE KEY-----
diff --git a/libs/ssl-config/src/test/resources/certs/pem-utils/testnode-unprotected.pem b/libs/ssl-config/src/test/resources/certs/pem-utils/testnode-unprotected.pem
new file mode 100644
index 0000000000000..1602461b11517
--- /dev/null
+++ b/libs/ssl-config/src/test/resources/certs/pem-utils/testnode-unprotected.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEA3rGZ1QbsW0+MuyrSLmMfDFKtLBkIFW8V0gRuurFg1PUKKNR1
+Mq2tMVwjjYETAU/UY0iKZOzjgvYPKhDTYBTte/WHR1ZK4CYVv7TQX/gtFQG/ge/c
+7u0sLch9p7fbd+/HZiLS/rBEZDIohvgUvzvnA8+OIYnw4kuxKo/5iboAIS41klMg
+/lATm8V71LMY68inht71/ZkQoAHKgcR9z4yNYvQ1WqKG8DG8KROXltll3sTrKbl5
+zJhn660es/1ZnR6nvwt6xnSTl/mNHMjkfv1bs4rJ/py3qPxicdoSIn/KyojUcgHV
+F38fuAy2CQTdjVG5fWj9iz+mQvLm3+qsIYQdFwIDAQABAoIBAQCrsz+qkjX2iTU2
+smNvxJkRxUUj9QDYlde7IBPH+i9wwqXcqULbmjEpF6OJ3PUsU8Kr8ihSfJtGRDWY
+93QiZsBcjQiKpiwA+xxVvTHUbONFiR2BL9GmnlMr0aOghw++P6/8Pel02a9BmbLJ
+rclBUQxjUcqCbXq+4CFvcaay3Chxa2E5nWgF2zpuVvyt/OOBAaLk2M2J2WFkIwd4
+0/4OXSInNwLS2+WHxpaWl12++MUzlAamNbiqJxogwodGl10ZdlHMq3uhVrJTTcuH
+AAF5uv+Rk+ojKO8A10H7b7citiLMzyBXDhusXAGiY4GecLzN3IdKjYUIuLy+3OAZ
+uSUvZnqBAoGBAPPCLWla35tqLF/+v3wPMGMcWFOUUEcjoC7swV6EZCEspyyWi1Y9
+oBTkMVUHlCvvsSukdfNkUGrTiXFhU1sJiC9wnIQsJ514D7zHOdYhSmPeA23b60Wr
+T/HObEpOyEJaUt2lWu3+wwqnsYIK+dz2MrLoi4qy/p/SNaGYlMabEuv3AoGBAOng
+nA7IUM2+1EH1sv/wUTKe7ugw7qvueN/JCSBbD19PwhslrIhWBGnPFR+42hqH/fY+
+2bnm3m+dE1+e2QgRCti2h3uO+IIJGBhkqKN1VgZ+OANT+QA2kRkcCeP7u/agINJX
+Z8sRR1rVvlm59dQ6DoONK5fgzpxQSVvm1iZ+Gs/hAoGBAM1nbg7eZZi34kbOCxuG
+TRbnVsKvMuqPabbBCL0VwK53yzN2dFmquk+AbtWg+kn/xALrpf0AJgeu0524Dp6j
+LKAVO77g2k0GU/SWyPTYvq/i8G7Np7bgghBYCsafFn9bT4K4iSQNztsWrizQBPce
+e2BpNtUcuhWA9HKEebHWER7HAoGBAJRkDOBcPNiuUaKgMDiv0/UYXmE4Wtb/8fWb
+aoz6+d/xjSkIWFYQrbAs0ixUbx4SVxKcgiyvzUZoFVjAQtgZaBZkicXdytNGzeD8
+TPuZgUGRaBzEdOKrvJh/786VdIdgxjJvfs2bYvjBUYtAsDc9tuPE+HnfC3imgEUt
+S4cU0BJBAoGARbDzZHxlsj+M7IMxbN7TIF5yIJdy4fr5xCM1WpAEn90Wa0rnCYQO
+9d7uS/WAeuMD3VSaeGGSKBJh3mz/SSbdX/YIXFpOn31FQUCT5lrkWwfwVPEiPpIL
+X+dCZ4s235uaqEBLdlHRlG8FNz9frc+EpwbyGd29opzfh8HZuaJzgZc=
+-----END RSA PRIVATE KEY-----
diff --git a/libs/ssl-config/src/test/resources/certs/pem-utils/testnode.crt b/libs/ssl-config/src/test/resources/certs/pem-utils/testnode.crt
new file mode 100644
index 0000000000000..08c160bcea5ff
--- /dev/null
+++ b/libs/ssl-config/src/test/resources/certs/pem-utils/testnode.crt
@@ -0,0 +1,23 @@
+-----BEGIN CERTIFICATE-----
+MIID0zCCArugAwIBAgIJALi5bDfjMszLMA0GCSqGSIb3DQEBCwUAMEgxDDAKBgNV
+BAoTA29yZzEWMBQGA1UECxMNZWxhc3RpY3NlYXJjaDEgMB4GA1UEAxMXRWxhc3Rp
+Y3NlYXJjaCBUZXN0IE5vZGUwHhcNMTUwOTIzMTg1MjU3WhcNMTkwOTIyMTg1MjU3
+WjBIMQwwCgYDVQQKEwNvcmcxFjAUBgNVBAsTDWVsYXN0aWNzZWFyY2gxIDAeBgNV
+BAMTF0VsYXN0aWNzZWFyY2ggVGVzdCBOb2RlMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEA3rGZ1QbsW0+MuyrSLmMfDFKtLBkIFW8V0gRuurFg1PUKKNR1
+Mq2tMVwjjYETAU/UY0iKZOzjgvYPKhDTYBTte/WHR1ZK4CYVv7TQX/gtFQG/ge/c
+7u0sLch9p7fbd+/HZiLS/rBEZDIohvgUvzvnA8+OIYnw4kuxKo/5iboAIS41klMg
+/lATm8V71LMY68inht71/ZkQoAHKgcR9z4yNYvQ1WqKG8DG8KROXltll3sTrKbl5
+zJhn660es/1ZnR6nvwt6xnSTl/mNHMjkfv1bs4rJ/py3qPxicdoSIn/KyojUcgHV
+F38fuAy2CQTdjVG5fWj9iz+mQvLm3+qsIYQdFwIDAQABo4G/MIG8MAkGA1UdEwQC
+MAAwHQYDVR0OBBYEFEMMWLWQi/g83PzlHYqAVnty5L7HMIGPBgNVHREEgYcwgYSC
+CWxvY2FsaG9zdIIVbG9jYWxob3N0LmxvY2FsZG9tYWluggpsb2NhbGhvc3Q0ghds
+b2NhbGhvc3Q0LmxvY2FsZG9tYWluNIIKbG9jYWxob3N0NoIXbG9jYWxob3N0Ni5s
+b2NhbGRvbWFpbjaHBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAEwDQYJKoZIhvcNAQEL
+BQADggEBAMjGGXT8Nt1tbl2GkiKtmiuGE2Ej66YuZ37WSJViaRNDVHLlg87TCcHe
+k2rdO+6sFqQbbzEfwQ05T7xGmVu7tm54HwKMRugoQ3wct0bQC5wEWYN+oMDvSyO6
+M28mZwWb4VtR2IRyWP+ve5DHwTM9mxWa6rBlGzsQqH6YkJpZojzqk/mQTug+Y8aE
+mVoqRIPMHq9ob+S9qd5lp09+MtYpwPfTPx/NN+xMEooXWW/ARfpGhWPkg/FuCu4z
+1tFmCqHgNcWirzMm3dQpF78muE9ng6OB2MXQwL4VgnVkxmlZNHbkR2v/t8MyZJxC
+y4g6cTMM3S/UMt5/+aIB2JAuMKyuD+A=
+-----END CERTIFICATE-----
diff --git a/libs/ssl-config/src/test/resources/certs/pem-utils/testnode.jks b/libs/ssl-config/src/test/resources/certs/pem-utils/testnode.jks
new file mode 100644
index 0000000000000000000000000000000000000000..ebe6146124e8fd607e46a1a3129bdf9b4de0370d
GIT binary patch
literal 9360
zcmdUV2T)YowrzLQl93!F2SGC3WF$w)IfLYGK$2vT*iDigl`N7oh@eEt83BokWI;th
zKok%V_zil_#dF{N=hmxN@83^VSMRxd)mp3enq$l{$L93&>1O}{0788*FTTk=VD28y
zE;cYOYe##Svj^8*Hy60QBMbllY8=Nx4**vTxdk91003Gv1c?>_L4sT!qk(`R5Eh_)
zU50G!4F3%T4;_m=TrvCr4+zA$0)Q|==^?b}XnJ5Y;(s+egq9o+ikg;}4=OCcDIVy2IuGcP39Gn>U%
zK!zH|Y#zr76z$THNrVtl2Hz*duE|_x{SHKW=Wy9~#x0?_!WxzreKbBd_{B!%FFc+}
z#hS1iA!Ph9s*KL}5ZCWMX)1FD)R@OQManE3e-@+qd0nOC5~I@Wvv)K719(Ez^^D!&
zt6;h^oFHXs|H>4zGnJ%}f+`Pp>rEdE;=+o<-25$T9lIy1B0c;S!Z_r&_k6nwupM+P
z`QIj_3i+10?X14(vmJKmiZElETC3=_VBSWbe19M>na%UTgjKu!)Hb0X5AJ>=1@Lw7vcxjN}1eMG&CU^70?>@)@Jj3w6do
zK=}wbX`7exBW2Hy$yVaDo$ICp=}fYb@;nQCWlEe6F`UeJ?OsbuRMGQ
zgd{VQ1`}qlEo37p4u-{8N|I&os?=jM_S_F93rh*dwGvc4>^v;F@<@1%AfI?JIxoOG#d{a^
zfIt8&OKT5%FPM$J8|t~~u}>Z;{sXMDD6qapfwk=?SP2aA8Y;D$$)co!mluiHCH4RE
zP$7X(VhABRHdYc#Wb!Vj4e%Nnnzf4)lmZJVJR-@b8DwGjw9y}xw)Rb-X`tOZ@L?r7m{x$tD>pf-DK8J8PK&?Rve%UmMWfn
zkY1J8$6e`6OS^j3!ZBdDpQ)-!J@JJ984?Dw%90
z9h9Kf+y~
zZ;X4CM_eV}GZ5(`=K*hqpZ8TuPP9ObD5GdI!|KXQ!DE#8?8q-!J#9&
z<{0CP?#6ZZ#oFo@=oP4^3J28dh2~!eXSx~~6ipLu^f&9MPd~6p^1dZo&i#yysyksD
zk}_18V>Qk~vQwzp^Jc<7;yAcH?9kbl>dg;vDb^QW7vRS}huY7Q{j*f_g#JtPV*wf`
zQ+Jb8TG{^cI3dbVTnP3#FtLfzT-or(bOGc4(Y5{#j-5r{Ppfe;7RN>qhPm0{UaW%Vx1xCa5qm=3uM*tyDmx^f
zYB_a@Gg~w4N2l?k$LGG^P!NIepM`u@h$CVHNvqI
zC)X{Bme~#(ow!*Q{K^N&ikb6+rMH->KX;ZbN6hV7!(1Bu`qVw?;aP#3&Yrekzjpej
zK?ev|N
zXpSYGJX7Ur@l^_!fa=P>E&WMk1O|eS+U?%te$I}MSaKjCeWH%kki*#p`IR^
zVzx|rZ=W!gg0!F0H2VQ^^Fbof!}9le_))5Y=S5|6FO7Cv
zbx_><@>*LeHI$ay>|Nv}{__#cG`gqE5HI=yy!zq2=%Sr>Y-uEp7wE@yxeIf4w6R19
z0pRVhFITw##6I{Y1PK;IRe#z)WcoxH4#%|zmFAbRe+4DeU7$D@vJi|;4ARs2BfumE
z$x8ph0~o}3YA&A69w?nhuWt|Yh7v%o{$vY`Up(-~NEBoI#tcxx3+_iT2BrpTtEzI+
znim8i1QFsDfC}