From 5139ac3d016332ccedb9b7a9fa0a3892ae77338d Mon Sep 17 00:00:00 2001 From: bpaunescu Date: Thu, 20 Sep 2018 16:22:27 +0100 Subject: [PATCH] CORDA-2001: added SNI header to TLS connections based on hashed CordaX500 (#3968) * CORDA-2001: added SNI header to TLS connections based on hashed CordaX500 * CORDA-2001: added newline * CORDA-2001: truncate hashed x500 to pass IDN validations * CORDA-2001: convert hostname to lower case * CORDA-2001: to lower after truncating, use of better suited method * CORDA-2001: to lower after truncating, use of better suited method * CORDA-2001: correctly set ssl parameters in the engine --- .../internal/protonwrapper/netty/AMQPClient.kt | 2 +- .../internal/protonwrapper/netty/SSLHelper.kt | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPClient.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPClient.kt index 3be66c2824..a9f16b8e77 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPClient.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPClient.kt @@ -123,7 +123,7 @@ class AMQPClient(val targets: List, override fun initChannel(ch: SocketChannel) { val pipeline = ch.pipeline() val target = parent.currentTarget - val handler = createClientSslHelper(target, keyManagerFactory, trustManagerFactory) + val handler = createClientSslHelper(target, parent.allowedRemoteLegalNames, keyManagerFactory, trustManagerFactory) pipeline.addLast("sslHandler", handler) if (conf.trace) pipeline.addLast("logger", LoggingHandler(LogLevel.INFO)) pipeline.addLast(AMQPChannelHandler(false, diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/SSLHelper.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/SSLHelper.kt index c54eed8ef5..9850e6a8ce 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/SSLHelper.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/SSLHelper.kt @@ -1,7 +1,9 @@ package net.corda.nodeapi.internal.protonwrapper.netty import io.netty.handler.ssl.SslHandler +import net.corda.core.crypto.SecureHash import net.corda.core.crypto.newSecureRandom +import net.corda.core.identity.CordaX500Name import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.contextLogger import net.corda.core.utilities.toHex @@ -16,6 +18,8 @@ import java.security.cert.* import java.util.* import javax.net.ssl.* +private const val HOSTNAME_FORMAT = "%s.corda.net" + internal class LoggingTrustManagerWrapper(val wrapped: X509ExtendedTrustManager) : X509ExtendedTrustManager() { companion object { val log = contextLogger() @@ -102,6 +106,7 @@ internal class LoggingTrustManagerWrapper(val wrapped: X509ExtendedTrustManager) } internal fun createClientSslHelper(target: NetworkHostAndPort, + expectedRemoteLegalNames: Set, keyManagerFactory: KeyManagerFactory, trustManagerFactory: TrustManagerFactory): SslHandler { val sslContext = SSLContext.getInstance("TLS") @@ -113,6 +118,11 @@ internal fun createClientSslHelper(target: NetworkHostAndPort, sslEngine.enabledProtocols = ArtemisTcpTransport.TLS_VERSIONS.toTypedArray() sslEngine.enabledCipherSuites = ArtemisTcpTransport.CIPHER_SUITES.toTypedArray() sslEngine.enableSessionCreation = true + if (expectedRemoteLegalNames.size == 1) { + val sslParameters = sslEngine.sslParameters + sslParameters.serverNames = listOf(SNIHostName(x500toHostName(expectedRemoteLegalNames.single()))) + sslEngine.sslParameters = sslParameters + } return SslHandler(sslEngine) } @@ -152,3 +162,10 @@ internal fun initialiseTrustStoreAndEnableCrlChecking(trustStore: CertificateSto fun KeyManagerFactory.init(keyStore: CertificateStore) = init(keyStore.value.internal, keyStore.password.toCharArray()) fun TrustManagerFactory.init(trustStore: CertificateStore) = init(trustStore.value.internal) + +internal fun x500toHostName(x500Name: CordaX500Name): String { + val secureHash = SecureHash.sha256(x500Name.toString()) + // RFC 1035 specifies a limit 255 bytes for hostnames with each label being 63 bytes or less. Due to this, the string + // representation of the SHA256 hash is truncated to 32 characters. + return String.format(HOSTNAME_FORMAT, secureHash.toString().substring(0..32).toLowerCase()) +}