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:
parent
023bb2f1e4
commit
6d99e790b3
|
@ -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'
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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";
|
||||
}
|
||||
}
|
|
@ -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 + "]");
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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(","));
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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";
|
||||
}
|
||||
}
|
|
@ -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 + "].");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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"));
|
||||
}
|
||||
}
|
|
@ -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()));
|
||||
}
|
||||
|
||||
}
|
|
@ -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.
|
@ -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-----
|
|
@ -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.
|
@ -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-----
|
|
@ -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.
|
@ -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-----
|
|
@ -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.
Binary file not shown.
Binary file not shown.
|
@ -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-----
|
|
@ -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-----
|
Binary file not shown.
|
@ -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-----
|
|
@ -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-----
|
Binary file not shown.
|
@ -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
|
||||
------
|
|
@ -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
|
|
@ -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-----
|
|
@ -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-----
|
|
@ -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-----
|
|
@ -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-----
|
|
@ -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-----
|
|
@ -0,0 +1,4 @@
|
|||
-----BEGIN EC PRIVATE KEY-----
|
||||
MDECAQEEILEXCgqp9wZqKVmG6HTESPeCyx2O4TDoFqyILz7OGocEoAoGCCqGSM49
|
||||
AwEH
|
||||
-----END EC PRIVATE KEY-----
|
|
@ -0,0 +1,7 @@
|
|||
-----BEGIN EC PARAMETERS-----
|
||||
Notvalidbutnotparsed
|
||||
-----END EC PARAMETERS-----
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
MDECAQEEILEXCgqp9wZqKVmG6HTESPeCyx2O4TDoFqyILz7OGocEoAoGCCqGSM49
|
||||
AwEH
|
||||
-----END EC PRIVATE KEY-----
|
|
@ -0,0 +1,4 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCCxFwoKqfcGailZhuh0
|
||||
xEj3gssdjuEw6BasiC8+zhqHBA==
|
||||
-----END PRIVATE KEY-----
|
|
@ -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-----
|
|
@ -0,0 +1,7 @@
|
|||
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
|
||||
QyNTUxOQAAACBqIPMG94HL7zedFzsvi45mHS8ZuyLQXqvHpHobcdNCJAAAAJimRM7VpkTO
|
||||
1QAAAAtzc2gtZWQyNTUxOQAAACBqIPMG94HL7zedFzsvi45mHS8ZuyLQXqvHpHobcdNCJA
|
||||
AAAEBvVc8FVPGUs3LZ1o+LnjW4uUlEnk/5LQQ9yO2eiI3SFGog8wb3gcvvN50XOy+LjmYd
|
||||
Lxm7ItBeq8ekehtx00IkAAAAEWlvYW5uaXNAc2VjdXJlYm94AQIDBA==
|
||||
-----END OPENSSH PRIVATE KEY-----
|
|
@ -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-----
|
|
@ -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-----
|
|
@ -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-----
|
|
@ -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-----
|
|
@ -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-----
|
|
@ -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-----
|
Binary file not shown.
|
@ -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-----
|
|
@ -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-----
|
|
@ -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'
|
||||
|
|
Loading…
Reference in New Issue