From 71e187dddbdd21d4c824878010cd6e29f6186108 Mon Sep 17 00:00:00 2001 From: Tom Stark <47384103+tomstark99@users.noreply.github.com> Date: Fri, 15 Dec 2023 16:24:11 +0000 Subject: [PATCH] ENT-11118: Netty SSL handshake fix (#5028) * ENT-11118: Added fix for netty SSL handshake fail --- .../messaging/NodeSSLContextFactory.kt | 79 +++++++++++++++++-- 1 file changed, 74 insertions(+), 5 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/NodeSSLContextFactory.kt b/node/src/main/kotlin/net/corda/node/services/messaging/NodeSSLContextFactory.kt index 38f60d7a57..c61e550c56 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/NodeSSLContextFactory.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/NodeSSLContextFactory.kt @@ -4,13 +4,19 @@ import io.netty.handler.ssl.SslContext import io.netty.handler.ssl.SslContextBuilder import io.netty.handler.ssl.SslProvider import net.corda.nodeapi.internal.ArtemisTcpTransport.Companion.TRUST_MANAGER_FACTORY_NAME -import net.corda.nodeapi.internal.config.CertificateStore import net.corda.nodeapi.internal.protonwrapper.netty.createAndInitSslContext -import net.corda.nodeapi.internal.protonwrapper.netty.keyManagerFactory import org.apache.activemq.artemis.core.remoting.impl.ssl.DefaultOpenSSLContextFactory import org.apache.activemq.artemis.core.remoting.impl.ssl.DefaultSSLContextFactory import org.apache.activemq.artemis.spi.core.remoting.ssl.SSLContextConfig -import java.nio.file.Paths +import org.apache.activemq.artemis.utils.ClassloadingUtil +import org.apache.commons.io.IOUtils +import java.io.File +import java.io.InputStream +import java.net.MalformedURLException +import java.net.URL +import java.security.AccessController +import java.security.KeyStore +import java.security.PrivilegedAction import javax.net.ssl.KeyManagerFactory import javax.net.ssl.SSLContext import javax.net.ssl.TrustManagerFactory @@ -54,6 +60,69 @@ class NodeOpenSSLContextFactory : DefaultOpenSSLContextFactory() { private fun loadKeyManagerFactory(config: SSLContextConfig): KeyManagerFactory { - val keyStore = CertificateStore.fromFile(Paths.get(config.keystorePath), config.keystorePassword, config.keystorePassword, false) - return keyManagerFactory(keyStore) + val keyStore = loadKeystore(config.keystoreProvider, config.keystoreType, config.keystorePath, config.keystorePassword) + val keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()) + keyManagerFactory.init(keyStore, config.keystorePassword?.toCharArray()) + return keyManagerFactory } + + +/** + * This is a copy of [SSLSupport.loadKeystore] so that we can use the [NodeSSLContextFactory] and + * maintain keystore loading with the correct [keystoreProvider]. + */ +private fun loadKeystore( + keystoreProvider: String?, + keystoreType: String, + keystorePath: String?, + keystorePassword: String? +) : KeyStore { + val keyStore = keystoreProvider?.let { KeyStore.getInstance(keystoreType, it) } ?: KeyStore.getInstance(keystoreType) + var inputStream : InputStream? = null + try { + if (keystorePath != null && keystorePath.isNotEmpty()) { + val keystoreURL = validateStoreURL(keystorePath) + inputStream = keystoreURL.openStream() + } + keyStore.load(inputStream, keystorePassword?.toCharArray()) + } finally { + inputStream?.closeQuietly() + } + return keyStore +} + + +/** + * This is a copy of [SSLSupport.validateStoreURL] so we can have a full copy of + * [SSLSupport.loadKeystore]. + */ +private fun validateStoreURL(storePath: String): URL { + return try { + URL(storePath) + } catch (e: MalformedURLException) { + val file = File(storePath) + if (file.exists() && file.isFile) { + file.toURI().toURL() + } else { + findResource(storePath) + } + } +} + + +/** + * This is a copy of [SSLSupport.findResource] so we can have a full copy of + * [SSLSupport.validateStoreURL] and. + */ +private fun findResource(resourceName: String): URL { + return AccessController.doPrivileged(PrivilegedAction { + ClassloadingUtil.findResource(resourceName) + }) +} + + +/** + * This is an inline function for [InputStream] so it can be closed and + * ignore an exception. + */ +private fun InputStream?.closeQuietly() = IOUtils.closeQuietly(this) \ No newline at end of file