Improve errors when TLS files cannot be read (#45122)

This change improves the exception messages that are thrown when the
system cannot read TLS resources such as keystores, truststores,
certificates, keys or certificate-chains (CAs).

This change specifically handles:

- Files that do not exist
- Files that cannot be read due to file-system permissions
- Files that cannot be read due to the ES security-manager

Backport of: #44787
This commit is contained in:
Tim Vernum 2019-08-02 12:29:43 +10:00 committed by GitHub
parent 590777150f
commit e21d58541a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 752 additions and 52 deletions

View File

@ -63,6 +63,10 @@ public class CertParsingUtils {
return PathUtils.get(path).normalize();
}
static List<Path> resolvePaths(List<String> certPaths, @Nullable Environment environment) {
return certPaths.stream().map(p -> resolvePath(p, environment)).collect(Collectors.toList());
}
public static KeyStore readKeyStore(Path path, String type, char[] password)
throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException {
try (InputStream in = Files.newInputStream(path)) {
@ -82,7 +86,7 @@ public class CertParsingUtils {
*/
public static Certificate[] readCertificates(List<String> certPaths, @Nullable Environment environment)
throws CertificateException, IOException {
final List<Path> resolvedPaths = certPaths.stream().map(p -> resolvePath(p, environment)).collect(Collectors.toList());
final List<Path> resolvedPaths = resolvePaths(certPaths, environment);
return readCertificates(resolvedPaths);
}

View File

@ -5,6 +5,7 @@
*/
package org.elasticsearch.xpack.core.ssl;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.env.Environment;
import org.elasticsearch.xpack.core.ssl.cert.CertificateInfo;
@ -12,7 +13,10 @@ import org.elasticsearch.xpack.core.ssl.cert.CertificateInfo;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager;
import java.io.IOException;
import java.nio.file.AccessDeniedException;
import java.nio.file.Path;
import java.security.AccessControlException;
import java.security.PrivateKey;
import java.util.Collection;
import java.util.Collections;
@ -64,6 +68,31 @@ abstract class KeyConfig extends TrustConfig {
abstract X509ExtendedKeyManager createKeyManager(@Nullable Environment environment);
/**
* generate a new exception caused by a missing file, that is required for this key config
*/
static ElasticsearchException missingKeyConfigFile(IOException cause, String fileType, Path path) {
return new ElasticsearchException(
"failed to initialize SSL KeyManager - " + fileType + " file [{}] does not exist", cause, path.toAbsolutePath());
}
/**
* generate a new exception caused by an unreadable file (i.e. file-system access denied), that is required for this key config
*/
static ElasticsearchException unreadableKeyConfigFile(AccessDeniedException cause, String fileType, Path path) {
return new ElasticsearchException(
"failed to initialize SSL KeyManager - not permitted to read " + fileType + " file [{}]", cause, path.toAbsolutePath());
}
/**
* generate a new exception caused by a blocked file (i.e. security-manager access denied), that is required for this key config
*/
static ElasticsearchException blockedKeyConfigFile(AccessControlException cause, Environment environment, String fileType, Path path) {
return new ElasticsearchException(
"failed to initialize SSL KeyManager - access to read {} file [{}] is blocked;" +
" SSL resources should be placed in the [{}] directory", cause, fileType, path, environment.configFile());
}
abstract List<PrivateKey> privateKeys(@Nullable Environment environment);
}

View File

@ -14,9 +14,13 @@ import org.elasticsearch.xpack.core.ssl.cert.CertificateInfo;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.AccessDeniedException;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.security.AccessControlException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
@ -35,6 +39,9 @@ import java.util.Objects;
*/
class PEMKeyConfig extends KeyConfig {
private static final String CERTIFICATE_FILE = "certificate";
private static final String KEY_FILE = "key";
private final String keyPath;
private final SecureString keyPassword;
private final String certPath;
@ -55,7 +62,7 @@ class PEMKeyConfig extends KeyConfig {
@Override
X509ExtendedKeyManager createKeyManager(@Nullable Environment environment) {
try {
PrivateKey privateKey = readPrivateKey(CertParsingUtils.resolvePath(keyPath, environment), keyPassword);
PrivateKey privateKey = readPrivateKey(keyPath, keyPassword, environment);
if (privateKey == null) {
throw new IllegalArgumentException("private key [" + keyPath + "] could not be loaded");
}
@ -63,12 +70,21 @@ class PEMKeyConfig extends KeyConfig {
return CertParsingUtils.keyManager(certificateChain, privateKey, keyPassword.getChars());
} catch (IOException | UnrecoverableKeyException | NoSuchAlgorithmException | CertificateException | KeyStoreException e) {
throw new ElasticsearchException("failed to initialize a KeyManagerFactory", e);
throw new ElasticsearchException("failed to initialize SSL KeyManagerFactory", e);
}
}
private Certificate[] getCertificateChain(@Nullable Environment environment) throws CertificateException, IOException {
return CertParsingUtils.readCertificates(Collections.singletonList(certPath), environment);
final Path certificate = CertParsingUtils.resolvePath(certPath, environment);
try {
return CertParsingUtils.readCertificates(Collections.singletonList(certificate));
} catch (FileNotFoundException | NoSuchFileException fileException) {
throw missingKeyConfigFile(fileException, CERTIFICATE_FILE, certificate);
} catch (AccessDeniedException accessException) {
throw unreadableKeyConfigFile(accessException, CERTIFICATE_FILE, certificate);
} catch (AccessControlException securityException) {
throw blockedKeyConfigFile(securityException, environment, CERTIFICATE_FILE, certificate);
}
}
@Override
@ -87,14 +103,23 @@ class PEMKeyConfig extends KeyConfig {
@Override
List<PrivateKey> privateKeys(@Nullable Environment environment) {
try {
return Collections.singletonList(readPrivateKey(CertParsingUtils.resolvePath(keyPath, environment), keyPassword));
return Collections.singletonList(readPrivateKey(keyPath, keyPassword, environment));
} catch (IOException e) {
throw new UncheckedIOException("failed to read key", e);
}
}
private static PrivateKey readPrivateKey(Path keyPath, SecureString keyPassword) throws IOException {
return PemUtils.readPrivateKey(keyPath, keyPassword::getChars);
private static PrivateKey readPrivateKey(String keyPath, SecureString keyPassword, Environment environment) throws IOException {
final Path key = CertParsingUtils.resolvePath(keyPath, environment);
try {
return PemUtils.readPrivateKey(key, keyPassword::getChars);
} catch (FileNotFoundException | NoSuchFileException fileException) {
throw missingKeyConfigFile(fileException, KEY_FILE, key);
} catch (AccessDeniedException accessException) {
throw unreadableKeyConfigFile(accessException, KEY_FILE, key);
} catch (AccessControlException securityException) {
throw blockedKeyConfigFile(securityException, environment, KEY_FILE, key);
}
}
@Override

View File

@ -14,7 +14,10 @@ import org.elasticsearch.xpack.core.ssl.cert.CertificateInfo;
import javax.net.ssl.X509ExtendedTrustManager;
import java.io.IOException;
import java.nio.file.AccessDeniedException;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.security.AccessControlException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
@ -29,10 +32,13 @@ import java.util.Objects;
*/
class PEMTrustConfig extends TrustConfig {
private static final String CA_FILE = "certificate_authorities";
private final List<String> caPaths;
/**
* Create a new trust configuration that is built from the certificate files
*
* @param caPaths the paths to the certificate files to trust
*/
PEMTrustConfig(List<String> caPaths) {
@ -44,8 +50,17 @@ class PEMTrustConfig extends TrustConfig {
try {
Certificate[] certificates = CertParsingUtils.readCertificates(caPaths, environment);
return CertParsingUtils.trustManager(certificates);
} catch (NoSuchFileException noSuchFileException) {
final Path missingPath = CertParsingUtils.resolvePath(noSuchFileException.getFile(), environment);
throw missingTrustConfigFile(noSuchFileException, CA_FILE, missingPath);
} catch (AccessDeniedException accessDeniedException) {
final Path missingPath = CertParsingUtils.resolvePath(accessDeniedException.getFile(), environment);
throw unreadableTrustConfigFile(accessDeniedException, CA_FILE, missingPath);
} catch (AccessControlException accessControlException) {
final List<Path> paths = CertParsingUtils.resolvePaths(caPaths, environment);
throw blockedTrustConfigFile(accessControlException, environment, CA_FILE, paths);
} catch (Exception e) {
throw new ElasticsearchException("failed to initialize a TrustManagerFactory", e);
throw new ElasticsearchException("failed to initialize SSL TrustManager", e);
}
}

View File

@ -16,6 +16,7 @@ import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.GeneralSecurityException;
import java.security.KeyException;
import java.security.KeyFactory;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
@ -72,7 +73,7 @@ public class PemUtils {
* @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) {
public static PrivateKey readPrivateKey(Path keyPath, Supplier<char[]> passwordSupplier) throws IOException {
try (BufferedReader bReader = Files.newBufferedReader(keyPath, StandardCharsets.UTF_8)) {
String line = bReader.readLine();
while (null != line && line.startsWith(HEADER) == false){
@ -103,7 +104,7 @@ public class PemUtils {
throw new IllegalStateException("Error parsing Private Key from: " + keyPath.toString() + ". File did not contain a " +
"supported key format");
}
} catch (IOException | GeneralSecurityException e) {
} catch (GeneralSecurityException e) {
throw new IllegalStateException("Error parsing Private Key from: " + keyPath.toString(), e);
}
}
@ -176,7 +177,7 @@ public class PemUtils {
line = bReader.readLine();
}
if (null == line || PKCS8_FOOTER.equals(line.trim()) == false) {
throw new IOException("Malformed PEM file, PEM footer is invalid or missing");
throw new KeyException("Malformed PEM file, PEM footer is invalid or missing");
}
byte[] keyBytes = Base64.getDecoder().decode(sb.toString());
String keyAlgo = getKeyAlgorithmIdentifier(keyBytes);

View File

@ -11,6 +11,7 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.lucene.util.SetOnce;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.common.CheckedSupplier;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.Settings;
@ -420,27 +421,33 @@ public class SSLService {
sslSettingsMap.putAll(getRealmsSSLSettings(settings));
sslSettingsMap.putAll(getMonitoringExporterSettings(settings));
sslSettingsMap.forEach((key, sslSettings) -> {
final SSLConfiguration configuration = new SSLConfiguration(sslSettings);
storeSslConfiguration(key, configuration);
sslContextHolders.computeIfAbsent(configuration, this::createSslContext);
});
sslSettingsMap.forEach((key, sslSettings) -> loadConfiguration(key, sslSettings, sslContextHolders));
final Settings transportSSLSettings = settings.getByPrefix(XPackSettings.TRANSPORT_SSL_PREFIX);
final SSLConfiguration transportSSLConfiguration = new SSLConfiguration(transportSSLSettings);
final SSLConfiguration transportSSLConfiguration =
loadConfiguration(XPackSettings.TRANSPORT_SSL_PREFIX, transportSSLSettings, sslContextHolders);
this.transportSSLConfiguration.set(transportSSLConfiguration);
storeSslConfiguration(XPackSettings.TRANSPORT_SSL_PREFIX, transportSSLConfiguration);
Map<String, Settings> profileSettings = getTransportProfileSSLSettings(settings);
sslContextHolders.computeIfAbsent(transportSSLConfiguration, this::createSslContext);
profileSettings.forEach((key, profileSetting) -> {
final SSLConfiguration configuration = new SSLConfiguration(profileSetting);
storeSslConfiguration(key, configuration);
sslContextHolders.computeIfAbsent(configuration, this::createSslContext);
});
profileSettings.forEach((key, profileSetting) -> loadConfiguration(key, profileSetting, sslContextHolders));
return Collections.unmodifiableMap(sslContextHolders);
}
private SSLConfiguration loadConfiguration(String key, Settings settings, Map<SSLConfiguration, SSLContextHolder> contextHolders) {
if (key.endsWith(".")) {
// Drop trailing '.' so that any exception messages are consistent
key = key.substring(0, key.length() - 1);
}
try {
final SSLConfiguration configuration = new SSLConfiguration(settings);
storeSslConfiguration(key, configuration);
contextHolders.computeIfAbsent(configuration, this::createSslContext);
return configuration;
} catch (Exception e) {
throw new ElasticsearchSecurityException("failed to load SSL configuration [{}]", e, key);
}
}
private void storeSslConfiguration(String key, SSLConfiguration configuration) {
if (key.endsWith(".")) {
key = key.substring(0, key.length() - 1);

View File

@ -14,17 +14,18 @@ import org.elasticsearch.xpack.core.ssl.cert.CertificateInfo;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.AccessDeniedException;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.security.AccessControlException;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
@ -38,6 +39,8 @@ import java.util.Objects;
*/
class StoreKeyConfig extends KeyConfig {
private static final String KEYSTORE_FILE = "keystore";
final String keyStorePath;
final String keyStoreType;
final SecureString keyStorePassword;
@ -68,28 +71,42 @@ class StoreKeyConfig extends KeyConfig {
@Override
X509ExtendedKeyManager createKeyManager(@Nullable Environment environment) {
Path ksPath = keyStorePath == null ? null : CertParsingUtils.resolvePath(keyStorePath, environment);
try {
KeyStore ks = getStore(environment, keyStorePath, keyStoreType, keyStorePassword);
KeyStore ks = getStore(ksPath, keyStoreType, keyStorePassword);
checkKeyStore(ks);
return CertParsingUtils.keyManager(ks, keyPassword.getChars(), keyStoreAlgorithm);
} catch (IOException | CertificateException | NoSuchAlgorithmException | UnrecoverableKeyException | KeyStoreException e) {
throw new ElasticsearchException("failed to initialize a KeyManagerFactory", e);
} catch (FileNotFoundException | NoSuchFileException e) {
throw missingKeyConfigFile(e, KEYSTORE_FILE, ksPath);
} catch (AccessDeniedException e) {
throw unreadableKeyConfigFile(e, KEYSTORE_FILE, ksPath);
} catch (AccessControlException e) {
throw blockedKeyConfigFile(e, environment, KEYSTORE_FILE, ksPath);
} catch (IOException | GeneralSecurityException e) {
throw new ElasticsearchException("failed to initialize SSL KeyManager", e);
}
}
@Override
X509ExtendedTrustManager createTrustManager(@Nullable Environment environment) {
final Path ksPath = CertParsingUtils.resolvePath(keyStorePath, environment);
try {
KeyStore ks = getStore(environment, keyStorePath, keyStoreType, keyStorePassword);
KeyStore ks = getStore(ksPath, keyStoreType, keyStorePassword);
return CertParsingUtils.trustManager(ks, trustStoreAlgorithm);
} catch (IOException | CertificateException | NoSuchAlgorithmException | KeyStoreException e) {
throw new ElasticsearchException("failed to initialize a TrustManagerFactory", e);
} catch (FileNotFoundException | NoSuchFileException e) {
throw missingTrustConfigFile(e, KEYSTORE_FILE, ksPath);
} catch (AccessDeniedException e) {
throw missingTrustConfigFile(e, KEYSTORE_FILE, ksPath);
} catch (AccessControlException e) {
throw blockedTrustConfigFile(e, environment, KEYSTORE_FILE, Collections.singletonList(ksPath));
} catch (IOException | GeneralSecurityException e) {
throw new ElasticsearchException("failed to initialize SSL TrustManager", e);
}
}
@Override
Collection<CertificateInfo> certificates(Environment environment) throws GeneralSecurityException, IOException {
final KeyStore trustStore = getStore(environment, keyStorePath, keyStoreType, keyStorePassword);
final KeyStore trustStore = getStore(CertParsingUtils.resolvePath(keyStorePath, environment), keyStoreType, keyStorePassword);
final List<CertificateInfo> certificates = new ArrayList<>();
final Enumeration<String> aliases = trustStore.aliases();
while (aliases.hasMoreElements()) {

View File

@ -13,8 +13,12 @@ import org.elasticsearch.xpack.core.ssl.cert.CertificateInfo;
import javax.net.ssl.X509ExtendedTrustManager;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.AccessDeniedException;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.security.AccessControlException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.cert.Certificate;
@ -31,6 +35,8 @@ import java.util.Objects;
*/
class StoreTrustConfig extends TrustConfig {
private static final String TRUSTSTORE_FILE = "truststore";
final String trustStorePath;
final String trustStoreType;
final SecureString trustStorePassword;
@ -54,11 +60,18 @@ class StoreTrustConfig extends TrustConfig {
@Override
X509ExtendedTrustManager createTrustManager(@Nullable Environment environment) {
final Path storePath = CertParsingUtils.resolvePath(trustStorePath, environment);
try {
KeyStore trustStore = getStore(environment, trustStorePath, trustStoreType, trustStorePassword);
KeyStore trustStore = getStore(storePath, trustStoreType, trustStorePassword);
return CertParsingUtils.trustManager(trustStore, trustStoreAlgorithm);
} catch (FileNotFoundException | NoSuchFileException e) {
throw missingTrustConfigFile(e, TRUSTSTORE_FILE, storePath);
} catch (AccessDeniedException e) {
throw unreadableTrustConfigFile(e, TRUSTSTORE_FILE, storePath);
} catch (AccessControlException e) {
throw blockedTrustConfigFile(e, environment, TRUSTSTORE_FILE, Collections.singletonList(storePath));
} catch (Exception e) {
throw new ElasticsearchException("failed to initialize a TrustManagerFactory", e);
throw new ElasticsearchException("failed to initialize SSL TrustManager", e);
}
}

View File

@ -12,11 +12,12 @@ import org.elasticsearch.env.Environment;
import org.elasticsearch.xpack.core.ssl.cert.CertificateInfo;
import javax.net.ssl.X509ExtendedTrustManager;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.AccessDeniedException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.AccessControlException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
@ -65,12 +66,20 @@ abstract class TrustConfig {
*/
public abstract int hashCode();
/**
* @deprecated Use {@link #getStore(Path, String, SecureString)} instead
*/
@Deprecated
KeyStore getStore(@Nullable Environment environment, @Nullable String storePath, String storeType, SecureString storePassword)
throws GeneralSecurityException, IOException {
return getStore(CertParsingUtils.resolvePath(storePath, environment), storeType, storePassword);
}
/**
* Loads and returns the appropriate {@link KeyStore} for the given configuration. The KeyStore can be backed by a file
* in any format that the Security Provider might support, or a cryptographic software or hardware token in the case
* of a PKCS#11 Provider.
*
* @param environment the environment to resolve files against or null in the case of running in a transport client
* @param storePath the path to the {@link KeyStore} to load, or null if a PKCS11 token is configured as the keystore/truststore
* of the JVM
* @param storeType the type of the {@link KeyStore}
@ -81,10 +90,9 @@ abstract class TrustConfig {
* @throws NoSuchAlgorithmException if the algorithm used to check the integrity of the keystore cannot be found
* @throws IOException if there is an I/O issue with the KeyStore data or the password is incorrect
*/
KeyStore getStore(@Nullable Environment environment, @Nullable String storePath, String storeType, SecureString storePassword)
throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException {
KeyStore getStore(@Nullable Path storePath, String storeType, SecureString storePassword) throws IOException, GeneralSecurityException {
if (null != storePath) {
try (InputStream in = Files.newInputStream(CertParsingUtils.resolvePath(storePath, environment))) {
try (InputStream in = Files.newInputStream(storePath)) {
KeyStore ks = KeyStore.getInstance(storeType);
ks.load(in, storePassword.getChars());
return ks;
@ -97,6 +105,44 @@ abstract class TrustConfig {
throw new IllegalArgumentException("keystore.path or truststore.path can only be empty when using a PKCS#11 token");
}
/**
* generate a new exception caused by a missing file, that is required for this trust config
*/
protected ElasticsearchException missingTrustConfigFile(IOException cause, String fileType, Path path) {
return new ElasticsearchException(
"failed to initialize SSL TrustManager - " + fileType + " file [{}] does not exist", cause, path.toAbsolutePath());
}
/**
* generate a new exception caused by an unreadable file (i.e. file-system access denied), that is required for this trust config
*/
protected ElasticsearchException unreadableTrustConfigFile(AccessDeniedException cause, String fileType, Path path) {
return new ElasticsearchException(
"failed to initialize SSL TrustManager - not permitted to read " + fileType + " file [{}]", cause, path.toAbsolutePath());
}
/**
* generate a new exception caused by a blocked file (i.e. security-manager access denied), that is required for this trust config
* @param paths A list of possible files. Depending on the context, it may not be possible to know exactly which file caused the
* exception, so this method accepts multiple paths.
*/
protected ElasticsearchException blockedTrustConfigFile(AccessControlException cause, Environment environment,
String fileType, List<Path> paths) {
if (paths.size() == 1) {
return new ElasticsearchException(
"failed to initialize SSL TrustManager - access to read {} file [{}] is blocked;" +
" SSL resources should be placed in the [{}] directory",
cause, fileType, paths.get(0).toAbsolutePath(), environment.configFile());
} else {
final String pathString = paths.stream().map(Path::toAbsolutePath).map(Path::toString).collect(Collectors.joining(", "));
return new ElasticsearchException(
"failed to initialize SSL TrustManager - access to read one or more of the {} files [{}] is blocked;" +
" SSL resources should be placed in the [{}] directory",
cause, fileType, pathString, environment.configFile());
}
}
/**
* A trust configuration that is a combination of a trust configuration with the default JDK trust configuration. This trust
* configuration returns a trust manager verifies certificates against both the default JDK trusted configurations and the specific

View File

@ -5,7 +5,10 @@
*/
package org.elasticsearch.test;
import org.hamcrest.BaseMatcher;
import org.hamcrest.CoreMatchers;
import org.hamcrest.CustomMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.Matchers;
@ -26,6 +29,39 @@ public class TestMatchers extends Matchers {
};
}
public static Matcher<Throwable> throwableWithMessage(String message) {
return throwableWithMessage(CoreMatchers.equalTo(message));
}
public static Matcher<Throwable> throwableWithMessage(Matcher<String> messageMatcher) {
return new BaseMatcher<Throwable>() {
@Override
public void describeTo(Description description) {
description.appendText("a throwable with message of ").appendDescriptionOf(messageMatcher);
}
@Override
public boolean matches(Object actual) {
if (actual instanceof Throwable) {
final Throwable throwable = (Throwable) actual;
return messageMatcher.matches(throwable.getMessage());
} else {
return false;
}
}
@Override
public void describeMismatch(Object item, Description description) {
super.describeMismatch(item, description);
if (item instanceof Throwable) {
Throwable e = (Throwable) item;
final StackTraceElement at = e.getStackTrace()[0];
description.appendText(" at ").appendText(at.toString());
}
}
};
}
public static <T> Matcher<Predicate<T>> predicateMatches(T value) {
return new CustomMatcher<Predicate<T>>("Matches " + value) {
@Override

View File

@ -8,6 +8,7 @@ package org.elasticsearch.xpack.core.ssl;
import org.elasticsearch.test.ESTestCase;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
@ -86,7 +87,7 @@ public class CertParsingUtilsTests extends ESTestCase {
assertEquals("EC", cert.getPublicKey().getAlgorithm());
}
private void verifyPrime256v1ECKey(Path keyPath) {
private void verifyPrime256v1ECKey(Path keyPath) throws IOException {
PrivateKey privateKey = PemUtils.readPrivateKey(keyPath, () -> null);
assertEquals("EC", privateKey.getAlgorithm());
assertThat(privateKey, instanceOf(ECPrivateKey.class));

View File

@ -69,6 +69,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import static org.elasticsearch.test.TestMatchers.throwableWithMessage;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.sameInstance;
@ -353,7 +354,7 @@ public class SSLConfigurationReloaderTests extends ESTestCase {
latch.await();
assertNotNull(exceptionRef.get());
assertThat(exceptionRef.get().getMessage(), containsString("failed to initialize a KeyManagerFactory"));
assertThat(exceptionRef.get(), throwableWithMessage(containsString("failed to initialize SSL KeyManager")));
assertThat(sslService.sslContextHolder(config).sslContext(), sameInstance(context));
}
@ -451,7 +452,7 @@ public class SSLConfigurationReloaderTests extends ESTestCase {
latch.await();
assertNotNull(exceptionRef.get());
assertThat(exceptionRef.get().getMessage(), containsString("failed to initialize a TrustManagerFactory"));
assertThat(exceptionRef.get(), throwableWithMessage(containsString("failed to initialize SSL TrustManager")));
assertThat(sslService.sslContextHolder(config).sslContext(), sameInstance(context));
}
@ -497,7 +498,7 @@ public class SSLConfigurationReloaderTests extends ESTestCase {
latch.await();
assertNotNull(exceptionRef.get());
assertThat(exceptionRef.get().getMessage(), containsString("failed to initialize a TrustManagerFactory"));
assertThat(exceptionRef.get(), throwableWithMessage(containsString("failed to initialize SSL TrustManager")));
assertThat(sslService.sslContextHolder(config).sslContext(), sameInstance(context));
}

View File

@ -60,6 +60,7 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import static org.elasticsearch.test.TestMatchers.throwableWithMessage;
import static org.hamcrest.Matchers.arrayContainingInAnyOrder;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsString;
@ -194,7 +195,8 @@ public class SSLServiceTests extends ESTestCase {
sslService.createSSLEngine(configuration, null, -1);
fail("expected an exception");
} catch (ElasticsearchException e) {
assertThat(e.getMessage(), containsString("failed to initialize a KeyManagerFactory"));
assertThat(e, throwableWithMessage("failed to load SSL configuration [xpack.security.transport.ssl]"));
assertThat(e.getCause(), throwableWithMessage(containsString("failed to initialize SSL KeyManager")));
}
}
@ -326,7 +328,8 @@ public class SSLServiceTests extends ESTestCase {
.build();
ElasticsearchException e =
expectThrows(ElasticsearchException.class, () -> new SSLService(settings, env));
assertThat(e.getMessage(), is("failed to initialize a TrustManagerFactory"));
assertThat(e, throwableWithMessage("failed to load SSL configuration [xpack.security.transport.ssl]"));
assertThat(e.getCause(), throwableWithMessage(containsString("failed to initialize SSL TrustManager")));
}
public void testThatKeystorePasswordIsRequired() throws Exception {
@ -336,7 +339,8 @@ public class SSLServiceTests extends ESTestCase {
.build();
ElasticsearchException e =
expectThrows(ElasticsearchException.class, () -> new SSLService(settings, env));
assertThat(e.getMessage(), is("failed to create trust manager"));
assertThat(e, throwableWithMessage("failed to load SSL configuration [xpack.security.transport.ssl]"));
assertThat(e.getCause(), throwableWithMessage("failed to create trust manager"));
}
public void testCiphersAndInvalidCiphersWork() throws Exception {
@ -369,9 +373,10 @@ public class SSLServiceTests extends ESTestCase {
.setSecureSettings(secureSettings)
.putList("xpack.security.transport.ssl.cipher_suites", new String[] { "foo", "bar" })
.build();
IllegalArgumentException e =
expectThrows(IllegalArgumentException.class, () -> new SSLService(settings, env));
assertThat(e.getMessage(), is("none of the ciphers [foo, bar] are supported by this JVM"));
ElasticsearchException e =
expectThrows(ElasticsearchException.class, () -> new SSLService(settings, env));
assertThat(e, throwableWithMessage("failed to load SSL configuration [xpack.security.transport.ssl]"));
assertThat(e.getCause(), throwableWithMessage("none of the ciphers [foo, bar] are supported by this JVM"));
}
public void testThatSSLEngineHasCipherSuitesOrderSet() throws Exception {

View File

@ -17,6 +17,7 @@ import javax.net.ssl.X509ExtendedKeyManager;
import java.security.PrivateKey;
import static org.elasticsearch.test.TestMatchers.throwableWithMessage;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.notNullValue;
@ -46,7 +47,7 @@ public class StoreKeyConfigTests extends ESTestCase {
KeyManagerFactory.getDefaultAlgorithm(), TrustManagerFactory.getDefaultAlgorithm());
ElasticsearchException ee = expectThrows(ElasticsearchException.class, () ->
keyConfigPkcs11.createKeyManager(TestEnvironment.newEnvironment(settings)));
assertThat(ee.getMessage(), containsString("failed to initialize a KeyManagerFactory"));
assertThat(ee, throwableWithMessage(containsString("failed to initialize SSL KeyManager")));
assertThat(ee.getCause().getMessage(), containsString("PKCS11 not found"));
}

View File

@ -0,0 +1,322 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.ssl;
import org.apache.lucene.util.Constants;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.env.TestEnvironment;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.core.ssl.SSLService;
import org.junit.Before;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.channels.Channels;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.AccessDeniedException;
import java.nio.file.CopyOption;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.security.AccessControlException;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import static org.elasticsearch.test.SecuritySettingsSource.addSecureSettings;
import static org.elasticsearch.test.TestMatchers.throwableWithMessage;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.instanceOf;
/**
* This is a suite of tests to ensure that meaningful error messages are generated for defined SSL configuration problems.
*/
public class SSLErrorMessageTests extends ESTestCase {
private Environment env;
private Map<String, Path> paths;
@Before
public void setup() throws Exception {
env = TestEnvironment.newEnvironment(Settings.builder().put("path.home", createTempDir()).build());
paths = new HashMap<>();
requirePath("ca1.p12");
requirePath("ca1.jks");
requirePath("ca1.crt");
requirePath("cert1a.p12");
requirePath("cert1a.jks");
requirePath("cert1a.crt");
requirePath("cert1a.key");
}
public void testMessageForMissingKeystore() {
checkMissingKeyManagerResource("keystore", "keystore.path", null);
}
public void testMessageForMissingPemCertificate() {
checkMissingKeyManagerResource("certificate", "certificate", withKey("cert1a.key"));
}
public void testMessageForMissingPemKey() {
checkMissingKeyManagerResource("key", "key", withCertificate("cert1a.crt"));
}
public void testMessageForMissingTruststore() {
checkMissingTrustManagerResource("truststore", "truststore.path");
}
public void testMessageForMissingCertificateAuthorities() {
checkMissingTrustManagerResource("certificate_authorities", "certificate_authorities");
}
public void testMessageForKeystoreWithoutReadAccess() throws Exception {
checkUnreadableKeyManagerResource("cert1a.p12", "keystore", "keystore.path", null);
}
public void testMessageForPemCertificateWithoutReadAccess() throws Exception {
checkUnreadableKeyManagerResource("cert1a.crt", "certificate", "certificate", withKey("cert1a.key"));
}
public void testMessageForPemKeyWithoutReadAccess() throws Exception {
checkUnreadableKeyManagerResource("cert1a.key", "key", "key", withCertificate("cert1a.crt"));
}
public void testMessageForTruststoreWithoutReadAccess() throws Exception {
checkUnreadableTrustManagerResource("cert1a.p12", "truststore", "truststore.path");
}
public void testMessageForCertificateAuthoritiesWithoutReadAccess() throws Exception {
checkUnreadableTrustManagerResource("ca1.crt", "certificate_authorities", "certificate_authorities");
}
public void testMessageForKeyStoreOutsideConfigDir() throws Exception {
checkBlockedKeyManagerResource("keystore", "keystore.path", null);
}
public void testMessageForPemCertificateOutsideConfigDir() throws Exception {
checkBlockedKeyManagerResource("certificate", "certificate", withKey("cert1a.key"));
}
public void testMessageForPemKeyOutsideConfigDir() throws Exception {
checkBlockedKeyManagerResource("key", "key", withCertificate("cert1a.crt"));
}
public void testMessageForTrustStoreOutsideConfigDir() throws Exception {
checkBlockedTrustManagerResource("truststore", "truststore.path");
}
public void testMessageForCertificateAuthoritiesOutsideConfigDir() throws Exception {
checkBlockedTrustManagerResource("certificate_authorities", "certificate_authorities");
}
private void checkMissingKeyManagerResource(String fileType, String configKey, @Nullable Settings.Builder additionalSettings) {
checkMissingResource("KeyManager", fileType, configKey,
(prefix, builder) -> buildKeyConfigSettings(additionalSettings, prefix, builder));
}
private void buildKeyConfigSettings(@Nullable Settings.Builder additionalSettings, String prefix, Settings.Builder builder) {
configureWorkingTruststore(prefix, builder);
if (additionalSettings != null) {
builder.put(additionalSettings.normalizePrefix(prefix + ".").build());
}
}
private void checkMissingTrustManagerResource(String fileType, String configKey) {
checkMissingResource("TrustManager", fileType, configKey, this::configureWorkingKeystore);
}
private void checkUnreadableKeyManagerResource(String fromResource, String fileType, String configKey,
@Nullable Settings.Builder additionalSettings) throws Exception {
checkUnreadableResource("KeyManager", fromResource, fileType, configKey,
(prefix, builder) -> buildKeyConfigSettings(additionalSettings, prefix, builder));
}
private void checkUnreadableTrustManagerResource(String fromResource, String fileType, String configKey) throws Exception {
checkUnreadableResource("TrustManager", fromResource, fileType, configKey, this::configureWorkingKeystore);
}
private void checkBlockedKeyManagerResource(String fileType, String configKey, Settings.Builder additionalSettings) throws Exception {
checkBlockedResource("KeyManager", fileType, configKey,
(prefix, builder) -> buildKeyConfigSettings(additionalSettings, prefix, builder));
}
private void checkBlockedTrustManagerResource(String fileType, String configKey) throws Exception {
checkBlockedResource("TrustManager", fileType, configKey, this::configureWorkingKeystore);
}
private void checkMissingResource(String sslManagerType, String fileType, String configKey,
BiConsumer<String, Settings.Builder> configure) {
final String prefix = randomSslPrefix();
final Settings.Builder settings = Settings.builder();
configure.accept(prefix, settings);
final String fileName = missingFile();
final String key = prefix + "." + configKey;
settings.put(key, fileName);
Throwable exception = expectFailure(settings);
assertThat(exception, throwableWithMessage("failed to load SSL configuration [" + prefix + "]"));
assertThat(exception, instanceOf(ElasticsearchSecurityException.class));
exception = exception.getCause();
assertThat(exception, throwableWithMessage(
"failed to initialize SSL " + sslManagerType + " - " + fileType + " file [" + fileName + "] does not exist"));
assertThat(exception, instanceOf(ElasticsearchException.class));
exception = exception.getCause();
assertThat(exception, instanceOf(NoSuchFileException.class));
assertThat(exception, throwableWithMessage(fileName));
}
private void checkUnreadableResource(String sslManagerType, String fromResource, String fileType, String configKey,
BiConsumer<String, Settings.Builder> configure) throws Exception {
final String prefix = randomSslPrefix();
final Settings.Builder settings = Settings.builder();
configure.accept(prefix, settings);
final String fileName = unreadableFile(fromResource);
final String key = prefix + "." + configKey;
settings.put(key, fileName);
Throwable exception = expectFailure(settings);
assertThat(exception, throwableWithMessage("failed to load SSL configuration [" + prefix + "]"));
assertThat(exception, instanceOf(ElasticsearchSecurityException.class));
exception = exception.getCause();
assertThat(exception, throwableWithMessage(
"failed to initialize SSL " + sslManagerType + " - not permitted to read " + fileType + " file [" + fileName + "]"));
assertThat(exception, instanceOf(ElasticsearchException.class));
exception = exception.getCause();
assertThat(exception, instanceOf(AccessDeniedException.class));
assertThat(exception, throwableWithMessage(fileName));
}
private void checkBlockedResource(String sslManagerType, String fileType, String configKey,
BiConsumer<String, Settings.Builder> configure) throws Exception {
final String prefix = randomSslPrefix();
final Settings.Builder settings = Settings.builder();
configure.accept(prefix, settings);
final String fileName = blockedFile();
final String key = prefix + "." + configKey;
settings.put(key, fileName);
Throwable exception = expectFailure(settings);
assertThat(exception, throwableWithMessage("failed to load SSL configuration [" + prefix + "]"));
assertThat(exception, instanceOf(ElasticsearchSecurityException.class));
exception = exception.getCause();
assertThat(exception, throwableWithMessage(
"failed to initialize SSL " + sslManagerType + " - access to read " + fileType + " file [" + fileName +
"] is blocked; SSL resources should be placed in the [" + env.configFile() + "] directory"));
assertThat(exception, instanceOf(ElasticsearchException.class));
exception = exception.getCause();
assertThat(exception, instanceOf(AccessControlException.class));
assertThat(exception, throwableWithMessage(containsString(fileName)));
}
private String missingFile() {
return resource("cert1a.p12").replace("cert1a.p12", "file.dne");
}
private String unreadableFile(String fromResource) throws IOException {
assumeFalse("This behaviour uses POSIX file permissions", Constants.WINDOWS);
final Path fromPath = this.paths.get(fromResource);
if (fromPath == null) {
throw new IllegalArgumentException("Test SSL resource " + fromResource + " has not been loaded");
}
return copy(fromPath, createTempFile(fromResource, "-no-read"), PosixFilePermissions.fromString("---------"));
}
private String blockedFile() throws IOException {
return "/this/path/is/outside/the/config/directory/file.error";
}
/**
* This more-or-less replicates the functionality of {@link Files#copy(Path, Path, CopyOption...)} but doing it this way allows us to
* set the file permissions when creating the file (which helps with security manager issues)
*/
private String copy(Path fromPath, Path toPath, Set<PosixFilePermission> permissions) throws IOException {
Files.deleteIfExists(toPath);
final FileAttribute<Set<PosixFilePermission>> fileAttributes = PosixFilePermissions.asFileAttribute(permissions);
final EnumSet<StandardOpenOption> options = EnumSet.of(StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE);
try (SeekableByteChannel channel = Files.newByteChannel(toPath, options, fileAttributes);
OutputStream out = Channels.newOutputStream(channel)) {
Files.copy(fromPath, out);
}
return toPath.toString();
}
private Settings.Builder withKey(String fileName) {
assertThat(fileName, endsWith(".key"));
return Settings.builder().put("key", resource(fileName));
}
private Settings.Builder withCertificate(String fileName) {
assertThat(fileName, endsWith(".crt"));
return Settings.builder().put("certificate", resource(fileName));
}
private Settings.Builder configureWorkingTruststore(String prefix, Settings.Builder settings) {
settings.put(prefix + ".truststore.path", resource("cert1a.p12"));
addSecureSettings(settings, secure -> secure.setString(prefix + ".truststore.secure_password", "cert1a-p12-password"));
return settings;
}
private Settings.Builder configureWorkingKeystore(String prefix, Settings.Builder settings) {
settings.put(prefix + ".keystore.path", resource("cert1a.p12"));
addSecureSettings(settings, secure -> secure.setString(prefix + ".keystore.secure_password", "cert1a-p12-password"));
return settings;
}
private ElasticsearchException expectFailure(Settings.Builder settings) {
return expectThrows(ElasticsearchException.class, () -> new SSLService(settings.build(), env));
}
private String resource(String fileName) {
final Path path = this.paths.get(fileName);
if (path == null) {
throw new IllegalArgumentException("Test SSL resource " + fileName + " has not been loaded");
}
return path.toString();
}
private void requirePath(String fileName) throws FileNotFoundException {
final Path path = getDataPath("/org/elasticsearch/xpack/ssl/SSLServiceErrorMessageTests/" + fileName);
if (Files.exists(path)) {
paths.put(fileName, path);
} else {
throw new FileNotFoundException("File " + path + " does not exist");
}
}
private String randomSslPrefix() {
return randomFrom(
"xpack.security.transport.ssl",
"xpack.security.http.ssl",
"xpack.http.ssl",
"xpack.security.authc.realms.ldap.ldap1.ssl",
"xpack.security.authc.realms.saml.saml1.ssl",
"xpack.monitoring.exporters.http.ssl"
);
}
}

View File

@ -0,0 +1,106 @@
#
# NOTE: This readme is also an executable shell script.
# Run it with bash ./README.txt
#
#
# Make sure we can call certutil
#
[ -n "$ES_HOME" ] || { printf '%s: $ES_HOME is not set\n' "$0" ; exit 1; }
[ -d "$ES_HOME" ] || { printf '%s: $ES_HOME is not a directory\n' "$0" ; exit 1; }
function certutil() { "$ES_HOME/bin/elasticsearch-certutil" "$@"; }
#
# Helper functions to generate files & convert file types
#
function new-p12-ca() {
local P12File="$1"
local P12Pass="$2"
local CaDn="$3"
certutil ca --ca-dn="$CaDn" --days=5000 --out ${PWD}/$P12File --pass="$P12Pass"
}
function new-p12-cert() {
local CertFile="$1"
local CertPass="$2"
local CertName="$3"
local CaFile="$4"
local CaPass="$5"
shift 5
certutil cert --ca="${PWD}/$CaFile" --ca-pass="$CaPass" --days=5000 --out ${PWD}/$CertFile --pass="$CertPass" --name="$CertName" "$@"
}
function p12-to-jks() {
local P12File="$1"
local P12Pass="$2"
local JksFile="$3"
local JksPass="$4"
keytool -importkeystore -srckeystore "${PWD}/$P12File" -srcstorepass "$P12Pass" \
-destkeystore "${PWD}/$JksFile" -deststoretype JKS -deststorepass "$JksPass"
}
function p12-export-cert() {
local P12File="$1"
local P12Pass="$2"
local P12Name="$3"
local PemFile="$4"
keytool -exportcert -keystore "${PWD}/$P12File" -storepass "$P12Pass" -alias "$P12Name" \
-rfc -file "${PWD}/$PemFile"
}
function p12-export-pair() {
local P12File="$1"
local P12Pass="$2"
local P12Name="$3"
local CrtFile="$4"
local KeyFile="$5"
local TmpFile="${PWD}/$(basename $P12File .p12).tmp.p12"
# OpenSSL doesn't have a way to export a single entry
# Keytool doesn't have a way to export keys
# So we use keytool to export the whole entry to a temporary PKCS#12 and then use openssl to export that to PEM
keytool -importkeystore -srckeystore "${PWD}/$P12File" -srcstorepass "$P12Pass" -srcalias "$P12Name" \
-destkeystore "$TmpFile" -deststorepass "tmp_password"
# This produces an unencrypted PKCS#1 key. Use other commands to convert it if needed
# The sed is to skip "BagAttributes" which we don't need
openssl pkcs12 -in "$TmpFile" -nodes -nocerts -passin "pass:tmp_password" | sed -n -e'/^-----/,/^-----/p' > $KeyFile
openssl pkcs12 -in "$TmpFile" -clcerts -nokeys -passin "pass:tmp_password" | sed -n -e'/^-----/,/^-----/p' > $CrtFile
rm $TmpFile
}
#
# Create a CA in PKCS#12
#
new-p12-ca ca1.p12 "ca1-p12-password" 'CN=Certificate Authority 1,OU=ssl-error-message-test,DC=elastic,DC=co'
# Make a JKS version of the CA
p12-to-jks ca1.p12 "ca1-p12-password" ca1.jks "ca1-jks-password"
# Make a PEM version of the CA cert
p12-export-cert ca1.p12 "ca1-p12-password" "ca" ca1.crt
#
# Create a Cert/Key Pair in PKCS#12
# - "cert1a" is signed by "ca1"
# - "cert1a.p12" is password protected, and can act as a keystore or truststore
#
new-p12-cert cert1a.p12 "cert1a-p12-password" "cert1a" "ca1.p12" "ca1-p12-password"
# Convert to JKS
# - "cert1a.jks" is password protected, and can act as a keystore or truststore
p12-to-jks cert1a.p12 "cert1a-p12-password" cert1a.jks "cert1a-jks-password"
# Convert to PEM
# - "cert1a.key" is an (unprotected) PKCS#1 key
p12-export-pair cert1a.p12 "cert1a-p12-password" "cert1a" cert1a.crt cert1a.key

View File

@ -0,0 +1,23 @@
-----BEGIN CERTIFICATE-----
MIIDwTCCAqmgAwIBAgIUK67pJtem4zfhIP4SpjoukdLlwnIwDQYJKoZIhvcNAQEL
BQAwcDESMBAGCgmSJomT8ixkARkWAmNvMRcwFQYKCZImiZPyLGQBGRYHZWxhc3Rp
YzEfMB0GA1UECxMWc3NsLWVycm9yLW1lc3NhZ2UtdGVzdDEgMB4GA1UEAxMXQ2Vy
dGlmaWNhdGUgQXV0aG9yaXR5IDEwHhcNMTkwNzE4MDc1NDMyWhcNMzMwMzI2MDc1
NDMyWjBwMRIwEAYKCZImiZPyLGQBGRYCY28xFzAVBgoJkiaJk/IsZAEZFgdlbGFz
dGljMR8wHQYDVQQLExZzc2wtZXJyb3ItbWVzc2FnZS10ZXN0MSAwHgYDVQQDExdD
ZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBAK/yaI+3JTakaGHrUBlVhvAP7Jeejkw0XevcRr4H96pO6fEZuq8Kkf73
/J/wZkzTqE6AGxVYGJV7brzZj8QwXPnkI4fCOLYLeBBX4333lb+X20RGmEjLeTKK
XkOA4FlpbDaecQwbUArBPF3sc2AXW62ZWEosUTR67wr3tFn3uX1RnX5OM0+7qI0Z
gcEm8ohqlig3NC7EmkuP/50CA2OnuuD8b57u0cdgM7uFXTzIASUCz5RU7SSYiI0q
HYOPza+CfUgevReRDc9rzIIVcxMmbab6gABysKGbUdlSTsqGqZMbEprFTv7zt7u1
lqHeVEjhL7F9769XGaqFeGfa9b1odokCAwEAAaNTMFEwHQYDVR0OBBYEFKHpV7oS
AMlY64s6FbHLPWYtoClTMB8GA1UdIwQYMBaAFKHpV7oSAMlY64s6FbHLPWYtoClT
MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAAg9Rq5QIiFpmmIN
yMMeSVCeqZJ1l03WO8YPNcMVGzvjsm12UCHK2ppCahOerGOchnX6tdsjj2aKIL0n
L8++LSDgxbsDA8kxkSldjcY8sJZhTMzvfUkabmWvr99UpVRDKT3EXkMmSaSIaA+U
1xEJAz8dPPFTXyxCNwEePyvy32I3JR6e/8UnUTVNlev8sOdwGvidqHq/PJvR4/qD
B5bn39gsQ6OgUaT6ye6Zp3iUx5uNipGwr2SzoK6ERJ2rwEC1/mQ321rDkSSdnHLT
E+hVL/qrGkg5cl0otPDFxIkOL33/lyyOW7cHdHEUkFsl9Osi6O0HFw1z4DoR7zT3
Ngckq2E=
-----END CERTIFICATE-----

View File

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIVAOsqJ9r+RWgkCe7vyZXYmF+sfBoNMA0GCSqGSIb3DQEB
CwUAMHAxEjAQBgoJkiaJk/IsZAEZFgJjbzEXMBUGCgmSJomT8ixkARkWB2VsYXN0
aWMxHzAdBgNVBAsTFnNzbC1lcnJvci1tZXNzYWdlLXRlc3QxIDAeBgNVBAMTF0Nl
cnRpZmljYXRlIEF1dGhvcml0eSAxMB4XDTE5MDcxODA3NTQzNVoXDTMzMDMyNjA3
NTQzNVowETEPMA0GA1UEAxMGY2VydDFhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAxZy4TEULFQxk988qZ4itwYo5oRnTfzrL5/Rb516Ll5Q9RrXnIFyg
u1CtF/eX0S1BGw9MCDSFK9fpwQMbDKo0G6E9arV9hTMo2MHMOhRXux6qnQlYzxW+
7dfpzoLQPb0ObYlvC9LclJwz2Hqz4qABSMfbHd789Y9CjHsNPb+9OZDMj6oJBT/G
nXiZxk0bJ/O8N1BN9nDUWnhqsDuuHVW30PuF0JVwHzqMqi/FVporabGcYnur+Pbd
kevpidWFFx6kpoAeXuYWPEeqTLiUpPyMwxNn620xUbr4bq8hEOkhswsdhZ90ffbv
lMZezS7vL5zBOGEtnGFXh0FaHfutZwX2nwIDAQABo00wSzAdBgNVHQ4EFgQUCcIu
5sjyXxEoFfz7zz3CVdpPGTMwHwYDVR0jBBgwFoAUoelXuhIAyVjrizoVscs9Zi2g
KVMwCQYDVR0TBAIwADANBgkqhkiG9w0BAQsFAAOCAQEAUtXdkttqA4BVyVhfjiOg
LA43V26gl/NsjVp/8SpEYahp/FPl2wQe3D/6Py9a0eVIdJQ7vwx15RqyeNi679Lg
hQxyh/dvLlnxhOY1LHAwc2v4zDr96h/JA8FeqemXNPmEIwdy930rZpgA/O/9dlh/
pVSRxg5mrNL1ekyzty44IOnbJ3URLyQmNOns3d6AKxwIxgkM041lex9NXsyw7gaz
GnxhORukdbuxW2zqweVKoTTo2AriUWic8FnZDNeaHgJ8LP1xvYECq1cb3S1qzzEO
LCofq9Lvxzemc/Q435khAvFAA4oB2qV37eZn3B1HT2RiPzHvM3ypjqhIAbqKX++w
AA==
-----END CERTIFICATE-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAxZy4TEULFQxk988qZ4itwYo5oRnTfzrL5/Rb516Ll5Q9RrXn
IFygu1CtF/eX0S1BGw9MCDSFK9fpwQMbDKo0G6E9arV9hTMo2MHMOhRXux6qnQlY
zxW+7dfpzoLQPb0ObYlvC9LclJwz2Hqz4qABSMfbHd789Y9CjHsNPb+9OZDMj6oJ
BT/GnXiZxk0bJ/O8N1BN9nDUWnhqsDuuHVW30PuF0JVwHzqMqi/FVporabGcYnur
+PbdkevpidWFFx6kpoAeXuYWPEeqTLiUpPyMwxNn620xUbr4bq8hEOkhswsdhZ90
ffbvlMZezS7vL5zBOGEtnGFXh0FaHfutZwX2nwIDAQABAoIBAQCfB1lVn7akcL4M
o36HpXnXqCpqmIMY/7M67u7LCs4h5R1O+3KOG50KQYmbIRjfMKEVasEQVVvahb7L
InmxPoQCvEbVykrCWAKGNafqEZbssmgxSmVa+jAV7k1tcN6u4KdsxU5FYKM9QVuD
2nNLbOK7tIKEzoAaCflPXnOwfs6ENLYz5vu96RAI5u8dn7GDw9gjuOF5Ij+IpY38
HBqrAhmZs4P7VFO03XwVXvuxEmuJ4Ng9n/bd+D8dO9WVLhRCNPY6HIyUTtRyHyvY
hW+ejkgBjrpdCh6jmWKUz3aD1m4QY1NSmuYvXCLJjeO9PLHj8Qib7BSET4/ZvLcn
xGmFlpDZAoGBAOLcV2PTYHeRSkgd1l3HRYV022TZLdqPsKs4OQnG0oz3OoLt8nBW
85VhDVYIgWF7Ro6SQBJUGUtCy/8pCySGcmm2RrS9VSunuiLr1mFTyZEg6Py5LixI
fimr8R5ZZp3yQtyDR/rX62z/F76HT87lBtAkc9gz50ceeHIbYjUK9zotAoGBAN7+
oB7JJnFtkKd7b17wEqz7xsGaeXKrZPt9LyTG0/3hTlqRKs51+TEo11R3sH6ny/so
VcobEnvLWJ1slRMFxbJTI2gQYjkRdxbgRKlL3D7nkXP6ZkyPTqZWHwnmL25HvkfT
Cg5t6uuUwu7zBx8pgvSVI7+ohNbIQihOuvSzwe97AoGAPz/vnYVxf+SiMTkga0UD
vRG3mYZzolwthY9HV9J3IZZMWfS9g1S61QsaDMKST/tu0JE2C+YmpzUYayumT6is
fYy0ae/fryw0WUgnnTsfF7d1PgDjPrV3d2bY8v05/w9sVM7FYsqQePMmS5iuR6DT
JXUEV2MhOFLmgengl6lXBIECgYEApaMs/LEdRRPFZyJgz3wz4xoBwL0liO+Wytdp
tT+bJ/G7abp6uXEX8FQN3kgpaWrqMVz8nGsPh7S71fNZqA+ZkaP/oMTKsOkwNGXR
mo8mwfLwL93HLwcjvJor5AZ/JMNVq7QuvBkZbnPJeAQ9PgBvrY06SJq8UW3e9mHE
rQ749xECgYA84wbJYvOBDzXq9QFBVgCI46i7rDH7L8fxSU5kXYqG8hQ0DMmjXPE1
aBD5a0qbeMbaA3yVYdtgSK2T47Kq+k/DE5Q/EFshuLShX9iEmB2WeNtLvTl4AO0i
9pwaI9uyzZCtK4bOUMq3Anf6AFnlrPRiXdhQYMrNQlc7cPULdawAtw==
-----END RSA PRIVATE KEY-----