From b9586501a6b6cdfe465302448018785652c9b966 Mon Sep 17 00:00:00 2001 From: Jon Meredith Date: Thu, 21 Sep 2023 16:07:29 -0600 Subject: [PATCH] Internode legacy SSL storage port certificate is not hot reloaded on update patch by Jon Meredith; reviewed by Dinesh Joshi, Francisco Guerrero for CASSANDRA-18681 --- CHANGES.txt | 1 + .../net/InboundConnectionInitiator.java | 3 +- .../apache/cassandra/net/InboundSockets.java | 12 ++ .../cassandra/net/MessagingService.java | 1 + .../net/MessagingServiceMBeanImpl.java | 10 +- .../net/OutboundConnectionInitiator.java | 4 +- .../apache/cassandra/security/SSLFactory.java | 84 ++++++++------ .../transport/PipelineConfigurator.java | 8 +- .../cassandra/transport/SimpleClient.java | 3 +- .../test/AbstractEncryptionOptionsImpl.java | 2 +- .../DefaultSslContextFactoryTest.java | 2 +- .../PEMBasedSslContextFactoryTest.java | 2 +- .../cassandra/security/SSLFactoryTest.java | 105 ++++++++++++------ 13 files changed, 153 insertions(+), 84 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index adc7860393..d902ca1e1c 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 4.1.4 + * Internode legacy SSL storage port certificate is not hot reloaded on update (CASSANDRA-18681) * Nodetool paxos-only repair is no longer incremental (CASSANDRA-18466) * Waiting indefinitely on ReceivedMessage response in StreamSession#receive() can cause deadlock (CASSANDRA-18733) * Allow empty keystore_password in encryption_options (CASSANDRA-18778) diff --git a/src/java/org/apache/cassandra/net/InboundConnectionInitiator.java b/src/java/org/apache/cassandra/net/InboundConnectionInitiator.java index 807d0262d8..3067b587c9 100644 --- a/src/java/org/apache/cassandra/net/InboundConnectionInitiator.java +++ b/src/java/org/apache/cassandra/net/InboundConnectionInitiator.java @@ -549,7 +549,8 @@ public class InboundConnectionInitiator { final boolean verifyPeerCertificate = true; SslContext sslContext = SSLFactory.getOrCreateSslContext(encryptionOptions, verifyPeerCertificate, - ISslContextFactory.SocketType.SERVER); + ISslContextFactory.SocketType.SERVER, + SSL_FACTORY_CONTEXT_DESCRIPTION); InetSocketAddress peer = encryptionOptions.require_endpoint_verification ? (InetSocketAddress) channel.remoteAddress() : null; SslHandler sslHandler = newSslHandler(channel, sslContext, peer); logger.trace("{} inbound netty SslContext: context={}, engine={}", description, sslContext.getClass().getName(), sslHandler.engine().getClass().getName()); diff --git a/src/java/org/apache/cassandra/net/InboundSockets.java b/src/java/org/apache/cassandra/net/InboundSockets.java index 58cd88e6d9..80309aa443 100644 --- a/src/java/org/apache/cassandra/net/InboundSockets.java +++ b/src/java/org/apache/cassandra/net/InboundSockets.java @@ -24,6 +24,8 @@ import java.util.function.Consumer; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; @@ -43,6 +45,7 @@ import org.apache.cassandra.utils.concurrent.FutureCombiner; class InboundSockets { + private static Logger logger = LoggerFactory.getLogger(InboundSockets.class); /** * A simple struct to wrap up the components needed for each listening socket. */ @@ -219,6 +222,15 @@ class InboundSockets if (settings.encryption.legacy_ssl_storage_port_enabled) { + // Initialize hot reloading here rather than in org.apache.cassandra.security.SSLFactory.initHotReloading + // as the legacySettings.encryption.sslContextFactory is not shared outside the messaging system. + // Any SslContexts created will be checked when checkCertFilesForHotReloading is called if initialization + // is successful. + try { + legacySettings.encryption.sslContextFactoryInstance.initHotReloading(); + } catch (Throwable tr) { + logger.warn("Unable to initialize hot reloading for legacy internode socket - continuing disabled", tr); + } out.add(new InboundSocket(legacySettings)); /* diff --git a/src/java/org/apache/cassandra/net/MessagingService.java b/src/java/org/apache/cassandra/net/MessagingService.java index f14835661e..910623aae6 100644 --- a/src/java/org/apache/cassandra/net/MessagingService.java +++ b/src/java/org/apache/cassandra/net/MessagingService.java @@ -213,6 +213,7 @@ public class MessagingService extends MessagingServiceMBeanImpl public static final int current_version = VERSION_40; static AcceptVersions accept_messaging = new AcceptVersions(minimum_version, current_version); static AcceptVersions accept_streaming = new AcceptVersions(current_version, current_version); + public static String SSL_FACTORY_CONTEXT_DESCRIPTION = "server_encryption_options"; public enum Version { diff --git a/src/java/org/apache/cassandra/net/MessagingServiceMBeanImpl.java b/src/java/org/apache/cassandra/net/MessagingServiceMBeanImpl.java index b77fa8396e..3b89834a31 100644 --- a/src/java/org/apache/cassandra/net/MessagingServiceMBeanImpl.java +++ b/src/java/org/apache/cassandra/net/MessagingServiceMBeanImpl.java @@ -17,15 +17,12 @@ */ package org.apache.cassandra.net; -import java.io.IOException; import java.net.UnknownHostException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import org.apache.cassandra.config.DatabaseDescriptor; -import org.apache.cassandra.config.EncryptionOptions; import org.apache.cassandra.locator.InetAddressAndPort; import org.apache.cassandra.metrics.InternodeOutboundMetrics; import org.apache.cassandra.metrics.MessagingMetrics; @@ -276,12 +273,9 @@ public class MessagingServiceMBeanImpl implements MessagingServiceMBean } @Override - public void reloadSslCertificates() throws IOException + public void reloadSslCertificates() { - final EncryptionOptions.ServerEncryptionOptions serverOpts = DatabaseDescriptor.getInternodeMessagingEncyptionOptions(); - final EncryptionOptions clientOpts = DatabaseDescriptor.getNativeProtocolEncryptionOptions(); - SSLFactory.validateSslCerts(serverOpts, clientOpts); - SSLFactory.checkCertFilesForHotReloading(serverOpts, clientOpts); + SSLFactory.forceCheckCertFiles(); } @Override diff --git a/src/java/org/apache/cassandra/net/OutboundConnectionInitiator.java b/src/java/org/apache/cassandra/net/OutboundConnectionInitiator.java index a187068ce7..5de2a080d9 100644 --- a/src/java/org/apache/cassandra/net/OutboundConnectionInitiator.java +++ b/src/java/org/apache/cassandra/net/OutboundConnectionInitiator.java @@ -59,6 +59,7 @@ import org.apache.cassandra.utils.JVMStabilityInspector; import org.apache.cassandra.utils.memory.BufferPools; import static java.util.concurrent.TimeUnit.*; +import static org.apache.cassandra.net.MessagingService.SSL_FACTORY_CONTEXT_DESCRIPTION; import static org.apache.cassandra.net.MessagingService.VERSION_40; import static org.apache.cassandra.net.HandshakeProtocol.*; import static org.apache.cassandra.net.ConnectionType.STREAMING; @@ -203,7 +204,8 @@ public class OutboundConnectionInitiator keysToCheck = new ArrayList<>(Collections.list(cachedSslContexts.keys())); + while (!keysToCheck.isEmpty()) { - if (options.sslContextFactoryInstance.shouldReload()) + CacheKey key = keysToCheck.remove(keysToCheck.size()-1); + final EncryptionOptions opts = key.encryptionOptions; + + logger.debug("Checking whether certificates have been updated for {}", key.contextDescription); + if (forceReload || opts.sslContextFactoryInstance.shouldReload()) { - logger.info("SSL certificates have been updated for {}. Resetting the ssl contexts for new " + - "connections.", options.getClass().getName()); - validateSslContext(contextDescription, options, verifyPeerCertificate, false); - clearSslContextCache(options); + try + { + validateSslContext(key.contextDescription, opts, + opts instanceof EncryptionOptions.ServerEncryptionOptions || opts.require_client_auth, false); + logger.info("SSL certificates have been updated for {}. Resetting the ssl contexts for new " + + "connections.", key.contextDescription); + clearSslContextCache(key.encryptionOptions, keysToCheck); + } + catch (Throwable tr) + { + logger.error("Failed to hot reload the SSL Certificates! Please check the certificate files for {}.", + key.contextDescription, tr); + } } } - catch(Exception e) - { - logger.error("Failed to hot reload the SSL Certificates! Please check the certificate files.", e); - } } /** @@ -225,12 +231,16 @@ public final class SSLFactory cachedSslContexts.clear(); } - private static void clearSslContextCache(EncryptionOptions options) + /** + * Clear all cachedSslContexts with this encryption option and remove them from future keys to check + */ + private static void clearSslContextCache(EncryptionOptions options, List keysToCheck) { cachedSslContexts.forEachKey(1, cacheKey -> { - if (cacheKey.encryptionOptions.equals(options)) + if (Objects.equals(options, cacheKey.encryptionOptions)) { cachedSslContexts.remove(cacheKey); + keysToCheck.remove(cacheKey); } }); } @@ -261,9 +271,7 @@ public final class SSLFactory if (!isHotReloadingInitialized) { ScheduledExecutors.scheduledTasks - .scheduleWithFixedDelay(() -> checkCertFilesForHotReloading( - DatabaseDescriptor.getInternodeMessagingEncyptionOptions(), - DatabaseDescriptor.getNativeProtocolEncryptionOptions()), + .scheduleWithFixedDelay(SSLFactory::checkCertFilesForHotReloading, DEFAULT_HOT_RELOAD_INITIAL_DELAY_SEC, DEFAULT_HOT_RELOAD_PERIOD_SEC, TimeUnit.SECONDS); } @@ -420,11 +428,13 @@ public final class SSLFactory { private final EncryptionOptions encryptionOptions; private final SocketType socketType; + private final String contextDescription; - public CacheKey(EncryptionOptions encryptionOptions, SocketType socketType) + public CacheKey(EncryptionOptions encryptionOptions, SocketType socketType, String contextDescription) { this.encryptionOptions = encryptionOptions; this.socketType = socketType; + this.contextDescription = contextDescription; } public boolean equals(Object o) @@ -433,7 +443,8 @@ public final class SSLFactory if (o == null || getClass() != o.getClass()) return false; CacheKey cacheKey = (CacheKey) o; return (socketType == cacheKey.socketType && - Objects.equals(encryptionOptions, cacheKey.encryptionOptions)); + Objects.equals(encryptionOptions, cacheKey.encryptionOptions) && + Objects.equals(contextDescription, cacheKey.contextDescription)); } public int hashCode() @@ -441,6 +452,7 @@ public final class SSLFactory int result = 0; result += 31 * socketType.hashCode(); result += 31 * encryptionOptions.hashCode(); + result += 31 * contextDescription.hashCode(); return result; } } diff --git a/src/java/org/apache/cassandra/transport/PipelineConfigurator.java b/src/java/org/apache/cassandra/transport/PipelineConfigurator.java index 81ff13605e..cefba02c59 100644 --- a/src/java/org/apache/cassandra/transport/PipelineConfigurator.java +++ b/src/java/org/apache/cassandra/transport/PipelineConfigurator.java @@ -63,6 +63,8 @@ public class PipelineConfigurator // which will throttle a system under any normal load. private static final boolean DEBUG = Boolean.getBoolean("cassandra.unsafe_verbose_debug_client_protocol"); + public static final String SSL_FACTORY_CONTEXT_DESCRIPTION = "client_encryption_options"; + // Stateless handlers private static final ConnectionLimitHandler connectionLimitHandler = new ConnectionLimitHandler(); @@ -164,7 +166,8 @@ public class PipelineConfigurator return channel -> { SslContext sslContext = SSLFactory.getOrCreateSslContext(encryptionOptions, encryptionOptions.require_client_auth, - ISslContextFactory.SocketType.SERVER); + ISslContextFactory.SocketType.SERVER, + SSL_FACTORY_CONTEXT_DESCRIPTION); channel.pipeline().addFirst(SSL_HANDLER, new ByteToMessageDecoder() { @@ -198,7 +201,8 @@ public class PipelineConfigurator return channel -> { SslContext sslContext = SSLFactory.getOrCreateSslContext(encryptionOptions, encryptionOptions.require_client_auth, - ISslContextFactory.SocketType.SERVER); + ISslContextFactory.SocketType.SERVER, + SSL_FACTORY_CONTEXT_DESCRIPTION); channel.pipeline().addFirst(SSL_HANDLER, sslContext.newHandler(channel.alloc())); }; default: diff --git a/src/java/org/apache/cassandra/transport/SimpleClient.java b/src/java/org/apache/cassandra/transport/SimpleClient.java index 43bb8addee..5ea0190aea 100644 --- a/src/java/org/apache/cassandra/transport/SimpleClient.java +++ b/src/java/org/apache/cassandra/transport/SimpleClient.java @@ -54,6 +54,7 @@ import org.apache.cassandra.utils.concurrent.UncheckedInterruptedException; import static org.apache.cassandra.transport.CQLMessageHandler.envelopeSize; import static org.apache.cassandra.transport.Flusher.MAX_FRAMED_PAYLOAD_SIZE; +import static org.apache.cassandra.transport.PipelineConfigurator.SSL_FACTORY_CONTEXT_DESCRIPTION; import static org.apache.cassandra.utils.concurrent.NonBlockingRateLimiter.NO_OP_LIMITER; import static org.apache.cassandra.utils.Clock.Global.currentTimeMillis; @@ -623,7 +624,7 @@ public class SimpleClient implements Closeable { super.initChannel(channel); SslContext sslContext = SSLFactory.getOrCreateSslContext(encryptionOptions, encryptionOptions.require_client_auth, - ISslContextFactory.SocketType.CLIENT); + ISslContextFactory.SocketType.CLIENT, SSL_FACTORY_CONTEXT_DESCRIPTION); channel.pipeline().addFirst("ssl", sslContext.newHandler(channel.alloc())); } } diff --git a/test/distributed/org/apache/cassandra/distributed/test/AbstractEncryptionOptionsImpl.java b/test/distributed/org/apache/cassandra/distributed/test/AbstractEncryptionOptionsImpl.java index 1c5ddbf6b5..b488867433 100644 --- a/test/distributed/org/apache/cassandra/distributed/test/AbstractEncryptionOptionsImpl.java +++ b/test/distributed/org/apache/cassandra/distributed/test/AbstractEncryptionOptionsImpl.java @@ -200,7 +200,7 @@ public class AbstractEncryptionOptionsImpl extends TestBaseImpl SslContext sslContext = SSLFactory.getOrCreateSslContext( encryptionOptions.withAcceptedProtocols(acceptedProtocols).withCipherSuites(cipherSuites), - true, ISslContextFactory.SocketType.CLIENT); + true, ISslContextFactory.SocketType.CLIENT, "test"); EventLoopGroup workerGroup = new NioEventLoopGroup(); Bootstrap b = new Bootstrap(); diff --git a/test/unit/org/apache/cassandra/security/DefaultSslContextFactoryTest.java b/test/unit/org/apache/cassandra/security/DefaultSslContextFactoryTest.java index 0657eb67a6..bec9d20042 100644 --- a/test/unit/org/apache/cassandra/security/DefaultSslContextFactoryTest.java +++ b/test/unit/org/apache/cassandra/security/DefaultSslContextFactoryTest.java @@ -62,7 +62,7 @@ public class DefaultSslContextFactoryTest .withKeyStorePassword("cassandra") .withRequireClientAuth(false) .withCipherSuites("TLS_RSA_WITH_AES_128_CBC_SHA"); - SslContext sslContext = SSLFactory.getOrCreateSslContext(options, true, ISslContextFactory.SocketType.CLIENT); + SslContext sslContext = SSLFactory.getOrCreateSslContext(options, true, ISslContextFactory.SocketType.CLIENT, "test"); Assert.assertNotNull(sslContext); if (OpenSsl.isAvailable()) Assert.assertTrue(sslContext instanceof OpenSslContext); diff --git a/test/unit/org/apache/cassandra/security/PEMBasedSslContextFactoryTest.java b/test/unit/org/apache/cassandra/security/PEMBasedSslContextFactoryTest.java index 243d300539..f44e34492e 100644 --- a/test/unit/org/apache/cassandra/security/PEMBasedSslContextFactoryTest.java +++ b/test/unit/org/apache/cassandra/security/PEMBasedSslContextFactoryTest.java @@ -216,7 +216,7 @@ public class PEMBasedSslContextFactoryTest .withRequireClientAuth(false) .withCipherSuites("TLS_RSA_WITH_AES_128_CBC_SHA") .withSslContextFactory(sslContextFactory); - SslContext sslContext = SSLFactory.getOrCreateSslContext(options, true, ISslContextFactory.SocketType.CLIENT); + SslContext sslContext = SSLFactory.getOrCreateSslContext(options, true, ISslContextFactory.SocketType.CLIENT, "test"); Assert.assertNotNull(sslContext); if (OpenSsl.isAvailable()) Assert.assertTrue(sslContext instanceof OpenSslContext); diff --git a/test/unit/org/apache/cassandra/security/SSLFactoryTest.java b/test/unit/org/apache/cassandra/security/SSLFactoryTest.java index e5aa4b1057..91e4062be4 100644 --- a/test/unit/org/apache/cassandra/security/SSLFactoryTest.java +++ b/test/unit/org/apache/cassandra/security/SSLFactoryTest.java @@ -19,7 +19,10 @@ package org.apache.cassandra.security; import org.apache.cassandra.io.util.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; +import java.security.KeyStore; import java.security.cert.CertificateException; import java.util.HashMap; import java.util.Map; @@ -61,6 +64,7 @@ public class SSLFactoryTest @Before public void setup() { + SSLFactory.clearSslContextCache(); encryptionOptions = new ServerEncryptionOptions() .withTrustStore("test/conf/cassandra_ssl_test.truststore") .withTrustStorePassword("cassandra") @@ -91,20 +95,24 @@ public class SSLFactoryTest { ServerEncryptionOptions options = addKeystoreOptions(encryptionOptions) .withInternodeEncryption(ServerEncryptionOptions.InternodeEncryption.all); + ServerEncryptionOptions legacyOptions = options.withOptional(false).withInternodeEncryption(ServerEncryptionOptions.InternodeEncryption.all); + options.sslContextFactoryInstance.initHotReloading(); + legacyOptions.sslContextFactoryInstance.initHotReloading(); - SSLFactory.initHotReloading(options, options, true); - - SslContext oldCtx = SSLFactory.getOrCreateSslContext(options, true, ISslContextFactory.SocketType.CLIENT); + SslContext oldCtx = SSLFactory.getOrCreateSslContext(options, true, ISslContextFactory.SocketType.CLIENT, "test"); + SslContext oldLegacyCtx = SSLFactory.getOrCreateSslContext(legacyOptions, true, ISslContextFactory.SocketType.CLIENT, "test legacy"); File keystoreFile = new File(options.keystore); - SSLFactory.checkCertFilesForHotReloading(options, options); + SSLFactory.checkCertFilesForHotReloading(); keystoreFile.trySetLastModified(System.currentTimeMillis() + 15000); - SSLFactory.checkCertFilesForHotReloading(options, options); - SslContext newCtx = SSLFactory.getOrCreateSslContext(options, true, ISslContextFactory.SocketType.CLIENT); + SSLFactory.checkCertFilesForHotReloading(); + SslContext newCtx = SSLFactory.getOrCreateSslContext(options, true, ISslContextFactory.SocketType.CLIENT, "test"); + SslContext newLegacyCtx = SSLFactory.getOrCreateSslContext(legacyOptions, true, ISslContextFactory.SocketType.CLIENT, "test legacy"); Assert.assertNotSame(oldCtx, newCtx); + Assert.assertNotSame(oldLegacyCtx, newLegacyCtx); } catch (Exception e) { @@ -122,21 +130,26 @@ public class SSLFactoryTest try { ServerEncryptionOptions options = addPEMKeystoreOptions(encryptionOptions) - .withInternodeEncryption(ServerEncryptionOptions.InternodeEncryption.all); + .withInternodeEncryption(ServerEncryptionOptions.InternodeEncryption.dc); + // emulate InboundSockets and share the cert but with different options, no extra hot reloading init + ServerEncryptionOptions legacyOptions = options.withOptional(false).withInternodeEncryption(ServerEncryptionOptions.InternodeEncryption.all); + options.sslContextFactoryInstance.initHotReloading(); + legacyOptions.sslContextFactoryInstance.initHotReloading(); - SSLFactory.initHotReloading(options, options, true); - - SslContext oldCtx = SSLFactory.getOrCreateSslContext(options, true, ISslContextFactory.SocketType.CLIENT); + SslContext oldCtx = SSLFactory.getOrCreateSslContext(options, true, ISslContextFactory.SocketType.CLIENT, "test"); + SslContext oldLegacyCtx = SSLFactory.getOrCreateSslContext(legacyOptions, true, ISslContextFactory.SocketType.CLIENT, "test legacy"); File keystoreFile = new File(options.keystore); - SSLFactory.checkCertFilesForHotReloading(options, options); + SSLFactory.checkCertFilesForHotReloading(); keystoreFile.trySetLastModified(System.currentTimeMillis() + 15000); - SSLFactory.checkCertFilesForHotReloading(options, options); - SslContext newCtx = SSLFactory.getOrCreateSslContext(options, true, ISslContextFactory.SocketType.CLIENT); + SSLFactory.checkCertFilesForHotReloading(); + SslContext newCtx = SSLFactory.getOrCreateSslContext(options, true, ISslContextFactory.SocketType.CLIENT, "test"); + SslContext newLegacyCtx = SSLFactory.getOrCreateSslContext(legacyOptions, true, ISslContextFactory.SocketType.CLIENT, "test legacy"); Assert.assertNotSame(oldCtx, newCtx); + Assert.assertNotSame(oldLegacyCtx, newLegacyCtx); } catch (Exception e) { @@ -164,20 +177,26 @@ public class SSLFactoryTest try { ServerEncryptionOptions options = addKeystoreOptions(encryptionOptions); + // emulate InboundSockets and share the cert but with different options, no extra hot reloading init + ServerEncryptionOptions legacyOptions = options.withOptional(false).withInternodeEncryption(ServerEncryptionOptions.InternodeEncryption.all); - SSLFactory.initHotReloading(options, options, true); - SslContext oldCtx = SSLFactory.getOrCreateSslContext(options, true, ISslContextFactory.SocketType.CLIENT); - File keystoreFile = new File(options.keystore); + File testKeystoreFile = new File(options.keystore + ".test"); + FileUtils.copyFile(new File(options.keystore).toJavaIOFile(), testKeystoreFile.toJavaIOFile()); + options = options.withKeyStore(testKeystoreFile.path()); - SSLFactory.checkCertFilesForHotReloading(options, options); - keystoreFile.trySetLastModified(System.currentTimeMillis() + 5000); + SSLFactory.initHotReloading(options, options, true); // deliberately not initializing with legacyOptions to match InboundSockets.addBindings - ServerEncryptionOptions modOptions = new ServerEncryptionOptions(options) - .withKeyStorePassword("bad password"); - SSLFactory.checkCertFilesForHotReloading(modOptions, modOptions); - SslContext newCtx = SSLFactory.getOrCreateSslContext(options, true, ISslContextFactory.SocketType.CLIENT); + SslContext oldCtx = SSLFactory.getOrCreateSslContext(options, true, ISslContextFactory.SocketType.CLIENT, "test"); + SslContext oldLegacyCtx = SSLFactory.getOrCreateSslContext(options, true, ISslContextFactory.SocketType.CLIENT, "test legacy"); + + changeKeystorePassword(options.keystore, options.keystore_password, "bad password"); + + SSLFactory.checkCertFilesForHotReloading(); + SslContext newCtx = SSLFactory.getOrCreateSslContext(options, true, ISslContextFactory.SocketType.CLIENT, "test"); + SslContext newLegacyCtx = SSLFactory.getOrCreateSslContext(options, true, ISslContextFactory.SocketType.CLIENT, "test legacy"); Assert.assertSame(oldCtx, newCtx); + Assert.assertSame(oldLegacyCtx, newLegacyCtx); } finally { @@ -198,14 +217,14 @@ public class SSLFactoryTest SSLFactory.initHotReloading(options, options, true); - SslContext oldCtx = SSLFactory.getOrCreateSslContext(options, true, ISslContextFactory.SocketType.CLIENT); - SSLFactory.checkCertFilesForHotReloading(options, options); + SslContext oldCtx = SSLFactory.getOrCreateSslContext(options, true, ISslContextFactory.SocketType.CLIENT, "test"); + SSLFactory.checkCertFilesForHotReloading(); testKeystoreFile.trySetLastModified(System.currentTimeMillis() + 15000); FileUtils.forceDelete(testKeystoreFile.toJavaIOFile()); - SSLFactory.checkCertFilesForHotReloading(options, options); - SslContext newCtx = SSLFactory.getOrCreateSslContext(options, true, ISslContextFactory.SocketType.CLIENT); + SSLFactory.checkCertFilesForHotReloading(); + SslContext newCtx = SSLFactory.getOrCreateSslContext(options, true, ISslContextFactory.SocketType.CLIENT, "test"); Assert.assertSame(oldCtx, newCtx); } @@ -228,7 +247,7 @@ public class SSLFactoryTest .withCipherSuites("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"); SslContext ctx1 = SSLFactory.getOrCreateSslContext(options, true, - ISslContextFactory.SocketType.SERVER); + ISslContextFactory.SocketType.SERVER, "test"); Assert.assertTrue(ctx1.isServer()); Assert.assertEquals(ctx1.cipherSuites(), options.cipher_suites); @@ -236,7 +255,7 @@ public class SSLFactoryTest options = options.withCipherSuites("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"); SslContext ctx2 = SSLFactory.getOrCreateSslContext(options, true, - ISslContextFactory.SocketType.CLIENT); + ISslContextFactory.SocketType.CLIENT, "test"); Assert.assertTrue(ctx2.isClient()); Assert.assertEquals(ctx2.cipherSuites(), options.cipher_suites); @@ -255,7 +274,7 @@ public class SSLFactoryTest .withRequireClientAuth(true) .withRequireEndpointVerification(false); - SSLFactory.CacheKey cacheKey1 = new SSLFactory.CacheKey(encryptionOptions1, ISslContextFactory.SocketType.SERVER + SSLFactory.CacheKey cacheKey1 = new SSLFactory.CacheKey(encryptionOptions1, ISslContextFactory.SocketType.SERVER, "test" ); Map parameters2 = new HashMap<>(); @@ -268,7 +287,7 @@ public class SSLFactoryTest .withRequireClientAuth(true) .withRequireEndpointVerification(false); - SSLFactory.CacheKey cacheKey2 = new SSLFactory.CacheKey(encryptionOptions2, ISslContextFactory.SocketType.SERVER + SSLFactory.CacheKey cacheKey2 = new SSLFactory.CacheKey(encryptionOptions2, ISslContextFactory.SocketType.SERVER, "test" ); Assert.assertEquals(cacheKey1, cacheKey2); @@ -285,7 +304,7 @@ public class SSLFactoryTest .withSslContextFactory(new ParameterizedClass(DummySslContextFactoryImpl.class.getName(), parameters1)) .withProtocol("TLSv1.1"); - SSLFactory.CacheKey cacheKey1 = new SSLFactory.CacheKey(encryptionOptions1, ISslContextFactory.SocketType.SERVER + SSLFactory.CacheKey cacheKey1 = new SSLFactory.CacheKey(encryptionOptions1, ISslContextFactory.SocketType.SERVER, "test" ); Map parameters2 = new HashMap<>(); @@ -296,9 +315,31 @@ public class SSLFactoryTest .withSslContextFactory(new ParameterizedClass(DummySslContextFactoryImpl.class.getName(), parameters2)) .withProtocol("TLSv1.1"); - SSLFactory.CacheKey cacheKey2 = new SSLFactory.CacheKey(encryptionOptions2, ISslContextFactory.SocketType.SERVER + SSLFactory.CacheKey cacheKey2 = new SSLFactory.CacheKey(encryptionOptions2, ISslContextFactory.SocketType.SERVER, "test" ); Assert.assertNotEquals(cacheKey1, cacheKey2); } + + void changeKeystorePassword(String filename, String currentPassword, String newPassword) + { + try + { + KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); + char[] loadPasswd = currentPassword.toCharArray(); + char[] storePasswd = newPassword.toCharArray(); + try (FileInputStream is = new FileInputStream(filename)) + { + keystore.load(is, loadPasswd); + } + try (FileOutputStream os = new FileOutputStream(filename)) + { + keystore.store(os, storePasswd); + } + } + catch (Throwable tr) + { + throw new RuntimeException(tr); + } + } }