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).
This commit is contained in:
Tim Vernum 2019-01-16 21:52:17 +11:00 committed by GitHub
parent 023bb2f1e4
commit 6d99e790b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
70 changed files with 5044 additions and 1 deletions

View File

@ -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'
}

View File

@ -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<String, String, String> 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<String, String, String> 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<String, String, String> 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<String, String, String> systemProperties) {
return systemProperties.apply("javax.net.ssl.trustStoreType", "").equalsIgnoreCase("PKCS11");
}
private static char[] getSystemTrustStorePassword(BiFunction<String, String, String> systemProperties) {
return systemProperties.apply("javax.net.ssl.trustStorePassword", "").toCharArray();
}
@Override
public Collection<Path> 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);
}
}

View File

@ -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.
* <p>
* 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.
* <p>
* In BER/DER encoding, length can be encoded in 2 forms:
* </p>
* <ul>
* <li>Short form. One octet. Bit 8 has value "0" and bits 7-1
* give the length.
* </li>
* <li>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.
* </li>
* </ul>
*
* @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.
* <p>
* The first byte in DER encoding is made of following fields:
* </p>
* <pre>
* -------------------------------------------------
* |Bit 8|Bit 7|Bit 6|Bit 5|Bit 4|Bit 3|Bit 2|Bit 1|
* -------------------------------------------------
* | Class | CF | + Type |
* -------------------------------------------------
* </pre>
* <ul>
* <li>Class: Universal, Application, Context or Private
* <li>CF: Constructed flag. If 1, the field is constructed.
* <li>Type: This is actually called tag in ASN.1. It
* indicates data type (Integer, String) or a construct
* (sequence, choice, set).
* </ul>
*
* @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();
}
}

View File

@ -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<Path> getDependentFiles() {
return Collections.emptyList();
}
@Override
public X509ExtendedKeyManager createKeyManager() {
return null;
}
@Override
public String toString() {
return "empty-key-config";
}
}

View File

@ -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 <em>file name</em> 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<Certificate> 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<Certificate> 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 + "]");
}
}

View File

@ -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<Path> getDependentFiles() {
return Arrays.asList(certificate, key);
}
@Override
public X509ExtendedKeyManager createKeyManager() {
PrivateKey privateKey = getPrivateKey();
List<Certificate> 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<Certificate> 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;
}
}

View File

@ -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<Path> 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
* <ol>
* <li>validation of the file (contents and accessibility) is deferred, and this constructor will <em>not fail</em> on missing
* of invalid files.</li>
* <li>
* 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.
* </li>
* </ol>
*/
public PemTrustConfig(List<Path> certificateAuthorities) {
this.certificateAuthorities = Collections.unmodifiableList(certificateAuthorities);
}
@Override
public Collection<Path> getDependentFiles() {
return certificateAuthorities;
}
@Override
public X509ExtendedTrustManager createTrustManager() {
try {
final List<Certificate> 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<Certificate> 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(","));
}
}

View File

@ -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<char[]> 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<char[]> passwordSupplier) throws IOException,
GeneralSecurityException {
StringBuilder sb = new StringBuilder();
String line = bReader.readLine();
Map<String, String> 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<char[]> passwordSupplier) throws IOException,
GeneralSecurityException {
StringBuilder sb = new StringBuilder();
String line = bReader.readLine();
Map<String, String> 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<char[]> passwordSupplier) throws IOException,
GeneralSecurityException {
StringBuilder sb = new StringBuilder();
String line = bReader.readLine();
Map<String, String> 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<String, String> pemHeaders, String keyContents, Supplier<char[]> 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)
* <p>
* 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<Certificate> readCertificates(Collection<Path> certPaths) throws CertificateException, IOException {
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
List<Certificate> 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);
}
}
}

View File

@ -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<String, SslClientAuthenticationMode> LOOKUP = Collections.unmodifiableMap(buildLookup());
static Map<String, SslClientAuthenticationMode> buildLookup() {
final Map<String, SslClientAuthenticationMode> 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;
}
}

View File

@ -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);
}
}

View File

@ -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<String> ciphers;
private final List<String> supportedProtocols;
public SslConfiguration(SslTrustConfig trustConfig, SslKeyConfig keyConfig, SslVerificationMode verificationMode,
SslClientAuthenticationMode clientAuth, List<String> ciphers, List<String> 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<String> getCipherSuites() {
return ciphers;
}
public List<String> 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<Path> getDependentFiles() {
Set<Path> 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);
}
}

View File

