Skip to content

Commit

Permalink
Extract SSLContext creation to plugin-toolkit
Browse files Browse the repository at this point in the history
  • Loading branch information
Praveen2112 committed Apr 5, 2022
1 parent 199c017 commit f36b9e7
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 187 deletions.
5 changes: 5 additions & 0 deletions lib/trino-plugin-toolkit/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@
<artifactId>parameternames</artifactId>
</dependency>

<dependency>
<groupId>io.airlift</groupId>
<artifactId>security</artifactId>
</dependency>

<dependency>
<groupId>io.airlift</groupId>
<artifactId>slice</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/*
* 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 io.trino.plugin.base.ssl;

import io.airlift.security.pem.PemReader;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import javax.security.auth.x500.X500Principal;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

import static java.util.Collections.list;

public final class SslUtils
{
private SslUtils() {}

public static SSLContext createSSLContext(
Optional<File> keyStorePath,
Optional<String> keyStorePassword,
Optional<File> trustStorePath,
Optional<String> trustStorePassword)
throws GeneralSecurityException, IOException
{
// load KeyStore if configured and get KeyManagers
KeyStore keyStore = null;
KeyManager[] keyManagers = null;
if (keyStorePath.isPresent()) {
char[] keyManagerPassword;
try {
// attempt to read the key store as a PEM file
keyStore = PemReader.loadKeyStore(keyStorePath.get(), keyStorePath.get(), keyStorePassword);
// for PEM encoded keys, the password is used to decrypt the specific key (and does not protect the keystore itself)
keyManagerPassword = new char[0];
}
catch (IOException | GeneralSecurityException ignored) {
keyManagerPassword = keyStorePassword.map(String::toCharArray).orElse(null);

keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
try (InputStream in = new FileInputStream(keyStorePath.get())) {
keyStore.load(in, keyManagerPassword);
}
}
validateCertificates(keyStore);
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, keyManagerPassword);
keyManagers = keyManagerFactory.getKeyManagers();
}

// load TrustStore if configured, otherwise use KeyStore
KeyStore trustStore = keyStore;
if (trustStorePath.isPresent()) {
trustStore = loadTrustStore(trustStorePath.get(), trustStorePassword);
}

// create TrustManagerFactory
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trustStore);

// get X509TrustManager
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new RuntimeException("Unexpected default trust managers:" + Arrays.toString(trustManagers));
}
// create SSLContext
SSLContext result = SSLContext.getInstance("SSL");
result.init(keyManagers, trustManagers, null);
return result;
}

private static KeyStore loadTrustStore(File trustStorePath, Optional<String> trustStorePassword)
throws IOException, GeneralSecurityException
{
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
try {
// attempt to read the trust store as a PEM file
List<X509Certificate> certificateChain = PemReader.readCertificateChain(trustStorePath);
if (!certificateChain.isEmpty()) {
trustStore.load(null, null);
for (X509Certificate certificate : certificateChain) {
X500Principal principal = certificate.getSubjectX500Principal();
trustStore.setCertificateEntry(principal.getName(), certificate);
}
return trustStore;
}
}
catch (IOException | GeneralSecurityException ignored) {
}

try (InputStream in = new FileInputStream(trustStorePath)) {
trustStore.load(in, trustStorePassword.map(String::toCharArray).orElse(null));
}
return trustStore;
}

private static void validateCertificates(KeyStore keyStore)
throws GeneralSecurityException
{
for (String alias : list(keyStore.aliases())) {
if (!keyStore.isKeyEntry(alias)) {
continue;
}
Certificate certificate = keyStore.getCertificate(alias);
if (!(certificate instanceof X509Certificate)) {
continue;
}

try {
((X509Certificate) certificate).checkValidity();
}
catch (CertificateExpiredException e) {
throw new CertificateExpiredException("KeyStore certificate is expired: " + e.getMessage());
}
catch (CertificateNotYetValidException e) {
throw new CertificateNotYetValidException("KeyStore certificate is not yet valid: " + e.getMessage());
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,7 @@

import javax.inject.Inject;
import javax.inject.Singleton;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import javax.security.auth.x500.X500Principal;

import java.io.File;
Expand All @@ -59,14 +54,14 @@
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

import static com.google.common.base.Preconditions.checkArgument;
import static io.airlift.configuration.ConfigBinder.configBinder;
import static io.airlift.json.JsonBinder.jsonBinder;
import static io.airlift.json.JsonCodecBinder.jsonCodecBinder;
import static io.trino.plugin.base.ssl.SslUtils.createSSLContext;
import static io.trino.plugin.cassandra.CassandraErrorCode.CASSANDRA_SSL_INITIALIZATION_FAILURE;
import static java.lang.Math.toIntExact;
import static java.util.Collections.list;
Expand Down Expand Up @@ -216,50 +211,7 @@ private static Optional<SSLContext> buildSslContext(
}

try {
// load KeyStore if configured and get KeyManagers
KeyStore keystore = null;
KeyManager[] keyManagers = null;
if (keystorePath.isPresent()) {
char[] keyManagerPassword;
try {
// attempt to read the key store as a PEM file
keystore = PemReader.loadKeyStore(keystorePath.get(), keystorePath.get(), keystorePassword);
// for PEM encoded keys, the password is used to decrypt the specific key (and does not protect the keystore itself)
keyManagerPassword = new char[0];
}
catch (IOException | GeneralSecurityException ignored) {
keyManagerPassword = keystorePassword.map(String::toCharArray).orElse(null);

keystore = KeyStore.getInstance(KeyStore.getDefaultType());
try (InputStream in = new FileInputStream(keystorePath.get())) {
keystore.load(in, keyManagerPassword);
}
}
validateCertificates(keystore);
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keystore, keyManagerPassword);
keyManagers = keyManagerFactory.getKeyManagers();
}

// load TrustStore if configured, otherwise use KeyStore
KeyStore truststore = keystore;
if (truststorePath.isPresent()) {
truststore = loadTrustStore(truststorePath.get(), truststorePassword);
}

// create TrustManagerFactory
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(truststore);

// get X509TrustManager
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new RuntimeException("Unexpected default trust managers:" + Arrays.toString(trustManagers));
}
// create SSLContext
SSLContext result = SSLContext.getInstance("SSL");
result.init(keyManagers, trustManagers, null);
return Optional.of(result);
return Optional.of(createSSLContext(keystorePath, keystorePassword, truststorePath, truststorePassword));
}
catch (GeneralSecurityException | IOException e) {
throw new TrinoException(CASSANDRA_SSL_INITIALIZATION_FAILURE, e);
Expand Down
5 changes: 0 additions & 5 deletions plugin/trino-elasticsearch/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,6 @@
<artifactId>log</artifactId>
</dependency>

<dependency>
<groupId>io.airlift</groupId>
<artifactId>security</artifactId>
</dependency>

<dependency>
<groupId>io.airlift</groupId>
<artifactId>stats</artifactId>
Expand Down
Loading

0 comments on commit f36b9e7

Please sign in to comment.