summaryrefslogtreecommitdiffstats
path: root/security-utils
diff options
context:
space:
mode:
authorBjørn Christian Seime <bjorncs@verizonmedia.com>2019-02-07 15:45:43 +0100
committerBjørn Christian Seime <bjorncs@verizonmedia.com>2019-02-19 17:00:32 +0100
commit68a35cf9ab94fe06cd3247b9fec1829be2b1c849 (patch)
tree17810bfaed63ff266a30eaa0e3fbc45a5491653b /security-utils
parent1a6f276068714ae18c2fb5094517d16132e26d56 (diff)
Misc changes to TlsContext and its implementations
- Add methods to retrieve underlying SSLContext and SSLParameters - Add createSslEngine() overload with peer host and port - Remove constructor DefaultTlsContext constructor taking path to config file. - Resolve valid ciphers and protcols in constructor. - Use mutual x509 key/trust manager in ReloadingTlsContext
Diffstat (limited to 'security-utils')
-rw-r--r--security-utils/src/main/java/com/yahoo/security/tls/DefaultTlsContext.java91
-rw-r--r--security-utils/src/main/java/com/yahoo/security/tls/ReloadingTlsContext.java98
-rw-r--r--security-utils/src/main/java/com/yahoo/security/tls/TlsContext.java7
3 files changed, 147 insertions, 49 deletions
diff --git a/security-utils/src/main/java/com/yahoo/security/tls/DefaultTlsContext.java b/security-utils/src/main/java/com/yahoo/security/tls/DefaultTlsContext.java
index 473e50bc128..c9c326df9ed 100644
--- a/security-utils/src/main/java/com/yahoo/security/tls/DefaultTlsContext.java
+++ b/security-utils/src/main/java/com/yahoo/security/tls/DefaultTlsContext.java
@@ -7,7 +7,7 @@ import com.yahoo.security.tls.policy.AuthorizedPeers;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
-import java.nio.file.Path;
+import javax.net.ssl.SSLParameters;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Arrays;
@@ -38,7 +38,8 @@ public class DefaultTlsContext implements TlsContext {
private static final Logger log = Logger.getLogger(DefaultTlsContext.class.getName());
private final SSLContext sslContext;
- private final List<String> acceptedCiphers;
+ private final String[] validCiphers;
+ private final String[] validProtocols;
public DefaultTlsContext(List<X509Certificate> certificates,
PrivateKey privateKey,
@@ -46,50 +47,77 @@ public class DefaultTlsContext implements TlsContext {
AuthorizedPeers authorizedPeers,
AuthorizationMode mode,
List<String> acceptedCiphers) {
- this.sslContext = createSslContext(certificates, privateKey, caCertificates, authorizedPeers, mode);
- this.acceptedCiphers = acceptedCiphers;
+ this(createSslContext(certificates, privateKey, caCertificates, authorizedPeers, mode),
+ acceptedCiphers);
}
- public DefaultTlsContext(Path tlsOptionsConfigFile, AuthorizationMode mode) {
- TransportSecurityOptions options = TransportSecurityOptions.fromJsonFile(tlsOptionsConfigFile);
- this.sslContext = createSslContext(options, mode);
- this.acceptedCiphers = options.getAcceptedCiphers();
- }
- @Override
- public SSLEngine createSslEngine() {
- SSLEngine sslEngine = sslContext.createSSLEngine();
- restrictSetOfEnabledCiphers(sslEngine, acceptedCiphers);
- restrictTlsProtocols(sslEngine);
- sslEngine.setNeedClientAuth(true);
- return sslEngine;
+ public DefaultTlsContext(SSLContext sslContext, List<String> acceptedCiphers) {
+ this.sslContext = sslContext;
+ this.validCiphers = getAllowedCiphers(sslContext, acceptedCiphers);
+ this.validProtocols = getAllowedProtocols(sslContext);
}
- private static void restrictSetOfEnabledCiphers(SSLEngine sslEngine, List<String> acceptedCiphers) {
- String[] validCipherSuites = Arrays.stream(sslEngine.getSupportedCipherSuites())
+
+ private static String[] getAllowedCiphers(SSLContext sslContext, List<String> acceptedCiphers) {
+ String[] supportedCipherSuites = sslContext.getSupportedSSLParameters().getCipherSuites();
+ String[] validCipherSuites = Arrays.stream(supportedCipherSuites)
.filter(suite -> ALLOWED_CIPHER_SUITES.contains(suite) && (acceptedCiphers.isEmpty() || acceptedCiphers.contains(suite)))
.toArray(String[]::new);
if (validCipherSuites.length == 0) {
throw new IllegalStateException(
String.format("None of the allowed cipher suites are supported " +
"(allowed-cipher-suites=%s, supported-cipher-suites=%s, accepted-cipher-suites=%s)",
- ALLOWED_CIPHER_SUITES, List.of(sslEngine.getSupportedCipherSuites()), acceptedCiphers));
+ ALLOWED_CIPHER_SUITES, List.of(supportedCipherSuites), acceptedCiphers));
}
- log.log(Level.FINE, () -> String.format("Allowed cipher suites that are supported: %s", Arrays.toString(validCipherSuites)));
- sslEngine.setEnabledCipherSuites(validCipherSuites);
+ log.log(Level.FINE, () -> String.format("Allowed cipher suites that are supported: %s", List.of(validCipherSuites)));
+ return validCipherSuites;
}
- private static void restrictTlsProtocols(SSLEngine sslEngine) {
- String[] validProtocols = Arrays.stream(sslEngine.getSupportedProtocols())
+ private static String[] getAllowedProtocols(SSLContext sslContext) {
+ String[] supportedProtocols = sslContext.getSupportedSSLParameters().getProtocols();
+ String[] validProtocols = Arrays.stream(supportedProtocols)
.filter(ALLOWED_PROTOCOLS::contains)
.toArray(String[]::new);
if (validProtocols.length == 0) {
throw new IllegalArgumentException(
String.format("None of the allowed protocols are supported (allowed-protocols=%s, supported-protocols=%s)",
- ALLOWED_PROTOCOLS, Arrays.toString(sslEngine.getSupportedProtocols())));
+ ALLOWED_PROTOCOLS, List.of(supportedProtocols)));
}
- log.log(Level.FINE, () -> String.format("Allowed protocols that are supported: %s", Arrays.toString(validProtocols)));
- sslEngine.setEnabledProtocols(validProtocols);
+ log.log(Level.FINE, () -> String.format("Allowed protocols that are supported: %s", List.of(validProtocols)));
+ return validProtocols;
+ }
+
+ @Override
+ public SSLContext context() {
+ return sslContext;
+ }
+
+ @Override
+ public SSLParameters parameters() {
+ return createSslParameters();
+ }
+
+ @Override
+ public SSLEngine createSslEngine() {
+ SSLEngine sslEngine = sslContext.createSSLEngine();
+ sslEngine.setSSLParameters(createSslParameters());
+ return sslEngine;
+ }
+
+ @Override
+ public SSLEngine createSslEngine(String peerHost, int peerPort) {
+ SSLEngine sslEngine = sslContext.createSSLEngine(peerHost, peerPort);
+ sslEngine.setSSLParameters(createSslParameters());
+ return sslEngine;
+ }
+
+ private SSLParameters createSslParameters() {
+ SSLParameters newParameters = sslContext.getDefaultSSLParameters();
+ newParameters.setCipherSuites(validCiphers);
+ newParameters.setProtocols(validProtocols);
+ newParameters.setNeedClientAuth(true);
+ return newParameters;
}
private static SSLContext createSslContext(List<X509Certificate> certificates,
@@ -110,16 +138,5 @@ public class DefaultTlsContext implements TlsContext {
return builder.build();
}
- private static SSLContext createSslContext(TransportSecurityOptions options, AuthorizationMode mode) {
- SslContextBuilder builder = new SslContextBuilder();
- options.getCertificatesFile()
- .ifPresent(certificates -> builder.withKeyStore(options.getPrivateKeyFile().get(), certificates));
- options.getCaCertificatesFile().ifPresent(builder::withTrustStore);
- if (mode != AuthorizationMode.DISABLE) {
- options.getAuthorizedPeers().ifPresent(
- authorizedPeers -> builder.withTrustManagerFactory(new PeerAuthorizerTrustManagersFactory(authorizedPeers, mode)));
- }
- return builder.build();
- }
}
diff --git a/security-utils/src/main/java/com/yahoo/security/tls/ReloadingTlsContext.java b/security-utils/src/main/java/com/yahoo/security/tls/ReloadingTlsContext.java
index 5add13e067d..ed5d893f6dc 100644
--- a/security-utils/src/main/java/com/yahoo/security/tls/ReloadingTlsContext.java
+++ b/security-utils/src/main/java/com/yahoo/security/tls/ReloadingTlsContext.java
@@ -1,13 +1,28 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.security.tls;
+import com.yahoo.security.KeyStoreBuilder;
+import com.yahoo.security.KeyStoreType;
+import com.yahoo.security.KeyUtils;
+import com.yahoo.security.SslContextBuilder;
+import com.yahoo.security.X509CertificateUtils;
+import com.yahoo.security.tls.authz.PeerAuthorizerTrustManager;
+
+import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.X509ExtendedTrustManager;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.Files;
import java.nio.file.Path;
+import java.security.KeyStore;
+import java.security.cert.X509Certificate;
import java.time.Duration;
+import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -23,8 +38,9 @@ public class ReloadingTlsContext implements TlsContext {
private static final Logger log = Logger.getLogger(ReloadingTlsContext.class.getName());
private final Path tlsOptionsConfigFile;
- private final AuthorizationMode mode;
- private final AtomicReference<TlsContext> currentTlsContext;
+ private final TlsContext tlsContext;
+ private final MutableX509TrustManager trustManager = new MutableX509TrustManager();
+ private final MutableX509KeyManager keyManager = new MutableX509KeyManager();
private final ScheduledExecutorService scheduler =
Executors.newSingleThreadScheduledExecutor(runnable -> {
Thread thread = new Thread(runnable, "tls-context-reloader");
@@ -34,19 +50,77 @@ public class ReloadingTlsContext implements TlsContext {
public ReloadingTlsContext(Path tlsOptionsConfigFile, AuthorizationMode mode) {
this.tlsOptionsConfigFile = tlsOptionsConfigFile;
- this.mode = mode;
- this.currentTlsContext = new AtomicReference<>(new DefaultTlsContext(tlsOptionsConfigFile, mode));
- this.scheduler.scheduleAtFixedRate(new SslContextReloader(),
+ TransportSecurityOptions options = TransportSecurityOptions.fromJsonFile(tlsOptionsConfigFile);
+ reloadCryptoMaterial(options, trustManager, keyManager);
+ this.tlsContext = createDefaultTlsContext(options, mode, trustManager, keyManager);
+ this.scheduler.scheduleAtFixedRate(new CryptoMaterialReloader(),
UPDATE_PERIOD.getSeconds()/*initial delay*/,
UPDATE_PERIOD.getSeconds(),
TimeUnit.SECONDS);
}
- @Override
- public SSLEngine createSslEngine() {
- return currentTlsContext.get().createSslEngine();
+ private static void reloadCryptoMaterial(TransportSecurityOptions options,
+ MutableX509TrustManager trustManager,
+ MutableX509KeyManager keyManager) {
+ if (options.getCaCertificatesFile().isPresent()) {
+ trustManager.updateTruststore(loadTruststore(options.getCaCertificatesFile().get()));
+ } else {
+ trustManager.useDefaultTruststore();
+ }
+
+ if (options.getPrivateKeyFile().isPresent() && options.getCertificatesFile().isPresent()) {
+ keyManager.updateKeystore(loadKeystore(options.getPrivateKeyFile().get(), options.getCertificatesFile().get()), new char[0]);
+ } else {
+ keyManager.useDefaultKeystore();
+ }
}
+ private static KeyStore loadTruststore(Path caCertificateFile) {
+ try {
+ List<X509Certificate> caCertificates = X509CertificateUtils.certificateListFromPem(Files.readString(caCertificateFile));
+ KeyStoreBuilder trustStoreBuilder = KeyStoreBuilder.withType(KeyStoreType.PKCS12);
+ for (int i = 0; i < caCertificates.size(); i++) {
+ trustStoreBuilder.withCertificateEntry("cert-" + i, caCertificates.get(i));
+ }
+ return trustStoreBuilder.build();
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ private static KeyStore loadKeystore(Path privateKeyFile, Path certificatesFile) {
+ try {
+ return KeyStoreBuilder.withType(KeyStoreType.PKCS12)
+ .withKeyEntry(
+ "default",
+ KeyUtils.fromPemEncodedPrivateKey(Files.readString(privateKeyFile)),
+ X509CertificateUtils.certificateListFromPem(Files.readString(certificatesFile)))
+ .build();
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ private static DefaultTlsContext createDefaultTlsContext(TransportSecurityOptions options,
+ AuthorizationMode mode,
+ MutableX509TrustManager mutableTrustManager,
+ MutableX509KeyManager mutableKeyManager) {
+ SSLContext sslContext = new SslContextBuilder()
+ .withKeyManagerFactory((ignoredKeystore, ignoredPassword) -> mutableKeyManager)
+ .withTrustManagerFactory(
+ ignoredTruststore -> options.getAuthorizedPeers()
+ .map(authorizedPeers -> (X509ExtendedTrustManager) new PeerAuthorizerTrustManager(authorizedPeers, mode, mutableTrustManager))
+ .orElse(mutableTrustManager))
+ .build();
+ return new DefaultTlsContext(sslContext, options.getAcceptedCiphers());
+ }
+
+ // Wrapped methods from TlsContext
+ @Override public SSLContext context() { return tlsContext.context(); }
+ @Override public SSLParameters parameters() { return tlsContext.parameters(); }
+ @Override public SSLEngine createSslEngine() { return tlsContext.createSslEngine(); }
+ @Override public SSLEngine createSslEngine(String peerHost, int peerPort) { return tlsContext.createSslEngine(peerHost, peerPort); }
+
@Override
public void close() {
try {
@@ -57,13 +131,13 @@ public class ReloadingTlsContext implements TlsContext {
}
}
- private class SslContextReloader implements Runnable {
+ private class CryptoMaterialReloader implements Runnable {
@Override
public void run() {
try {
- currentTlsContext.set(new DefaultTlsContext(tlsOptionsConfigFile, mode));
+ reloadCryptoMaterial(TransportSecurityOptions.fromJsonFile(tlsOptionsConfigFile), trustManager, keyManager);
} catch (Throwable t) {
- log.log(Level.SEVERE, String.format("Failed to load SSLContext (path='%s'): %s", tlsOptionsConfigFile, t.getMessage()), t);
+ log.log(Level.SEVERE, String.format("Failed reload crypto material (path='%s'): %s", tlsOptionsConfigFile, t.getMessage()), t);
}
}
}
diff --git a/security-utils/src/main/java/com/yahoo/security/tls/TlsContext.java b/security-utils/src/main/java/com/yahoo/security/tls/TlsContext.java
index 58687a0ba8f..b315dd00b31 100644
--- a/security-utils/src/main/java/com/yahoo/security/tls/TlsContext.java
+++ b/security-utils/src/main/java/com/yahoo/security/tls/TlsContext.java
@@ -3,6 +3,7 @@ package com.yahoo.security.tls;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLParameters;
/**
* A simplified version of {@link SSLContext} modelled as an interface.
@@ -11,8 +12,14 @@ import javax.net.ssl.SSLEngine;
*/
public interface TlsContext extends AutoCloseable {
+ SSLContext context();
+
+ SSLParameters parameters();
+
SSLEngine createSslEngine();
+ SSLEngine createSslEngine(String peerHost, int peerPort);
+
@Override default void close() {}
}