@ -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<String> 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<String> 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<String> 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<String> 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);
}
}

View File

@ -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<String> DEFAULT_PROTOCOLS = Arrays.asList("TLSv1.2", "TLSv1.1", "TLSv1");
static final List<String> 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<String> defaultCiphers;
private List<String> 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<String> 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<String> 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()}).
* <p>
* 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()}).
* <p>
* 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<String> 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<String> protocols = resolveListSetting(PROTOCOLS, Function.identity(), defaultProtocols);
final List<String> 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<Path> 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> V resolveSetting(String key, Function<String, V> 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 <V> List<V> resolveListSetting(String key, Function<String, V> parser, List<V> defaultValue) {
try {
final List<String> 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<String> loadDefaultCiphers() {
final List<String> 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<String> 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<String> 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;
}
}
}

View File

@ -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<Path> getDependentFiles();
/**
* @return A new {@link X509ExtendedKeyManager}.
* @throws SslConfigException if there is a problem configuring the key manager.
*/
X509ExtendedKeyManager createKeyManager();
}

View File

@ -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<Path> getDependentFiles();
/**
* @return A new {@link X509ExtendedTrustManager}.
* @throws SslConfigException if there is a problem configuring the trust manager.
*/
X509ExtendedTrustManager createTrustManager();
}

View File

@ -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<String, SslVerificationMode> LOOKUP = Collections.unmodifiableMap(buildLookup());
private static Map<String, SslVerificationMode> buildLookup() {
Map<String, SslVerificationMode> 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;
}
}

View File

@ -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<Path> 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<String> 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);
}
}

View File

@ -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<Path> 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 ? "<empty>" : "<non-empty>") + "]", e);
}
}
/**
* Verifies that the keystore contains at least 1 trusted certificate entry.
*/
private void checkTrustStore(KeyStore store) throws GeneralSecurityException {
Enumeration<String> 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);
}
}

View File

@ -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<Path> getDependentFiles() {
return Collections.emptyList();
}
@Override
public X509ExtendedTrustManager createTrustManager() {
return TRUST_MANAGER;
}
@Override
public String toString() {
return "trust everything";
}
}

View File

@ -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<String, String, String> 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<X509Certificate> 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 + "].");
}
}
}

View File

@ -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));
}
}

View File

@ -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<String> 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));
}
}

View File

@ -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<char[]> EMPTY_PASSWORD = () -> new char[0];
private static final Supplier<char[]> 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());
}
}
}

View File

@ -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<String> 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());
}
}

View File

@ -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<String> ciphers = randomSubsetOf(randomIntBetween(1, DEFAULT_CIPHERS.size()), DEFAULT_CIPHERS);
final List<String> 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<String> ciphers = randomSubsetOf(randomIntBetween(1, DEFAULT_CIPHERS.size() - 1), DEFAULT_CIPHERS);
final List<String> 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);
}
}

View File

@ -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"));
}
}

View File

@ -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<String> 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()));
}
}

View File

@ -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

Binary file not shown.

Binary file not shown.

View File

@ -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-----

View File

@ -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-----

Binary file not shown.

