diff --git a/bridge/src/main/kotlin/net/corda/bridge/services/api/BridgeConfiguration.kt b/bridge/src/main/kotlin/net/corda/bridge/services/api/BridgeConfiguration.kt index efdf08dbe3..306ffa2ec4 100644 --- a/bridge/src/main/kotlin/net/corda/bridge/services/api/BridgeConfiguration.kt +++ b/bridge/src/main/kotlin/net/corda/bridge/services/api/BridgeConfiguration.kt @@ -38,6 +38,14 @@ enum class BridgeMode { FloatOuter } +interface BridgeSSLConfiguration : SSLConfiguration { + override val keyStorePassword: String + override val trustStorePassword: String + override val sslKeystore: Path + override val trustStoreFile: Path +} + + /** * Details of the local Artemis broker. * Required in SenderReceiver and FloatInner modes. @@ -45,7 +53,7 @@ enum class BridgeMode { interface BridgeOutboundConfiguration { val artemisBrokerAddress: NetworkHostAndPort // Allows override of [KeyStore] details for the artemis connection, otherwise the general top level details are used. - val customSSLConfiguration: SSLConfiguration? + val customSSLConfiguration: BridgeSSLConfiguration? // Allows use of a SOCKS 4/5 proxy val socksProxyConfig: SocksProxyConfig? } @@ -58,7 +66,7 @@ interface BridgeOutboundConfiguration { interface BridgeInboundConfiguration { val listeningAddress: NetworkHostAndPort // Allows override of [KeyStore] details for the AMQP listener port, otherwise the general top level details are used. - val customSSLConfiguration: SSLConfiguration? + val customSSLConfiguration: BridgeSSLConfiguration? } /** @@ -70,9 +78,9 @@ interface FloatInnerConfiguration { val expectedCertificateSubject: CordaX500Name // Allows override of [KeyStore] details for the control port, otherwise the general top level details are used. // Used for connection to Float in DMZ - val customSSLConfiguration: SSLConfiguration? + val customSSLConfiguration: BridgeSSLConfiguration? // The SSL keystores to provision into the Float in DMZ - val customFloatOuterSSLConfiguration: SSLConfiguration? + val customFloatOuterSSLConfiguration: BridgeSSLConfiguration? } /** @@ -83,7 +91,7 @@ interface FloatOuterConfiguration { val floatAddress: NetworkHostAndPort val expectedCertificateSubject: CordaX500Name // Allows override of [KeyStore] details for the control port, otherwise the general top level details are used. - val customSSLConfiguration: SSLConfiguration? + val customSSLConfiguration: BridgeSSLConfiguration? } interface BridgeConfiguration : NodeSSLConfiguration { diff --git a/bridge/src/main/kotlin/net/corda/bridge/services/artemis/BridgeArtemisConnectionServiceImpl.kt b/bridge/src/main/kotlin/net/corda/bridge/services/artemis/BridgeArtemisConnectionServiceImpl.kt index 139595f5e0..a33bb67ee8 100644 --- a/bridge/src/main/kotlin/net/corda/bridge/services/artemis/BridgeArtemisConnectionServiceImpl.kt +++ b/bridge/src/main/kotlin/net/corda/bridge/services/artemis/BridgeArtemisConnectionServiceImpl.kt @@ -10,10 +10,8 @@ package net.corda.bridge.services.artemis -import net.corda.bridge.services.api.BridgeArtemisConnectionService -import net.corda.bridge.services.api.BridgeAuditService -import net.corda.bridge.services.api.BridgeConfiguration -import net.corda.bridge.services.api.ServiceStateSupport +import net.corda.bridge.services.api.* +import net.corda.bridge.services.config.BridgeSSLConfigurationImpl import net.corda.bridge.services.util.ServiceStateCombiner import net.corda.bridge.services.util.ServiceStateHelper import net.corda.core.internal.ThreadBox @@ -23,7 +21,6 @@ import net.corda.nodeapi.ArtemisTcpTransport import net.corda.nodeapi.ConnectionDirection import net.corda.nodeapi.internal.ArtemisMessagingClient import net.corda.nodeapi.internal.ArtemisMessagingComponent -import net.corda.nodeapi.internal.config.SSLConfiguration import org.apache.activemq.artemis.api.core.client.ActiveMQClient import org.apache.activemq.artemis.api.core.client.FailoverEventType import org.apache.activemq.artemis.api.core.client.ServerLocator @@ -46,13 +43,13 @@ class BridgeArtemisConnectionServiceImpl(val conf: BridgeConfiguration, } private val state = ThreadBox(InnerState()) - private val sslConfiguration: SSLConfiguration + private val sslConfiguration: BridgeSSLConfiguration private val statusFollower: ServiceStateCombiner private var statusSubscriber: Subscription? = null init { statusFollower = ServiceStateCombiner(listOf(auditService)) - sslConfiguration = conf.outboundConfig?.customSSLConfiguration ?: conf + sslConfiguration = conf.outboundConfig?.customSSLConfiguration ?: BridgeSSLConfigurationImpl(conf) } override fun start() { diff --git a/bridge/src/main/kotlin/net/corda/bridge/services/config/BridgeConfigurationImpl.kt b/bridge/src/main/kotlin/net/corda/bridge/services/config/BridgeConfigurationImpl.kt index d6d9c5f16c..5ccacf678e 100644 --- a/bridge/src/main/kotlin/net/corda/bridge/services/config/BridgeConfigurationImpl.kt +++ b/bridge/src/main/kotlin/net/corda/bridge/services/config/BridgeConfigurationImpl.kt @@ -13,38 +13,47 @@ package net.corda.bridge.services.config import com.typesafe.config.Config import net.corda.bridge.services.api.* import net.corda.core.identity.CordaX500Name +import net.corda.core.internal.div import net.corda.core.utilities.NetworkHostAndPort import net.corda.nodeapi.internal.ArtemisMessagingComponent -import net.corda.nodeapi.internal.config.SSLConfiguration +import net.corda.nodeapi.internal.config.NodeSSLConfiguration import net.corda.nodeapi.internal.config.parseAs import net.corda.nodeapi.internal.protonwrapper.netty.SocksProxyConfig import java.nio.file.Path +import java.nio.file.Paths fun Config.parseAsBridgeConfiguration(): BridgeConfiguration = parseAs() -data class CustomSSLConfiguration(override val keyStorePassword: String, - override val trustStorePassword: String, - override val certificatesDirectory: Path) : SSLConfiguration +data class BridgeSSLConfigurationImpl(override val keyStorePassword: String, + override val trustStorePassword: String, + override val certificatesDirectory: Path = Paths.get("certificates"), + override val sslKeystore: Path = certificatesDirectory / "sslkeystore.jks", + override val trustStoreFile: Path = certificatesDirectory / "truststore.jks") : BridgeSSLConfiguration { + constructor(config: NodeSSLConfiguration) : this(config.keyStorePassword, config.trustStorePassword, config.certificatesDirectory, config.sslKeystore, config.trustStoreFile) +} data class BridgeOutboundConfigurationImpl(override val artemisBrokerAddress: NetworkHostAndPort, - override val customSSLConfiguration: CustomSSLConfiguration?, + override val customSSLConfiguration: BridgeSSLConfigurationImpl?, override val socksProxyConfig: SocksProxyConfig? = null) : BridgeOutboundConfiguration data class BridgeInboundConfigurationImpl(override val listeningAddress: NetworkHostAndPort, - override val customSSLConfiguration: CustomSSLConfiguration?) : BridgeInboundConfiguration + override val customSSLConfiguration: BridgeSSLConfigurationImpl?) : BridgeInboundConfiguration data class FloatInnerConfigurationImpl(override val floatAddresses: List, override val expectedCertificateSubject: CordaX500Name, - override val customSSLConfiguration: CustomSSLConfiguration?, - override val customFloatOuterSSLConfiguration: CustomSSLConfiguration?) : FloatInnerConfiguration + override val customSSLConfiguration: BridgeSSLConfigurationImpl?, + override val customFloatOuterSSLConfiguration: BridgeSSLConfigurationImpl?) : FloatInnerConfiguration data class FloatOuterConfigurationImpl(override val floatAddress: NetworkHostAndPort, override val expectedCertificateSubject: CordaX500Name, - override val customSSLConfiguration: CustomSSLConfiguration?) : FloatOuterConfiguration + override val customSSLConfiguration: BridgeSSLConfigurationImpl?) : FloatOuterConfiguration data class BridgeConfigurationImpl( override val baseDirectory: Path, + override val certificatesDirectory: Path = baseDirectory / "certificates", + override val sslKeystore: Path = certificatesDirectory / "sslkeystore.jks", + override val trustStoreFile: Path = certificatesDirectory / "truststore.jks", override val keyStorePassword: String, override val trustStorePassword: String, override val bridgeMode: BridgeMode, diff --git a/bridge/src/main/kotlin/net/corda/bridge/services/receiver/FloatControlListenerService.kt b/bridge/src/main/kotlin/net/corda/bridge/services/receiver/FloatControlListenerService.kt index ea48f7066d..538c44f37e 100644 --- a/bridge/src/main/kotlin/net/corda/bridge/services/receiver/FloatControlListenerService.kt +++ b/bridge/src/main/kotlin/net/corda/bridge/services/receiver/FloatControlListenerService.kt @@ -11,6 +11,7 @@ package net.corda.bridge.services.receiver import net.corda.bridge.services.api.* +import net.corda.bridge.services.config.BridgeSSLConfigurationImpl import net.corda.bridge.services.receiver.FloatControlTopics.FLOAT_DATA_TOPIC import net.corda.bridge.services.util.ServiceStateCombiner import net.corda.bridge.services.util.ServiceStateHelper @@ -21,7 +22,6 @@ import net.corda.core.serialization.serialize import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.contextLogger import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_PREFIX -import net.corda.nodeapi.internal.config.SSLConfiguration import net.corda.nodeapi.internal.protonwrapper.messages.MessageStatus import net.corda.nodeapi.internal.protonwrapper.messages.ReceivedMessage import net.corda.nodeapi.internal.protonwrapper.netty.AMQPServer @@ -47,7 +47,7 @@ class FloatControlListenerService(val conf: BridgeConfiguration, private var connectSubscriber: Subscription? = null private var receiveSubscriber: Subscription? = null private var amqpControlServer: AMQPServer? = null - private val sslConfiguration: SSLConfiguration + private val sslConfiguration: BridgeSSLConfiguration private val keyStore: KeyStore private val keyStorePrivateKeyPassword: String private val trustStore: KeyStore @@ -59,7 +59,7 @@ class FloatControlListenerService(val conf: BridgeConfiguration, init { statusFollower = ServiceStateCombiner(listOf(auditService, amqpListener)) - sslConfiguration = conf.floatOuterConfig?.customSSLConfiguration ?: conf + sslConfiguration = conf.floatOuterConfig?.customSSLConfiguration ?: BridgeSSLConfigurationImpl(conf) keyStore = sslConfiguration.loadSslKeyStore().internal keyStorePrivateKeyPassword = sslConfiguration.keyStorePassword trustStore = sslConfiguration.loadTrustStore().internal diff --git a/bridge/src/test/kotlin/net/corda/bridge/ConfigTest.kt b/bridge/src/test/kotlin/net/corda/bridge/ConfigTest.kt index 0d6a6e5769..037068326d 100644 --- a/bridge/src/test/kotlin/net/corda/bridge/ConfigTest.kt +++ b/bridge/src/test/kotlin/net/corda/bridge/ConfigTest.kt @@ -19,6 +19,7 @@ import org.junit.Assert.assertNull import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder +import java.nio.file.Paths class ConfigTest { @Rule @@ -63,4 +64,44 @@ class ConfigTest { assertEquals(NetworkHostAndPort("localhost", 12005), config.floatOuterConfig!!.floatAddress) assertEquals(CordaX500Name.parse("O=Bank A, L=London, C=GB"), config.floatOuterConfig!!.expectedCertificateSubject) } + + @Test + fun `Load overridden cert config`() { + val configResource = "/net/corda/bridge/custombasecerts/bridge.conf" + val config = createAndLoadConfigFromResource(tempFolder.root.toPath(), configResource) + assertEquals(Paths.get("customcerts/mysslkeystore.jks"), config.sslKeystore) + assertEquals(Paths.get("customcerts/mytruststore.jks"), config.trustStoreFile) + } + + @Test + fun `Load custom inner certificate config`() { + val configResource = "/net/corda/bridge/separatedwithcustomcerts/bridge/bridge.conf" + val config = createAndLoadConfigFromResource(tempFolder.root.toPath(), configResource) + assertEquals(Paths.get("outboundcerts/outboundkeys.jks"), config.outboundConfig!!.customSSLConfiguration!!.sslKeystore) + assertEquals(Paths.get("outboundcerts/outboundtrust.jks"), config.outboundConfig!!.customSSLConfiguration!!.trustStoreFile) + assertEquals("outboundkeypassword", config.outboundConfig!!.customSSLConfiguration!!.keyStorePassword) + assertEquals("outboundtrustpassword", config.outboundConfig!!.customSSLConfiguration!!.trustStorePassword) + assertNull(config.inboundConfig) + assertEquals(Paths.get("tunnelcerts/tunnelkeys.jks"), config.floatInnerConfig!!.customSSLConfiguration!!.sslKeystore) + assertEquals(Paths.get("tunnelcerts/tunneltrust.jks"), config.floatInnerConfig!!.customSSLConfiguration!!.trustStoreFile) + assertEquals("tunnelkeypassword", config.floatInnerConfig!!.customSSLConfiguration!!.keyStorePassword) + assertEquals("tunneltrustpassword", config.floatInnerConfig!!.customSSLConfiguration!!.trustStorePassword) + assertNull(config.floatOuterConfig) + } + + @Test + fun `Load custom outer certificate config`() { + val configResource = "/net/corda/bridge/separatedwithcustomcerts/float/bridge.conf" + val config = createAndLoadConfigFromResource(tempFolder.root.toPath(), configResource) + assertEquals(Paths.get("inboundcerts/inboundkeys.jks"), config.inboundConfig!!.customSSLConfiguration!!.sslKeystore) + assertEquals(Paths.get("inboundcerts/inboundtrust.jks"), config.inboundConfig!!.customSSLConfiguration!!.trustStoreFile) + assertEquals("inboundkeypassword", config.inboundConfig!!.customSSLConfiguration!!.keyStorePassword) + assertEquals("inboundtrustpassword", config.inboundConfig!!.customSSLConfiguration!!.trustStorePassword) + assertNull(config.outboundConfig) + assertEquals(Paths.get("tunnelcerts/tunnelkeys.jks"), config.floatOuterConfig!!.customSSLConfiguration!!.sslKeystore) + assertEquals(Paths.get("tunnelcerts/tunneltrust.jks"), config.floatOuterConfig!!.customSSLConfiguration!!.trustStoreFile) + assertEquals("tunnelkeypassword", config.floatOuterConfig!!.customSSLConfiguration!!.keyStorePassword) + assertEquals("tunneltrustpassword", config.floatOuterConfig!!.customSSLConfiguration!!.trustStorePassword) + assertNull(config.floatInnerConfig) + } } \ No newline at end of file diff --git a/bridge/src/test/resources/net/corda/bridge/custombasecerts/bridge.conf b/bridge/src/test/resources/net/corda/bridge/custombasecerts/bridge.conf new file mode 100644 index 0000000000..65fb7c136d --- /dev/null +++ b/bridge/src/test/resources/net/corda/bridge/custombasecerts/bridge.conf @@ -0,0 +1,19 @@ +// +// R3 Proprietary and Confidential +// +// Copyright (c) 2018 R3 Limited. All rights reserved. +// +// The intellectual and technical concepts contained herein are proprietary to R3 and its suppliers and are protected by trade secret law. +// +// Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited. + +bridgeMode = SenderReceiver +sslKeystore = "customcerts/mysslkeystore.jks" +trustStoreFile = "customcerts/mytruststore.jks" +outboundConfig : { + artemisBrokerAddress = "localhost:11005" +} +inboundConfig : { + listeningAddress = "0.0.0.0:10005" +} +networkParametersPath = network-parameters diff --git a/bridge/src/test/resources/net/corda/bridge/separatedwithcustomcerts/bridge/bridge.conf b/bridge/src/test/resources/net/corda/bridge/separatedwithcustomcerts/bridge/bridge.conf new file mode 100644 index 0000000000..70a79d00e9 --- /dev/null +++ b/bridge/src/test/resources/net/corda/bridge/separatedwithcustomcerts/bridge/bridge.conf @@ -0,0 +1,30 @@ +// +// R3 Proprietary and Confidential +// +// Copyright (c) 2018 R3 Limited. All rights reserved. +// +// The intellectual and technical concepts contained herein are proprietary to R3 and its suppliers and are protected by trade secret law. +// +// Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited. + +bridgeMode = FloatInner +outboundConfig : { + artemisBrokerAddress = "localhost:11005" + customSSLConfiguration : { + keyStorePassword = "outboundkeypassword" + trustStorePassword = "outboundtrustpassword" + sslKeystore = "outboundcerts/outboundkeys.jks" + trustStoreFile = "outboundcerts/outboundtrust.jks" + } +} +floatInnerConfig : { + floatAddresses = [ "localhost:12005" ] + expectedCertificateSubject = "O=Bank A, L=London, C=GB" + customSSLConfiguration : { + keyStorePassword = "tunnelkeypassword" + trustStorePassword = "tunneltrustpassword" + sslKeystore = "tunnelcerts/tunnelkeys.jks" + trustStoreFile = "tunnelcerts/tunneltrust.jks" + } +} +networkParametersPath = network-parameters diff --git a/bridge/src/test/resources/net/corda/bridge/separatedwithcustomcerts/float/bridge.conf b/bridge/src/test/resources/net/corda/bridge/separatedwithcustomcerts/float/bridge.conf new file mode 100644 index 0000000000..e2819dc280 --- /dev/null +++ b/bridge/src/test/resources/net/corda/bridge/separatedwithcustomcerts/float/bridge.conf @@ -0,0 +1,30 @@ +// +// R3 Proprietary and Confidential +// +// Copyright (c) 2018 R3 Limited. All rights reserved. +// +// The intellectual and technical concepts contained herein are proprietary to R3 and its suppliers and are protected by trade secret law. +// +// Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited. + +bridgeMode = FloatOuter +inboundConfig : { + listeningAddress = "0.0.0.0:10005" + customSSLConfiguration : { + keyStorePassword = "inboundkeypassword" + trustStorePassword = "inboundtrustpassword" + sslKeystore = "inboundcerts/inboundkeys.jks" + trustStoreFile = "inboundcerts/inboundtrust.jks" + } +} +floatOuterConfig : { + floatAddress = "localhost:12005" + expectedCertificateSubject = "O=Bank A, L=London, C=GB" + customSSLConfiguration : { + keyStorePassword = "tunnelkeypassword" + trustStorePassword = "tunneltrustpassword" + sslKeystore = "tunnelcerts/tunnelkeys.jks" + trustStoreFile = "tunnelcerts/tunneltrust.jks" + } +} +networkParametersPath = network-parameters