View File

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDCTCCAfGgAwIBAgIUdWLBV2ebmR6Ktf9mWqnlKvVwG7QwDQYJKoZIhvcNAQEL
BQAwFDESMBAGA1UEAxMJVGVzdCBDQSAyMB4XDTE5MDEwMzA3Mzk1MloXDTQ2MDUy
MDA3Mzk1MlowFDESMBAGA1UEAxMJVGVzdCBDQSAyMIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEAnuHEkW7zlFqUYTDEWmrEoKaEc4ERcyGklDdJiOEUvqiy
fo2DPjkQp9+Ix6bkQQSog9cPfYr3+Fp+LMpS5f59PtXLl6lIBjQhcRc+Bh82QLMG
h1yXBbxLmbl0sDfkkv+Y+T17wowN4mWhrwmXYTaAV633Nx72h3Q3NJfeeWpJkPv9
8x88pRRejxfjTisc/X7CsYjmFMrbMemmMQ4Xk3Uhu2PBv4Ln+69oyADHHVQoKPJy
g2l2PrZGRvxdqlf5iLH8oaxLy83tAbdbwu0CeopIG0ET9XtbsHK7IKxwsCWSy/+J
qvV0Gl9urQexyYRkF7AN4Jy53w3Qvrj4RtBxdkneDQIDAQABo1MwUTAdBgNVHQ4E
FgQUTqJ154oBVLiPoWsZEq5O0R6cWr4wHwYDVR0jBBgwFoAUTqJ154oBVLiPoWsZ
Eq5O0R6cWr4wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAHvHI
Dkhev16VcAWJTT4Ti8oJNClfneehkAMXZhRegBYUNbD+4KbURcz8TVSybMb86nZV
/Lkr8gffYXYZb2ibElvNv0gprutzplCpYCNg/iu8bvSw86dr62s9IKIkAtlDkxzz
jlVKhoUUTKngBNT1CVPMYwJyBwUDpa346DxcQ636b0QU9lQPikYqORaj/9IOc5IX
MriFFpX0gC9U8y+gvGdmFmSkufGRZlJS75/fJfVxpB4Qm9FNoGkJy+wZDbw4TfIO
mZhXbAycIUMsYJ8BjUKhlqXWqGC/6afVRHKekSsAbXo6AGk9ty8i6itj/aeUB33w
BqPROIFyhhdUAZSHyg==
-----END CERTIFICATE-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAnuHEkW7zlFqUYTDEWmrEoKaEc4ERcyGklDdJiOEUvqiyfo2D
PjkQp9+Ix6bkQQSog9cPfYr3+Fp+LMpS5f59PtXLl6lIBjQhcRc+Bh82QLMGh1yX
BbxLmbl0sDfkkv+Y+T17wowN4mWhrwmXYTaAV633Nx72h3Q3NJfeeWpJkPv98x88
pRRejxfjTisc/X7CsYjmFMrbMemmMQ4Xk3Uhu2PBv4Ln+69oyADHHVQoKPJyg2l2
PrZGRvxdqlf5iLH8oaxLy83tAbdbwu0CeopIG0ET9XtbsHK7IKxwsCWSy/+JqvV0
Gl9urQexyYRkF7AN4Jy53w3Qvrj4RtBxdkneDQIDAQABAoIBABppr/L5ffboxAgQ
QmRBoaSPai+FgnAgZKrbMhdWS8uSYfIV9n6OoA04ZRXD0ehZLOaWBxY41xZrfNRX
Ykan8wxSIIF6++VEH1ccpQwBflRtLqWsJ9MlRXAt2488C3zAjx7IMN3byKcdfC6M
KqVXmSh6XEHGnPdRw6ezo6GNoONALVXKVZkeHKeMpjfThoA/ydGTpG72AxDY/EW+
RLyHniKAm90VddPrjnWwgCKL8nG8avdznqS2hGKka1o5a32waYCFb13rZi3IyhnR
Lu9oVhbPDENkhK4R4KfG4baus24y5HGihB4FdzbemBK7zqrAvbxR3FuIZRJNmh0v
tTHaKCECgYEA6vPg+mx3zXwip/wlIyeD6F/4SMR3G+m77UoqswXQcjnKCljsBDDg
kLCnVXjmzY6bryCMbAiecZrNVDiqGGgwi/Cj2xjTgH9YS7VMGgWvsCtRBrdLB1zR
X7EdkOtcbVFByKU/U2WHc1piHYxwTTWtDaDJKCp/CnL/veJb7xJQoRUCgYEArR1f
x6BnnBokERXIc/qI0ff1vtRwvewju7RVXZRU5cgS89GcV9vJjGDHS+2wXhyEAIFH
PoICF/zgiJJAXdK1k1noUotMGOEKo//sUNb+mk6anWkHa10KHQMnW7PDnzhIuUeH
C6n5b5oJBRBFwK7BULmk+27A/zx1SdW9CSH8VxkCgYEAybrn+lxTaN0irHU0NcDh
4w0zktcNJaxELPM3QkrFtK2lqci7rMWCqvjiU+Lg2LGPPoiFyOSFlilCDwQwF5Ct
zhmptp7USkoMt8RMOTOUq4Alq8yI4SNyqeTa6+kJjNrtzqcDfkl4STTbdV91tPVX
RpI85P3H4mLm7lSCdvyUuhkCgYAHNsEmBXYr2B8Gozy+MIOBFG8mK54jG/MFQGeK
RcMf7C12AZcdRiho9CN584a09UU+7CQ2454It933cvjBsCUm5ck7n1hldQNHgEOt
vrfPYFUrGBRaEf945AfA14XgXa0SI3vqLYQadXXIwzvU4rNllMbeP2hFepR8pi6B
cewdCQKBgGkc5J/oWnxMcamlMP/W7Vk7YxsPiEO6KMx6m9b1VY0G5ACHEkTP+Glp
uDgLw5Me/dLG+hvY9NV9GPG76boRBGhFbriPpVARa49rY0ShTDPmz+SzqZ0L/N09
msH+f0K9O+KvXjw9h7lHF/vg/exrRDS6my5bgmj/UtIHwgmcSUdm
-----END RSA PRIVATE KEY-----

Binary file not shown.

View File

@ -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-----

View File

@ -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-----

Binary file not shown.

View File

@ -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-----

View File

@ -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-----

View File

@ -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-----

View File

@ -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-----

View File

@ -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 <NAME>.cert -keyout <NAME>.pem -days 1460 -config config.cnf
-----------------------------------------------------------------------------------------------------------
When prompted the password is always set to the value of <NAME>.
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 <NAME> -in <NAME>.cert -inkey <NAME>.pem -out <NAME>.p12
-----------------------------------------------------------------------------------------------------------
== Creating the Keystore
We need to create a keystore from the created PKCS12 certificate.
[source,shell]
-----------------------------------------------------------------------------------------------------------
keytool -importkeystore -destkeystore <NAME>.jks -srckeystore <NAME>.p12 -srcstoretype pkcs12 -alias <NAME>
-----------------------------------------------------------------------------------------------------------
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
------

View File

@ -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

View File

@ -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-----

View File

@ -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-----

View File

@ -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-----

View File

@ -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-----

View File

@ -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-----

View File

@ -0,0 +1,4 @@
-----BEGIN EC PRIVATE KEY-----
MDECAQEEILEXCgqp9wZqKVmG6HTESPeCyx2O4TDoFqyILz7OGocEoAoGCCqGSM49
AwEH
-----END EC PRIVATE KEY-----

View File

@ -0,0 +1,7 @@
-----BEGIN EC PARAMETERS-----
Notvalidbutnotparsed
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MDECAQEEILEXCgqp9wZqKVmG6HTESPeCyx2O4TDoFqyILz7OGocEoAoGCCqGSM49
AwEH
-----END EC PRIVATE KEY-----

View File

@ -0,0 +1,4 @@
-----BEGIN PRIVATE KEY-----
MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCCxFwoKqfcGailZhuh0
xEj3gssdjuEw6BasiC8+zhqHBA==
-----END PRIVATE KEY-----

View File

@ -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-----

View File

@ -0,0 +1,7 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACBqIPMG94HL7zedFzsvi45mHS8ZuyLQXqvHpHobcdNCJAAAAJimRM7VpkTO
1QAAAAtzc2gtZWQyNTUxOQAAACBqIPMG94HL7zedFzsvi45mHS8ZuyLQXqvHpHobcdNCJA
AAAEBvVc8FVPGUs3LZ1o+LnjW4uUlEnk/5LQQ9yO2eiI3SFGog8wb3gcvvN50XOy+LjmYd
Lxm7ItBeq8ekehtx00IkAAAAEWlvYW5uaXNAc2VjdXJlYm94AQIDBA==
-----END OPENSSH PRIVATE KEY-----

View File

@ -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-----

View File

@ -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-----

View File

@ -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-----

View File

@ -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-----

View File

@ -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-----

View File

@ -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-----

View File

@ -0,0 +1,30 @@
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,9D867F7E0C94D013
dVoVCjPeg1wgS7rVtOvGfQcrZyLkx393aWRnFq45tbjKBVuITtJ9vI7o4QXOV/15
Gnb6WhXGIdWrzsxEAd46K6hIuNSISd4Emsx6c2Q5hTqWXXfexbOZBNfTtXtdJPnJ
1jAaikhtztLo3JSLTKNY5sNxd+XbaQyYVUWvueK6zOaIIMETvB+VPVFd9i1ROibk
Sgdtyj01KjkoalifqK/tA0CIYNKL0S6/eoK3UhAlpIprlpV+cnXa940C6bjLeJPt
PMAGGp5RrplxSgrSerw3I9DOWkHGtpqzIka3XneNUXJP8k4HUJ+aZkGH2ZILKS8d
4KMIb+KZSpHEGn+6uGccWLtZZmAjWJrDw56JbQtSHdRYLBRSOjLbTvQoPu/2Hpli
7HOxbotlvjptMunncq5aqK57SHA1dh0cwF7J3LUmGFJ67eoz+VV3b5qMn4MopSeI
mS16Ydd3nGpjSrln/elM0CQxqWfcOAXRZpDpFUQoXcBrLVzvz2DBl/0CrTRLhgzi
CO+5/IVcBWRlYpRNGgjjP7q0j6URID3jk5J06fYQXmBiwQT5j+GZqqzpMCJ9mIy2
1O9SN1hebJnIcEU+E0njn/MGjlYdPywhaCy8pqElp6Q8TUEJpwLRFO/owCoBet/n
ZmCXUjfCGhc1pWHufFcDEQ6xMgEWWY/tdwCZeSU7EhErTjCbfupg+55A5fpDml0m
3wH4CFcuRjlqyx6Ywixm1ATeitDtJl5HQTw6b8OtEXwSgRmZ0eSqSRVk9QbVS7gu
IpQe09/Zimb5HzjZqZ3fdqHlcW4xax8hyJeyIvF5ZJ57eY8CBvu/wP2GDn26QnvF
xQqdfDbq1H4JmpwUHpbFwBoQK4Q6WFd1z4EA9bRQeo3H9PoqoOwMDjzajwLRF7b7
q6tYH/n9PyHwdf1c4fFwgSmL1toXGfKlA9hjIaLsRSDD6srT5EdUk78bsnddwI51
tu7C7P4JG+h1VdRNMNTlqtileWsIE7Nn2A1OkcUxZdF5mamENpDpJcHePLto6c8q
FKiwyFMsxhgsj6HK2HqO+UA4sX5Ni4oHwiPmb//EZLn045M5i1AN26KosJmb8++D
sgR5reWRy+UqJCTYblVg+7Dx++ggUnfxVyQEsWmw5r5f4KU5wXBkvoVMGtPNa9DE
n/uLtObD1qkNL38pRsr2OGRchYCgEoKGqEISBP4knfGXLOlWiW/246j9QzI97r1u
tvy7fKg28G7AUz9l6bpewsPHefBUeRQeieP9eJINaEpxkF/w2RpKDLpQjWxwDDOM
s+D0mrBMJve17AmJ8rMw6dIQPZYNZ88/jz1uQuUwQ2YlbmtZbCG81k9YMFGEU9XS
cyhJxj8hvYnt2PR5Z9/cJPyWOs0m/ufOeeQQ8SnU/lzmrQnpzUd2Z6p5i/B7LdRP
n1kX+l1qynuPnjvBz4nJQE0p6nzW8RyCDSniC9mtYtZmhgC8icqxgbvS7uEOBIYJ
NbK+0bEETTO34iY/JVTIqLOw3iQZYMeUpxpj6Phgx/oooxMTquMecPKNgeVtaBst
qjTNPX0ti1/HYpZqzYi8SV8YjHSJWCVMsZjKPr3W/HIcCKqYoIfgzi83Ha2KMQx6
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,32 @@
Bag Attributes
friendlyName: testnode_rsa
localKeyID: 54 69 6D 65 20 31 35 32 35 33 33 36 38 32 39 33 39 37
Key Attributes: <No Attributes>
-----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-----

View File

@ -96,6 +96,7 @@ if (isEclipse) {
projects << 'libs:secure-sm-tests'
projects << 'libs:grok-tests'
projects << 'libs:geo-tests'
projects << 'libs:ssl-config'
}
include projects.toArray(new String[0])
@ -134,7 +135,12 @@ if (isEclipse) {
project(":libs:geo").projectDir = new File(rootProject.projectDir, 'libs/geo/src/main')
project(":libs:geo").buildFileName = 'eclipse-build.gradle'
project(":libs:geo-tests").projectDir = new File(rootProject.projectDir, 'libs/geo/src/test')
project(":libs:geo-tests").buildFileName = 'eclipse-build.gradle'}
project(":libs:geo-tests").buildFileName = 'eclipse-build.gradle'
project(":libs:ssl-config").projectDir = new File(rootProject.projectDir, 'libs/ssl-config/src/main')
project(":libs:ssl-config").buildFileName = 'eclipse-build.gradle'
project(":libs:ssl-config-tests").projectDir = new File(rootProject.projectDir, 'libs/ssl-config/src/test')
project(":libs:ssl-config-tests").buildFileName = 'eclipse-build.gradle'
}
// look for extra plugins for elasticsearch
File extraProjects = new File(rootProject.projectDir.parentFile, "${dirName}-extra")
@ -146,3 +152,4 @@ if (extraProjects.exists()) {
project(":libs:cli").name = 'elasticsearch-cli'
project(":libs:geo").name = 'elasticsearch-geo'
project(":libs:ssl-config").name = 'elasticsearch-ssl-config'