diff --git a/bridge/bridgecapsule/src/smoke-test/kotlin/net/corda/bridge/smoketest/BridgeSmokeTest.kt b/bridge/bridgecapsule/src/smoke-test/kotlin/net/corda/bridge/smoketest/BridgeSmokeTest.kt index 3648f0cd33..0d7a16b993 100644 --- a/bridge/bridgecapsule/src/smoke-test/kotlin/net/corda/bridge/smoketest/BridgeSmokeTest.kt +++ b/bridge/bridgecapsule/src/smoke-test/kotlin/net/corda/bridge/smoketest/BridgeSmokeTest.kt @@ -17,12 +17,12 @@ import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.messaging.ArtemisMessagingServer import net.corda.nodeapi.internal.* import net.corda.nodeapi.internal.bridging.BridgeControl -import net.corda.nodeapi.internal.config.NodeSSLConfiguration -import net.corda.nodeapi.internal.config.SSLConfiguration +import net.corda.nodeapi.internal.config.MutualSslConfiguration import net.corda.nodeapi.internal.crypto.* import net.corda.nodeapi.internal.network.NetworkParametersCopier import net.corda.testing.core.* import net.corda.testing.internal.rigorousMock +import net.corda.testing.internal.stubs.CertificateStoreStubs import org.apache.activemq.artemis.api.core.RoutingType import org.apache.activemq.artemis.api.core.SimpleString import org.apache.curator.test.TestingServer @@ -67,22 +67,20 @@ class BridgeSmokeTest { @Test fun `Run full features bridge from jar to ensure everything works`() { - val artemisConfig = object : NodeSSLConfiguration { - override val baseDirectory: Path = tempFolder.root.toPath() - override val keyStorePassword: String = "cordacadevpass" - override val trustStorePassword: String = "trustpass" - override val crlCheckSoftFail: Boolean = true - } + + val baseDirectory = tempFolder.root.toPath().createDirectories() + val artemisConfig = CertificateStoreStubs.P2P.withBaseDirectory(baseDirectory) + artemisConfig.createBridgeKeyStores(DUMMY_BANK_A_NAME) copyBridgeResource("corda-firewall.jar") copyBridgeResource("firewall.conf") - createNetworkParams(tempFolder.root.toPath()) + createNetworkParams(baseDirectory) val (artemisServer, artemisClient) = createArtemis() val zkServer = TestingServer(11105, false) try { installBridgeControlResponder(artemisClient) zkServer.start() - val bridge = startBridge(tempFolder.root.toPath()) + val bridge = startBridge(baseDirectory) waitForBridge(bridge) } finally { zkServer.close() @@ -115,18 +113,18 @@ class BridgeSmokeTest { copier.install(baseDirectory) } - private fun SSLConfiguration.createBridgeKeyStores(legalName: CordaX500Name, - rootCert: X509Certificate = DEV_ROOT_CA.certificate, - intermediateCa: CertificateAndKeyPair = DEV_INTERMEDIATE_CA) { + private fun MutualSslConfiguration.createBridgeKeyStores(legalName: CordaX500Name, + rootCert: X509Certificate = DEV_ROOT_CA.certificate, + intermediateCa: CertificateAndKeyPair = DEV_INTERMEDIATE_CA) { - certificatesDirectory.createDirectories() - if (!trustStoreFile.exists()) { - loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/$DEV_CA_TRUST_STORE_FILE"), DEV_CA_TRUST_STORE_PASS).save(trustStoreFile, trustStorePassword) + if (!trustStore.path.exists()) { + val trustStore = trustStore.get(true) + loadDevCaTrustStore().copyTo(trustStore) } val (nodeCaCert, nodeCaKeyPair) = createDevNodeCa(intermediateCa, legalName) - val sslKeyStore = loadSslKeyStore(createNew = true) + val sslKeyStore = keyStore.get(createNew = true) sslKeyStore.update { val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, legalName.x500Principal, tlsKeyPair.public) @@ -203,17 +201,23 @@ class BridgeSmokeTest { } private fun createArtemis(): Pair<ArtemisMessagingServer, ArtemisMessagingClient> { + val baseDirectory = tempFolder.root.toPath() + val certificatesDirectory = baseDirectory / "certificates" + val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory) + val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory) + val artemisConfig = rigorousMock<AbstractNodeConfiguration>().also { - doReturn(tempFolder.root.toPath()).whenever(it).baseDirectory + doReturn(baseDirectory).whenever(it).baseDirectory + doReturn(certificatesDirectory).whenever(it).certificatesDirectory doReturn(ALICE_NAME).whenever(it).myLegalName - doReturn("trustpass").whenever(it).trustStorePassword - doReturn("cordacadevpass").whenever(it).keyStorePassword + doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions + doReturn(signingCertificateStore).whenever(it).signingCertificateStore doReturn(NetworkHostAndPort("localhost", 11005)).whenever(it).p2pAddress doReturn(null).whenever(it).jmxMonitoringHttpPort doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000), externalBridge = true)).whenever(it).enterpriseConfiguration } val artemisServer = ArtemisMessagingServer(artemisConfig, NetworkHostAndPort("0.0.0.0", 11005), MAX_MESSAGE_SIZE) - val artemisClient = ArtemisMessagingClient(artemisConfig, NetworkHostAndPort("localhost", 11005), MAX_MESSAGE_SIZE) + val artemisClient = ArtemisMessagingClient(artemisConfig.p2pSslOptions, NetworkHostAndPort("localhost", 11005), MAX_MESSAGE_SIZE) artemisServer.start() artemisClient.start() return Pair(artemisServer, artemisClient) diff --git a/bridge/src/integration-test/kotlin/net/corda/bridge/BridgeIntegrationTest.kt b/bridge/src/integration-test/kotlin/net/corda/bridge/BridgeIntegrationTest.kt index 050f3f7fa5..926dae3e16 100644 --- a/bridge/src/integration-test/kotlin/net/corda/bridge/BridgeIntegrationTest.kt +++ b/bridge/src/integration-test/kotlin/net/corda/bridge/BridgeIntegrationTest.kt @@ -28,6 +28,7 @@ import net.corda.testing.core.DUMMY_BANK_A_NAME import net.corda.testing.core.MAX_MESSAGE_SIZE import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.internal.rigorousMock +import net.corda.testing.internal.stubs.CertificateStoreStubs import org.apache.activemq.artemis.api.core.RoutingType import org.apache.activemq.artemis.api.core.SimpleString import org.apache.curator.test.TestingServer @@ -387,61 +388,76 @@ class BridgeIntegrationTest { private fun createArtemis(): Pair<ArtemisMessagingServer, ArtemisMessagingClient> { + val baseDirectory = tempFolder.root.toPath() + val certificatesDirectory = baseDirectory / "certificates" + val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory) + val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory) val artemisConfig = rigorousMock<AbstractNodeConfiguration>().also { - doReturn(tempFolder.root.toPath()).whenever(it).baseDirectory + doReturn(baseDirectory).whenever(it).baseDirectory + doReturn(certificatesDirectory).whenever(it).certificatesDirectory doReturn(ALICE_NAME).whenever(it).myLegalName - doReturn("trustpass").whenever(it).trustStorePassword - doReturn("cordacadevpass").whenever(it).keyStorePassword + doReturn(signingCertificateStore).whenever(it).signingCertificateStore + doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions doReturn(NetworkHostAndPort("localhost", 11005)).whenever(it).p2pAddress doReturn(null).whenever(it).jmxMonitoringHttpPort doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000), externalBridge = true)).whenever(it).enterpriseConfiguration } val artemisServer = ArtemisMessagingServer(artemisConfig, NetworkHostAndPort("0.0.0.0", 11005), MAX_MESSAGE_SIZE) - val artemisClient = ArtemisMessagingClient(artemisConfig, NetworkHostAndPort("localhost", 11005), MAX_MESSAGE_SIZE) + val artemisClient = ArtemisMessagingClient(artemisConfig.p2pSslOptions, NetworkHostAndPort("localhost", 11005), MAX_MESSAGE_SIZE) artemisServer.start() artemisClient.start() return Pair(artemisServer, artemisClient) } private fun createArtemis2(): Pair<ArtemisMessagingServer, ArtemisMessagingClient> { - val originalCertsFolderPath = tempFolder.root.toPath() / "certificates" - val folderPath = tempFolder.root.toPath() / "artemis2" + val baseDirectory = tempFolder.root.toPath() + val originalCertsFolderPath = baseDirectory / "certificates" + val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(originalCertsFolderPath) + val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(originalCertsFolderPath) + + val folderPath = baseDirectory / "artemis2" val newCertsFolderPath = folderPath / "certificates" newCertsFolderPath.createDirectories() (originalCertsFolderPath / "truststore.jks").copyToDirectory(newCertsFolderPath) (originalCertsFolderPath / "sslkeystore.jks").copyToDirectory(newCertsFolderPath) val artemisConfig = rigorousMock<AbstractNodeConfiguration>().also { doReturn(folderPath).whenever(it).baseDirectory + doReturn(originalCertsFolderPath).whenever(it).certificatesDirectory doReturn(ALICE_NAME).whenever(it).myLegalName - doReturn("trustpass").whenever(it).trustStorePassword - doReturn("cordacadevpass").whenever(it).keyStorePassword + doReturn(signingCertificateStore).whenever(it).signingCertificateStore + doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions doReturn(NetworkHostAndPort("localhost", 12005)).whenever(it).p2pAddress doReturn(null).whenever(it).jmxMonitoringHttpPort doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000), externalBridge = true)).whenever(it).enterpriseConfiguration } val artemisServer = ArtemisMessagingServer(artemisConfig, NetworkHostAndPort("0.0.0.0", 12005), MAX_MESSAGE_SIZE) - val artemisClient = ArtemisMessagingClient(artemisConfig, NetworkHostAndPort("localhost", 12005), MAX_MESSAGE_SIZE) + val artemisClient = ArtemisMessagingClient(artemisConfig.p2pSslOptions, NetworkHostAndPort("localhost", 12005), MAX_MESSAGE_SIZE) return Pair(artemisServer, artemisClient) } private fun createDummyPeerArtemis(): Pair<ArtemisMessagingServer, ArtemisMessagingClient> { - val originalCertsFolderPath = tempFolder.root.toPath() / "certificates" - val folderPath = tempFolder.root.toPath() / "artemis3" + val baseDirectory = tempFolder.root.toPath() + val originalCertsFolderPath = baseDirectory / "certificates" + val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(originalCertsFolderPath) + val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(originalCertsFolderPath) + + val folderPath = baseDirectory / "artemis3" val newCertsFolderPath = folderPath / "certificates" newCertsFolderPath.createDirectories() (originalCertsFolderPath / "truststore.jks").copyToDirectory(newCertsFolderPath) (originalCertsFolderPath / "sslkeystore.jks").copyToDirectory(newCertsFolderPath) val artemisConfig = rigorousMock<AbstractNodeConfiguration>().also { doReturn(folderPath).whenever(it).baseDirectory + doReturn(newCertsFolderPath).whenever(it).certificatesDirectory doReturn(DUMMY_BANK_A_NAME).whenever(it).myLegalName - doReturn("trustpass").whenever(it).trustStorePassword - doReturn("cordacadevpass").whenever(it).keyStorePassword + doReturn(signingCertificateStore).whenever(it).signingCertificateStore + doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions doReturn(NetworkHostAndPort("localhost", 7890)).whenever(it).p2pAddress doReturn(null).whenever(it).jmxMonitoringHttpPort doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000), externalBridge = true)).whenever(it).enterpriseConfiguration } val artemisServer = ArtemisMessagingServer(artemisConfig, NetworkHostAndPort("0.0.0.0", 7890), MAX_MESSAGE_SIZE) - val artemisClient = ArtemisMessagingClient(artemisConfig, NetworkHostAndPort("localhost", 7890), MAX_MESSAGE_SIZE) + val artemisClient = ArtemisMessagingClient(artemisConfig.p2pSslOptions, NetworkHostAndPort("localhost", 7890), MAX_MESSAGE_SIZE) artemisServer.start() artemisClient.start() artemisClient.started!!.session.createQueue(SimpleString("${P2P_PREFIX}12345"), RoutingType.ANYCAST, SimpleString("${P2P_PREFIX}12345"), true) diff --git a/bridge/src/integration-test/kotlin/net/corda/bridge/services/AMQPListenerTest.kt b/bridge/src/integration-test/kotlin/net/corda/bridge/services/AMQPListenerTest.kt index 75133a6b4c..63bfbeae50 100644 --- a/bridge/src/integration-test/kotlin/net/corda/bridge/services/AMQPListenerTest.kt +++ b/bridge/src/integration-test/kotlin/net/corda/bridge/services/AMQPListenerTest.kt @@ -12,6 +12,7 @@ import net.corda.core.internal.div import net.corda.core.internal.readAll import net.corda.core.utilities.NetworkHostAndPort import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEERS_PREFIX +import net.corda.nodeapi.internal.config.CertificateStore import net.corda.nodeapi.internal.crypto.X509KeyStore import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.protonwrapper.messages.MessageStatus @@ -25,7 +26,6 @@ import org.junit.Assert.assertArrayEquals import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder -import java.security.KeyStore import kotlin.test.assertEquals class AMQPListenerTest { @@ -58,14 +58,14 @@ class AMQPListenerTest { assertEquals(true, stateFollower.next()) assertEquals(true, amqpListenerService.active) assertEquals(false, serverListening("localhost", 10005)) - val keyStoreBytes = bridgeConfig.sslKeystore.readAll() - val trustStoreBytes = bridgeConfig.trustStoreFile.readAll() + val keyStoreBytes = bridgeConfig.p2pSslOptions.keyStore.path.readAll() + val trustStoreBytes = bridgeConfig.p2pSslOptions.trustStore.path.readAll() // start listening amqpListenerService.provisionKeysAndActivate(keyStoreBytes, - bridgeConfig.keyStorePassword.toCharArray(), - bridgeConfig.keyStorePassword.toCharArray(), + bridgeConfig.p2pSslOptions.keyStore.password.toCharArray(), + bridgeConfig.p2pSslOptions.keyStore.password.toCharArray(), trustStoreBytes, - bridgeConfig.trustStorePassword.toCharArray()) + bridgeConfig.p2pSslOptions.trustStore.password.toCharArray()) // Fire lots of activity to prove we are good assertEquals(TestAuditService.AuditEvent.STATUS_CHANGE, auditFollower.next()) assertEquals(true, amqpListenerService.active) @@ -76,12 +76,11 @@ class AMQPListenerTest { assertEquals(TestAuditService.AuditEvent.FAILED_CONNECTION, auditFollower.next()) val clientConfig = createAndLoadConfigFromResource(tempFolder.root.toPath() / "client", configResource) clientConfig.createBridgeKeyStores(DUMMY_BANK_B_NAME) - val clientKeyStore = clientConfig.loadSslKeyStore().internal - val clientTrustStore = clientConfig.loadTrustStore().internal + val clientKeyStore = clientConfig.p2pSslOptions.keyStore.get() + val clientTrustStore = clientConfig.p2pSslOptions.trustStore.get() val amqpConfig = object : AMQPConfiguration { - override val keyStore: KeyStore = clientKeyStore - override val keyStorePrivateKeyPassword: CharArray = clientConfig.keyStorePassword.toCharArray() - override val trustStore: KeyStore = clientTrustStore + override val keyStore = clientKeyStore + override val trustStore = clientTrustStore override val maxMessageSize: Int = maxMessageSize override val trace: Boolean = true } @@ -134,26 +133,28 @@ class AMQPListenerTest { val amqpListenerService = BridgeAMQPListenerServiceImpl(bridgeConfig, maxMessageSize, auditService) amqpListenerService.start() auditService.start() - val keyStoreBytes = bridgeConfig.sslKeystore.readAll() - val trustStoreBytes = bridgeConfig.trustStoreFile.readAll() + val keyStoreBytes = bridgeConfig.p2pSslOptions.keyStore.path.readAll() + val trustStoreBytes = bridgeConfig.p2pSslOptions.trustStore.path.readAll() // start listening amqpListenerService.provisionKeysAndActivate(keyStoreBytes, - bridgeConfig.keyStorePassword.toCharArray(), - bridgeConfig.keyStorePassword.toCharArray(), + bridgeConfig.p2pSslOptions.keyStore.password.toCharArray(), + bridgeConfig.p2pSslOptions.keyStore.password.toCharArray(), trustStoreBytes, - bridgeConfig.trustStorePassword.toCharArray()) + bridgeConfig.p2pSslOptions.trustStore.password.toCharArray()) val connectionFollower = amqpListenerService.onConnection.toBlocking().iterator val auditFollower = auditService.onAuditEvent.toBlocking().iterator val clientKeys = Crypto.generateKeyPair(ECDSA_SECP256R1_SHA256) val clientCert = X509Utilities.createSelfSignedCACertificate(ALICE_NAME.x500Principal, clientKeys) - val clientKeyStore = X509KeyStore("password") - clientKeyStore.setPrivateKey("TLS_CERT", clientKeys.private, listOf(clientCert)) - val clientTrustStore = X509KeyStore("password") - clientTrustStore.setCertificate("TLS_ROOT", clientCert) + val clientKeyStoreRaw = X509KeyStore("password") + clientKeyStoreRaw.setPrivateKey("TLS_CERT", clientKeys.private, listOf(clientCert)) + val clientKeyStore = CertificateStore.of(clientKeyStoreRaw, "password") + + val clientTrustStoreRaw = X509KeyStore("password") + clientTrustStoreRaw.setCertificate("TLS_ROOT", clientCert) + val clientTrustStore = CertificateStore.of(clientTrustStoreRaw, "password") val amqpConfig = object : AMQPConfiguration { - override val keyStore: KeyStore = clientKeyStore.internal - override val keyStorePrivateKeyPassword: CharArray = "password".toCharArray() - override val trustStore: KeyStore = clientTrustStore.internal + override val keyStore = clientKeyStore + override val trustStore = clientTrustStore override val maxMessageSize: Int = maxMessageSize override val trace: Boolean = true } diff --git a/bridge/src/integration-test/kotlin/net/corda/bridge/services/ArtemisConnectionTest.kt b/bridge/src/integration-test/kotlin/net/corda/bridge/services/ArtemisConnectionTest.kt index 94e7d0d675..f9f46a2b60 100644 --- a/bridge/src/integration-test/kotlin/net/corda/bridge/services/ArtemisConnectionTest.kt +++ b/bridge/src/integration-test/kotlin/net/corda/bridge/services/ArtemisConnectionTest.kt @@ -6,6 +6,7 @@ import net.corda.bridge.createAndLoadConfigFromResource import net.corda.bridge.createBridgeKeyStores import net.corda.bridge.createNetworkParams import net.corda.bridge.services.artemis.BridgeArtemisConnectionServiceImpl +import net.corda.core.internal.div import net.corda.core.utilities.NetworkHostAndPort import net.corda.node.services.config.EnterpriseConfiguration import net.corda.node.services.config.MutualExclusionConfiguration @@ -15,6 +16,7 @@ import net.corda.testing.core.DUMMY_BANK_A_NAME import net.corda.testing.core.MAX_MESSAGE_SIZE import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.internal.rigorousMock +import net.corda.testing.internal.stubs.CertificateStoreStubs import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder @@ -84,11 +86,17 @@ class ArtemisConnectionTest { private fun createArtemis(): ArtemisMessagingServer { + + val baseDirectory = tempFolder.root.toPath() + val certificatesDirectory = baseDirectory / "certificates" + val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory) + val p2pSslOptions = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory) val artemisConfig = rigorousMock<AbstractNodeConfiguration>().also { - doReturn(tempFolder.root.toPath()).whenever(it).baseDirectory + doReturn(baseDirectory).whenever(it).baseDirectory + doReturn(certificatesDirectory).whenever(it).certificatesDirectory doReturn(DUMMY_BANK_A_NAME).whenever(it).myLegalName - doReturn("trustpass").whenever(it).trustStorePassword - doReturn("cordacadevpass").whenever(it).keyStorePassword + doReturn(signingCertificateStore).whenever(it).signingCertificateStore + doReturn(p2pSslOptions).whenever(it).p2pSslOptions doReturn(NetworkHostAndPort("localhost", 11005)).whenever(it).p2pAddress doReturn(null).whenever(it).jmxMonitoringHttpPort doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000), externalBridge = true)).whenever(it).enterpriseConfiguration diff --git a/bridge/src/main/kotlin/net/corda/bridge/services/api/FirewallConfiguration.kt b/bridge/src/main/kotlin/net/corda/bridge/services/api/FirewallConfiguration.kt index e67e5e8973..720f93362e 100644 --- a/bridge/src/main/kotlin/net/corda/bridge/services/api/FirewallConfiguration.kt +++ b/bridge/src/main/kotlin/net/corda/bridge/services/api/FirewallConfiguration.kt @@ -2,8 +2,7 @@ package net.corda.bridge.services.api import net.corda.core.identity.CordaX500Name import net.corda.core.utilities.NetworkHostAndPort -import net.corda.nodeapi.internal.config.NodeSSLConfiguration -import net.corda.nodeapi.internal.config.SSLConfiguration +import net.corda.nodeapi.internal.config.MutualSslConfiguration import net.corda.nodeapi.internal.protonwrapper.netty.SocksProxyConfig import java.nio.file.Path @@ -28,12 +27,7 @@ enum class FirewallMode { FloatOuter } -interface BridgeSSLConfiguration : SSLConfiguration { - override val keyStorePassword: String - override val trustStorePassword: String - override val sslKeystore: Path - override val trustStoreFile: Path -} +interface BridgeSSLConfiguration : MutualSslConfiguration /** @@ -91,7 +85,8 @@ interface FloatOuterConfiguration { val customSSLConfiguration: BridgeSSLConfiguration? } -interface FirewallConfiguration : NodeSSLConfiguration { +interface FirewallConfiguration { + val baseDirectory: Path val firewallMode: FirewallMode val outboundConfig: BridgeOutboundConfiguration? val inboundConfig: BridgeInboundConfiguration? @@ -115,4 +110,6 @@ interface FirewallConfiguration : NodeSSLConfiguration { // This is relevant to bridges, because we push messages into the inbox and use the async acknowledgement responses to reply to sender. val p2pConfirmationWindowSize: Int val whitelistedHeaders: List<String> + val crlCheckSoftFail: Boolean + val p2pSslOptions: MutualSslConfiguration } \ No newline at end of file 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 b2e987aede..fcabe28b38 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 @@ -1,15 +1,15 @@ package net.corda.bridge.services.artemis 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 import net.corda.core.serialization.internal.nodeSerializationEnv import net.corda.core.utilities.contextLogger -import net.corda.nodeapi.ArtemisTcpTransport import net.corda.nodeapi.internal.ArtemisMessagingClient import net.corda.nodeapi.internal.ArtemisMessagingComponent +import net.corda.nodeapi.internal.InternalArtemisTcpTransport +import net.corda.nodeapi.internal.config.MutualSslConfiguration 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 @@ -34,13 +34,13 @@ class BridgeArtemisConnectionServiceImpl(val conf: FirewallConfiguration, } private val state = ThreadBox(InnerState()) - private val sslConfiguration: BridgeSSLConfiguration + private val sslConfiguration: MutualSslConfiguration private val statusFollower: ServiceStateCombiner private var statusSubscriber: Subscription? = null init { statusFollower = ServiceStateCombiner(listOf(auditService)) - sslConfiguration = conf.outboundConfig?.customSSLConfiguration ?: BridgeSSLConfigurationImpl(conf) + sslConfiguration = conf.outboundConfig?.customSSLConfiguration ?: conf.p2pSslOptions } override fun start() { @@ -61,7 +61,7 @@ class BridgeArtemisConnectionServiceImpl(val conf: FirewallConfiguration, log.info("Connecting to message broker: ${outboundConf.artemisBrokerAddress}") val brokerAddresses = listOf(outboundConf.artemisBrokerAddress) + outboundConf.alternateArtemisBrokerAddresses // TODO Add broker CN to config for host verification in case the embedded broker isn't used - val tcpTransports = brokerAddresses.map { ArtemisTcpTransport.p2pConnectorTcpTransport(it, sslConfiguration) } + val tcpTransports = brokerAddresses.map { InternalArtemisTcpTransport.p2pConnectorTcpTransport(it, sslConfiguration) } locator = ActiveMQClient.createServerLocatorWithoutHA(*tcpTransports.toTypedArray()).apply { // Never time out on our loopback Artemis connections. If we switch back to using the InVM transport this // would be the default and the two lines below can be deleted. diff --git a/bridge/src/main/kotlin/net/corda/bridge/services/config/FirewallConfigurationImpl.kt b/bridge/src/main/kotlin/net/corda/bridge/services/config/FirewallConfigurationImpl.kt index 42f3bb485a..980c145bc6 100644 --- a/bridge/src/main/kotlin/net/corda/bridge/services/config/FirewallConfigurationImpl.kt +++ b/bridge/src/main/kotlin/net/corda/bridge/services/config/FirewallConfigurationImpl.kt @@ -6,22 +6,23 @@ 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.NodeSSLConfiguration +import net.corda.nodeapi.internal.config.FileBasedCertificateStoreSupplier +import net.corda.nodeapi.internal.config.SslConfiguration +import net.corda.nodeapi.internal.config.MutualSslConfiguration 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.parseAsFirewallConfiguration(): FirewallConfiguration = parseAs<FirewallConfigurationImpl>() -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", - override val crlCheckSoftFail: Boolean) : BridgeSSLConfiguration { - constructor(config: NodeSSLConfiguration) : this(config.keyStorePassword, config.trustStorePassword, config.certificatesDirectory, config.sslKeystore, config.trustStoreFile, config.crlCheckSoftFail) +data class BridgeSSLConfigurationImpl(private val sslKeystore: Path, + private val keyStorePassword: String, + private val trustStoreFile: Path, + private val trustStorePassword: String, + private val crlCheckSoftFail: Boolean) : BridgeSSLConfiguration { + + override val keyStore = FileBasedCertificateStoreSupplier(sslKeystore, keyStorePassword) + override val trustStore = FileBasedCertificateStoreSupplier(trustStoreFile, trustStorePassword) } data class BridgeOutboundConfigurationImpl(override val artemisBrokerAddress: NetworkHostAndPort, @@ -45,12 +46,12 @@ data class BridgeHAConfigImpl(override val haConnectionString: String, override data class FirewallConfigurationImpl( override val baseDirectory: Path, - override val certificatesDirectory: Path = baseDirectory / "certificates", - override val sslKeystore: Path = certificatesDirectory / "sslkeystore.jks", - override val trustStoreFile: Path = certificatesDirectory / "truststore.jks", + private val certificatesDirectory: Path = baseDirectory / "certificates", + private val sslKeystore: Path = certificatesDirectory / "sslkeystore.jks", + private val trustStoreFile: Path = certificatesDirectory / "truststore.jks", override val crlCheckSoftFail: Boolean, - override val keyStorePassword: String, - override val trustStorePassword: String, + private val keyStorePassword: String, + private val trustStorePassword: String, override val firewallMode: FirewallMode, override val networkParametersPath: Path, override val outboundConfig: BridgeOutboundConfigurationImpl?, @@ -74,6 +75,12 @@ data class FirewallConfigurationImpl( require(inboundConfig != null && floatOuterConfig != null) { "Missing required configuration" } } } + + private val p2pKeystorePath = sslKeystore + private val p2pKeyStore = FileBasedCertificateStoreSupplier(p2pKeystorePath, keyStorePassword) + private val p2pTrustStoreFilePath = trustStoreFile + private val p2pTrustStore = FileBasedCertificateStoreSupplier(p2pTrustStoreFilePath, trustStorePassword) + override val p2pSslOptions: MutualSslConfiguration = SslConfiguration.mutual(p2pKeyStore, p2pTrustStore) } diff --git a/bridge/src/main/kotlin/net/corda/bridge/services/receiver/BridgeAMQPListenerServiceImpl.kt b/bridge/src/main/kotlin/net/corda/bridge/services/receiver/BridgeAMQPListenerServiceImpl.kt index 8371b5d112..6b14b98cc3 100644 --- a/bridge/src/main/kotlin/net/corda/bridge/services/receiver/BridgeAMQPListenerServiceImpl.kt +++ b/bridge/src/main/kotlin/net/corda/bridge/services/receiver/BridgeAMQPListenerServiceImpl.kt @@ -7,7 +7,9 @@ import net.corda.bridge.services.api.ServiceStateSupport import net.corda.bridge.services.util.ServiceStateCombiner import net.corda.bridge.services.util.ServiceStateHelper import net.corda.core.utilities.contextLogger +import net.corda.nodeapi.internal.config.CertificateStore import net.corda.nodeapi.internal.crypto.KEYSTORE_TYPE +import net.corda.nodeapi.internal.crypto.X509KeyStore import net.corda.nodeapi.internal.protonwrapper.messages.ReceivedMessage import net.corda.nodeapi.internal.protonwrapper.netty.AMQPConfiguration import net.corda.nodeapi.internal.protonwrapper.netty.AMQPServer @@ -48,13 +50,13 @@ class BridgeAMQPListenerServiceImpl(val conf: FirewallConfiguration, trustStorePassword: CharArray) { require(active) { "AuditService must be active" } require(keyStorePassword !== keyStorePrivateKeyPassword) { "keyStorePassword and keyStorePrivateKeyPassword must reference distinct arrays!" } - val keyStore = loadKeyStoreAndWipeKeys(keyStoreBytes, keyStorePassword) - val trustStore = loadKeyStoreAndWipeKeys(trustStoreBytes, trustStorePassword) + + val keyStore = CertificateStore.of(loadKeyStore(keyStoreBytes, keyStorePassword), java.lang.String.valueOf(keyStorePrivateKeyPassword)).also { wipeKeys(keyStoreBytes, keyStorePassword) } + val trustStore = CertificateStore.of(loadKeyStore(trustStoreBytes, trustStorePassword), java.lang.String.valueOf(trustStorePassword)).also { wipeKeys(trustStoreBytes, trustStorePassword) } val bindAddress = conf.inboundConfig!!.listeningAddress val amqpConfiguration = object : AMQPConfiguration { - override val keyStore: KeyStore = keyStore - override val keyStorePrivateKeyPassword: CharArray = keyStorePrivateKeyPassword - override val trustStore: KeyStore = trustStore + override val keyStore = keyStore + override val trustStore = trustStore override val crlCheckSoftFail: Boolean = conf.crlCheckSoftFail override val maxMessageSize: Int = maximumMessageSize override val trace: Boolean = conf.enableAMQPPacketTrace @@ -80,15 +82,18 @@ class BridgeAMQPListenerServiceImpl(val conf: FirewallConfiguration, consoleLogger.info(msg) } - private fun loadKeyStoreAndWipeKeys(keyStoreBytes: ByteArray, keyStorePassword: CharArray): KeyStore { + private fun wipeKeys(keyStoreBytes: ByteArray, keyStorePassword: CharArray) { + // We overwrite the keys we don't need anymore + Arrays.fill(keyStoreBytes, 0xAA.toByte()) + Arrays.fill(keyStorePassword, 0xAA55.toChar()) + } + + private fun loadKeyStore(keyStoreBytes: ByteArray, keyStorePassword: CharArray): X509KeyStore { val keyStore = KeyStore.getInstance(KEYSTORE_TYPE) ByteArrayInputStream(keyStoreBytes).use { keyStore.load(it, keyStorePassword) } - // We overwrite the keys we don't need anymore - Arrays.fill(keyStoreBytes, 0xAA.toByte()) - Arrays.fill(keyStorePassword, 0xAA55.toChar()) - return keyStore + return X509KeyStore(keyStore, java.lang.String.valueOf(keyStorePassword)) } override fun wipeKeysAndDeactivate() { 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 b7fe56ee52..6871ca6806 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 @@ -1,7 +1,6 @@ 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 @@ -12,13 +11,13 @@ 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.MutualSslConfiguration import net.corda.nodeapi.internal.protonwrapper.messages.MessageStatus import net.corda.nodeapi.internal.protonwrapper.messages.ReceivedMessage import net.corda.nodeapi.internal.protonwrapper.netty.AMQPConfiguration import net.corda.nodeapi.internal.protonwrapper.netty.AMQPServer import net.corda.nodeapi.internal.protonwrapper.netty.ConnectionChange import rx.Subscription -import java.security.KeyStore import java.util.concurrent.locks.ReentrantLock import kotlin.concurrent.withLock @@ -39,7 +38,7 @@ class FloatControlListenerService(val conf: FirewallConfiguration, private var connectSubscriber: Subscription? = null private var receiveSubscriber: Subscription? = null private var amqpControlServer: AMQPServer? = null - private val sslConfiguration: BridgeSSLConfiguration + private val sslConfiguration: MutualSslConfiguration private val floatControlAddress = conf.floatOuterConfig!!.floatAddress private val floatClientName = conf.floatOuterConfig!!.expectedCertificateSubject private var activeConnectionInfo: ConnectionChange? = null @@ -48,7 +47,7 @@ class FloatControlListenerService(val conf: FirewallConfiguration, init { statusFollower = ServiceStateCombiner(listOf(auditService, amqpListener)) - sslConfiguration = conf.floatOuterConfig?.customSSLConfiguration ?: BridgeSSLConfigurationImpl(conf) + sslConfiguration = conf.floatOuterConfig?.customSSLConfiguration ?: conf.p2pSslOptions } @@ -68,15 +67,13 @@ class FloatControlListenerService(val conf: FirewallConfiguration, private fun startControlListener() { lock.withLock { - val keyStore = sslConfiguration.loadSslKeyStore().internal - val keyStorePrivateKeyPassword = sslConfiguration.keyStorePassword - val trustStore = sslConfiguration.loadTrustStore().internal + val keyStore = sslConfiguration.keyStore.get() + val trustStore = sslConfiguration.trustStore.get() val amqpConfig = object : AMQPConfiguration { override val userName: String? = null override val password: String? = null - override val keyStore: KeyStore = keyStore - override val keyStorePrivateKeyPassword: CharArray = keyStorePrivateKeyPassword.toCharArray() - override val trustStore: KeyStore = trustStore + override val keyStore = keyStore + override val trustStore = trustStore override val crlCheckSoftFail: Boolean = conf.crlCheckSoftFail override val maxMessageSize: Int = maximumMessageSize override val trace: Boolean = conf.enableAMQPPacketTrace diff --git a/bridge/src/main/kotlin/net/corda/bridge/services/receiver/InProcessBridgeReceiverService.kt b/bridge/src/main/kotlin/net/corda/bridge/services/receiver/InProcessBridgeReceiverService.kt index bd751819ff..540cf2c320 100644 --- a/bridge/src/main/kotlin/net/corda/bridge/services/receiver/InProcessBridgeReceiverService.kt +++ b/bridge/src/main/kotlin/net/corda/bridge/services/receiver/InProcessBridgeReceiverService.kt @@ -5,7 +5,7 @@ import net.corda.bridge.services.util.ServiceStateCombiner import net.corda.bridge.services.util.ServiceStateHelper import net.corda.core.internal.readAll import net.corda.core.utilities.contextLogger -import net.corda.nodeapi.internal.config.SSLConfiguration +import net.corda.nodeapi.internal.config.MutualSslConfiguration import net.corda.nodeapi.internal.protonwrapper.messages.ReceivedMessage import rx.Subscription @@ -22,23 +22,23 @@ class InProcessBridgeReceiverService(val conf: FirewallConfiguration, private val statusFollower: ServiceStateCombiner private var statusSubscriber: Subscription? = null private var receiveSubscriber: Subscription? = null - private val sslConfiguration: SSLConfiguration + private val sslConfiguration: MutualSslConfiguration init { statusFollower = ServiceStateCombiner(listOf(auditService, haService, amqpListenerService, filterService)) - sslConfiguration = conf.inboundConfig?.customSSLConfiguration ?: conf + sslConfiguration = conf.inboundConfig?.customSSLConfiguration ?: conf.p2pSslOptions } override fun start() { statusSubscriber = statusFollower.activeChange.subscribe({ if (it) { - val keyStoreBytes = sslConfiguration.sslKeystore.readAll() - val trustStoreBytes = sslConfiguration.trustStoreFile.readAll() + val keyStoreBytes = sslConfiguration.keyStore.path.readAll() + val trustStoreBytes = sslConfiguration.trustStore.path.readAll() amqpListenerService.provisionKeysAndActivate(keyStoreBytes, - sslConfiguration.keyStorePassword.toCharArray(), - sslConfiguration.keyStorePassword.toCharArray(), + sslConfiguration.keyStore.password.toCharArray(), + sslConfiguration.keyStore.password.toCharArray(), trustStoreBytes, - sslConfiguration.trustStorePassword.toCharArray()) + sslConfiguration.trustStore.password.toCharArray()) } else { if (amqpListenerService.running) { amqpListenerService.wipeKeysAndDeactivate() diff --git a/bridge/src/main/kotlin/net/corda/bridge/services/receiver/TunnelingBridgeReceiverService.kt b/bridge/src/main/kotlin/net/corda/bridge/services/receiver/TunnelingBridgeReceiverService.kt index 5555455e57..887946d7a7 100644 --- a/bridge/src/main/kotlin/net/corda/bridge/services/receiver/TunnelingBridgeReceiverService.kt +++ b/bridge/src/main/kotlin/net/corda/bridge/services/receiver/TunnelingBridgeReceiverService.kt @@ -13,7 +13,7 @@ import net.corda.core.serialization.serialize import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.contextLogger import net.corda.core.utilities.debug -import net.corda.nodeapi.internal.config.SSLConfiguration +import net.corda.nodeapi.internal.config.MutualSslConfiguration import net.corda.nodeapi.internal.protonwrapper.messages.MessageStatus import net.corda.nodeapi.internal.protonwrapper.messages.ReceivedMessage import net.corda.nodeapi.internal.protonwrapper.netty.AMQPClient @@ -21,7 +21,6 @@ import net.corda.nodeapi.internal.protonwrapper.netty.AMQPConfiguration import net.corda.nodeapi.internal.protonwrapper.netty.ConnectionChange import rx.Subscription import java.io.ByteArrayOutputStream -import java.security.KeyStore import java.security.SecureRandom import java.util.concurrent.TimeUnit import java.util.concurrent.TimeoutException @@ -41,15 +40,15 @@ class TunnelingBridgeReceiverService(val conf: FirewallConfiguration, private var connectSubscriber: Subscription? = null private var receiveSubscriber: Subscription? = null private var amqpControlClient: AMQPClient? = null - private val controlLinkSSLConfiguration: SSLConfiguration - private val floatListenerSSLConfiguration: SSLConfiguration + private val controlLinkSSLConfiguration: MutualSslConfiguration + private val floatListenerSSLConfiguration: MutualSslConfiguration private val expectedCertificateSubject: CordaX500Name private val secureRandom: SecureRandom = newSecureRandom() init { statusFollower = ServiceStateCombiner(listOf(auditService, haService, filterService)) - controlLinkSSLConfiguration = conf.bridgeInnerConfig?.customSSLConfiguration ?: conf - floatListenerSSLConfiguration = conf.bridgeInnerConfig?.customFloatOuterSSLConfiguration ?: conf + controlLinkSSLConfiguration = conf.bridgeInnerConfig?.customSSLConfiguration ?: conf.p2pSslOptions + floatListenerSSLConfiguration = conf.bridgeInnerConfig?.customFloatOuterSSLConfiguration ?: conf.p2pSslOptions expectedCertificateSubject = conf.bridgeInnerConfig!!.expectedCertificateSubject } @@ -58,15 +57,13 @@ class TunnelingBridgeReceiverService(val conf: FirewallConfiguration, statusSubscriber = statusFollower.activeChange.subscribe({ if (it) { val floatAddresses = conf.bridgeInnerConfig!!.floatAddresses - val controlLinkKeyStore = controlLinkSSLConfiguration.loadSslKeyStore().internal - val controLinkKeyStorePrivateKeyPassword = controlLinkSSLConfiguration.keyStorePassword - val controlLinkTrustStore = controlLinkSSLConfiguration.loadTrustStore().internal + val controlLinkKeyStore = controlLinkSSLConfiguration.keyStore.get() + val controlLinkTrustStore = controlLinkSSLConfiguration.trustStore.get() val amqpConfig = object : AMQPConfiguration { override val userName: String? = null override val password: String? = null - override val keyStore: KeyStore = controlLinkKeyStore - override val keyStorePrivateKeyPassword: CharArray = controLinkKeyStorePrivateKeyPassword.toCharArray() - override val trustStore: KeyStore = controlLinkTrustStore + override val keyStore = controlLinkKeyStore + override val trustStore = controlLinkTrustStore override val crlCheckSoftFail: Boolean = conf.crlCheckSoftFail override val maxMessageSize: Int = maximumMessageSize override val trace: Boolean = conf.enableAMQPPacketTrace @@ -124,12 +121,12 @@ class TunnelingBridgeReceiverService(val conf: FirewallConfiguration, auditService.statusChangeEvent("Connection change on float control port $connectionChange") if (connectionChange.connected) { val (freshKeyStorePassword, freshKeyStoreKeyPassword, recodedKeyStore) = recodeKeyStore(floatListenerSSLConfiguration) - val trustStoreBytes = floatListenerSSLConfiguration.trustStoreFile.readAll() + val trustStoreBytes = floatListenerSSLConfiguration.trustStore.path.readAll() val activateMessage = ActivateFloat(recodedKeyStore, freshKeyStorePassword, freshKeyStoreKeyPassword, trustStoreBytes, - floatListenerSSLConfiguration.trustStorePassword.toCharArray()) + floatListenerSSLConfiguration.trustStore.password.toCharArray()) val amqpActivateMessage = amqpControlClient!!.createMessage(activateMessage.serialize(context = SerializationDefaults.P2P_CONTEXT).bytes, FLOAT_CONTROL_TOPIC, expectedCertificateSubject.toString(), @@ -150,9 +147,9 @@ class TunnelingBridgeReceiverService(val conf: FirewallConfiguration, } // Recode KeyStore to use a fresh random password for entries and overall - private fun recodeKeyStore(sslConfiguration: SSLConfiguration): Triple<CharArray, CharArray, ByteArray> { - val keyStoreOriginal = sslConfiguration.loadSslKeyStore().internal - val originalKeyStorePassword = sslConfiguration.keyStorePassword.toCharArray() + private fun recodeKeyStore(sslConfiguration: MutualSslConfiguration): Triple<CharArray, CharArray, ByteArray> { + val keyStoreOriginal = sslConfiguration.keyStore.get().value.internal + val originalKeyStorePassword = sslConfiguration.keyStore.password.toCharArray() val freshKeyStorePassword = CharArray(20) { secureRandom.nextInt(0xD800).toChar() } // Stick to single character Unicode range val freshPrivateKeyPassword = CharArray(20) { secureRandom.nextInt(0xD800).toChar() } // Stick to single character Unicode range for (alias in keyStoreOriginal.aliases()) { diff --git a/bridge/src/main/kotlin/net/corda/bridge/services/sender/DirectBridgeSenderService.kt b/bridge/src/main/kotlin/net/corda/bridge/services/sender/DirectBridgeSenderService.kt index 9948394432..e27f788a0c 100644 --- a/bridge/src/main/kotlin/net/corda/bridge/services/sender/DirectBridgeSenderService.kt +++ b/bridge/src/main/kotlin/net/corda/bridge/services/sender/DirectBridgeSenderService.kt @@ -23,7 +23,7 @@ class DirectBridgeSenderService(val conf: FirewallConfiguration, private val statusFollower: ServiceStateCombiner private var statusSubscriber: Subscription? = null private var listenerActiveSubscriber: Subscription? = null - private var bridgeControlListener: BridgeControlListener = BridgeControlListener(conf, conf.outboundConfig!!.socksProxyConfig, maxMessageSize, { ForwardingArtemisMessageClient(artemisConnectionService) }) + private var bridgeControlListener: BridgeControlListener = BridgeControlListener(conf.p2pSslOptions, conf.outboundConfig!!.socksProxyConfig, maxMessageSize, { ForwardingArtemisMessageClient(artemisConnectionService) }) init { statusFollower = ServiceStateCombiner(listOf(auditService, artemisConnectionService, haService)) diff --git a/bridge/src/test/kotlin/net/corda/bridge/BridgeTestHelper.kt b/bridge/src/test/kotlin/net/corda/bridge/BridgeTestHelper.kt index 467a533423..a9a9bde0c0 100644 --- a/bridge/src/test/kotlin/net/corda/bridge/BridgeTestHelper.kt +++ b/bridge/src/test/kotlin/net/corda/bridge/BridgeTestHelper.kt @@ -7,9 +7,8 @@ import net.corda.core.internal.createDirectories import net.corda.core.internal.exists import net.corda.core.node.NetworkParameters import net.corda.core.node.NotaryInfo -import net.corda.core.node.services.AttachmentId import net.corda.nodeapi.internal.* -import net.corda.nodeapi.internal.config.SSLConfiguration +import net.corda.nodeapi.internal.config.MutualSslConfiguration import net.corda.nodeapi.internal.crypto.* import net.corda.nodeapi.internal.network.NetworkParametersCopier import net.corda.testing.core.DUMMY_NOTARY_NAME @@ -33,7 +32,7 @@ fun createNetworkParams(baseDirectory: Path): Int { maxMessageSize = 10485760, maxTransactionSize = 40000, epoch = 1, - whitelistedContractImplementations = emptyMap<String, List<AttachmentId>>() + whitelistedContractImplementations = emptyMap() ) val copier = NetworkParametersCopier(networkParameters, overwriteFile = true) copier.install(baseDirectory) @@ -55,18 +54,22 @@ fun createAndLoadConfigFromResource(baseDirectory: Path, configResource: String) return config } -fun SSLConfiguration.createBridgeKeyStores(legalName: CordaX500Name, - rootCert: X509Certificate = DEV_ROOT_CA.certificate, - intermediateCa: CertificateAndKeyPair = DEV_INTERMEDIATE_CA) { +fun FirewallConfiguration.createBridgeKeyStores(legalName: CordaX500Name, + rootCert: X509Certificate = DEV_ROOT_CA.certificate, + intermediateCa: CertificateAndKeyPair = DEV_INTERMEDIATE_CA) = p2pSslOptions.createBridgeKeyStores(legalName, rootCert, intermediateCa) - certificatesDirectory.createDirectories() - if (!trustStoreFile.exists()) { - loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/${DEV_CA_TRUST_STORE_FILE}"), DEV_CA_TRUST_STORE_PASS).save(trustStoreFile, trustStorePassword) +fun MutualSslConfiguration.createBridgeKeyStores(legalName: CordaX500Name, + rootCert: X509Certificate = DEV_ROOT_CA.certificate, + intermediateCa: CertificateAndKeyPair = DEV_INTERMEDIATE_CA) { + + if (!trustStore.path.exists()) { + val trustStore = trustStore.get(true) + loadDevCaTrustStore().copyTo(trustStore) } val (nodeCaCert, nodeCaKeyPair) = createDevNodeCa(intermediateCa, legalName) - val sslKeyStore = loadSslKeyStore(createNew = true) + val sslKeyStore = keyStore.get(createNew = true) sslKeyStore.update { val tlsKeyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, legalName.x500Principal, tlsKeyPair.public) diff --git a/bridge/src/test/kotlin/net/corda/bridge/ConfigTest.kt b/bridge/src/test/kotlin/net/corda/bridge/ConfigTest.kt index ce660ce9fc..2bdada596c 100644 --- a/bridge/src/test/kotlin/net/corda/bridge/ConfigTest.kt +++ b/bridge/src/test/kotlin/net/corda/bridge/ConfigTest.kt @@ -62,23 +62,23 @@ class ConfigTest { fun `Load overridden cert config`() { val configResource = "/net/corda/bridge/custombasecerts/firewall.conf" val config = createAndLoadConfigFromResource(tempFolder.root.toPath(), configResource) - assertEquals(Paths.get("customcerts/mysslkeystore.jks"), config.sslKeystore) - assertEquals(Paths.get("customcerts/mytruststore.jks"), config.trustStoreFile) + assertEquals(Paths.get("customcerts/mysslkeystore.jks"), config.p2pSslOptions.keyStore.path) + assertEquals(Paths.get("customcerts/mytruststore.jks"), config.p2pSslOptions.trustStore.path) } @Test fun `Load custom inner certificate config`() { val configResource = "/net/corda/bridge/separatedwithcustomcerts/bridge/firewall.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) + assertEquals(Paths.get("outboundcerts/outboundkeys.jks"), config.outboundConfig!!.customSSLConfiguration!!.keyStore.path) + assertEquals(Paths.get("outboundcerts/outboundtrust.jks"), config.outboundConfig!!.customSSLConfiguration!!.trustStore.path) + assertEquals("outboundkeypassword", config.outboundConfig!!.customSSLConfiguration!!.keyStore.password) + assertEquals("outboundtrustpassword", config.outboundConfig!!.customSSLConfiguration!!.trustStore.password) assertNull(config.inboundConfig) - assertEquals(Paths.get("tunnelcerts/tunnelkeys.jks"), config.bridgeInnerConfig!!.customSSLConfiguration!!.sslKeystore) - assertEquals(Paths.get("tunnelcerts/tunneltrust.jks"), config.bridgeInnerConfig!!.customSSLConfiguration!!.trustStoreFile) - assertEquals("tunnelkeypassword", config.bridgeInnerConfig!!.customSSLConfiguration!!.keyStorePassword) - assertEquals("tunneltrustpassword", config.bridgeInnerConfig!!.customSSLConfiguration!!.trustStorePassword) + assertEquals(Paths.get("tunnelcerts/tunnelkeys.jks"), config.bridgeInnerConfig!!.customSSLConfiguration!!.keyStore.path) + assertEquals(Paths.get("tunnelcerts/tunneltrust.jks"), config.bridgeInnerConfig!!.customSSLConfiguration!!.trustStore.path) + assertEquals("tunnelkeypassword", config.bridgeInnerConfig!!.customSSLConfiguration!!.keyStore.password) + assertEquals("tunneltrustpassword", config.bridgeInnerConfig!!.customSSLConfiguration!!.trustStore.password) assertNull(config.floatOuterConfig) } @@ -86,15 +86,15 @@ class ConfigTest { fun `Load custom outer certificate config`() { val configResource = "/net/corda/bridge/separatedwithcustomcerts/float/firewall.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) + assertEquals(Paths.get("inboundcerts/inboundkeys.jks"), config.inboundConfig!!.customSSLConfiguration!!.keyStore.path) + assertEquals(Paths.get("inboundcerts/inboundtrust.jks"), config.inboundConfig!!.customSSLConfiguration!!.trustStore.path) + assertEquals("inboundkeypassword", config.inboundConfig!!.customSSLConfiguration!!.keyStore.password) + assertEquals("inboundtrustpassword", config.inboundConfig!!.customSSLConfiguration!!.trustStore.password) 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) + assertEquals(Paths.get("tunnelcerts/tunnelkeys.jks"), config.floatOuterConfig!!.customSSLConfiguration!!.keyStore.path) + assertEquals(Paths.get("tunnelcerts/tunneltrust.jks"), config.floatOuterConfig!!.customSSLConfiguration!!.trustStore.path) + assertEquals("tunnelkeypassword", config.floatOuterConfig!!.customSSLConfiguration!!.keyStore.password) + assertEquals("tunneltrustpassword", config.floatOuterConfig!!.customSSLConfiguration!!.trustStore.password) assertNull(config.bridgeInnerConfig) } diff --git a/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java b/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java index ed26949527..97fbc522f6 100644 --- a/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java +++ b/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java @@ -61,7 +61,6 @@ public class CordaRPCJavaClientTest extends NodeBasedTest { @Before public void setUp() throws Exception { - super.setUp(); node = startNode(ALICE_NAME, 1000, singletonList(rpcUser)); client = new CordaRPCClient(requireNonNull(node.getNode().getConfiguration().getRpcOptions().getAddress())); } diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/CordaRPCClient.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/CordaRPCClient.kt index d2b3fa0f09..9a90b28476 100644 --- a/client/rpc/src/main/kotlin/net/corda/client/rpc/CordaRPCClient.kt +++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/CordaRPCClient.kt @@ -7,12 +7,11 @@ import net.corda.core.context.Trace import net.corda.core.messaging.CordaRPCOps import net.corda.core.serialization.internal.effectiveSerializationEnv import net.corda.core.utilities.NetworkHostAndPort -import net.corda.nodeapi.ArtemisTcpTransport.Companion.rpcConnectorTcpTransport import net.corda.core.messaging.ClientRpcSslOptions import net.corda.core.utilities.days import net.corda.core.utilities.minutes import net.corda.core.utilities.seconds -import net.corda.nodeapi.internal.config.SSLConfiguration +import net.corda.nodeapi.internal.InternalArtemisTcpTransport.Companion.rpcConnectorTcpTransport import net.corda.serialization.internal.AMQP_RPC_CLIENT_CONTEXT import java.time.Duration @@ -243,10 +242,8 @@ class CordaRPCClient private constructor( private val hostAndPort: NetworkHostAndPort, private val configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT, private val sslConfiguration: ClientRpcSslOptions? = null, - private val nodeSslConfiguration: SSLConfiguration? = null, private val classLoader: ClassLoader? = null, - private val haAddressPool: List<NetworkHostAndPort> = emptyList(), - private val internalConnection: Boolean = false + private val haAddressPool: List<NetworkHostAndPort> = emptyList() ) { @JvmOverloads constructor(hostAndPort: NetworkHostAndPort, @@ -260,7 +257,7 @@ class CordaRPCClient private constructor( * @param configuration An optional configuration used to tweak client behaviour. */ @JvmOverloads - constructor(haAddressPool: List<NetworkHostAndPort>, configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT) : this(haAddressPool.first(), configuration, null, null, null, haAddressPool) + constructor(haAddressPool: List<NetworkHostAndPort>, configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT) : this(haAddressPool.first(), configuration, null, null, haAddressPool) companion object { fun createWithSsl( @@ -285,16 +282,7 @@ class CordaRPCClient private constructor( sslConfiguration: ClientRpcSslOptions? = null, classLoader: ClassLoader? = null ): CordaRPCClient { - return CordaRPCClient(hostAndPort, configuration, sslConfiguration, null, classLoader) - } - - internal fun createWithInternalSslAndClassLoader( - hostAndPort: NetworkHostAndPort, - configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT, - sslConfiguration: SSLConfiguration?, - classLoader: ClassLoader? = null - ): CordaRPCClient { - return CordaRPCClient(hostAndPort, configuration, null, sslConfiguration, classLoader, internalConnection = true) + return CordaRPCClient(hostAndPort, configuration, sslConfiguration, classLoader) } internal fun createWithSslAndClassLoader( @@ -303,7 +291,7 @@ class CordaRPCClient private constructor( sslConfiguration: ClientRpcSslOptions? = null, classLoader: ClassLoader? = null ): CordaRPCClient { - return CordaRPCClient(haAddressPool.first(), configuration, sslConfiguration, null, classLoader, haAddressPool) + return CordaRPCClient(haAddressPool.first(), configuration, sslConfiguration, classLoader, haAddressPool) } } @@ -321,10 +309,7 @@ class CordaRPCClient private constructor( private fun getRpcClient(): RPCClient<CordaRPCOps> { return when { - // Node->RPC broker, mutually authenticated SSL. This is used when connecting the integrated shell - internalConnection == true -> RPCClient(hostAndPort, nodeSslConfiguration!!) - - // Client->RPC broker + // Client->RPC broker haAddressPool.isEmpty() -> RPCClient( rpcConnectorTcpTransport(hostAndPort, config = sslConfiguration), configuration, diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/CordaRPCClientUtils.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/CordaRPCClientUtils.kt index 5908bb3fbe..2f60b2514a 100644 --- a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/CordaRPCClientUtils.kt +++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/CordaRPCClientUtils.kt @@ -6,7 +6,6 @@ import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.pendingFlowsCount import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.messaging.ClientRpcSslOptions -import net.corda.nodeapi.internal.config.SSLConfiguration import rx.Observable /** Utility which exposes the internal Corda RPC constructor to other internal Corda components */ @@ -17,13 +16,6 @@ fun createCordaRPCClientWithSslAndClassLoader( classLoader: ClassLoader? = null ) = CordaRPCClient.createWithSslAndClassLoader(hostAndPort, configuration, sslConfiguration, classLoader) -fun createCordaRPCClientWithInternalSslAndClassLoader( - hostAndPort: NetworkHostAndPort, - configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT, - sslConfiguration: SSLConfiguration? = null, - classLoader: ClassLoader? = null -) = CordaRPCClient.createWithInternalSslAndClassLoader(hostAndPort, configuration, sslConfiguration, classLoader) - fun createCordaRPCClientWithSslAndClassLoader( haAddressPool: List<NetworkHostAndPort>, configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT, diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClient.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClient.kt index 1d7969caaa..1665aa0159 100644 --- a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClient.kt +++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClient.kt @@ -15,11 +15,11 @@ import net.corda.core.serialization.SerializationDefaults import net.corda.core.serialization.internal.nodeSerializationEnv import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.contextLogger -import net.corda.nodeapi.ArtemisTcpTransport.Companion.rpcConnectorTcpTransport -import net.corda.nodeapi.ArtemisTcpTransport.Companion.rpcConnectorTcpTransportsFromList -import net.corda.nodeapi.ArtemisTcpTransport.Companion.rpcInternalClientTcpTransport import net.corda.nodeapi.RPCApi -import net.corda.nodeapi.internal.config.SSLConfiguration +import net.corda.nodeapi.internal.InternalArtemisTcpTransport.Companion.rpcConnectorTcpTransport +import net.corda.nodeapi.internal.InternalArtemisTcpTransport.Companion.rpcConnectorTcpTransportsFromList +import net.corda.nodeapi.internal.InternalArtemisTcpTransport.Companion.rpcInternalClientTcpTransport +import net.corda.nodeapi.internal.config.SslConfiguration import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.api.core.TransportConfiguration import org.apache.activemq.artemis.api.core.client.ActiveMQClient @@ -43,7 +43,7 @@ class RPCClient<I : RPCOps>( constructor( hostAndPort: NetworkHostAndPort, - sslConfiguration: SSLConfiguration, + sslConfiguration: SslConfiguration, configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT, serializationContext: SerializationContext = SerializationDefaults.RPC_CLIENT_CONTEXT ) : this(rpcInternalClientTcpTransport(hostAndPort, sslConfiguration), configuration, serializationContext) diff --git a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt index 8de2892c33..6d11610d5f 100644 --- a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt @@ -4,22 +4,14 @@ package net.corda.core.internal import net.corda.core.DeleteForDJVM import net.corda.core.KeepForDJVM -import net.corda.core.cordapp.Cordapp -import net.corda.core.cordapp.CordappConfig -import net.corda.core.cordapp.CordappContext import net.corda.core.crypto.* import net.corda.core.flows.FlowLogic import net.corda.core.node.ServicesForResolution import net.corda.core.schemas.MappedSchema import net.corda.core.serialization.* -import net.corda.core.transactions.LedgerTransaction -import net.corda.core.transactions.SignedTransaction -import net.corda.core.transactions.TransactionBuilder -import net.corda.core.transactions.WireTransaction import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.UntrustworthyData import org.slf4j.Logger -import org.slf4j.MDC import rx.Observable import rx.Observer import rx.subjects.PublishSubject diff --git a/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt b/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt index 6213c7794a..29d261cb92 100644 --- a/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt +++ b/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt @@ -96,6 +96,12 @@ data class StateMachineTransactionMapping(val stateMachineRunId: StateMachineRun /** RPC operations that the node exposes to clients. */ interface CordaRPCOps : RPCOps { + /** + * Returns the RPC protocol version, which is the same the node's Platform Version. Exists since version 1 so guaranteed + * to be present. + */ + override val protocolVersion: Int get() = nodeInfo().platformVersion + /** Returns a list of currently in-progress state machine infos. */ fun stateMachinesSnapshot(): List<StateMachineInfo> diff --git a/experimental/flow-worker/src/integration-test/kotlin/net/corda/flowworker/FlowWorkerTest.kt b/experimental/flow-worker/src/integration-test/kotlin/net/corda/flowworker/FlowWorkerTest.kt index b0d5513ef8..8ac9d49e09 100644 --- a/experimental/flow-worker/src/integration-test/kotlin/net/corda/flowworker/FlowWorkerTest.kt +++ b/experimental/flow-worker/src/integration-test/kotlin/net/corda/flowworker/FlowWorkerTest.kt @@ -91,8 +91,8 @@ class FlowWorkerTest { // create test certificates config.configureWithDevSSLCertificate() - val trustRoot = config.loadTrustStore().getCertificate(X509Utilities.CORDA_ROOT_CA) - val nodeCa = config.loadNodeKeyStore().getCertificate(X509Utilities.CORDA_CLIENT_CA) + val trustRoot = config.p2pSslOptions.trustStore.get().query { getCertificate(X509Utilities.CORDA_ROOT_CA) } + val nodeCa = config.signingCertificateStore.get().query { getCertificate(X509Utilities.CORDA_CLIENT_CA) } val broker = createFlowWorkerBroker(config, networkParameters.maxMessageSize) val bridgeControlListener = createBridgeControlListener(config, networkParameters.maxMessageSize) @@ -146,8 +146,8 @@ class FlowWorkerTest { // create test certificates bankAConfig.configureWithDevSSLCertificate() - val bankATrustRoot = bankAConfig.loadTrustStore().getCertificate(X509Utilities.CORDA_ROOT_CA) - val bankANodeCa = bankAConfig.loadNodeKeyStore().getCertificate(X509Utilities.CORDA_CLIENT_CA) + val bankATrustRoot = bankAConfig.p2pSslOptions.trustStore.get().query { getCertificate(X509Utilities.CORDA_ROOT_CA) } + val bankANodeCa = bankAConfig.signingCertificateStore.get().query { getCertificate(X509Utilities.CORDA_CLIENT_CA) } val bankABroker = createFlowWorkerBroker(bankAConfig, networkParameters.maxMessageSize) val bankABridgeControlListener = createBridgeControlListener(bankAConfig, networkParameters.maxMessageSize) @@ -166,8 +166,8 @@ class FlowWorkerTest { // create test certificates bankBConfig.configureWithDevSSLCertificate() - val bankBTrustRoot = bankBConfig.loadTrustStore().getCertificate(X509Utilities.CORDA_ROOT_CA) - val bankBNodeCa = bankBConfig.loadNodeKeyStore().getCertificate(X509Utilities.CORDA_CLIENT_CA) + val bankBTrustRoot = bankBConfig.p2pSslOptions.trustStore.get().query { getCertificate(X509Utilities.CORDA_ROOT_CA) } + val bankBNodeCa = bankBConfig.signingCertificateStore.get().query { getCertificate(X509Utilities.CORDA_CLIENT_CA) } // NetworkParametersCopier(networkParameters).install(bankBConfig.baseDirectory) val bankBBroker = createFlowWorkerBroker(bankBConfig, networkParameters.maxMessageSize) @@ -234,13 +234,13 @@ class FlowWorkerTest { } private fun createBridgeControlListener(config: NodeConfiguration, maxMessageSize: Int): BridgeControlListener { - val bridgeControlListener = BridgeControlListener(config, config.messagingServerAddress!!, maxMessageSize) + val bridgeControlListener = BridgeControlListener(config.p2pSslOptions, config.messagingServerAddress!!, maxMessageSize) bridgeControlListener.start() return bridgeControlListener } private fun createArtemisClient(config: NodeConfiguration, queueAddress: String): Triple<ClientSession, ClientConsumer, ClientProducer> { - val artemisClient = ArtemisMessagingClient(config, config.messagingServerAddress!!, MAX_MESSAGE_SIZE) + val artemisClient = ArtemisMessagingClient(config.p2pSslOptions, config.messagingServerAddress!!, MAX_MESSAGE_SIZE) val started = artemisClient.start() started.session.createQueue(queueAddress, RoutingType.ANYCAST, queueAddress, true) return Triple(started.session, started.session.createConsumer(queueAddress), started.session.createProducer()) diff --git a/experimental/flow-worker/src/main/kotlin/net/corda/flowworker/FlowWorker.kt b/experimental/flow-worker/src/main/kotlin/net/corda/flowworker/FlowWorker.kt index c13de9727c..4454d99e09 100644 --- a/experimental/flow-worker/src/main/kotlin/net/corda/flowworker/FlowWorker.kt +++ b/experimental/flow-worker/src/main/kotlin/net/corda/flowworker/FlowWorker.kt @@ -43,7 +43,7 @@ class FlowWorker(flowWorkerId: String, private val flowWorkerServiceHub: FlowWor flowWorkerServiceHub.start() runOnStop += { flowWorkerServiceHub.stop() } - val flowWorkerMessagingClient = ArtemisMessagingClient(flowWorkerServiceHub.configuration, flowWorkerServiceHub.configuration.messagingServerAddress!!, flowWorkerServiceHub.networkParameters.maxMessageSize) + val flowWorkerMessagingClient = ArtemisMessagingClient(flowWorkerServiceHub.configuration.p2pSslOptions, flowWorkerServiceHub.configuration.messagingServerAddress!!, flowWorkerServiceHub.networkParameters.maxMessageSize) runOnStop += { flowWorkerMessagingClient.stop() } val session = flowWorkerMessagingClient.start().session diff --git a/experimental/rpc-worker/src/integration-test/kotlin/net/corda/rpcWorker/RpcFlowWorkerDriver.kt b/experimental/rpc-worker/src/integration-test/kotlin/net/corda/rpcWorker/RpcFlowWorkerDriver.kt index 8b0a913352..4e2b152b43 100644 --- a/experimental/rpc-worker/src/integration-test/kotlin/net/corda/rpcWorker/RpcFlowWorkerDriver.kt +++ b/experimental/rpc-worker/src/integration-test/kotlin/net/corda/rpcWorker/RpcFlowWorkerDriver.kt @@ -59,8 +59,8 @@ data class RpcFlowWorkerDriverDSL(private val driverDSL: DriverDSLImpl) : Intern fun startRpcFlowWorker(myLegalName: CordaX500Name, rpcUsers: List<net.corda.testing.node.User>, numberOfFlowWorkers: Int = 1): CordaFuture<RpcFlowWorkerHandle> { val (config, rpcWorkerConfig, flowWorkerConfigs) = generateConfigs(myLegalName, rpcUsers, numberOfFlowWorkers) - val trustRoot = rpcWorkerConfig.loadTrustStore().getCertificate(X509Utilities.CORDA_ROOT_CA) - val nodeCa = rpcWorkerConfig.loadNodeKeyStore().getCertificate(X509Utilities.CORDA_CLIENT_CA) + val trustRoot = rpcWorkerConfig.p2pSslOptions.trustStore.get().query { getCertificate(X509Utilities.CORDA_ROOT_CA) } + val nodeCa = rpcWorkerConfig.signingCertificateStore.get().query { getCertificate(X509Utilities.CORDA_CLIENT_CA) } val ourKeyPair = Crypto.generateKeyPair() val ourParty = Party(myLegalName, ourKeyPair.public) @@ -151,9 +151,9 @@ private fun createRpcWorkerBroker(config: NodeConfiguration, maxMessageSize: Int val rpcOptions = config.rpcOptions val securityManager = RPCSecurityManagerImpl(SecurityConfiguration.AuthService.fromUsers(config.rpcUsers)) val broker = if (rpcOptions.useSsl) { - ArtemisRpcBroker.withSsl(config, rpcOptions.address, rpcOptions.adminAddress, rpcOptions.sslConfig!!, securityManager, maxMessageSize, false, config.baseDirectory / "artemis", false) + ArtemisRpcBroker.withSsl(config.p2pSslOptions, rpcOptions.address, rpcOptions.adminAddress, rpcOptions.sslConfig!!, securityManager, maxMessageSize, false, config.baseDirectory / "artemis", false) } else { - ArtemisRpcBroker.withoutSsl(config, rpcOptions.address, rpcOptions.adminAddress, securityManager, maxMessageSize, false, config.baseDirectory / "artemis", false) + ArtemisRpcBroker.withoutSsl(config.p2pSslOptions, rpcOptions.address, rpcOptions.adminAddress, securityManager, maxMessageSize, false, config.baseDirectory / "artemis", false) } broker.start() return broker @@ -180,7 +180,7 @@ private fun createFlowWorker(config: NodeConfiguration, myInfo: NodeInfo, networ } private fun createBridgeControlListener(config: NodeConfiguration, maxMessageSize: Int): BridgeControlListener { - val bridgeControlListener = BridgeControlListener(config, config.messagingServerAddress!!, maxMessageSize) + val bridgeControlListener = BridgeControlListener(config.p2pSslOptions, config.messagingServerAddress!!, maxMessageSize) bridgeControlListener.start() return bridgeControlListener } \ No newline at end of file diff --git a/experimental/rpc-worker/src/main/kotlin/net/corda/rpcWorker/CordaRpcWorkerOps.kt b/experimental/rpc-worker/src/main/kotlin/net/corda/rpcWorker/CordaRpcWorkerOps.kt index 074a933db5..21628ed90f 100644 --- a/experimental/rpc-worker/src/main/kotlin/net/corda/rpcWorker/CordaRpcWorkerOps.kt +++ b/experimental/rpc-worker/src/main/kotlin/net/corda/rpcWorker/CordaRpcWorkerOps.kt @@ -56,14 +56,13 @@ class CordaRpcWorkerOps( const val RPC_WORKER_QUEUE_ADDRESS_PREFIX = "${ArtemisMessagingComponent.INTERNAL_PREFIX}rpc.worker." } - override val protocolVersion: Int = 1000 private val flowWorkerQueueAddress = "${FlowWorker.FLOW_WORKER_QUEUE_ADDRESS_PREFIX}${services.myInfo.legalIdentities[0].owningKey.toStringShort()}" private val rpcWorkerQueueAddress = "$RPC_WORKER_QUEUE_ADDRESS_PREFIX${services.myInfo.legalIdentities[0].owningKey.toStringShort()}" private val rpcWorkerId = UUID.randomUUID().toString() private val rpcWorkerQueueName = "$rpcWorkerQueueAddress.$rpcWorkerId" - private val artemisClient = ArtemisMessagingClient(services.configuration, services.configuration.messagingServerAddress!!, services.networkParameters.maxMessageSize) + private val artemisClient = ArtemisMessagingClient(services.configuration.p2pSslOptions, services.configuration.messagingServerAddress!!, services.networkParameters.maxMessageSize) private lateinit var session: ClientSession private lateinit var producer: ClientProducer diff --git a/experimental/rpc-worker/src/main/kotlin/net/corda/rpcWorker/RpcWorker.kt b/experimental/rpc-worker/src/main/kotlin/net/corda/rpcWorker/RpcWorker.kt index bdd71d0acc..ff2b8b1430 100644 --- a/experimental/rpc-worker/src/main/kotlin/net/corda/rpcWorker/RpcWorker.kt +++ b/experimental/rpc-worker/src/main/kotlin/net/corda/rpcWorker/RpcWorker.kt @@ -77,8 +77,8 @@ class Main : Runnable { val ourKeyPair = getIdentity() val myInfo = getNodeInfo() - val trustRoot = rpcWorkerConfig.loadTrustStore().getCertificate(X509Utilities.CORDA_ROOT_CA) - val nodeCa = rpcWorkerConfig.loadNodeKeyStore().getCertificate(X509Utilities.CORDA_CLIENT_CA) + val trustRoot = rpcWorkerConfig.p2pSslOptions.trustStore.get().query { getCertificate(X509Utilities.CORDA_ROOT_CA) } + val nodeCa = rpcWorkerConfig.signingCertificateStore.get().query { getCertificate(X509Utilities.CORDA_CLIENT_CA) } val signedNetworkParameters = NetworkParametersReader(trustRoot, null, rpcWorkerConfig.baseDirectory).read() val rpcWorkerBroker = createRpcWorkerBroker(rpcWorkerConfig, signedNetworkParameters.networkParameters.maxMessageSize) @@ -101,9 +101,9 @@ class Main : Runnable { val rpcOptions = config.rpcOptions val securityManager = RPCSecurityManagerImpl(SecurityConfiguration.AuthService.fromUsers(config.rpcUsers)) val broker = if (rpcOptions.useSsl) { - ArtemisRpcBroker.withSsl(config, rpcOptions.address, rpcOptions.adminAddress, rpcOptions.sslConfig!!, securityManager, maxMessageSize, false, config.baseDirectory / "artemis", false) + ArtemisRpcBroker.withSsl(config.p2pSslOptions, rpcOptions.address, rpcOptions.adminAddress, rpcOptions.sslConfig!!, securityManager, maxMessageSize, false, config.baseDirectory / "artemis", false) } else { - ArtemisRpcBroker.withoutSsl(config, rpcOptions.address, rpcOptions.adminAddress, securityManager, maxMessageSize, false, config.baseDirectory / "artemis", false) + ArtemisRpcBroker.withoutSsl(config.p2pSslOptions, rpcOptions.address, rpcOptions.adminAddress, securityManager, maxMessageSize, false, config.baseDirectory / "artemis", false) } broker.start() return broker @@ -126,9 +126,9 @@ class RpcWorker(private val rpcWorkerServiceHub: RpcWorkerServiceHub, private va rpcThreadPoolSize = rpcWorkerServiceHub.configuration.enterpriseConfiguration.tuning.rpcThreadPoolSize ) val securityManager = RPCSecurityManagerImpl(SecurityConfiguration.AuthService.fromUsers(rpcWorkerServiceHub.configuration.rpcUsers)) - val nodeName = CordaX500Name.build(rpcWorkerServiceHub.configuration.loadSslKeyStore().getCertificate(X509Utilities.CORDA_CLIENT_TLS).subjectX500Principal) + val nodeName = CordaX500Name.build(rpcWorkerServiceHub.configuration.p2pSslOptions.keyStore.get().query { getCertificate(X509Utilities.CORDA_CLIENT_TLS).subjectX500Principal }) - val internalRpcMessagingClient = InternalRPCMessagingClient(rpcWorkerServiceHub.configuration, rpcWorkerServiceHub.configuration.rpcOptions.adminAddress, Node.MAX_RPC_MESSAGE_SIZE, nodeName, rpcServerConfiguration) + val internalRpcMessagingClient = InternalRPCMessagingClient(rpcWorkerServiceHub.configuration.p2pSslOptions, rpcWorkerServiceHub.configuration.rpcOptions.adminAddress, Node.MAX_RPC_MESSAGE_SIZE, nodeName, rpcServerConfiguration) internalRpcMessagingClient.init(rpcWorkerServiceHub.rpcOps, securityManager) internalRpcMessagingClient.start(serverControl) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/ArtemisTcpTransport.kt b/node-api/src/main/kotlin/net/corda/nodeapi/ArtemisTcpTransport.kt index 18fbad6ff2..210f04586a 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/ArtemisTcpTransport.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/ArtemisTcpTransport.kt @@ -1,12 +1,16 @@ package net.corda.nodeapi import net.corda.core.messaging.ClientRpcSslOptions -import net.corda.core.serialization.internal.nodeSerializationEnv import net.corda.core.utilities.NetworkHostAndPort +import net.corda.nodeapi.internal.InternalArtemisTcpTransport +import net.corda.nodeapi.internal.InternalArtemisTcpTransport.Companion.acceptorFactoryClassName +import net.corda.nodeapi.internal.InternalArtemisTcpTransport.Companion.connectorFactoryClassName +import net.corda.nodeapi.internal.InternalArtemisTcpTransport.Companion.defaultArtemisOptions +import net.corda.nodeapi.internal.InternalArtemisTcpTransport.Companion.defaultSSLOptions import net.corda.nodeapi.internal.config.SSLConfiguration +import net.corda.nodeapi.internal.config.SslConfiguration import net.corda.nodeapi.internal.requireOnDefaultFileSystem import org.apache.activemq.artemis.api.core.TransportConfiguration -import org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnectorFactory import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants import java.nio.file.Path @@ -38,26 +42,6 @@ class ArtemisTcpTransport { /** Supported TLS versions, currently TLSv1.2 only. */ val TLS_VERSIONS = listOf("TLSv1.2") - private fun defaultArtemisOptions(hostAndPort: NetworkHostAndPort) = mapOf( - // Basic TCP target details. - TransportConstants.HOST_PROP_NAME to hostAndPort.host, - TransportConstants.PORT_PROP_NAME to hostAndPort.port, - - // Turn on AMQP support, which needs the protocol jar on the classpath. - // Unfortunately we cannot disable core protocol as artemis only uses AMQP for interop. - // It does not use AMQP messages for its own messages e.g. topology and heartbeats. - // TODO further investigate how to ensure we use a well defined wire level protocol for Node to Node communications. - TransportConstants.PROTOCOLS_PROP_NAME to "CORE,AMQP", - TransportConstants.USE_GLOBAL_WORKER_POOL_PROP_NAME to (nodeSerializationEnv != null), - TransportConstants.REMOTING_THREADS_PROPNAME to (if (nodeSerializationEnv != null) -1 else 1), - // turn off direct delivery in Artemis - this is latency optimisation that can lead to - //hick-ups under high load (CORDA-1336) - TransportConstants.DIRECT_DELIVER to false) - - private val defaultSSLOptions = mapOf( - TransportConstants.ENABLED_CIPHER_SUITES_PROP_NAME to CIPHER_SUITES.joinToString(","), - TransportConstants.ENABLED_PROTOCOLS_PROP_NAME to TLS_VERSIONS.joinToString(",")) - private fun SSLConfiguration.toTransportOptions() = mapOf( TransportConstants.SSL_ENABLED_PROP_NAME to true, TransportConstants.KEYSTORE_PROVIDER_PROP_NAME to "JKS", @@ -68,22 +52,6 @@ class ArtemisTcpTransport { TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME to trustStorePassword, TransportConstants.NEED_CLIENT_AUTH_PROP_NAME to true) - private fun ClientRpcSslOptions.toTransportOptions() = mapOf( - TransportConstants.SSL_ENABLED_PROP_NAME to true, - TransportConstants.TRUSTSTORE_PROVIDER_PROP_NAME to trustStoreProvider, - TransportConstants.TRUSTSTORE_PATH_PROP_NAME to trustStorePath, - TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME to trustStorePassword) - - private fun BrokerRpcSslOptions.toTransportOptions() = mapOf( - TransportConstants.SSL_ENABLED_PROP_NAME to true, - TransportConstants.KEYSTORE_PROVIDER_PROP_NAME to "JKS", - TransportConstants.KEYSTORE_PATH_PROP_NAME to keyStorePath, - TransportConstants.KEYSTORE_PASSWORD_PROP_NAME to keyStorePassword, - TransportConstants.NEED_CLIENT_AUTH_PROP_NAME to false) - - private val acceptorFactoryClassName = "org.apache.activemq.artemis.core.remoting.impl.netty.NettyAcceptorFactory" - private val connectorFactoryClassName = NettyConnectorFactory::class.java.name - fun p2pAcceptorTcpTransport(hostAndPort: NetworkHostAndPort, config: SSLConfiguration?, enableSSL: Boolean = true): TransportConfiguration { val options = defaultArtemisOptions(hostAndPort).toMutableMap() @@ -110,27 +78,13 @@ class ArtemisTcpTransport { /** [TransportConfiguration] for RPC TCP communication - server side. */ fun rpcAcceptorTcpTransport(hostAndPort: NetworkHostAndPort, config: BrokerRpcSslOptions?, enableSSL: Boolean = true): TransportConfiguration { - val options = defaultArtemisOptions(hostAndPort).toMutableMap() - - if (config != null && enableSSL) { - config.keyStorePath.requireOnDefaultFileSystem() - options.putAll(config.toTransportOptions()) - options.putAll(defaultSSLOptions) - } - return TransportConfiguration(acceptorFactoryClassName, options) + return InternalArtemisTcpTransport.rpcAcceptorTcpTransport(hostAndPort, config, enableSSL) } /** [TransportConfiguration] for RPC TCP communication * This is the Transport that connects the client JVM to the broker. */ fun rpcConnectorTcpTransport(hostAndPort: NetworkHostAndPort, config: ClientRpcSslOptions?, enableSSL: Boolean = true): TransportConfiguration { - val options = defaultArtemisOptions(hostAndPort).toMutableMap() - - if (config != null && enableSSL) { - config.trustStorePath.requireOnDefaultFileSystem() - options.putAll(config.toTransportOptions()) - options.putAll(defaultSSLOptions) - } - return TransportConfiguration(connectorFactoryClassName, options) + return InternalArtemisTcpTransport.rpcConnectorTcpTransport(hostAndPort, config, enableSSL) } /** Create as list of [TransportConfiguration]. **/ @@ -138,12 +92,12 @@ class ArtemisTcpTransport { rpcConnectorTcpTransport(it, config, enableSSL) } - fun rpcInternalClientTcpTransport(hostAndPort: NetworkHostAndPort, config: SSLConfiguration): TransportConfiguration { - return TransportConfiguration(connectorFactoryClassName, defaultArtemisOptions(hostAndPort) + defaultSSLOptions + config.toTransportOptions()) + fun rpcInternalClientTcpTransport(hostAndPort: NetworkHostAndPort, config: SslConfiguration): TransportConfiguration { + return InternalArtemisTcpTransport.rpcInternalClientTcpTransport(hostAndPort, config) } - fun rpcInternalAcceptorTcpTransport(hostAndPort: NetworkHostAndPort, config: SSLConfiguration): TransportConfiguration { - return TransportConfiguration(acceptorFactoryClassName, defaultArtemisOptions(hostAndPort) + defaultSSLOptions + config.toTransportOptions()) + fun rpcInternalAcceptorTcpTransport(hostAndPort: NetworkHostAndPort, config: SslConfiguration): TransportConfiguration { + return InternalArtemisTcpTransport.rpcInternalAcceptorTcpTransport(hostAndPort, config) } } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/ArtemisMessagingClient.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/ArtemisMessagingClient.kt index f814f18795..ae1a9defca 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/ArtemisMessagingClient.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/ArtemisMessagingClient.kt @@ -3,9 +3,9 @@ package net.corda.nodeapi.internal import net.corda.core.serialization.internal.nodeSerializationEnv import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.loggerFor -import net.corda.nodeapi.ArtemisTcpTransport import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_P2P_USER -import net.corda.nodeapi.internal.config.SSLConfiguration +import net.corda.nodeapi.internal.config.MutualSslConfiguration +import org.apache.activemq.artemis.api.core.client.ActiveMQClient import org.apache.activemq.artemis.api.core.client.* import org.apache.activemq.artemis.api.core.client.ActiveMQClient.DEFAULT_ACK_BATCH_SIZE @@ -15,14 +15,12 @@ interface ArtemisSessionProvider { val started: ArtemisMessagingClient.Started? } -class ArtemisMessagingClient( - private val config: SSLConfiguration, - private val serverAddress: NetworkHostAndPort, - private val maxMessageSize: Int, - private val autoCommitSends: Boolean = true, - private val autoCommitAcks: Boolean = true, - private val confirmationWindowSize: Int = -1 -) : ArtemisSessionProvider { +class ArtemisMessagingClient(private val config: MutualSslConfiguration, + private val serverAddress: NetworkHostAndPort, + private val maxMessageSize: Int, + private val autoCommitSends: Boolean = true, + private val autoCommitAcks: Boolean = true, + private val confirmationWindowSize: Int = -1) : ArtemisSessionProvider { companion object { private val log = loggerFor<ArtemisMessagingClient>() } @@ -36,7 +34,7 @@ class ArtemisMessagingClient( check(started == null) { "start can't be called twice" } log.info("Connecting to message broker: $serverAddress") // TODO Add broker CN to config for host verification in case the embedded broker isn't used - val tcpTransport = ArtemisTcpTransport.p2pConnectorTcpTransport(serverAddress, config) + val tcpTransport = InternalArtemisTcpTransport.p2pConnectorTcpTransport(serverAddress, config) val locator = ActiveMQClient.createServerLocatorWithoutHA(tcpTransport).apply { // Never time out on our loopback Artemis connections. If we switch back to using the InVM transport this // would be the default and the two lines below can be deleted. diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/DevIdentityGenerator.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/DevIdentityGenerator.kt index 080bf680c4..ab840ed387 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/DevIdentityGenerator.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/DevIdentityGenerator.kt @@ -7,7 +7,8 @@ import net.corda.core.identity.Party import net.corda.core.internal.createDirectories import net.corda.core.internal.div import net.corda.core.utilities.trace -import net.corda.nodeapi.internal.config.NodeSSLConfiguration +import net.corda.nodeapi.internal.config.FileBasedCertificateStoreSupplier +import net.corda.nodeapi.internal.config.SslConfiguration import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509KeyStore import net.corda.nodeapi.internal.crypto.X509Utilities @@ -31,15 +32,15 @@ object DevIdentityGenerator { /** Install a node key store for the given node directory using the given legal name. */ fun installKeyStoreWithNodeIdentity(nodeDir: Path, legalName: CordaX500Name): Party { - val nodeSslConfig = object : NodeSSLConfiguration { - override val baseDirectory = nodeDir - override val keyStorePassword: String = "cordacadevpass" - override val trustStorePassword get() = throw NotImplementedError("Not expected to be called") - override val crlCheckSoftFail: Boolean = true - } + val certificatesDirectory = nodeDir / "certificates" + val signingCertStore = FileBasedCertificateStoreSupplier(certificatesDirectory / "nodekeystore.jks", "cordacadevpass") + val p2pKeyStore = FileBasedCertificateStoreSupplier(certificatesDirectory / "sslkeystore.jks", "cordacadevpass") + val p2pTrustStore = FileBasedCertificateStoreSupplier(certificatesDirectory / "truststore.jks", "trustpass") + val p2pSslConfig = SslConfiguration.mutual(p2pKeyStore, p2pTrustStore) - nodeSslConfig.certificatesDirectory.createDirectories() - val (nodeKeyStore) = nodeSslConfig.createDevKeyStores(legalName) + certificatesDirectory.createDirectories() + val nodeKeyStore = signingCertStore.get(true).also { it.registerDevSigningCertificates(legalName) } + p2pSslConfig.keyStore.get(true).also { it.registerDevP2pCertificates(legalName) } val identity = nodeKeyStore.storeLegalIdentity("$NODE_IDENTITY_ALIAS_PREFIX-private-key") return identity.party diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/InternalArtemisTcpTransport.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/InternalArtemisTcpTransport.kt new file mode 100644 index 0000000000..85513c9ad1 --- /dev/null +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/InternalArtemisTcpTransport.kt @@ -0,0 +1,166 @@ +package net.corda.nodeapi.internal + +import net.corda.core.messaging.ClientRpcSslOptions +import net.corda.core.serialization.internal.nodeSerializationEnv +import net.corda.core.utilities.NetworkHostAndPort +import net.corda.nodeapi.BrokerRpcSslOptions +import net.corda.nodeapi.internal.config.CertificateStore +import net.corda.nodeapi.internal.config.FileBasedCertificateStoreSupplier +import net.corda.nodeapi.internal.config.SslConfiguration +import net.corda.nodeapi.internal.config.MutualSslConfiguration +import org.apache.activemq.artemis.api.core.TransportConfiguration +import org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnectorFactory +import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants +import java.nio.file.Path + +// This avoids internal types from leaking in the public API. The "external" ArtemisTcpTransport delegates to this internal one. +class InternalArtemisTcpTransport { + companion object { + val CIPHER_SUITES = listOf( + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256" + ) + + val TLS_VERSIONS = listOf("TLSv1.2") + + internal fun defaultArtemisOptions(hostAndPort: NetworkHostAndPort) = mapOf( + // Basic TCP target details. + TransportConstants.HOST_PROP_NAME to hostAndPort.host, + TransportConstants.PORT_PROP_NAME to hostAndPort.port, + + // Turn on AMQP support, which needs the protocol jar on the classpath. + // Unfortunately we cannot disable core protocol as artemis only uses AMQP for interop. + // It does not use AMQP messages for its own messages e.g. topology and heartbeats. + // TODO further investigate how to ensure we use a well defined wire level protocol for Node to Node communications. + TransportConstants.PROTOCOLS_PROP_NAME to "CORE,AMQP", + TransportConstants.USE_GLOBAL_WORKER_POOL_PROP_NAME to (nodeSerializationEnv != null), + TransportConstants.REMOTING_THREADS_PROPNAME to (if (nodeSerializationEnv != null) -1 else 1), + // turn off direct delivery in Artemis - this is latency optimisation that can lead to + //hick-ups under high load (CORDA-1336) + TransportConstants.DIRECT_DELIVER to false) + + internal val defaultSSLOptions = mapOf( + TransportConstants.ENABLED_CIPHER_SUITES_PROP_NAME to CIPHER_SUITES.joinToString(","), + TransportConstants.ENABLED_PROTOCOLS_PROP_NAME to TLS_VERSIONS.joinToString(",")) + + private fun SslConfiguration.toTransportOptions(): Map<String, Any> { + + val options = mutableMapOf<String, Any>() + (keyStore to trustStore).addToTransportOptions(options) + return options + } + + private fun Pair<FileBasedCertificateStoreSupplier?, FileBasedCertificateStoreSupplier?>.addToTransportOptions(options: MutableMap<String, Any>) { + + val keyStore = first + val trustStore = second + keyStore?.let { + with (it) { + path.requireOnDefaultFileSystem() + options.putAll(get().toKeyStoreTransportOptions(path)) + } + } + trustStore?.let { + with (it) { + path.requireOnDefaultFileSystem() + options.putAll(get().toTrustStoreTransportOptions(path)) + } + } + } + + private fun CertificateStore.toKeyStoreTransportOptions(path: Path) = mapOf( + TransportConstants.SSL_ENABLED_PROP_NAME to true, + TransportConstants.KEYSTORE_PROVIDER_PROP_NAME to "JKS", + TransportConstants.KEYSTORE_PATH_PROP_NAME to path, + TransportConstants.KEYSTORE_PASSWORD_PROP_NAME to password, + TransportConstants.NEED_CLIENT_AUTH_PROP_NAME to true) + + private fun CertificateStore.toTrustStoreTransportOptions(path: Path) = mapOf( + TransportConstants.SSL_ENABLED_PROP_NAME to true, + TransportConstants.TRUSTSTORE_PROVIDER_PROP_NAME to "JKS", + TransportConstants.TRUSTSTORE_PATH_PROP_NAME to path, + TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME to password, + TransportConstants.NEED_CLIENT_AUTH_PROP_NAME to true) + + private fun ClientRpcSslOptions.toTransportOptions() = mapOf( + TransportConstants.SSL_ENABLED_PROP_NAME to true, + TransportConstants.TRUSTSTORE_PROVIDER_PROP_NAME to trustStoreProvider, + TransportConstants.TRUSTSTORE_PATH_PROP_NAME to trustStorePath, + TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME to trustStorePassword) + + private fun BrokerRpcSslOptions.toTransportOptions() = mapOf( + TransportConstants.SSL_ENABLED_PROP_NAME to true, + TransportConstants.KEYSTORE_PROVIDER_PROP_NAME to "JKS", + TransportConstants.KEYSTORE_PATH_PROP_NAME to keyStorePath, + TransportConstants.KEYSTORE_PASSWORD_PROP_NAME to keyStorePassword, + TransportConstants.NEED_CLIENT_AUTH_PROP_NAME to false) + + internal val acceptorFactoryClassName = "org.apache.activemq.artemis.core.remoting.impl.netty.NettyAcceptorFactory" + internal val connectorFactoryClassName = NettyConnectorFactory::class.java.name + + fun p2pAcceptorTcpTransport(hostAndPort: NetworkHostAndPort, config: MutualSslConfiguration?, enableSSL: Boolean = true): TransportConfiguration { + + return p2pAcceptorTcpTransport(hostAndPort, config?.keyStore, config?.trustStore, enableSSL = enableSSL) + } + + fun p2pConnectorTcpTransport(hostAndPort: NetworkHostAndPort, config: MutualSslConfiguration?, enableSSL: Boolean = true): TransportConfiguration { + + return p2pConnectorTcpTransport(hostAndPort, config?.keyStore, config?.trustStore, enableSSL = enableSSL) + } + + fun p2pAcceptorTcpTransport(hostAndPort: NetworkHostAndPort, keyStore: FileBasedCertificateStoreSupplier?, trustStore: FileBasedCertificateStoreSupplier?, enableSSL: Boolean = true): TransportConfiguration { + + val options = defaultArtemisOptions(hostAndPort).toMutableMap() + if (enableSSL) { + options.putAll(defaultSSLOptions) + (keyStore to trustStore).addToTransportOptions(options) + } + return TransportConfiguration(acceptorFactoryClassName, options) + } + + fun p2pConnectorTcpTransport(hostAndPort: NetworkHostAndPort, keyStore: FileBasedCertificateStoreSupplier?, trustStore: FileBasedCertificateStoreSupplier?, enableSSL: Boolean = true): TransportConfiguration { + + val options = defaultArtemisOptions(hostAndPort).toMutableMap() + if (enableSSL) { + options.putAll(defaultSSLOptions) + (keyStore to trustStore).addToTransportOptions(options) + } + return TransportConfiguration(connectorFactoryClassName, options) + } + + fun rpcAcceptorTcpTransport(hostAndPort: NetworkHostAndPort, config: BrokerRpcSslOptions?, enableSSL: Boolean = true): TransportConfiguration { + val options = defaultArtemisOptions(hostAndPort).toMutableMap() + + if (config != null && enableSSL) { + config.keyStorePath.requireOnDefaultFileSystem() + options.putAll(config.toTransportOptions()) + options.putAll(defaultSSLOptions) + } + return TransportConfiguration(acceptorFactoryClassName, options) + } + + fun rpcConnectorTcpTransport(hostAndPort: NetworkHostAndPort, config: ClientRpcSslOptions?, enableSSL: Boolean = true): TransportConfiguration { + val options = defaultArtemisOptions(hostAndPort).toMutableMap() + + if (config != null && enableSSL) { + config.trustStorePath.requireOnDefaultFileSystem() + options.putAll(config.toTransportOptions()) + options.putAll(defaultSSLOptions) + } + return TransportConfiguration(connectorFactoryClassName, options) + } + + fun rpcConnectorTcpTransportsFromList(hostAndPortList: List<NetworkHostAndPort>, config: ClientRpcSslOptions?, enableSSL: Boolean = true): List<TransportConfiguration> = hostAndPortList.map { + rpcConnectorTcpTransport(it, config, enableSSL) + } + + fun rpcInternalClientTcpTransport(hostAndPort: NetworkHostAndPort, config: SslConfiguration): TransportConfiguration { + return TransportConfiguration(connectorFactoryClassName, defaultArtemisOptions(hostAndPort) + defaultSSLOptions + config.toTransportOptions()) + } + + fun rpcInternalAcceptorTcpTransport(hostAndPort: NetworkHostAndPort, config: SslConfiguration): TransportConfiguration { + return TransportConfiguration(acceptorFactoryClassName, defaultArtemisOptions(hostAndPort) + defaultSSLOptions + config.toTransportOptions()) + } + } +} \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt index 6d2ca5b271..d8a856afa5 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt @@ -5,7 +5,7 @@ import net.corda.core.crypto.Crypto.generateKeyPair import net.corda.core.identity.CordaX500Name import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.toX500Name -import net.corda.nodeapi.internal.config.SSLConfiguration +import net.corda.nodeapi.internal.config.CertificateStore import net.corda.nodeapi.internal.crypto.* import org.bouncycastle.asn1.x509.GeneralName import org.bouncycastle.asn1.x509.GeneralSubtree @@ -20,48 +20,43 @@ import javax.security.auth.x500.X500Principal * Create the node and SSL key stores needed by a node. The node key store will be populated with a node CA cert (using * the given legal name), and the SSL key store will store the TLS cert which is a sub-cert of the node CA. */ -fun SSLConfiguration.createDevKeyStores(legalName: CordaX500Name, - rootCert: X509Certificate = DEV_ROOT_CA.certificate, - intermediateCa: CertificateAndKeyPair = DEV_INTERMEDIATE_CA): Pair<X509KeyStore, X509KeyStore> { - val (nodeCaCert, nodeCaKeyPair) = createDevNodeCa(intermediateCa, legalName) - val nodeKeyStore = loadNodeKeyStore(createNew = true) - nodeKeyStore.update { - setPrivateKey( - X509Utilities.CORDA_CLIENT_CA, - nodeCaKeyPair.private, - listOf(nodeCaCert, intermediateCa.certificate, rootCert)) +fun CertificateStore.registerDevSigningCertificates(legalName: CordaX500Name, + rootCert: X509Certificate = DEV_ROOT_CA.certificate, + intermediateCa: CertificateAndKeyPair = DEV_INTERMEDIATE_CA, + devNodeCa: CertificateAndKeyPair = createDevNodeCa(intermediateCa, legalName)) { + + update { + setPrivateKey(X509Utilities.CORDA_CLIENT_CA, devNodeCa.keyPair.private, listOf(devNodeCa.certificate, intermediateCa.certificate, rootCert)) } - - val sslKeyStore = loadSslKeyStore(createNew = true) - sslKeyStore.update { - val tlsKeyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, legalName.x500Principal, tlsKeyPair.public) - setPrivateKey( - X509Utilities.CORDA_CLIENT_TLS, - tlsKeyPair.private, - listOf(tlsCert, nodeCaCert, intermediateCa.certificate, rootCert)) - } - - return Pair(nodeKeyStore, sslKeyStore) } -fun X509KeyStore.storeLegalIdentity(alias: String, keyPair: KeyPair = Crypto.generateKeyPair()): PartyAndCertificate { - val nodeCaCertPath = getCertificateChain(X509Utilities.CORDA_CLIENT_CA) - // Assume key password = store password. - val nodeCaCertAndKeyPair = getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA) - // Create new keys and store in keystore. - val identityCert = X509Utilities.createCertificate( - CertificateType.LEGAL_IDENTITY, - nodeCaCertAndKeyPair.certificate, - nodeCaCertAndKeyPair.keyPair, - nodeCaCertAndKeyPair.certificate.subjectX500Principal, - keyPair.public) - // TODO: X509Utilities.validateCertificateChain() - // Assume key password = store password. - val identityCertPath = listOf(identityCert) + nodeCaCertPath - setPrivateKey(alias, keyPair.private, identityCertPath) - save() +fun CertificateStore.registerDevP2pCertificates(legalName: CordaX500Name, + rootCert: X509Certificate = DEV_ROOT_CA.certificate, + intermediateCa: CertificateAndKeyPair = DEV_INTERMEDIATE_CA, + devNodeCa: CertificateAndKeyPair = createDevNodeCa(intermediateCa, legalName)) { + + update { + val tlsKeyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) + val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, devNodeCa.certificate, devNodeCa.keyPair, legalName.x500Principal, tlsKeyPair.public) + setPrivateKey(X509Utilities.CORDA_CLIENT_TLS, tlsKeyPair.private, listOf(tlsCert, devNodeCa.certificate, intermediateCa.certificate, rootCert)) + } +} + +fun CertificateStore.storeLegalIdentity(alias: String, keyPair: KeyPair = Crypto.generateKeyPair()): PartyAndCertificate { + val identityCertPath = query { + val nodeCaCertPath = getCertificateChain(X509Utilities.CORDA_CLIENT_CA) + // Assume key password = store password. + val nodeCaCertAndKeyPair = getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA) + // Create new keys and store in keystore. + val identityCert = X509Utilities.createCertificate(CertificateType.LEGAL_IDENTITY, nodeCaCertAndKeyPair.certificate, nodeCaCertAndKeyPair.keyPair, nodeCaCertAndKeyPair.certificate.subjectX500Principal, keyPair.public) + // TODO: X509Utilities.validateCertificateChain() + // Assume key password = store password. + listOf(identityCert) + nodeCaCertPath + } + update { + setPrivateKey(alias, keyPair.private, identityCertPath) + } return PartyAndCertificate(X509Utilities.buildCertPath(identityCertPath)) } @@ -105,8 +100,10 @@ const val DEV_CA_TRUST_STORE_PASS: String = "trustpass" // We need a class so that we can get hold of the class loader internal object DevCaHelper { fun loadDevCa(alias: String): CertificateAndKeyPair { - // TODO: Should be identity scheme - val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/$DEV_CA_KEY_STORE_FILE"), DEV_CA_KEY_STORE_PASS) - return caKeyStore.getCertificateAndKeyPair(alias, DEV_CA_PRIVATE_KEY_PASS) + return loadDevCaKeyStore().query { getCertificateAndKeyPair(alias, DEV_CA_PRIVATE_KEY_PASS) } } } + +fun loadDevCaKeyStore(classLoader: ClassLoader = DevCaHelper::class.java.classLoader): CertificateStore = CertificateStore.fromResource("certificates/$DEV_CA_KEY_STORE_FILE", DEV_CA_KEY_STORE_PASS, classLoader) + +fun loadDevCaTrustStore(classLoader: ClassLoader = DevCaHelper::class.java.classLoader): CertificateStore = CertificateStore.fromResource("certificates/$DEV_CA_TRUST_STORE_FILE", DEV_CA_TRUST_STORE_PASS, classLoader) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/bridging/AMQPBridgeManager.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/bridging/AMQPBridgeManager.kt index cf46ee8b6a..26093597ab 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/bridging/AMQPBridgeManager.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/bridging/AMQPBridgeManager.kt @@ -14,7 +14,8 @@ import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2PMessagi import net.corda.nodeapi.internal.ArtemisMessagingComponent.RemoteInboxAddress.Companion.translateLocalQueueToInboxAddress import net.corda.nodeapi.internal.ArtemisSessionProvider import net.corda.nodeapi.internal.bridging.AMQPBridgeManager.AMQPBridge.Companion.getBridgeName -import net.corda.nodeapi.internal.config.NodeSSLConfiguration +import net.corda.nodeapi.internal.config.CertificateStore +import net.corda.nodeapi.internal.config.MutualSslConfiguration import net.corda.nodeapi.internal.protonwrapper.messages.MessageStatus import net.corda.nodeapi.internal.protonwrapper.netty.AMQPClient import net.corda.nodeapi.internal.protonwrapper.netty.AMQPConfiguration @@ -26,7 +27,6 @@ import org.apache.activemq.artemis.api.core.client.ClientMessage import org.apache.activemq.artemis.api.core.client.ClientSession import org.slf4j.MDC import rx.Subscription -import java.security.KeyStore import java.util.concurrent.locks.ReentrantLock import kotlin.concurrent.withLock @@ -38,30 +38,23 @@ import kotlin.concurrent.withLock * The Netty thread pool used by the AMQPBridges is also shared and managed by the AMQPBridgeManager. */ @VisibleForTesting -class AMQPBridgeManager(config: NodeSSLConfiguration, socksProxyConfig: SocksProxyConfig? = null, - maxMessageSize: Int, private val artemisMessageClientFactory: () -> ArtemisSessionProvider) : BridgeManager { +class AMQPBridgeManager(config: MutualSslConfiguration, socksProxyConfig: SocksProxyConfig? = null, maxMessageSize: Int, private val artemisMessageClientFactory: () -> ArtemisSessionProvider) : BridgeManager { private val lock = ReentrantLock() private val bridgeNameToBridgeMap = mutableMapOf<String, AMQPBridge>() - private class AMQPConfigurationImpl private constructor(override val keyStore: KeyStore, - override val keyStorePrivateKeyPassword: CharArray, - override val trustStore: KeyStore, + private class AMQPConfigurationImpl private constructor(override val keyStore: CertificateStore, + override val trustStore: CertificateStore, override val socksProxyConfig: SocksProxyConfig?, override val maxMessageSize: Int) : AMQPConfiguration { - constructor(config: NodeSSLConfiguration, socksProxyConfig: SocksProxyConfig?, maxMessageSize: Int) : this(config.loadSslKeyStore().internal, - config.keyStorePassword.toCharArray(), - config.loadTrustStore().internal, - socksProxyConfig, - maxMessageSize) + constructor(config: MutualSslConfiguration, socksProxyConfig: SocksProxyConfig?, maxMessageSize: Int) : this(config.keyStore.get(), config.trustStore.get(), socksProxyConfig, maxMessageSize) } private val amqpConfig: AMQPConfiguration = AMQPConfigurationImpl(config, socksProxyConfig, maxMessageSize) private var sharedEventLoopGroup: EventLoopGroup? = null private var artemis: ArtemisSessionProvider? = null - constructor(config: NodeSSLConfiguration, p2pAddress: NetworkHostAndPort, maxMessageSize: Int, socksProxyConfig: SocksProxyConfig? = null) : this(config, socksProxyConfig, maxMessageSize, { ArtemisMessagingClient(config, p2pAddress, maxMessageSize) }) - + constructor(config: MutualSslConfiguration, p2pAddress: NetworkHostAndPort, maxMessageSize: Int, socksProxyConfig: SocksProxyConfig? = null) : this(config, socksProxyConfig, maxMessageSize, { ArtemisMessagingClient(config, p2pAddress, maxMessageSize) }) companion object { private const val NUM_BRIDGE_THREADS = 0 // Default sized pool diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/bridging/BridgeControlListener.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/bridging/BridgeControlListener.kt index dca59de998..b9037e3f87 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/bridging/BridgeControlListener.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/bridging/BridgeControlListener.kt @@ -11,9 +11,9 @@ import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.BRIDGE_NOT import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_PREFIX import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEERS_PREFIX import net.corda.nodeapi.internal.ArtemisSessionProvider -import net.corda.nodeapi.internal.config.NodeSSLConfiguration import net.corda.nodeapi.internal.protonwrapper.netty.SocksProxyConfig import org.apache.activemq.artemis.api.core.ActiveMQQueueExistsException +import net.corda.nodeapi.internal.config.MutualSslConfiguration import org.apache.activemq.artemis.api.core.RoutingType import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.api.core.client.ClientConsumer @@ -23,8 +23,7 @@ import rx.Observable import rx.subjects.PublishSubject import java.util.* -class BridgeControlListener(val config: NodeSSLConfiguration, - +class BridgeControlListener(val config: MutualSslConfiguration, socksProxyConfig: SocksProxyConfig? = null, maxMessageSize: Int, val artemisMessageClientFactory: () -> ArtemisSessionProvider) : AutoCloseable { @@ -38,7 +37,7 @@ class BridgeControlListener(val config: NodeSSLConfiguration, private var controlConsumer: ClientConsumer? = null private var notifyConsumer: ClientConsumer? = null - constructor(config: NodeSSLConfiguration, + constructor(config: MutualSslConfiguration, p2pAddress: NetworkHostAndPort, maxMessageSize: Int, diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/CertificateStore.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/CertificateStore.kt new file mode 100644 index 0000000000..3ca6be6d6b --- /dev/null +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/CertificateStore.kt @@ -0,0 +1,82 @@ +package net.corda.nodeapi.internal.config + +import net.corda.core.internal.outputStream +import net.corda.nodeapi.internal.crypto.X509KeyStore +import net.corda.nodeapi.internal.crypto.X509Utilities +import net.corda.nodeapi.internal.crypto.addOrReplaceCertificate +import java.io.InputStream +import java.io.OutputStream +import java.nio.file.OpenOption +import java.nio.file.Path +import java.security.cert.X509Certificate + +interface CertificateStore : Iterable<Pair<String, X509Certificate>> { + + companion object { + + fun of(store: X509KeyStore, password: String): CertificateStore = DelegatingCertificateStore(store, password) + + fun fromFile(storePath: Path, password: String, createNew: Boolean): CertificateStore = DelegatingCertificateStore(X509KeyStore.fromFile(storePath, password, createNew), password) + + fun fromInputStream(stream: InputStream, password: String): CertificateStore = DelegatingCertificateStore(X509KeyStore.fromInputStream(stream, password), password) + + fun fromResource(storeResourceName: String, password: String, classLoader: ClassLoader = Thread.currentThread().contextClassLoader): CertificateStore = fromInputStream(classLoader.getResourceAsStream(storeResourceName), password) + } + + val value: X509KeyStore + val password: String + + fun writeTo(stream: OutputStream) = value.internal.store(stream, password.toCharArray()) + + fun writeTo(path: Path, vararg options: OpenOption) = path.outputStream(*options) + + fun update(action: X509KeyStore.() -> Unit) { + val result = action.invoke(value) + value.save() + return result + } + + fun <RESULT> query(action: X509KeyStore.() -> RESULT): RESULT { + return action.invoke(value) + } + + operator fun set(alias: String, certificate: X509Certificate) { + + update { + internal.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, certificate) + } + } + + override fun iterator(): Iterator<Pair<String, X509Certificate>> { + + return query { + aliases() + }.asSequence().map { alias -> alias to get(alias) }.iterator() + } + + fun forEach(action: (alias: String, certificate: X509Certificate) -> Unit) { + + forEach { (alias, certificate) -> action.invoke(alias, certificate) } + } + + /** + * @throws IllegalArgumentException if no certificate for the alias is found, or if the certificate is not an [X509Certificate]. + */ + operator fun get(alias: String): X509Certificate { + + return query { + getCertificate(alias) + } + } + + operator fun contains(alias: String): Boolean = value.contains(alias) + + fun copyTo(certificateStore: CertificateStore) { + + certificateStore.update { + this@CertificateStore.forEach(::setCertificate) + } + } +} + +private class DelegatingCertificateStore(override val value: X509KeyStore, override val password: String) : CertificateStore \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/CertificateStoreSupplier.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/CertificateStoreSupplier.kt new file mode 100644 index 0000000000..3703742813 --- /dev/null +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/CertificateStoreSupplier.kt @@ -0,0 +1,24 @@ +package net.corda.nodeapi.internal.config + +import java.io.IOException +import java.nio.file.Path + +interface CertificateStoreSupplier { + + fun get(createNew: Boolean = false): CertificateStore + + fun getOptional(): CertificateStore? { + + return try { + get() + } catch (e: IOException) { + null + } + } +} + +// TODO replace reference to FileBasedCertificateStoreSupplier with CertificateStoreSupplier, after coming up with a way of passing certificate stores to Artemis. +class FileBasedCertificateStoreSupplier(val path: Path, val password: String) : CertificateStoreSupplier { + + override fun get(createNew: Boolean) = CertificateStore.fromFile(path, password, createNew) +} \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/SSLConfiguration.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/SslConfiguration.kt similarity index 54% rename from node-api/src/main/kotlin/net/corda/nodeapi/internal/config/SSLConfiguration.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/config/SslConfiguration.kt index e8c63fc1f3..1011d49f9d 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/SSLConfiguration.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/SslConfiguration.kt @@ -4,12 +4,34 @@ import net.corda.core.internal.div import net.corda.nodeapi.internal.crypto.X509KeyStore import java.nio.file.Path +interface SslConfiguration { + + val keyStore: FileBasedCertificateStoreSupplier? + val trustStore: FileBasedCertificateStoreSupplier? + + companion object { + + fun mutual(keyStore: FileBasedCertificateStoreSupplier, trustStore: FileBasedCertificateStoreSupplier): MutualSslConfiguration { + + return MutualSslOptions(keyStore, trustStore) + } + } +} + +interface MutualSslConfiguration : SslConfiguration { + + override val keyStore: FileBasedCertificateStoreSupplier + override val trustStore: FileBasedCertificateStoreSupplier +} + +private class MutualSslOptions(override val keyStore: FileBasedCertificateStoreSupplier, override val trustStore: FileBasedCertificateStoreSupplier) : MutualSslConfiguration + +// Don't use this internally. It's still here because it's used by ArtemisTcpTransport, which is in public node-api by mistake. interface SSLConfiguration { val keyStorePassword: String val trustStorePassword: String val certificatesDirectory: Path val sslKeystore: Path get() = certificatesDirectory / "sslkeystore.jks" - // TODO This looks like it should be in NodeSSLConfiguration val nodeKeystore: Path get() = certificatesDirectory / "nodekeystore.jks" val trustStoreFile: Path get() = certificatesDirectory / "truststore.jks" val crlCheckSoftFail: Boolean @@ -25,9 +47,4 @@ interface SSLConfiguration { fun loadSslKeyStore(createNew: Boolean = false): X509KeyStore { return X509KeyStore.fromFile(sslKeystore, keyStorePassword, createNew) } -} - -interface NodeSSLConfiguration : SSLConfiguration { - val baseDirectory: Path - override val certificatesDirectory: Path get() = baseDirectory / "certificates" -} +} \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509KeyStore.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509KeyStore.kt index 1fa56bebea..5c0c4b501b 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509KeyStore.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509KeyStore.kt @@ -2,6 +2,7 @@ package net.corda.nodeapi.internal.crypto import net.corda.core.crypto.Crypto import net.corda.core.internal.uncheckedCast +import java.io.InputStream import java.nio.file.Path import java.security.KeyPair import java.security.KeyStore @@ -30,6 +31,14 @@ class X509KeyStore private constructor(val internal: KeyStore, private val store val internal: KeyStore = if (createNew) loadOrCreateKeyStore(keyStoreFile, storePassword) else loadKeyStore(keyStoreFile, storePassword) return X509KeyStore(internal, storePassword, keyStoreFile) } + + /** + * Reads a [KeyStore] from an [InputStream]. + */ + fun fromInputStream(stream: InputStream, storePassword: String): X509KeyStore { + val internal = loadKeyStore(stream, storePassword) + return X509KeyStore(internal, storePassword) + } } operator fun contains(alias: String): Boolean = internal.containsAlias(alias) 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 b73b6a8bb4..db2bba18d3 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 @@ -132,7 +132,7 @@ class AMQPClient(val targets: List<NetworkHostAndPort>, private val conf = parent.configuration init { - keyManagerFactory.init(conf.keyStore, conf.keyStorePrivateKeyPassword) + keyManagerFactory.init(conf.keyStore) trustManagerFactory.init(initialiseTrustStoreAndEnableCrlChecking(conf.trustStore, conf.crlCheckSoftFail)) } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPConfiguration.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPConfiguration.kt index 4e873cdbaa..d39c5b4647 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPConfiguration.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPConfiguration.kt @@ -1,6 +1,7 @@ package net.corda.nodeapi.internal.protonwrapper.netty import net.corda.nodeapi.internal.ArtemisMessagingComponent +import net.corda.nodeapi.internal.config.CertificateStore import java.security.KeyStore interface AMQPConfiguration { @@ -21,19 +22,14 @@ interface AMQPConfiguration { get() = ArtemisMessagingComponent.PEER_USER /** - * The keystore used for TLS connections + * The key store used for TLS connections */ - val keyStore: KeyStore + val keyStore: CertificateStore /** - * Password used to unlock TLS private keys in the KeyStore. + * The trust root key store to validate the peer certificates against */ - val keyStorePrivateKeyPassword: CharArray - - /** - * The trust root KeyStore to validate the peer certificates against - */ - val trustStore: KeyStore + val trustStore: CertificateStore /** * Setting crlCheckSoftFail to true allows certificate paths where some leaf certificates do not contain cRLDistributionPoints diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPServer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPServer.kt index 7c16a0547e..d65b77139c 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPServer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPServer.kt @@ -60,7 +60,7 @@ class AMQPServer(val hostName: String, private val conf = parent.configuration init { - keyManagerFactory.init(conf.keyStore, conf.keyStorePrivateKeyPassword) + keyManagerFactory.init(conf.keyStore.value.internal, conf.keyStore.password.toCharArray()) trustManagerFactory.init(initialiseTrustStoreAndEnableCrlChecking(conf.trustStore, conf.crlCheckSoftFail)) } 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 1f4328a8ee..6ffd49cc2d 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 @@ -5,14 +5,13 @@ import net.corda.core.crypto.newSecureRandom import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.contextLogger import net.corda.core.utilities.toHex -import net.corda.nodeapi.ArtemisTcpTransport +import net.corda.nodeapi.internal.InternalArtemisTcpTransport +import net.corda.nodeapi.internal.config.CertificateStore import net.corda.nodeapi.internal.crypto.toBc import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier import org.bouncycastle.asn1.x509.Extension import org.bouncycastle.asn1.x509.SubjectKeyIdentifier import java.net.Socket -import java.security.KeyStore -import java.security.SecureRandom import java.security.cert.* import java.util.* import javax.net.ssl.* @@ -111,8 +110,8 @@ internal fun createClientSslHelper(target: NetworkHostAndPort, sslContext.init(keyManagers, trustManagers, newSecureRandom()) val sslEngine = sslContext.createSSLEngine(target.host, target.port) sslEngine.useClientMode = true - sslEngine.enabledProtocols = ArtemisTcpTransport.TLS_VERSIONS.toTypedArray() - sslEngine.enabledCipherSuites = ArtemisTcpTransport.CIPHER_SUITES.toTypedArray() + sslEngine.enabledProtocols = InternalArtemisTcpTransport.TLS_VERSIONS.toTypedArray() + sslEngine.enabledCipherSuites = InternalArtemisTcpTransport.CIPHER_SUITES.toTypedArray() sslEngine.enableSessionCreation = true return SslHandler(sslEngine) } @@ -126,13 +125,13 @@ internal fun createServerSslHelper(keyManagerFactory: KeyManagerFactory, val sslEngine = sslContext.createSSLEngine() sslEngine.useClientMode = false sslEngine.needClientAuth = true - sslEngine.enabledProtocols = ArtemisTcpTransport.TLS_VERSIONS.toTypedArray() - sslEngine.enabledCipherSuites = ArtemisTcpTransport.CIPHER_SUITES.toTypedArray() + sslEngine.enabledProtocols = InternalArtemisTcpTransport.TLS_VERSIONS.toTypedArray() + sslEngine.enabledCipherSuites = InternalArtemisTcpTransport.CIPHER_SUITES.toTypedArray() sslEngine.enableSessionCreation = true return SslHandler(sslEngine) } -internal fun initialiseTrustStoreAndEnableCrlChecking(trustStore: KeyStore, crlCheckSoftFail: Boolean): ManagerFactoryParameters { +internal fun initialiseTrustStoreAndEnableCrlChecking(trustStore: CertificateStore, crlCheckSoftFail: Boolean): ManagerFactoryParameters { val certPathBuilder = CertPathBuilder.getInstance("PKIX") val revocationChecker = certPathBuilder.revocationChecker as PKIXRevocationChecker revocationChecker.options = EnumSet.of( @@ -145,7 +144,11 @@ internal fun initialiseTrustStoreAndEnableCrlChecking(trustStore: KeyStore, crlC // the following reasons: The CRL or OCSP response cannot be obtained because of a network error. revocationChecker.options = revocationChecker.options + PKIXRevocationChecker.Option.SOFT_FAIL } - val pkixParams = PKIXBuilderParameters(trustStore, X509CertSelector()) + val pkixParams = PKIXBuilderParameters(trustStore.value.internal, X509CertSelector()) pkixParams.addCertPathChecker(revocationChecker) return CertPathTrustManagerParameters(pkixParams) } + +fun KeyManagerFactory.init(keyStore: CertificateStore) = init(keyStore.value.internal, keyStore.password.toCharArray()) + +fun TrustManagerFactory.init(trustStore: CertificateStore) = init(trustStore.value.internal) diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/DevCertificatesTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/DevCertificatesTest.kt index 4fdc43dd2d..e586a6c642 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/DevCertificatesTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/DevCertificatesTest.kt @@ -1,8 +1,7 @@ package net.corda.nodeapi.internal.crypto import net.corda.core.internal.validate -import net.corda.nodeapi.internal.DEV_CA_TRUST_STORE_FILE -import net.corda.nodeapi.internal.DEV_CA_TRUST_STORE_PASS +import net.corda.nodeapi.internal.loadDevCaTrustStore import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder @@ -22,8 +21,8 @@ class DevCertificatesTest { @Test fun `create server certificate in keystore for SSL`() { // given - val newTrustStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/$DEV_CA_TRUST_STORE_FILE"), DEV_CA_TRUST_STORE_PASS) - val newTrustRoot = newTrustStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA) + val newTrustStore = loadDevCaTrustStore() + val newTrustRoot = newTrustStore[X509Utilities.CORDA_ROOT_CA] val newTrustAnchor = TrustAnchor(newTrustRoot, null) val oldNodeCaKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("regression-test/$OLD_NODE_DEV_KEYSTORE_FILE_NAME"), OLD_DEV_KEYSTORE_PASS) diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt index 1df89e1070..d389e5b27d 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt @@ -10,8 +10,11 @@ import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.node.serialization.amqp.AMQPServerSerializationScheme -import net.corda.nodeapi.internal.config.SSLConfiguration -import net.corda.nodeapi.internal.createDevKeyStores +import net.corda.nodeapi.internal.config.MutualSslConfiguration +import net.corda.nodeapi.internal.createDevNodeCa +import net.corda.nodeapi.internal.protonwrapper.netty.init +import net.corda.nodeapi.internal.registerDevP2pCertificates +import net.corda.nodeapi.internal.registerDevSigningCertificates import net.corda.serialization.internal.AllWhitelist import net.corda.serialization.internal.SerializationContextImpl import net.corda.serialization.internal.SerializationFactoryImpl @@ -19,6 +22,7 @@ import net.corda.serialization.internal.amqp.amqpMagic import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.BOB_NAME import net.corda.testing.core.TestIdentity +import net.corda.testing.internal.stubs.CertificateStoreStubs import net.corda.testing.internal.createDevIntermediateCaCertPath import org.assertj.core.api.Assertions.assertThat import org.bouncycastle.asn1.x509.* @@ -31,7 +35,6 @@ import java.io.IOException import java.net.InetAddress import java.net.InetSocketAddress import java.nio.file.Path -import java.security.SecureRandom import java.security.cert.CertPath import java.security.cert.X509Certificate import java.util.* @@ -180,29 +183,27 @@ class X509UtilitiesTest { @Test fun `create server certificate in keystore for SSL`() { - val sslConfig = object : SSLConfiguration { - override val certificatesDirectory = tempFolder.root.toPath() - override val keyStorePassword = "serverstorepass" - override val trustStorePassword = "trustpass" - override val crlCheckSoftFail: Boolean = true - } + val certificatesDirectory = tempFolder.root.toPath() + val signingCertStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory, "serverstorepass") + val p2pSslConfig = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory, keyStorePassword = "serverstorepass") val (rootCa, intermediateCa) = createDevIntermediateCaCertPath() // Generate server cert and private key and populate another keystore suitable for SSL - sslConfig.createDevKeyStores(MEGA_CORP.name, rootCa.certificate, intermediateCa) - + val nodeCa = createDevNodeCa(intermediateCa, MEGA_CORP.name) + signingCertStore.get(createNew = true).also { it.registerDevSigningCertificates(MEGA_CORP.name, rootCa.certificate, intermediateCa, nodeCa) } + p2pSslConfig.keyStore.get(createNew = true).also { it.registerDevP2pCertificates(MEGA_CORP.name, rootCa.certificate, intermediateCa, nodeCa) } // Load back server certificate - val serverKeyStore = loadKeyStore(sslConfig.nodeKeystore, sslConfig.keyStorePassword) - val (serverCert, serverKeyPair) = serverKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, sslConfig.keyStorePassword) + val serverKeyStore = signingCertStore.get().value + val (serverCert, serverKeyPair) = serverKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA) serverCert.checkValidity() serverCert.verify(intermediateCa.certificate.publicKey) assertThat(CordaX500Name.build(serverCert.subjectX500Principal)).isEqualTo(MEGA_CORP.name) // Load back SSL certificate - val sslKeyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword) - val (sslCert) = sslKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_TLS, sslConfig.keyStorePassword) + val sslKeyStoreReloaded = p2pSslConfig.keyStore.get() + val (sslCert) = sslKeyStoreReloaded.query { getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_TLS, p2pSslConfig.keyStore.password) } sslCert.checkValidity() sslCert.verify(serverCert.publicKey) @@ -216,25 +217,20 @@ class X509UtilitiesTest { @Test fun `create server cert and use in SSL socket`() { - val sslConfig = object : SSLConfiguration { - override val certificatesDirectory = tempFolder.root.toPath() - override val keyStorePassword = "serverstorepass" - override val trustStorePassword = "trustpass" - override val crlCheckSoftFail: Boolean = true - } + val sslConfig = CertificateStoreStubs.P2P.withCertificatesDirectory(tempFolder.root.toPath(), keyStorePassword = "serverstorepass") val (rootCa, intermediateCa) = createDevIntermediateCaCertPath() // Generate server cert and private key and populate another keystore suitable for SSL - sslConfig.createDevKeyStores(MEGA_CORP.name, rootCa.certificate, intermediateCa) + sslConfig.keyStore.get(true).registerDevP2pCertificates(MEGA_CORP.name, rootCa.certificate, intermediateCa) sslConfig.createTrustStore(rootCa.certificate) - val keyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword) - val trustStore = loadKeyStore(sslConfig.trustStoreFile, sslConfig.trustStorePassword) + val keyStore = sslConfig.keyStore.get() + val trustStore = sslConfig.trustStore.get() val context = SSLContext.getInstance("TLS") val keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()) - keyManagerFactory.init(keyStore, sslConfig.keyStorePassword.toCharArray()) + keyManagerFactory.init(keyStore) val keyManagers = keyManagerFactory.keyManagers val trustMgrFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) trustMgrFactory.init(trustStore) @@ -313,10 +309,9 @@ class X509UtilitiesTest { private fun tempFile(name: String): Path = tempFolder.root.toPath() / name - private fun SSLConfiguration.createTrustStore(rootCert: X509Certificate) { - val trustStore = loadOrCreateKeyStore(trustStoreFile, trustStorePassword) - trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCert) - trustStore.save(trustStoreFile, trustStorePassword) + private fun MutualSslConfiguration.createTrustStore(rootCert: X509Certificate) { + val trustStore = this.trustStore.get(true) + trustStore[X509Utilities.CORDA_ROOT_CA] = rootCert } @Test diff --git a/node/src/integration-test/kotlin/net/corda/node/BootTests.kt b/node/src/integration-test/kotlin/net/corda/node/BootTests.kt index 410ce8b2de..94cafdf4f6 100644 --- a/node/src/integration-test/kotlin/net/corda/node/BootTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/BootTests.kt @@ -12,19 +12,15 @@ import net.corda.core.messaging.startFlow import net.corda.core.utilities.getOrThrow import net.corda.node.internal.NodeStartup import net.corda.node.services.Permissions.Companion.startFlow -import net.corda.nodeapi.exceptions.InternalNodeException import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.BOB_NAME import net.corda.testing.core.DUMMY_BANK_A_NAME import net.corda.testing.driver.DriverParameters -import net.corda.testing.driver.NodeHandle -import net.corda.testing.driver.NodeParameters import net.corda.testing.driver.driver import net.corda.testing.internal.IntegrationTest import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.internal.toDatabaseSchemaName import net.corda.testing.node.User -import net.corda.testing.node.internal.startNode import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.ClassRule import org.junit.Test diff --git a/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt b/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt index e2a3cd4eec..7cf7830a5d 100644 --- a/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt @@ -4,20 +4,19 @@ import net.corda.core.crypto.Crypto import net.corda.core.internal.div import net.corda.core.utilities.getOrThrow import net.corda.node.services.config.configureDevKeyAndTrustStores -import net.corda.nodeapi.internal.config.SSLConfiguration import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.driver.DriverParameters import net.corda.testing.driver.driver +import net.corda.testing.internal.stubs.CertificateStoreStubs import net.corda.testing.internal.IntegrationTest import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.internal.toDatabaseSchemaName import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.ClassRule import org.junit.Test -import java.nio.file.Path import javax.security.auth.x500.X500Principal class NodeKeystoreCheckTest : IntegrationTest() { @@ -41,13 +40,11 @@ class NodeKeystoreCheckTest : IntegrationTest() { driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList())) { // Create keystores val keystorePassword = "password" - val config = object : SSLConfiguration { - override val keyStorePassword: String = keystorePassword - override val trustStorePassword: String = keystorePassword - override val certificatesDirectory: Path = baseDirectory(ALICE_NAME) / "certificates" - override val crlCheckSoftFail: Boolean = true - } - config.configureDevKeyAndTrustStores(ALICE_NAME) + val certificatesDirectory = baseDirectory(ALICE_NAME) / "certificates" + val signingCertStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory, keystorePassword) + val p2pSslConfig = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory, keyStorePassword = keystorePassword, trustStorePassword = keystorePassword) + + p2pSslConfig.configureDevKeyAndTrustStores(ALICE_NAME, signingCertStore, certificatesDirectory) // This should pass with correct keystore. val node = startNode( @@ -59,7 +56,7 @@ class NodeKeystoreCheckTest : IntegrationTest() { node.stop() // Fiddle with node keystore. - config.loadNodeKeyStore().update { + signingCertStore.get().update { // Self signed root val badRootKeyPair = Crypto.generateKeyPair() val badRoot = X509Utilities.createSelfSignedCACertificate(X500Principal("O=Bad Root,L=Lodnon,C=GB"), badRootKeyPair) diff --git a/node/src/integration-test/kotlin/net/corda/node/amqp/AMQPBridgeTest.kt b/node/src/integration-test/kotlin/net/corda/node/amqp/AMQPBridgeTest.kt index 6d1d97f6a9..9ce9900b62 100644 --- a/node/src/integration-test/kotlin/net/corda/node/amqp/AMQPBridgeTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/amqp/AMQPBridgeTest.kt @@ -23,6 +23,7 @@ import net.corda.testing.core.BOB_NAME import net.corda.testing.core.MAX_MESSAGE_SIZE import net.corda.testing.core.TestIdentity import net.corda.testing.driver.PortAllocation +import net.corda.testing.internal.stubs.CertificateStoreStubs import net.corda.testing.internal.rigorousMock import org.apache.activemq.artemis.api.core.Message.HDR_DUPLICATE_DETECTION_ID import org.apache.activemq.artemis.api.core.RoutingType @@ -33,7 +34,6 @@ import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder -import java.security.KeyStore import java.util.* import kotlin.system.measureNanoTime import kotlin.system.measureTimeMillis @@ -242,12 +242,17 @@ class AMQPBridgeTest { private fun createArtemis(sourceQueueName: String?): Triple<ArtemisMessagingServer, ArtemisMessagingClient, BridgeManager> { + val baseDir = temporaryFolder.root.toPath() / "artemis" + val certificatesDirectory = baseDir / "certificates" + val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory) + val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory) val artemisConfig = rigorousMock<AbstractNodeConfiguration>().also { - doReturn(temporaryFolder.root.toPath() / "artemis").whenever(it).baseDirectory + doReturn(baseDir).whenever(it).baseDirectory doReturn(ALICE_NAME).whenever(it).myLegalName - doReturn("trustpass").whenever(it).trustStorePassword + doReturn(certificatesDirectory).whenever(it).certificatesDirectory + doReturn(signingCertificateStore).whenever(it).signingCertificateStore + doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions doReturn(true).whenever(it).crlCheckSoftFail - doReturn("cordacadevpass").whenever(it).keyStorePassword doReturn(artemisAddress).whenever(it).p2pAddress doReturn(null).whenever(it).jmxMonitoringHttpPort doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000))).whenever(it).enterpriseConfiguration @@ -255,11 +260,11 @@ class AMQPBridgeTest { artemisConfig.configureWithDevSSLCertificate() val artemisServer = ArtemisMessagingServer(artemisConfig, artemisAddress.copy(host = "0.0.0.0"), MAX_MESSAGE_SIZE) - val artemisClient = ArtemisMessagingClient(artemisConfig, artemisAddress, MAX_MESSAGE_SIZE) + val artemisClient = ArtemisMessagingClient(artemisConfig.p2pSslOptions, artemisAddress, MAX_MESSAGE_SIZE) artemisServer.start() artemisClient.start() - val bridgeManager = AMQPBridgeManager(artemisConfig, artemisAddress, MAX_MESSAGE_SIZE) + val bridgeManager = AMQPBridgeManager(artemisConfig.p2pSslOptions, artemisAddress, MAX_MESSAGE_SIZE) bridgeManager.start() val artemis = artemisClient.started!! if (sourceQueueName != null) { @@ -272,18 +277,23 @@ class AMQPBridgeTest { private fun createArtemisReceiver(targetAdress: NetworkHostAndPort, workingDir: String): Pair<ArtemisMessagingServer, ArtemisMessagingClient> { + val baseDir = temporaryFolder.root.toPath() / workingDir + val certificatesDirectory = baseDir / "certificates" + val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory) + val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory) val artemisConfig = rigorousMock<AbstractNodeConfiguration>().also { - doReturn(temporaryFolder.root.toPath() / workingDir).whenever(it).baseDirectory + doReturn(baseDir).whenever(it).baseDirectory + doReturn(certificatesDirectory).whenever(it).certificatesDirectory doReturn(BOB_NAME).whenever(it).myLegalName - doReturn("trustpass").whenever(it).trustStorePassword - doReturn("cordacadevpass").whenever(it).keyStorePassword + doReturn(signingCertificateStore).whenever(it).signingCertificateStore + doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions doReturn(targetAdress).whenever(it).p2pAddress doReturn("").whenever(it).jmxMonitoringHttpPort doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000))).whenever(it).enterpriseConfiguration } artemisConfig.configureWithDevSSLCertificate() val artemisServer = ArtemisMessagingServer(artemisConfig, NetworkHostAndPort("0.0.0.0", targetAdress.port), MAX_MESSAGE_SIZE) - val artemisClient = ArtemisMessagingClient(artemisConfig, targetAdress, MAX_MESSAGE_SIZE, confirmationWindowSize = 10 * 1024) + val artemisClient = ArtemisMessagingClient(artemisConfig.p2pSslOptions, targetAdress, MAX_MESSAGE_SIZE, confirmationWindowSize = 10 * 1024) artemisServer.start() artemisClient.start() @@ -292,18 +302,23 @@ class AMQPBridgeTest { } private fun createAMQPServer(maxMessageSize: Int = MAX_MESSAGE_SIZE): AMQPServer { + val baseDir = temporaryFolder.root.toPath() / "server" + val certificatesDirectory = baseDir / "certificates" + val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory) + val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory) val serverConfig = rigorousMock<AbstractNodeConfiguration>().also { doReturn(temporaryFolder.root.toPath() / "server").whenever(it).baseDirectory doReturn(BOB_NAME).whenever(it).myLegalName - doReturn("trustpass").whenever(it).trustStorePassword - doReturn("cordacadevpass").whenever(it).keyStorePassword + doReturn(certificatesDirectory).whenever(it).certificatesDirectory + doReturn(signingCertificateStore).whenever(it).signingCertificateStore + doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions } serverConfig.configureWithDevSSLCertificate() + val keyStore = serverConfig.p2pSslOptions.keyStore.get() val amqpConfig = object : AMQPConfiguration { - override val keyStore: KeyStore = serverConfig.loadSslKeyStore().internal - override val keyStorePrivateKeyPassword: CharArray = serverConfig.keyStorePassword.toCharArray() - override val trustStore: KeyStore = serverConfig.loadTrustStore().internal + override val keyStore = keyStore + override val trustStore = serverConfig.p2pSslOptions.trustStore.get() override val trace: Boolean = true override val maxMessageSize: Int = maxMessageSize } diff --git a/node/src/integration-test/kotlin/net/corda/node/amqp/CertificateRevocationListNodeTests.kt b/node/src/integration-test/kotlin/net/corda/node/amqp/CertificateRevocationListNodeTests.kt index cb766bbcf8..d2ef1dbe5c 100644 --- a/node/src/integration-test/kotlin/net/corda/node/amqp/CertificateRevocationListNodeTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/amqp/CertificateRevocationListNodeTests.kt @@ -13,7 +13,8 @@ import net.corda.core.utilities.seconds import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.configureWithDevSSLCertificate import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_PREFIX -import net.corda.nodeapi.internal.config.SSLConfiguration +import net.corda.nodeapi.internal.config.CertificateStoreSupplier +import net.corda.nodeapi.internal.config.MutualSslConfiguration import net.corda.nodeapi.internal.crypto.* import net.corda.nodeapi.internal.protonwrapper.messages.MessageStatus import net.corda.nodeapi.internal.protonwrapper.netty.AMQPClient @@ -24,6 +25,7 @@ import net.corda.testing.core.BOB_NAME import net.corda.testing.core.CHARLIE_NAME import net.corda.testing.core.MAX_MESSAGE_SIZE import net.corda.testing.driver.PortAllocation +import net.corda.testing.internal.stubs.CertificateStoreStubs import net.corda.testing.internal.DEV_INTERMEDIATE_CA import net.corda.testing.internal.DEV_ROOT_CA import net.corda.testing.internal.rigorousMock @@ -50,7 +52,6 @@ import java.io.Closeable import java.math.BigInteger import java.net.InetSocketAddress import java.security.KeyPair -import java.security.KeyStore import java.security.PrivateKey import java.security.Security import java.security.cert.X509CRL @@ -330,22 +331,25 @@ class CertificateRevocationListNodeTests { nodeCrlDistPoint: String = "http://${server.hostAndPort}/crl/node.crl", tlsCrlDistPoint: String? = "http://${server.hostAndPort}/crl/empty.crl", maxMessageSize: Int = MAX_MESSAGE_SIZE): Pair<AMQPClient, X509Certificate> { + val baseDirectory = temporaryFolder.root.toPath() / "client" + val certificatesDirectory = baseDirectory / "certificates" + val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory) + val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory) val clientConfig = rigorousMock<AbstractNodeConfiguration>().also { - doReturn(temporaryFolder.root.toPath() / "client").whenever(it).baseDirectory + doReturn(baseDirectory).whenever(it).baseDirectory + doReturn(certificatesDirectory).whenever(it).certificatesDirectory doReturn(BOB_NAME).whenever(it).myLegalName - doReturn("trustpass").whenever(it).trustStorePassword - doReturn("cordacadevpass").whenever(it).keyStorePassword + doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions + doReturn(signingCertificateStore).whenever(it).signingCertificateStore doReturn(crlCheckSoftFail).whenever(it).crlCheckSoftFail } clientConfig.configureWithDevSSLCertificate() - val nodeCert = clientConfig.recreateNodeCaAndTlsCertificates(nodeCrlDistPoint, tlsCrlDistPoint) - val clientTruststore = clientConfig.loadTrustStore().internal - val clientKeystore = clientConfig.loadSslKeyStore().internal + val nodeCert = (signingCertificateStore to p2pSslConfiguration).recreateNodeCaAndTlsCertificates(nodeCrlDistPoint, tlsCrlDistPoint) + val keyStore = clientConfig.p2pSslOptions.keyStore.get() val amqpConfig = object : AMQPConfiguration { - override val keyStore: KeyStore = clientKeystore - override val keyStorePrivateKeyPassword: CharArray = clientConfig.keyStorePassword.toCharArray() - override val trustStore: KeyStore = clientTruststore + override val keyStore = keyStore + override val trustStore = clientConfig.p2pSslOptions.trustStore.get() override val crlCheckSoftFail: Boolean = crlCheckSoftFail override val maxMessageSize: Int = maxMessageSize } @@ -360,21 +364,24 @@ class CertificateRevocationListNodeTests { nodeCrlDistPoint: String = "http://${server.hostAndPort}/crl/node.crl", tlsCrlDistPoint: String? = "http://${server.hostAndPort}/crl/empty.crl", maxMessageSize: Int = MAX_MESSAGE_SIZE): Pair<AMQPServer, X509Certificate> { + val baseDirectory = temporaryFolder.root.toPath() / "server" + val certificatesDirectory = baseDirectory / "certificates" + val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory) + val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory) val serverConfig = rigorousMock<AbstractNodeConfiguration>().also { - doReturn(temporaryFolder.root.toPath() / "server").whenever(it).baseDirectory + doReturn(baseDirectory).whenever(it).baseDirectory + doReturn(certificatesDirectory).whenever(it).certificatesDirectory doReturn(name).whenever(it).myLegalName - doReturn("trustpass").whenever(it).trustStorePassword - doReturn("cordacadevpass").whenever(it).keyStorePassword + doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions + doReturn(signingCertificateStore).whenever(it).signingCertificateStore doReturn(crlCheckSoftFail).whenever(it).crlCheckSoftFail } serverConfig.configureWithDevSSLCertificate() - val nodeCert = serverConfig.recreateNodeCaAndTlsCertificates(nodeCrlDistPoint, tlsCrlDistPoint) - val serverTruststore = serverConfig.loadTrustStore().internal - val serverKeystore = serverConfig.loadSslKeyStore().internal + val nodeCert = (signingCertificateStore to p2pSslConfiguration).recreateNodeCaAndTlsCertificates(nodeCrlDistPoint, tlsCrlDistPoint) + val keyStore = serverConfig.p2pSslOptions.keyStore.get() val amqpConfig = object : AMQPConfiguration { - override val keyStore: KeyStore = serverKeystore - override val keyStorePrivateKeyPassword: CharArray = serverConfig.keyStorePassword.toCharArray() - override val trustStore: KeyStore = serverTruststore + override val keyStore = keyStore + override val trustStore = serverConfig.p2pSslOptions.trustStore.get() override val crlCheckSoftFail: Boolean = crlCheckSoftFail override val maxMessageSize: Int = maxMessageSize } @@ -384,22 +391,28 @@ class CertificateRevocationListNodeTests { amqpConfig), nodeCert) } - private fun SSLConfiguration.recreateNodeCaAndTlsCertificates(nodeCaCrlDistPoint: String, tlsCrlDistPoint: String?): X509Certificate { - val nodeKeyStore = loadNodeKeyStore() - val (nodeCert, nodeKeys) = nodeKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA) + private fun Pair<CertificateStoreSupplier, MutualSslConfiguration>.recreateNodeCaAndTlsCertificates(nodeCaCrlDistPoint: String, tlsCrlDistPoint: String?): X509Certificate { + + val signingCertificateStore = first + val p2pSslConfiguration = second + val nodeKeyStore = signingCertificateStore.get() + val (nodeCert, nodeKeys) = nodeKeyStore.query { getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA) } val newNodeCert = replaceCrlDistPointCaCertificate(nodeCert, CertificateType.NODE_CA, INTERMEDIATE_CA.keyPair, nodeCaCrlDistPoint) - val nodeCertChain = listOf(newNodeCert, INTERMEDIATE_CA.certificate, *nodeKeyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA).drop(2).toTypedArray()) - nodeKeyStore.internal.deleteEntry(X509Utilities.CORDA_CLIENT_CA) - nodeKeyStore.save() + val nodeCertChain = listOf(newNodeCert, INTERMEDIATE_CA.certificate, *nodeKeyStore.query { getCertificateChain(X509Utilities.CORDA_CLIENT_CA) }.drop(2).toTypedArray()) + nodeKeyStore.update { + internal.deleteEntry(X509Utilities.CORDA_CLIENT_CA) + } nodeKeyStore.update { setPrivateKey(X509Utilities.CORDA_CLIENT_CA, nodeKeys.private, nodeCertChain) } - val sslKeyStore = loadSslKeyStore() - val (tlsCert, tlsKeys) = sslKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_TLS) + val sslKeyStore = p2pSslConfiguration.keyStore.get() + val (tlsCert, tlsKeys) = sslKeyStore.query { getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_TLS) } val newTlsCert = replaceCrlDistPointCaCertificate(tlsCert, CertificateType.TLS, nodeKeys, tlsCrlDistPoint, X500Name.getInstance(ROOT_CA.certificate.subjectX500Principal.encoded)) - val sslCertChain = listOf(newTlsCert, newNodeCert, INTERMEDIATE_CA.certificate, *sslKeyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).drop(3).toTypedArray()) - sslKeyStore.internal.deleteEntry(X509Utilities.CORDA_CLIENT_TLS) - sslKeyStore.save() + val sslCertChain = listOf(newTlsCert, newNodeCert, INTERMEDIATE_CA.certificate, *sslKeyStore.query { getCertificateChain(X509Utilities.CORDA_CLIENT_TLS) }.drop(3).toTypedArray()) + + sslKeyStore.update { + internal.deleteEntry(X509Utilities.CORDA_CLIENT_TLS) + } sslKeyStore.update { setPrivateKey(X509Utilities.CORDA_CLIENT_TLS, tlsKeys.private, sslCertChain) } diff --git a/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt b/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt index fa83c8fd71..90b737b559 100644 --- a/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt @@ -14,16 +14,18 @@ import net.corda.node.services.config.MutualExclusionConfiguration import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.configureWithDevSSLCertificate import net.corda.node.services.messaging.ArtemisMessagingServer -import net.corda.nodeapi.ArtemisTcpTransport.Companion.CIPHER_SUITES import net.corda.nodeapi.internal.ArtemisMessagingClient import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_PREFIX -import net.corda.nodeapi.internal.config.SSLConfiguration -import net.corda.nodeapi.internal.createDevKeyStores +import net.corda.nodeapi.internal.InternalArtemisTcpTransport +import net.corda.nodeapi.internal.config.MutualSslConfiguration +import net.corda.nodeapi.internal.registerDevP2pCertificates import net.corda.nodeapi.internal.crypto.* import net.corda.nodeapi.internal.protonwrapper.messages.MessageStatus import net.corda.nodeapi.internal.protonwrapper.netty.AMQPClient import net.corda.nodeapi.internal.protonwrapper.netty.AMQPConfiguration import net.corda.nodeapi.internal.protonwrapper.netty.AMQPServer +import net.corda.nodeapi.internal.protonwrapper.netty.init +import net.corda.nodeapi.internal.registerDevSigningCertificates import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.BOB_NAME import net.corda.testing.core.CHARLIE_NAME @@ -31,13 +33,13 @@ import net.corda.testing.core.MAX_MESSAGE_SIZE import net.corda.testing.driver.PortAllocation import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.testing.internal.rigorousMock +import net.corda.testing.internal.stubs.CertificateStoreStubs import org.apache.activemq.artemis.api.core.RoutingType import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Assert.assertArrayEquals import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder -import java.security.KeyStore import java.security.cert.X509Certificate import javax.net.ssl.* import kotlin.concurrent.thread @@ -120,34 +122,30 @@ class ProtonWrapperTests { } } - private fun SSLConfiguration.createTrustStore(rootCert: X509Certificate) { - val trustStore = loadOrCreateKeyStore(trustStoreFile, trustStorePassword) - trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCert) - trustStore.save(trustStoreFile, trustStorePassword) - } + private fun MutualSslConfiguration.createTrustStore(rootCert: X509Certificate) { + trustStore.get(true)[X509Utilities.CORDA_ROOT_CA] = rootCert + } @Test fun `Test AMQP Client with invalid root certificate`() { - val sslConfig = object : SSLConfiguration { - override val certificatesDirectory = temporaryFolder.root.toPath() - override val keyStorePassword = "serverstorepass" - override val trustStorePassword = "trustpass" - override val crlCheckSoftFail: Boolean = true - } + val certificatesDirectory = temporaryFolder.root.toPath() + val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory, "serverstorepass") + val sslConfig = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory, keyStorePassword = "serverstorepass") val (rootCa, intermediateCa) = createDevIntermediateCaCertPath() // Generate server cert and private key and populate another keystore suitable for SSL - sslConfig.createDevKeyStores(ALICE_NAME, rootCa.certificate, intermediateCa) + signingCertificateStore.get(true).also { it.registerDevSigningCertificates(ALICE_NAME, rootCa.certificate, intermediateCa) } + sslConfig.keyStore.get(true).also { it.registerDevP2pCertificates(ALICE_NAME, rootCa.certificate, intermediateCa) } sslConfig.createTrustStore(rootCa.certificate) - val keyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword) - val trustStore = loadKeyStore(sslConfig.trustStoreFile, sslConfig.trustStorePassword) + val keyStore = sslConfig.keyStore.get() + val trustStore = sslConfig.trustStore.get() val context = SSLContext.getInstance("TLS") val keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()) - keyManagerFactory.init(keyStore, sslConfig.keyStorePassword.toCharArray()) + keyManagerFactory.init(keyStore) val keyManagers = keyManagerFactory.keyManagers val trustMgrFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) trustMgrFactory.init(trustStore) @@ -157,7 +155,7 @@ class ProtonWrapperTests { val serverSocketFactory = context.serverSocketFactory val serverSocket = serverSocketFactory.createServerSocket(serverPort) as SSLServerSocket - val serverParams = SSLParameters(CIPHER_SUITES.toTypedArray(), + val serverParams = SSLParameters(InternalArtemisTcpTransport.CIPHER_SUITES.toTypedArray(), arrayOf("TLSv1.2")) serverParams.wantClientAuth = true serverParams.needClientAuth = true @@ -406,11 +404,16 @@ class ProtonWrapperTests { } private fun createArtemisServerAndClient(maxMessageSize: Int = MAX_MESSAGE_SIZE): Pair<ArtemisMessagingServer, ArtemisMessagingClient> { + val baseDirectory = temporaryFolder.root.toPath() / "artemis" + val certificatesDirectory = baseDirectory / "certificates" + val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory) + val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory) val artemisConfig = rigorousMock<AbstractNodeConfiguration>().also { - doReturn(temporaryFolder.root.toPath() / "artemis").whenever(it).baseDirectory + doReturn(baseDirectory).whenever(it).baseDirectory + doReturn(certificatesDirectory).whenever(it).certificatesDirectory doReturn(CHARLIE_NAME).whenever(it).myLegalName - doReturn("trustpass").whenever(it).trustStorePassword - doReturn("cordacadevpass").whenever(it).keyStorePassword + doReturn(signingCertificateStore).whenever(it).signingCertificateStore + doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions doReturn(NetworkHostAndPort("0.0.0.0", artemisPort)).whenever(it).p2pAddress doReturn(null).whenever(it).jmxMonitoringHttpPort doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000))).whenever(it).enterpriseConfiguration @@ -419,28 +422,32 @@ class ProtonWrapperTests { artemisConfig.configureWithDevSSLCertificate() val server = ArtemisMessagingServer(artemisConfig, NetworkHostAndPort("0.0.0.0", artemisPort), maxMessageSize) - val client = ArtemisMessagingClient(artemisConfig, NetworkHostAndPort("localhost", artemisPort), maxMessageSize) + val client = ArtemisMessagingClient(artemisConfig.p2pSslOptions, NetworkHostAndPort("localhost", artemisPort), maxMessageSize) server.start() client.start() return Pair(server, client) } private fun createClient(maxMessageSize: Int = MAX_MESSAGE_SIZE): AMQPClient { + val baseDirectory = temporaryFolder.root.toPath() / "client" + val certificatesDirectory = baseDirectory / "certificates" + val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory) + val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory) val clientConfig = rigorousMock<AbstractNodeConfiguration>().also { - doReturn(temporaryFolder.root.toPath() / "client").whenever(it).baseDirectory + doReturn(baseDirectory).whenever(it).baseDirectory + doReturn(certificatesDirectory).whenever(it).certificatesDirectory doReturn(BOB_NAME).whenever(it).myLegalName - doReturn("trustpass").whenever(it).trustStorePassword - doReturn("cordacadevpass").whenever(it).keyStorePassword + doReturn(signingCertificateStore).whenever(it).signingCertificateStore + doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions doReturn(true).whenever(it).crlCheckSoftFail } clientConfig.configureWithDevSSLCertificate() - val clientTruststore = clientConfig.loadTrustStore().internal - val clientKeystore = clientConfig.loadSslKeyStore().internal + val clientTruststore = clientConfig.p2pSslOptions.trustStore.get() + val clientKeystore = clientConfig.p2pSslOptions.keyStore.get() val amqpConfig = object : AMQPConfiguration { - override val keyStore: KeyStore = clientKeystore - override val keyStorePrivateKeyPassword: CharArray = clientConfig.keyStorePassword.toCharArray() - override val trustStore: KeyStore = clientTruststore + override val keyStore = clientKeystore + override val trustStore = clientTruststore override val trace: Boolean = true override val maxMessageSize: Int = maxMessageSize } @@ -453,21 +460,25 @@ class ProtonWrapperTests { } private fun createSharedThreadsClient(sharedEventGroup: EventLoopGroup, id: Int, maxMessageSize: Int = MAX_MESSAGE_SIZE): AMQPClient { + val baseDirectory = temporaryFolder.root.toPath() / "client_%$id" + val certificatesDirectory = baseDirectory / "certificates" + val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory) + val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory) val clientConfig = rigorousMock<AbstractNodeConfiguration>().also { - doReturn(temporaryFolder.root.toPath() / "client_%$id").whenever(it).baseDirectory + doReturn(baseDirectory).whenever(it).baseDirectory + doReturn(certificatesDirectory).whenever(it).certificatesDirectory doReturn(CordaX500Name(null, "client $id", "Corda", "London", null, "GB")).whenever(it).myLegalName - doReturn("trustpass").whenever(it).trustStorePassword - doReturn("cordacadevpass").whenever(it).keyStorePassword + doReturn(signingCertificateStore).whenever(it).signingCertificateStore + doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions doReturn(true).whenever(it).crlCheckSoftFail } clientConfig.configureWithDevSSLCertificate() - val clientTruststore = clientConfig.loadTrustStore().internal - val clientKeystore = clientConfig.loadSslKeyStore().internal + val clientTruststore = clientConfig.p2pSslOptions.trustStore.get() + val clientKeystore = clientConfig.p2pSslOptions.keyStore.get() val amqpConfig = object : AMQPConfiguration { - override val keyStore: KeyStore = clientKeystore - override val keyStorePrivateKeyPassword: CharArray = clientConfig.keyStorePassword.toCharArray() - override val trustStore: KeyStore = clientTruststore + override val keyStore = clientKeystore + override val trustStore = clientTruststore override val trace: Boolean = true override val maxMessageSize: Int = maxMessageSize } @@ -478,25 +489,26 @@ class ProtonWrapperTests { sharedThreadPool = sharedEventGroup) } - - private fun createServer(port: Int, name: CordaX500Name = ALICE_NAME, maxMessageSize: Int = MAX_MESSAGE_SIZE, - crlCheckSoftFail: Boolean = true - ): AMQPServer { + private fun createServer(port: Int, name: CordaX500Name = ALICE_NAME, maxMessageSize: Int = MAX_MESSAGE_SIZE, crlCheckSoftFail: Boolean = true): AMQPServer { + val baseDirectory = temporaryFolder.root.toPath() / "server" + val certificatesDirectory = baseDirectory / "certificates" + val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory) + val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory) val serverConfig = rigorousMock<AbstractNodeConfiguration>().also { - doReturn(temporaryFolder.root.toPath() / "server").whenever(it).baseDirectory + doReturn(baseDirectory).whenever(it).baseDirectory + doReturn(certificatesDirectory).whenever(it).certificatesDirectory doReturn(name).whenever(it).myLegalName - doReturn("trustpass").whenever(it).trustStorePassword - doReturn("cordacadevpass").whenever(it).keyStorePassword + doReturn(signingCertificateStore).whenever(it).signingCertificateStore + doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions doReturn(crlCheckSoftFail).whenever(it).crlCheckSoftFail } serverConfig.configureWithDevSSLCertificate() - val serverTruststore = serverConfig.loadTrustStore().internal - val serverKeystore = serverConfig.loadSslKeyStore().internal + val serverTruststore = serverConfig.p2pSslOptions.trustStore.get() + val serverKeystore = serverConfig.p2pSslOptions.keyStore.get() val amqpConfig = object : AMQPConfiguration { - override val keyStore: KeyStore = serverKeystore - override val keyStorePrivateKeyPassword: CharArray = serverConfig.keyStorePassword.toCharArray() - override val trustStore: KeyStore = serverTruststore + override val keyStore = serverKeystore + override val trustStore = serverTruststore override val trace: Boolean = true override val maxMessageSize: Int = maxMessageSize } diff --git a/node/src/integration-test/kotlin/net/corda/node/amqp/SocksTests.kt b/node/src/integration-test/kotlin/net/corda/node/amqp/SocksTests.kt index 3c5f69db3a..006ee4c50c 100644 --- a/node/src/integration-test/kotlin/net/corda/node/amqp/SocksTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/amqp/SocksTests.kt @@ -26,6 +26,7 @@ import net.corda.nodeapi.internal.protonwrapper.netty.* import net.corda.testing.core.* import net.corda.testing.driver.PortAllocation import net.corda.testing.internal.rigorousMock +import net.corda.testing.internal.stubs.CertificateStoreStubs import org.apache.activemq.artemis.api.core.RoutingType import org.junit.After import org.junit.Assert.assertArrayEquals @@ -33,7 +34,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder -import java.security.KeyStore import kotlin.test.assertEquals class SocksTests { @@ -266,11 +266,17 @@ class SocksTests { } private fun createArtemisServerAndClient(): Pair<ArtemisMessagingServer, ArtemisMessagingClient> { + val baseDirectory = temporaryFolder.root.toPath() / "artemis" + val certificatesDirectory = baseDirectory / "certificates" + val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory) + val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory) + val artemisConfig = rigorousMock<AbstractNodeConfiguration>().also { - doReturn(temporaryFolder.root.toPath() / "artemis").whenever(it).baseDirectory + doReturn(baseDirectory).whenever(it).baseDirectory + doReturn(certificatesDirectory).whenever(it).certificatesDirectory doReturn(CHARLIE_NAME).whenever(it).myLegalName - doReturn("trustpass").whenever(it).trustStorePassword - doReturn("cordacadevpass").whenever(it).keyStorePassword + doReturn(signingCertificateStore).whenever(it).signingCertificateStore + doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions doReturn(NetworkHostAndPort("0.0.0.0", artemisPort)).whenever(it).p2pAddress doReturn(null).whenever(it).jmxMonitoringHttpPort doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000))).whenever(it).enterpriseConfiguration @@ -278,27 +284,32 @@ class SocksTests { artemisConfig.configureWithDevSSLCertificate() val server = ArtemisMessagingServer(artemisConfig, NetworkHostAndPort("0.0.0.0", artemisPort), MAX_MESSAGE_SIZE) - val client = ArtemisMessagingClient(artemisConfig, NetworkHostAndPort("localhost", artemisPort), MAX_MESSAGE_SIZE) + val client = ArtemisMessagingClient(artemisConfig.p2pSslOptions, NetworkHostAndPort("localhost", artemisPort), MAX_MESSAGE_SIZE) server.start() client.start() return Pair(server, client) } private fun createClient(): AMQPClient { + val baseDirectory = temporaryFolder.root.toPath() / "client" + val certificatesDirectory = baseDirectory / "certificates" + val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory) + val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory) + val clientConfig = rigorousMock<AbstractNodeConfiguration>().also { doReturn(temporaryFolder.root.toPath() / "client").whenever(it).baseDirectory + doReturn(certificatesDirectory).whenever(it).certificatesDirectory doReturn(BOB_NAME).whenever(it).myLegalName - doReturn("trustpass").whenever(it).trustStorePassword - doReturn("cordacadevpass").whenever(it).keyStorePassword + doReturn(signingCertificateStore).whenever(it).signingCertificateStore + doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions } clientConfig.configureWithDevSSLCertificate() - val clientTruststore = clientConfig.loadTrustStore().internal - val clientKeystore = clientConfig.loadSslKeyStore().internal + val clientTruststore = clientConfig.p2pSslOptions.trustStore.get() + val clientKeystore = clientConfig.p2pSslOptions.keyStore.get() val amqpConfig = object : AMQPConfiguration { - override val keyStore: KeyStore = clientKeystore - override val keyStorePrivateKeyPassword: CharArray = clientConfig.keyStorePassword.toCharArray() - override val trustStore: KeyStore = clientTruststore + override val keyStore = clientKeystore + override val trustStore = clientTruststore override val trace: Boolean = true override val maxMessageSize: Int = MAX_MESSAGE_SIZE override val socksProxyConfig: SocksProxyConfig? = SocksProxyConfig(SocksProxyVersion.SOCKS5, NetworkHostAndPort("127.0.0.1", socksPort), null, null) @@ -312,20 +323,25 @@ class SocksTests { } private fun createSharedThreadsClient(sharedEventGroup: EventLoopGroup, id: Int): AMQPClient { + val baseDirectory = temporaryFolder.root.toPath() / "client_%$id" + val certificatesDirectory = baseDirectory / "certificates" + val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory) + val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory) + val clientConfig = rigorousMock<AbstractNodeConfiguration>().also { - doReturn(temporaryFolder.root.toPath() / "client_%$id").whenever(it).baseDirectory + doReturn(baseDirectory).whenever(it).baseDirectory + doReturn(certificatesDirectory).whenever(it).certificatesDirectory doReturn(CordaX500Name(null, "client $id", "Corda", "London", null, "GB")).whenever(it).myLegalName - doReturn("trustpass").whenever(it).trustStorePassword - doReturn("cordacadevpass").whenever(it).keyStorePassword + doReturn(signingCertificateStore).whenever(it).signingCertificateStore + doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions } clientConfig.configureWithDevSSLCertificate() - val clientTruststore = clientConfig.loadTrustStore().internal - val clientKeystore = clientConfig.loadSslKeyStore().internal + val clientTruststore = clientConfig.p2pSslOptions.trustStore.get() + val clientKeystore = clientConfig.p2pSslOptions.keyStore.get() val amqpConfig = object : AMQPConfiguration { - override val keyStore: KeyStore = clientKeystore - override val keyStorePrivateKeyPassword: CharArray = clientConfig.keyStorePassword.toCharArray() - override val trustStore: KeyStore = clientTruststore + override val keyStore = clientKeystore + override val trustStore = clientTruststore override val trace: Boolean = true override val maxMessageSize: Int = MAX_MESSAGE_SIZE override val socksProxyConfig: SocksProxyConfig? = SocksProxyConfig(SocksProxyVersion.SOCKS5, NetworkHostAndPort("127.0.0.1", socksPort), null, null) @@ -339,20 +355,25 @@ class SocksTests { } private fun createServer(port: Int, name: CordaX500Name = ALICE_NAME): AMQPServer { + val baseDirectory = temporaryFolder.root.toPath() / "server" + val certificatesDirectory = baseDirectory / "certificates" + val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory) + val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory) + val serverConfig = rigorousMock<AbstractNodeConfiguration>().also { - doReturn(temporaryFolder.root.toPath() / "server").whenever(it).baseDirectory + doReturn(baseDirectory).whenever(it).baseDirectory + doReturn(certificatesDirectory).whenever(it).certificatesDirectory doReturn(name).whenever(it).myLegalName - doReturn("trustpass").whenever(it).trustStorePassword - doReturn("cordacadevpass").whenever(it).keyStorePassword + doReturn(signingCertificateStore).whenever(it).signingCertificateStore + doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions } serverConfig.configureWithDevSSLCertificate() - val serverTruststore = serverConfig.loadTrustStore().internal - val serverKeystore = serverConfig.loadSslKeyStore().internal + val serverTruststore = serverConfig.p2pSslOptions.trustStore.get() + val serverKeystore = serverConfig.p2pSslOptions.keyStore.get() val amqpConfig = object : AMQPConfiguration { - override val keyStore: KeyStore = serverKeystore - override val keyStorePrivateKeyPassword: CharArray = serverConfig.keyStorePassword.toCharArray() - override val trustStore: KeyStore = serverTruststore + override val keyStore = serverKeystore + override val trustStore = serverTruststore override val trace: Boolean = true override val maxMessageSize: Int = MAX_MESSAGE_SIZE } diff --git a/node/src/integration-test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTest.kt index 82b5e96725..0ca24ab327 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTest.kt @@ -5,6 +5,7 @@ import com.nhaarman.mockito_kotlin.doReturn import com.nhaarman.mockito_kotlin.whenever import com.typesafe.config.ConfigFactory import net.corda.core.crypto.generateKeyPair +import net.corda.core.internal.div import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.seconds import net.corda.node.internal.configureDatabase @@ -19,6 +20,7 @@ import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.MAX_MESSAGE_SIZE import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.driver.PortAllocation +import net.corda.testing.internal.stubs.CertificateStoreStubs import net.corda.testing.internal.LogHelper import net.corda.testing.internal.rigorousMock import net.corda.testing.node.internal.MOCK_VERSION_INFO @@ -69,11 +71,18 @@ class ArtemisMessagingTest { @Before fun setUp() { abstract class AbstractNodeConfiguration : NodeConfiguration + + val baseDirectory = temporaryFolder.root.toPath() + val certificatesDirectory = baseDirectory / "certificates" + val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory) + val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory) + config = rigorousMock<AbstractNodeConfiguration>().also { doReturn(temporaryFolder.root.toPath()).whenever(it).baseDirectory doReturn(ALICE_NAME).whenever(it).myLegalName - doReturn("trustpass").whenever(it).trustStorePassword - doReturn("cordacadevpass").whenever(it).keyStorePassword + doReturn(certificatesDirectory).whenever(it).certificatesDirectory + doReturn(signingCertificateStore).whenever(it).signingCertificateStore + doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions doReturn(NetworkHostAndPort("0.0.0.0", serverPort)).whenever(it).p2pAddress doReturn(null).whenever(it).jmxMonitoringHttpPort doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000))).whenever(it).enterpriseConfiguration diff --git a/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt index b556a6f2c1..6a63669bce 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt @@ -1,7 +1,5 @@ package net.corda.node.services.network -import net.corda.cordform.CordformNode -import net.corda.core.concurrent.CordaFuture import net.corda.core.crypto.random63BitValue import net.corda.core.internal.* import net.corda.core.internal.concurrent.transpose @@ -10,9 +8,7 @@ import net.corda.core.node.NodeInfo import net.corda.core.serialization.serialize import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.seconds -import net.corda.node.services.config.configureDevKeyAndTrustStores import net.corda.nodeapi.internal.NODE_INFO_DIRECTORY -import net.corda.nodeapi.internal.config.NodeSSLConfiguration import net.corda.nodeapi.internal.network.NETWORK_PARAMS_FILE_NAME import net.corda.nodeapi.internal.network.NETWORK_PARAMS_UPDATE_FILE_NAME import net.corda.nodeapi.internal.network.SignedNetworkParameters @@ -26,7 +22,6 @@ import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.internal.toDatabaseSchemaName import net.corda.testing.node.internal.* import net.corda.testing.node.internal.network.NetworkMapServer -import net.corda.testing.node.internal.startNode import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.* diff --git a/node/src/integration-test/kotlin/net/corda/node/services/rpc/ArtemisRpcTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/rpc/ArtemisRpcTests.kt index 9a6192b4b1..c64a3773f5 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/rpc/ArtemisRpcTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/rpc/ArtemisRpcTests.kt @@ -17,13 +17,13 @@ import net.corda.node.services.messaging.RPCServerConfiguration import net.corda.node.utilities.createKeyPairAndSelfSignedTLSCertificate import net.corda.node.utilities.saveToKeyStore import net.corda.node.utilities.saveToTrustStore -import net.corda.nodeapi.ArtemisTcpTransport.Companion.rpcConnectorTcpTransport import net.corda.nodeapi.BrokerRpcSslOptions -import net.corda.nodeapi.internal.config.SSLConfiguration +import net.corda.nodeapi.internal.InternalArtemisTcpTransport.Companion.rpcConnectorTcpTransport +import net.corda.nodeapi.internal.config.MutualSslConfiguration import net.corda.nodeapi.internal.config.User import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.driver.PortAllocation -import net.corda.testing.internal.createNodeSslConfig +import net.corda.testing.internal.p2pSslOptions import org.apache.activemq.artemis.api.core.ActiveMQConnectionTimedOutException import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl import org.assertj.core.api.Assertions.assertThat @@ -58,12 +58,12 @@ class ArtemisRpcTests { val brokerSslOptions = BrokerRpcSslOptions(keyStorePath, "password") val trustStorePath = saveToTrustStore(tempFile("rpcTruststore.jks"), selfSignCert) val clientSslOptions = ClientRpcSslOptions(trustStorePath, "password") - testSslCommunication(createNodeSslConfig(tempFolder.root.toPath()), brokerSslOptions, true, clientSslOptions) + testSslCommunication(p2pSslOptions(tempFolder.root.toPath()), brokerSslOptions, true, clientSslOptions) } @Test fun rpc_with_ssl_disabled() { - testSslCommunication(createNodeSslConfig(tempFolder.root.toPath()), null, false, null) + testSslCommunication(p2pSslOptions(tempFolder.root.toPath()), null, false, null) } @Test @@ -73,7 +73,7 @@ class ArtemisRpcTests { val brokerSslOptions = BrokerRpcSslOptions(keyStorePath, "password") // here client sslOptions are passed null (as in, do not use SSL) assertThatThrownBy { - testSslCommunication(createNodeSslConfig(tempFolder.root.toPath()), brokerSslOptions, true, null) + testSslCommunication(p2pSslOptions(tempFolder.root.toPath()), brokerSslOptions, true, null) }.isInstanceOf(ActiveMQConnectionTimedOutException::class.java) } @@ -91,11 +91,11 @@ class ArtemisRpcTests { val clientSslOptions = ClientRpcSslOptions(trustStorePath, "password") assertThatThrownBy { - testSslCommunication(createNodeSslConfig(tempFolder.root.toPath()), brokerSslOptions, true, clientSslOptions) + testSslCommunication(p2pSslOptions(tempFolder.root.toPath()), brokerSslOptions, true, clientSslOptions) }.isInstanceOf(RPCException::class.java) } - private fun testSslCommunication(nodeSSlconfig: SSLConfiguration, + private fun testSslCommunication(nodeSSlconfig: MutualSslConfiguration, brokerSslOptions: BrokerRpcSslOptions?, useSslForBroker: Boolean, clientSslOptions: ClientRpcSslOptions?, diff --git a/node/src/integration-test/kotlin/net/corda/node/services/vault/VaultQueryIntegrationTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/vault/VaultQueryIntegrationTests.kt index da7322d334..0bf1291493 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/vault/VaultQueryIntegrationTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/vault/VaultQueryIntegrationTests.kt @@ -1,29 +1,29 @@ -package net.corda.node.services.vault - - -import net.corda.core.identity.CordaX500Name -import net.corda.testing.core.TestIdentity -import net.corda.testing.internal.GlobalDatabaseRule -import net.corda.testing.internal.toDatabaseSchemaName -import org.junit.ClassRule -import org.junit.Rule -import org.junit.rules.RuleChain - -class VaultQueryIntegrationTests : VaultQueryTestsBase(), VaultQueryParties by vaultQueryTestRule { - - companion object { - val MEGA_CORP = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).name - val databaseSchemas = listOf(Companion.MEGA_CORP.toDatabaseSchemaName()) - - val globalDatabaseRule = GlobalDatabaseRule(databaseSchemas) - val vaultQueryTestRule = VaultQueryTestRule() - - @ClassRule @JvmField - val ruleChain = RuleChain.outerRule(globalDatabaseRule).around(vaultQueryTestRule) - } - - @Suppress("LeakingThis") - @Rule - @JvmField - val transactionRule = VaultQueryRollbackRule(this) -} +//package net.corda.node.services.vault +// +// +//import net.corda.core.identity.CordaX500Name +//import net.corda.testing.core.TestIdentity +//import net.corda.testing.internal.GlobalDatabaseRule +//import net.corda.testing.internal.toDatabaseSchemaName +//import org.junit.ClassRule +//import org.junit.Rule +//import org.junit.rules.RuleChain +// +//class VaultQueryIntegrationTests : VaultQueryTestsBase(), VaultQueryParties by vaultQueryTestRule { +// +// companion object { +// val MEGA_CORP = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).name +// val databaseSchemas = listOf(Companion.MEGA_CORP.toDatabaseSchemaName()) +// +// val globalDatabaseRule = GlobalDatabaseRule(databaseSchemas) +// val vaultQueryTestRule = VaultQueryTestRule() +// +// @ClassRule @JvmField +// val ruleChain = RuleChain.outerRule(globalDatabaseRule).around(vaultQueryTestRule) +// } +// +// @Suppress("LeakingThis") +// @Rule +// @JvmField +// val transactionRule = VaultQueryRollbackRule(this) +//} diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt index feda5b405f..7fb2986bc4 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt @@ -2,7 +2,6 @@ package net.corda.services.messaging import net.corda.core.crypto.Crypto import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.copyTo import net.corda.core.internal.createDirectories import net.corda.core.internal.exists import net.corda.core.internal.toX500Name @@ -11,9 +10,10 @@ import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_P2P_U import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEER_USER import net.corda.nodeapi.internal.DEV_INTERMEDIATE_CA import net.corda.nodeapi.internal.DEV_ROOT_CA -import net.corda.nodeapi.internal.config.SSLConfiguration import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509Utilities +import net.corda.nodeapi.internal.loadDevCaTrustStore +import net.corda.testing.internal.stubs.CertificateStoreStubs import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration import org.apache.activemq.artemis.api.core.ActiveMQClusterSecurityException import org.apache.activemq.artemis.api.core.ActiveMQNotConnectedException @@ -84,57 +84,35 @@ class MQSecurityAsNodeTest : P2PMQSecurityTest() { @Test fun `login with invalid certificate chain`() { - val sslConfig = object : SSLConfiguration { - override val certificatesDirectory = Files.createTempDirectory("certs") - override val keyStorePassword: String get() = "cordacadevpass" - override val trustStorePassword: String get() = "trustpass" - override val crlCheckSoftFail: Boolean = true + val certsDir = Files.createTempDirectory("certs") + certsDir.createDirectories() + val signingCertStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certsDir) + val p2pSslConfig = CertificateStoreStubs.P2P.withCertificatesDirectory(certsDir) - init { - val legalName = CordaX500Name("MegaCorp", "London", "GB") - certificatesDirectory.createDirectories() - if (!trustStoreFile.exists()) { - javaClass.classLoader.getResourceAsStream("certificates/cordatruststore.jks").use { it.copyTo(trustStoreFile) } - } - - val clientKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - // Set name constrain to the legal name. - val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.toX500Name()))), arrayOf()) - val clientCACert = X509Utilities.createCertificate( - CertificateType.INTERMEDIATE_CA, - DEV_INTERMEDIATE_CA.certificate, - DEV_INTERMEDIATE_CA.keyPair, - legalName.x500Principal, - clientKeyPair.public, - nameConstraints = nameConstraints) - - val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - // Using different x500 name in the TLS cert which is not allowed in the name constraints. - val clientTLSCert = X509Utilities.createCertificate( - CertificateType.TLS, - clientCACert, - clientKeyPair, - CordaX500Name("MiniCorp", "London", "GB").x500Principal, - tlsKeyPair.public) - - loadNodeKeyStore(createNew = true).update { - setPrivateKey( - X509Utilities.CORDA_CLIENT_CA, - clientKeyPair.private, - listOf(clientCACert, DEV_INTERMEDIATE_CA.certificate, DEV_ROOT_CA.certificate)) - } - - loadSslKeyStore(createNew = true).update { - setPrivateKey( - X509Utilities.CORDA_CLIENT_TLS, - tlsKeyPair.private, - listOf(clientTLSCert, clientCACert, DEV_INTERMEDIATE_CA.certificate, DEV_ROOT_CA.certificate)) - } - } + val legalName = CordaX500Name("MegaCorp", "London", "GB") + if (!p2pSslConfig.trustStore.path.exists()) { + val trustStore = p2pSslConfig.trustStore.get(true) + loadDevCaTrustStore().copyTo(trustStore) } - val attacker = clientTo(alice.node.configuration.p2pAddress, sslConfig) + val clientKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) + // Set name constrain to the legal name. + val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.toX500Name()))), arrayOf()) + val clientCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, DEV_INTERMEDIATE_CA.certificate, DEV_INTERMEDIATE_CA.keyPair, legalName.x500Principal, clientKeyPair.public, nameConstraints = nameConstraints) + val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) + // Using different x500 name in the TLS cert which is not allowed in the name constraints. + val clientTLSCert = X509Utilities.createCertificate(CertificateType.TLS, clientCACert, clientKeyPair, CordaX500Name("MiniCorp", "London", "GB").x500Principal, tlsKeyPair.public) + + signingCertStore.get(createNew = true).update { + setPrivateKey(X509Utilities.CORDA_CLIENT_CA, clientKeyPair.private, listOf(clientCACert, DEV_INTERMEDIATE_CA.certificate, DEV_ROOT_CA.certificate)) + } + + p2pSslConfig.keyStore.get(createNew = true).update { + setPrivateKey(X509Utilities.CORDA_CLIENT_TLS, tlsKeyPair.private, listOf(clientTLSCert, clientCACert, DEV_INTERMEDIATE_CA.certificate, DEV_ROOT_CA.certificate)) + } + + val attacker = clientTo(alice.node.configuration.p2pAddress, p2pSslConfig) assertThatExceptionOfType(ActiveMQNotConnectedException::class.java).isThrownBy { attacker.start(PEER_USER, PEER_USER) } diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt index 2ba6d4c917..437239e1ad 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt @@ -18,7 +18,7 @@ import net.corda.node.internal.NodeWithInfo import net.corda.nodeapi.RPCApi import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.INTERNAL_PREFIX import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NOTIFICATIONS_ADDRESS -import net.corda.nodeapi.internal.config.SSLConfiguration +import net.corda.nodeapi.internal.config.MutualSslConfiguration import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.BOB_NAME import net.corda.testing.internal.IntegrationTestSchemas @@ -103,7 +103,7 @@ abstract class MQSecurityTest : NodeBasedTest() { assertAllQueueCreationAttacksFail(randomQueue) } - fun clientTo(target: NetworkHostAndPort, sslConfiguration: SSLConfiguration? = configureTestSSL(CordaX500Name("MegaCorp", "London", "GB"))): SimpleMQClient { + fun clientTo(target: NetworkHostAndPort, sslConfiguration: MutualSslConfiguration? = configureTestSSL(CordaX500Name("MegaCorp", "London", "GB"))): SimpleMQClient { val client = SimpleMQClient(target, sslConfiguration) clients += client return client diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/SimpleMQClient.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/SimpleMQClient.kt index 58e04948c4..e70e77d6b5 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/SimpleMQClient.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/SimpleMQClient.kt @@ -3,8 +3,8 @@ package net.corda.services.messaging import net.corda.core.identity.CordaX500Name import net.corda.core.serialization.internal.nodeSerializationEnv import net.corda.core.utilities.NetworkHostAndPort -import net.corda.nodeapi.ArtemisTcpTransport -import net.corda.nodeapi.internal.config.SSLConfiguration +import net.corda.nodeapi.internal.InternalArtemisTcpTransport.Companion.p2pConnectorTcpTransport +import net.corda.nodeapi.internal.config.MutualSslConfiguration import net.corda.testing.internal.configureTestSSL import org.apache.activemq.artemis.api.core.client.* @@ -12,7 +12,7 @@ import org.apache.activemq.artemis.api.core.client.* * As the name suggests this is a simple client for connecting to MQ brokers. */ class SimpleMQClient(val target: NetworkHostAndPort, - private val config: SSLConfiguration? = configureTestSSL(DEFAULT_MQ_LEGAL_NAME)) { + private val config: MutualSslConfiguration? = configureTestSSL(DEFAULT_MQ_LEGAL_NAME)) { companion object { val DEFAULT_MQ_LEGAL_NAME = CordaX500Name(organisation = "SimpleMQClient", locality = "London", country = "GB") } @@ -22,7 +22,7 @@ class SimpleMQClient(val target: NetworkHostAndPort, lateinit var producer: ClientProducer fun start(username: String? = null, password: String? = null, enableSSL: Boolean = true) { - val tcpTransport = ArtemisTcpTransport.p2pConnectorTcpTransport(target, config, enableSSL = enableSSL) + val tcpTransport = p2pConnectorTcpTransport(target, config, enableSSL = enableSSL) val locator = ActiveMQClient.createServerLocatorWithoutHA(tcpTransport).apply { isBlockOnNonDurableSend = true threadPoolMaxSize = 1 diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 2c47696745..215ecc19e4 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -46,6 +46,7 @@ import net.corda.node.services.FinalityHandler import net.corda.node.services.NotaryChangeHandler import net.corda.node.services.api.* import net.corda.node.services.config.* +import net.corda.node.services.config.rpc.NodeRpcOptions import net.corda.node.services.config.shell.toShellConfig import net.corda.node.services.events.NodeSchedulerService import net.corda.node.services.events.ScheduledActivityObserver @@ -257,7 +258,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration, val trustRoot = initKeyStore() val (identity, identityKeyPair) = obtainIdentity(notaryConfig = null) startDatabase() - val nodeCa = configuration.loadNodeKeyStore().getCertificate(X509Utilities.CORDA_CLIENT_CA) + val nodeCa = configuration.signingCertificateStore.get()[X509Utilities.CORDA_CLIENT_CA] identityService.start(trustRoot, listOf(identity.certificate, nodeCa)) return database.use { it.transaction { @@ -287,7 +288,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration, log.info("Node starting up ...") val trustRoot = initKeyStore() - val nodeCa = configuration.loadNodeKeyStore().getCertificate(X509Utilities.CORDA_CLIENT_CA) + val nodeCa = configuration.signingCertificateStore.get()[X509Utilities.CORDA_CLIENT_CA] initialiseJVMAgents() schemaService.mappedSchemasWarnings().forEach { @@ -392,10 +393,10 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration, open fun startShell() { if (configuration.shouldInitCrashShell()) { val shellConfiguration = configuration.toShellConfig() - shellConfiguration.sshHostKeyDirectory?.let { + shellConfiguration.sshdPort?.let { log.info("Binding Shell SSHD server on port $it.") } - InteractiveShell.startShellInternal(shellConfiguration, cordappLoader.appClassLoader) + InteractiveShell.startShell(shellConfiguration, cordappLoader.appClassLoader) } } @@ -720,8 +721,8 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration, private fun validateKeyStore(): X509Certificate { val containCorrectKeys = try { // This will throw IOException if key file not found or KeyStoreException if keystore password is incorrect. - val sslKeystore = configuration.loadSslKeyStore() - val identitiesKeystore = configuration.loadNodeKeyStore() + val sslKeystore = configuration.p2pSslOptions.keyStore.get() + val identitiesKeystore = configuration.signingCertificateStore.get() X509Utilities.CORDA_CLIENT_TLS in sslKeystore && X509Utilities.CORDA_CLIENT_CA in identitiesKeystore } catch (e: KeyStoreException) { log.warn("Certificate key store found but key store password does not match configuration.") @@ -738,15 +739,15 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration, } // Check all cert path chain to the trusted root - val sslCertChainRoot = configuration.loadSslKeyStore().getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).last() - val nodeCaCertChainRoot = configuration.loadNodeKeyStore().getCertificateChain(X509Utilities.CORDA_CLIENT_CA).last() - val trustRoot = configuration.loadTrustStore().getCertificate(X509Utilities.CORDA_ROOT_CA) + val sslCertChainRoot = configuration.p2pSslOptions.keyStore.get().query { getCertificateChain(X509Utilities.CORDA_CLIENT_TLS) }.last() + val nodeCaCertChainRoot = configuration.signingCertificateStore.get().query { getCertificateChain(X509Utilities.CORDA_CLIENT_CA) }.last() + val trustRoot = configuration.p2pSslOptions.trustStore.get()[X509Utilities.CORDA_ROOT_CA] require(sslCertChainRoot == trustRoot) { "TLS certificate must chain to the trusted root." } require(nodeCaCertChainRoot == trustRoot) { "Client CA certificate must chain to the trusted root." } if (configuration.devMode) { - val blacklisted = isCRLDistributionPointBlacklisted(configuration.loadNodeKeyStore().getCertificateChain(X509Utilities.CORDA_CLIENT_CA)) + val blacklisted = isCRLDistributionPointBlacklisted(configuration.signingCertificateStore.get().query { getCertificateChain(X509Utilities.CORDA_CLIENT_CA) }) if (blacklisted) { log.warn("The format of the autogenerated dev. mode certificate this system uses has been deprecated. Please contact support@r3.com for information on how to upgrade.") } @@ -797,7 +798,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration, return notaryConfig.run { when { raft != null -> { - val uniquenessProvider = RaftUniquenessProvider(configuration, database, platformClock, monitoringService.metrics, raft) + val uniquenessProvider = RaftUniquenessProvider(configuration.baseDirectory, configuration.p2pSslOptions, database, platformClock, monitoringService.metrics, raft) (if (validating) ::RaftValidatingNotaryService else ::RaftNonValidatingNotaryService)(services, notaryKey, uniquenessProvider) } bftSMaRt != null -> { @@ -844,7 +845,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration, networkParameters: NetworkParameters) private fun obtainIdentity(notaryConfig: NotaryConfig?): Pair<PartyAndCertificate, KeyPair> { - val keyStore = configuration.loadNodeKeyStore() + val keyStore = configuration.signingCertificateStore.get() val (id, singleName) = if (notaryConfig == null || !notaryConfig.isClusterConfig) { // Node's main identity or if it's a single node notary @@ -859,25 +860,25 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration, if (privateKeyAlias !in keyStore) { singleName ?: throw IllegalArgumentException( "Unable to find in the key store the identity of the distributed notary the node is part of") - log.info("$privateKeyAlias not found in key store ${configuration.nodeKeystore}, generating fresh key!") + log.info("$privateKeyAlias not found in key store, generating fresh key!") // TODO This check shouldn't be needed check(singleName == configuration.myLegalName) keyStore.storeLegalIdentity(privateKeyAlias, generateKeyPair()) } - val (x509Cert, keyPair) = keyStore.getCertificateAndKeyPair(privateKeyAlias) + val (x509Cert, keyPair) = keyStore.query { getCertificateAndKeyPair(privateKeyAlias) } // TODO: Use configuration to indicate composite key should be used instead of public key for the identity. val compositeKeyAlias = "$id-composite-key" val certificates = if (compositeKeyAlias in keyStore) { // Use composite key instead if it exists - val certificate = keyStore.getCertificate(compositeKeyAlias) + val certificate = keyStore[compositeKeyAlias] // We have to create the certificate chain for the composite key manually, this is because we don't have a keystore // provider that understand compositeKey-privateKey combo. The cert chain is created using the composite key certificate + // the tail of the private key certificates, as they are both signed by the same certificate chain. - listOf(certificate) + keyStore.getCertificateChain(privateKeyAlias).drop(1) + listOf(certificate) + keyStore.query { getCertificateChain(privateKeyAlias) }.drop(1) } else { - keyStore.getCertificateChain(privateKeyAlias).let { + keyStore.query { getCertificateChain(privateKeyAlias) }.let { check(it[0] == x509Cert) { "Certificates from key store do not line up!" } it } @@ -1075,4 +1076,13 @@ fun CordaPersistence.startHikariPool(hikariProperties: Properties, databaseConfi else -> throw CouldNotCreateDataSourceException("Could not create the DataSource: ${ex.message}", ex) } } +} + +fun clientSslOptionsCompatibleWith(nodeRpcOptions: NodeRpcOptions): ClientRpcSslOptions? { + + if (!nodeRpcOptions.useSsl || nodeRpcOptions.sslConfig == null) { + return null + } + // Here we're using the node's RPC key store as the RPC client's trust store. + return ClientRpcSslOptions(trustStorePath = nodeRpcOptions.sslConfig!!.keyStorePath, trustStorePassword = nodeRpcOptions.sslConfig!!.keyStorePassword) } \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/internal/Node.kt b/node/src/main/kotlin/net/corda/node/internal/Node.kt index 6757953e9a..51e84f2048 100644 --- a/node/src/main/kotlin/net/corda/node/internal/Node.kt +++ b/node/src/main/kotlin/net/corda/node/internal/Node.kt @@ -233,7 +233,7 @@ open class Node(configuration: NodeConfiguration, val externalBridge = configuration.enterpriseConfiguration.externalBridge val bridgeControlListener = if (externalBridge == null || !externalBridge) { - BridgeControlListener(configuration, network.serverAddress, networkParameters.maxMessageSize) + BridgeControlListener(configuration.p2pSslOptions, network.serverAddress, networkParameters.maxMessageSize) } else { null } @@ -244,7 +244,7 @@ open class Node(configuration: NodeConfiguration, rpcThreadPoolSize = configuration.enterpriseConfiguration.tuning.rpcThreadPoolSize ) rpcServerAddresses?.let { - internalRpcMessagingClient = InternalRPCMessagingClient(configuration, it.admin, MAX_RPC_MESSAGE_SIZE, CordaX500Name.build(configuration.loadSslKeyStore().getCertificate(X509Utilities.CORDA_CLIENT_TLS).subjectX500Principal), rpcServerConfiguration) + internalRpcMessagingClient = InternalRPCMessagingClient(configuration.p2pSslOptions, it.admin, MAX_RPC_MESSAGE_SIZE, CordaX500Name.build(configuration.p2pSslOptions.keyStore.get()[X509Utilities.CORDA_CLIENT_TLS].subjectX500Principal), rpcServerConfiguration) printBasicNodeInfo("RPC connection address", it.primary.toString()) printBasicNodeInfo("RPC admin connection address", it.admin.toString()) } @@ -284,9 +284,9 @@ open class Node(configuration: NodeConfiguration, val rpcBrokerDirectory: Path = baseDirectory / "brokers" / "rpc" with(rpcOptions) { rpcBroker = if (useSsl) { - ArtemisRpcBroker.withSsl(configuration, this.address, adminAddress, sslConfig!!, securityManager, MAX_RPC_MESSAGE_SIZE, jmxMonitoringHttpPort != null, rpcBrokerDirectory, shouldStartLocalShell()) + ArtemisRpcBroker.withSsl(configuration.p2pSslOptions, this.address, adminAddress, sslConfig!!, securityManager, MAX_RPC_MESSAGE_SIZE, jmxMonitoringHttpPort != null, rpcBrokerDirectory, shouldStartLocalShell()) } else { - ArtemisRpcBroker.withoutSsl(configuration, this.address, adminAddress, securityManager, MAX_RPC_MESSAGE_SIZE, jmxMonitoringHttpPort != null, rpcBrokerDirectory, shouldStartLocalShell()) + ArtemisRpcBroker.withoutSsl(configuration.p2pSslOptions, this.address, adminAddress, securityManager, MAX_RPC_MESSAGE_SIZE, jmxMonitoringHttpPort != null, rpcBrokerDirectory, shouldStartLocalShell()) } } rpcBroker!!.closeOnStop() diff --git a/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt index 81c7992d13..96f0383fba 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt @@ -10,11 +10,10 @@ import net.corda.core.internal.createDirectories import net.corda.core.internal.div import net.corda.core.internal.exists import net.corda.nodeapi.internal.* -import net.corda.nodeapi.internal.config.SSLConfiguration +import net.corda.nodeapi.internal.config.FileBasedCertificateStoreSupplier +import net.corda.nodeapi.internal.config.MutualSslConfiguration import net.corda.nodeapi.internal.config.toProperties import net.corda.nodeapi.internal.crypto.X509KeyStore -import net.corda.nodeapi.internal.crypto.loadKeyStore -import net.corda.nodeapi.internal.crypto.save import org.slf4j.LoggerFactory import java.nio.file.Path import kotlin.math.min @@ -83,22 +82,33 @@ object ConfigHelper { * the CA certs in Node resources. Then provision KeyStores into certificates folder under node path. */ // TODO Move this to KeyStoreConfigHelpers -fun NodeConfiguration.configureWithDevSSLCertificate() = configureDevKeyAndTrustStores(myLegalName) +fun NodeConfiguration.configureWithDevSSLCertificate() = p2pSslOptions.configureDevKeyAndTrustStores(myLegalName, signingCertificateStore, certificatesDirectory) // TODO Move this to KeyStoreConfigHelpers -fun SSLConfiguration.configureDevKeyAndTrustStores(myLegalName: CordaX500Name) { +fun MutualSslConfiguration.configureDevKeyAndTrustStores(myLegalName: CordaX500Name, signingCertificateStore: FileBasedCertificateStoreSupplier, certificatesDirectory: Path) { + + val specifiedTrustStore = trustStore.getOptional() + + val specifiedKeyStore = keyStore.getOptional() + val specifiedSigningStore = signingCertificateStore.getOptional() + + if (specifiedTrustStore != null && specifiedKeyStore != null && specifiedSigningStore != null) return certificatesDirectory.createDirectories() - if (!trustStoreFile.exists()) { - loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/$DEV_CA_TRUST_STORE_FILE"), DEV_CA_TRUST_STORE_PASS).save(trustStoreFile, trustStorePassword) + + if (specifiedTrustStore == null) { + loadDevCaTrustStore().copyTo(trustStore.get(true)) } - if (!sslKeystore.exists() || !nodeKeystore.exists()) { - val (nodeKeyStore) = createDevKeyStores(myLegalName) + + if (keyStore.getOptional() == null || signingCertificateStore.getOptional() == null) { + val signingKeyStore = FileBasedCertificateStoreSupplier(signingCertificateStore.path, signingCertificateStore.password).get(true).also { it.registerDevSigningCertificates(myLegalName) } + + FileBasedCertificateStoreSupplier(keyStore.path, keyStore.password).get(true).also { it.registerDevP2pCertificates(myLegalName) } // Move distributed service composite key (generated by IdentityGenerator.generateToDisk) to keystore if exists. val distributedServiceKeystore = certificatesDirectory / "distributedService.jks" if (distributedServiceKeystore.exists()) { val serviceKeystore = X509KeyStore.fromFile(distributedServiceKeystore, DEV_CA_KEY_STORE_PASS) - nodeKeyStore.update { + signingKeyStore.update { serviceKeystore.aliases().forEach { if (serviceKeystore.internal.isKeyEntry(it)) { setPrivateKey(it, serviceKeystore.getPrivateKey(it, DEV_CA_PRIVATE_KEY_PASS), serviceKeystore.getCertificateChain(it)) diff --git a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt index ddecc2fd7d..b8cd514f49 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt @@ -11,7 +11,9 @@ import net.corda.core.utilities.loggerFor import net.corda.core.utilities.seconds import net.corda.node.services.config.rpc.NodeRpcOptions import net.corda.nodeapi.BrokerRpcSslOptions -import net.corda.nodeapi.internal.config.NodeSSLConfiguration +import net.corda.nodeapi.internal.config.FileBasedCertificateStoreSupplier +import net.corda.nodeapi.internal.config.SslConfiguration +import net.corda.nodeapi.internal.config.MutualSslConfiguration import net.corda.nodeapi.internal.config.UnknownConfigKeysPolicy import net.corda.nodeapi.internal.config.User import net.corda.nodeapi.internal.config.parseAs @@ -32,7 +34,7 @@ private val DEFAULT_FLOW_MONITOR_PERIOD_MILLIS: Duration = Duration.ofMinutes(1) private val DEFAULT_FLOW_MONITOR_SUSPENSION_LOGGING_THRESHOLD_MILLIS: Duration = Duration.ofMinutes(1) private const val CORDAPPS_DIR_NAME_DEFAULT = "cordapps" -interface NodeConfiguration : NodeSSLConfiguration { +interface NodeConfiguration { val myLegalName: CordaX500Name val emailAddress: String val jmxMonitoringHttpPort: Int? @@ -75,8 +77,16 @@ interface NodeConfiguration : NodeSSLConfiguration { val effectiveH2Settings: NodeH2Settings? val flowMonitorPeriodMillis: Duration get() = DEFAULT_FLOW_MONITOR_PERIOD_MILLIS val flowMonitorSuspensionLoggingThresholdMillis: Duration get() = DEFAULT_FLOW_MONITOR_SUSPENSION_LOGGING_THRESHOLD_MILLIS - val cordappDirectories: List<Path> get() = listOf(baseDirectory / CORDAPPS_DIR_NAME_DEFAULT) + val crlCheckSoftFail: Boolean val jmxReporterType : JmxReporterType? get() = defaultJmxReporterType + + val baseDirectory: Path + val certificatesDirectory: Path + val signingCertificateStore: FileBasedCertificateStoreSupplier + val p2pSslOptions: MutualSslConfiguration + + val cordappDirectories: List<Path> + fun validate(): List<String> companion object { @@ -162,7 +172,7 @@ data class MySQLConfiguration( * further commit requests. */ val maxQueueSize: Int = 100_000 - ) { +) { init { require(connectionRetries >= 0) { "connectionRetries cannot be negative" } } @@ -220,8 +230,8 @@ data class NodeConfigurationImpl( override val myLegalName: CordaX500Name, override val jmxMonitoringHttpPort: Int? = null, override val emailAddress: String, - override val keyStorePassword: String, - override val trustStorePassword: String, + private val keyStorePassword: String, + private val trustStorePassword: String, override val crlCheckSoftFail: Boolean, override val dataSourceProperties: Properties, override val compatibilityZoneURL: URL? = null, @@ -292,6 +302,17 @@ data class NodeConfigurationImpl( } } + override val certificatesDirectory = baseDirectory / "certificates" + + private val signingCertificateStorePath = certificatesDirectory / "nodekeystore.jks" + override val signingCertificateStore = FileBasedCertificateStoreSupplier(signingCertificateStorePath, keyStorePassword) + + private val p2pKeystorePath: Path get() = certificatesDirectory / "sslkeystore.jks" + private val p2pKeyStore = FileBasedCertificateStoreSupplier(p2pKeystorePath, keyStorePassword) + private val p2pTrustStoreFilePath: Path get() = certificatesDirectory / "truststore.jks" + private val p2pTrustStore = FileBasedCertificateStoreSupplier(p2pTrustStoreFilePath, trustStorePassword) + override val p2pSslOptions: MutualSslConfiguration = SslConfiguration.mutual(p2pKeyStore, p2pTrustStore) + override val rpcOptions: NodeRpcOptions get() { return actualRpcSettings.asOptions() @@ -430,8 +451,6 @@ data class NodeConfigurationImpl( } } - - data class NodeRpcSettings( val address: NetworkHostAndPort?, val adminAddress: NetworkHostAndPort?, diff --git a/node/src/main/kotlin/net/corda/node/services/config/shell/ShellConfig.kt b/node/src/main/kotlin/net/corda/node/services/config/shell/ShellConfig.kt index 5a0eaa4ac9..3138a29934 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/shell/ShellConfig.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/shell/ShellConfig.kt @@ -1,6 +1,7 @@ package net.corda.node.services.config.shell import net.corda.core.internal.div +import net.corda.node.internal.clientSslOptionsCompatibleWith import net.corda.node.services.config.NodeConfiguration import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.INTERNAL_SHELL_USER import net.corda.tools.shell.ShellConfiguration @@ -14,8 +15,8 @@ fun NodeConfiguration.toShellConfig() = ShellConfiguration( cordappsDirectory = this.baseDirectory.toString() / CORDAPPS_DIR, user = INTERNAL_SHELL_USER, password = INTERNAL_SHELL_USER, - hostAndPort = this.rpcOptions.adminAddress, - nodeSslConfig = this, + hostAndPort = this.rpcOptions.address, + ssl = clientSslOptionsCompatibleWith(this.rpcOptions), sshdPort = this.sshd?.port, sshHostKeyDirectory = this.baseDirectory / SSHD_HOSTKEY_DIR, noLocalShell = this.noLocalShell) diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt index 11c6b8a37c..0569e71258 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt @@ -1,6 +1,5 @@ package net.corda.node.services.messaging -import io.netty.channel.unix.Errors import net.corda.core.internal.ThreadBox import net.corda.core.internal.div import net.corda.core.serialization.SingletonSerializeAsToken @@ -12,13 +11,13 @@ import net.corda.node.internal.artemis.BrokerJaasLoginModule.Companion.NODE_P2P_ import net.corda.node.internal.artemis.BrokerJaasLoginModule.Companion.PEER_ROLE import net.corda.core.internal.errors.AddressBindingException import net.corda.node.services.config.NodeConfiguration -import net.corda.nodeapi.ArtemisTcpTransport.Companion.p2pAcceptorTcpTransport import net.corda.nodeapi.internal.AmqpMessageSizeChecksInterceptor import net.corda.nodeapi.internal.ArtemisMessageSizeChecksInterceptor import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.INTERNAL_PREFIX import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.JOURNAL_HEADER_SIZE import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NOTIFICATIONS_ADDRESS import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_PREFIX +import net.corda.nodeapi.internal.InternalArtemisTcpTransport.Companion.p2pAcceptorTcpTransport import net.corda.nodeapi.internal.requireOnDefaultFileSystem import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl @@ -120,7 +119,7 @@ class ArtemisMessagingServer(private val config: NodeConfiguration, bindingsDirectory = (artemisDir / "bindings").toString() journalDirectory = (artemisDir / "journal").toString() largeMessagesDirectory = (artemisDir / "large-messages").toString() - acceptorConfigurations = mutableSetOf(p2pAcceptorTcpTransport(NetworkHostAndPort(messagingServerAddress.host, messagingServerAddress.port), config)) + acceptorConfigurations = mutableSetOf(p2pAcceptorTcpTransport(NetworkHostAndPort(messagingServerAddress.host, messagingServerAddress.port), config.p2pSslOptions)) // Enable built in message deduplication. Note we still have to do our own as the delayed commits // and our own definition of commit mean that the built in deduplication cannot remove all duplicates. idCacheSize = 2000 // Artemis Default duplicate cache size i.e. a guess @@ -163,8 +162,8 @@ class ArtemisMessagingServer(private val config: NodeConfiguration, @Throws(IOException::class, KeyStoreException::class) private fun createArtemisSecurityManager(): ActiveMQJAASSecurityManager { - val keyStore = config.loadSslKeyStore().internal - val trustStore = config.loadTrustStore().internal + val keyStore = config.p2pSslOptions.keyStore.get().value.internal + val trustStore = config.p2pSslOptions.trustStore.get().value.internal val securityConfig = object : SecurityConfiguration() { // Override to make it work with our login module diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/InternalRPCMessagingClient.kt b/node/src/main/kotlin/net/corda/node/services/messaging/InternalRPCMessagingClient.kt index 9b5191b312..abb07763d1 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/InternalRPCMessagingClient.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/InternalRPCMessagingClient.kt @@ -6,9 +6,9 @@ import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.internal.nodeSerializationEnv import net.corda.core.utilities.NetworkHostAndPort import net.corda.node.internal.security.RPCSecurityManager -import net.corda.nodeapi.ArtemisTcpTransport import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_RPC_USER -import net.corda.nodeapi.internal.config.SSLConfiguration +import net.corda.nodeapi.internal.InternalArtemisTcpTransport +import net.corda.nodeapi.internal.config.MutualSslConfiguration import org.apache.activemq.artemis.api.core.client.ActiveMQClient import org.apache.activemq.artemis.api.core.client.ServerLocator import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl @@ -16,13 +16,13 @@ import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl /** * Used by the Node to communicate with the RPC broker. */ -class InternalRPCMessagingClient(val sslConfig: SSLConfiguration, val serverAddress: NetworkHostAndPort, val maxMessageSize: Int, val nodeName: CordaX500Name, val rpcServerConfiguration: RPCServerConfiguration) : SingletonSerializeAsToken(), AutoCloseable { +class InternalRPCMessagingClient(val sslConfig: MutualSslConfiguration, val serverAddress: NetworkHostAndPort, val maxMessageSize: Int, val nodeName: CordaX500Name, val rpcServerConfiguration: RPCServerConfiguration) : SingletonSerializeAsToken(), AutoCloseable { private var locator: ServerLocator? = null private var rpcServer: RPCServer? = null fun init(rpcOps: RPCOps, securityManager: RPCSecurityManager) = synchronized(this) { - val tcpTransport = ArtemisTcpTransport.rpcInternalClientTcpTransport(serverAddress, sslConfig) + val tcpTransport = InternalArtemisTcpTransport.rpcInternalClientTcpTransport(serverAddress, sslConfig) locator = ActiveMQClient.createServerLocatorWithoutHA(tcpTransport).apply { // Never time out on our loopback Artemis connections. If we switch back to using the InVM transport this // would be the default and the two lines below can be deleted. diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessagingClient.kt b/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessagingClient.kt index 84d7a6185d..e022b46d0d 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessagingClient.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessagingClient.kt @@ -27,7 +27,6 @@ import net.corda.node.services.statemachine.DeduplicationId import net.corda.node.services.statemachine.ExternalEvent import net.corda.node.services.statemachine.SenderDeduplicationId import net.corda.node.utilities.AffinityExecutor -import net.corda.nodeapi.ArtemisTcpTransport.Companion.p2pConnectorTcpTransport import net.corda.nodeapi.internal.ArtemisMessagingComponent import net.corda.nodeapi.internal.ArtemisMessagingComponent.* import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.BRIDGE_CONTROL @@ -35,6 +34,7 @@ import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.BRIDGE_NOT import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.JOURNAL_HEADER_SIZE import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2PMessagingHeaders import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEERS_PREFIX +import net.corda.nodeapi.internal.InternalArtemisTcpTransport.Companion.p2pConnectorTcpTransport import net.corda.nodeapi.internal.bridging.BridgeControl import net.corda.nodeapi.internal.bridging.BridgeEntry import net.corda.nodeapi.internal.persistence.CordaPersistence @@ -152,7 +152,7 @@ class P2PMessagingClient(val config: NodeConfiguration, started = true log.info("Connecting to message broker: $serverAddress") // TODO Add broker CN to config for host verification in case the embedded broker isn't used - val tcpTransport = p2pConnectorTcpTransport(serverAddress, config) + val tcpTransport = p2pConnectorTcpTransport(serverAddress, config.p2pSslOptions) locator = ActiveMQClient.createServerLocatorWithoutHA(tcpTransport).apply { // Never time out on our loopback Artemis connections. If we switch back to using the InVM transport this // would be the default and the two lines below can be deleted. diff --git a/node/src/main/kotlin/net/corda/node/services/rpc/ArtemisRpcBroker.kt b/node/src/main/kotlin/net/corda/node/services/rpc/ArtemisRpcBroker.kt index 5fc3c98bc8..af6158e33a 100644 --- a/node/src/main/kotlin/net/corda/node/services/rpc/ArtemisRpcBroker.kt +++ b/node/src/main/kotlin/net/corda/node/services/rpc/ArtemisRpcBroker.kt @@ -8,7 +8,7 @@ import net.corda.node.internal.artemis.BrokerJaasLoginModule.Companion.NODE_SECU import net.corda.node.internal.artemis.BrokerJaasLoginModule.Companion.RPC_SECURITY_CONFIG import net.corda.node.internal.security.RPCSecurityManager import net.corda.nodeapi.BrokerRpcSslOptions -import net.corda.nodeapi.internal.config.SSLConfiguration +import net.corda.nodeapi.internal.config.MutualSslConfiguration import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl import org.apache.activemq.artemis.core.config.impl.SecurityConfiguration import org.apache.activemq.artemis.core.server.ActiveMQServer @@ -28,17 +28,17 @@ class ArtemisRpcBroker internal constructor( private val maxMessageSize: Int, private val jmxEnabled: Boolean = false, private val baseDirectory: Path, - private val nodeConfiguration: SSLConfiguration, + private val nodeConfiguration: MutualSslConfiguration, private val shouldStartLocalShell: Boolean) : ArtemisBroker { companion object { private val logger = loggerFor<ArtemisRpcBroker>() - fun withSsl(configuration: SSLConfiguration, address: NetworkHostAndPort, adminAddress: NetworkHostAndPort, sslOptions: BrokerRpcSslOptions, securityManager: RPCSecurityManager, maxMessageSize: Int, jmxEnabled: Boolean, baseDirectory: Path, shouldStartLocalShell: Boolean): ArtemisBroker { + fun withSsl(configuration: MutualSslConfiguration, address: NetworkHostAndPort, adminAddress: NetworkHostAndPort, sslOptions: BrokerRpcSslOptions, securityManager: RPCSecurityManager, maxMessageSize: Int, jmxEnabled: Boolean, baseDirectory: Path, shouldStartLocalShell: Boolean): ArtemisBroker { return ArtemisRpcBroker(address, adminAddress, sslOptions, true, securityManager, maxMessageSize, jmxEnabled, baseDirectory, configuration, shouldStartLocalShell) } - fun withoutSsl(configuration: SSLConfiguration, address: NetworkHostAndPort, adminAddress: NetworkHostAndPort, securityManager: RPCSecurityManager, maxMessageSize: Int, jmxEnabled: Boolean, baseDirectory: Path, shouldStartLocalShell: Boolean): ArtemisBroker { + fun withoutSsl(configuration: MutualSslConfiguration, address: NetworkHostAndPort, adminAddress: NetworkHostAndPort, securityManager: RPCSecurityManager, maxMessageSize: Int, jmxEnabled: Boolean, baseDirectory: Path, shouldStartLocalShell: Boolean): ArtemisBroker { return ArtemisRpcBroker(address, adminAddress, null, false, securityManager, maxMessageSize, jmxEnabled, baseDirectory, configuration, shouldStartLocalShell) } } @@ -83,14 +83,14 @@ class ArtemisRpcBroker internal constructor( @Throws(IOException::class, KeyStoreException::class) private fun createArtemisSecurityManager(loginListener: LoginListener): ActiveMQJAASSecurityManager { - val keyStore = nodeConfiguration.loadSslKeyStore().internal - val trustStore = nodeConfiguration.loadTrustStore().internal + val keyStore = nodeConfiguration.keyStore.get() + val trustStore = nodeConfiguration.trustStore.get() val securityConfig = object : SecurityConfiguration() { override fun getAppConfigurationEntry(name: String): Array<AppConfigurationEntry> { val options = mapOf( RPC_SECURITY_CONFIG to RPCJaasConfig(securityManager, loginListener, useSsl), - NODE_SECURITY_CONFIG to NodeJaasConfig(keyStore, trustStore) + NODE_SECURITY_CONFIG to NodeJaasConfig(keyStore.value.internal, trustStore.value.internal) ) return arrayOf(AppConfigurationEntry(name, AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options)) } diff --git a/node/src/main/kotlin/net/corda/node/services/rpc/RpcBrokerConfiguration.kt b/node/src/main/kotlin/net/corda/node/services/rpc/RpcBrokerConfiguration.kt index 3859e7cd8c..f17dcee8aa 100644 --- a/node/src/main/kotlin/net/corda/node/services/rpc/RpcBrokerConfiguration.kt +++ b/node/src/main/kotlin/net/corda/node/services/rpc/RpcBrokerConfiguration.kt @@ -4,12 +4,12 @@ import net.corda.core.internal.div import net.corda.core.utilities.NetworkHostAndPort import net.corda.node.internal.artemis.BrokerJaasLoginModule import net.corda.node.internal.artemis.SecureArtemisConfiguration -import net.corda.nodeapi.ArtemisTcpTransport.Companion.rpcAcceptorTcpTransport -import net.corda.nodeapi.ArtemisTcpTransport.Companion.rpcInternalAcceptorTcpTransport import net.corda.nodeapi.BrokerRpcSslOptions import net.corda.nodeapi.RPCApi import net.corda.nodeapi.internal.ArtemisMessagingComponent -import net.corda.nodeapi.internal.config.SSLConfiguration +import net.corda.nodeapi.internal.InternalArtemisTcpTransport.Companion.rpcAcceptorTcpTransport +import net.corda.nodeapi.internal.InternalArtemisTcpTransport.Companion.rpcInternalAcceptorTcpTransport +import net.corda.nodeapi.internal.config.MutualSslConfiguration import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.core.config.CoreQueueConfiguration import org.apache.activemq.artemis.core.security.Role @@ -17,7 +17,7 @@ import org.apache.activemq.artemis.core.settings.impl.AddressFullMessagePolicy import org.apache.activemq.artemis.core.settings.impl.AddressSettings import java.nio.file.Path -internal class RpcBrokerConfiguration(baseDirectory: Path, maxMessageSize: Int, jmxEnabled: Boolean, address: NetworkHostAndPort, adminAddress: NetworkHostAndPort?, sslOptions: BrokerRpcSslOptions?, useSsl: Boolean, nodeConfiguration: SSLConfiguration, shouldStartLocalShell: Boolean) : SecureArtemisConfiguration() { +internal class RpcBrokerConfiguration(baseDirectory: Path, maxMessageSize: Int, jmxEnabled: Boolean, address: NetworkHostAndPort, adminAddress: NetworkHostAndPort?, sslOptions: BrokerRpcSslOptions?, useSsl: Boolean, nodeConfiguration: MutualSslConfiguration, shouldStartLocalShell: Boolean) : SecureArtemisConfiguration() { val loginListener: (String) -> Unit init { diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/RaftUniquenessProvider.kt b/node/src/main/kotlin/net/corda/node/services/transactions/RaftUniquenessProvider.kt index 1e5c46d28b..1265461d17 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/RaftUniquenessProvider.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/RaftUniquenessProvider.kt @@ -28,8 +28,7 @@ import net.corda.core.utilities.debug import net.corda.node.services.config.RaftConfig import net.corda.node.services.transactions.RaftTransactionCommitLog.Commands.CommitTransaction import net.corda.node.utilities.AppendOnlyPersistentMap -import net.corda.nodeapi.internal.config.NodeSSLConfiguration -import net.corda.nodeapi.internal.config.SSLConfiguration +import net.corda.nodeapi.internal.config.MutualSslConfiguration import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX import java.nio.file.Path @@ -51,7 +50,8 @@ import javax.persistence.Table */ @ThreadSafe class RaftUniquenessProvider( - private val transportConfiguration: NodeSSLConfiguration, + private val storagePath: Path, + private val transportConfiguration: MutualSslConfiguration, private val db: CordaPersistence, private val clock: Clock, private val metrics: MetricRegistry, @@ -96,8 +96,6 @@ class RaftUniquenessProvider( var index: Long = 0 ) - /** Directory storing the Raft log and state machine snapshots */ - private val storagePath: Path = transportConfiguration.baseDirectory private lateinit var _clientFuture: CompletableFuture<CopycatClient> private lateinit var server: CopycatServer @@ -155,14 +153,14 @@ class RaftUniquenessProvider( .build() } - private fun buildTransport(config: SSLConfiguration): Transport? { + private fun buildTransport(config: MutualSslConfiguration): Transport? { return NettyTransport.builder() .withSsl() .withSslProtocol(SslProtocol.TLSv1_2) - .withKeyStorePath(config.sslKeystore.toString()) - .withKeyStorePassword(config.keyStorePassword) - .withTrustStorePath(config.trustStoreFile.toString()) - .withTrustStorePassword(config.trustStorePassword) + .withKeyStorePath(config.keyStore.path.toString()) + .withKeyStorePassword(config.keyStore.password) + .withTrustStorePath(config.trustStore.path.toString()) + .withTrustStorePassword(config.trustStore.password) .build() } diff --git a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt index 4ec5319c28..b3186b8d7b 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt @@ -6,7 +6,8 @@ import net.corda.core.internal.* import net.corda.core.utilities.contextLogger import net.corda.node.NodeRegistrationOption import net.corda.node.services.config.NodeConfiguration -import net.corda.nodeapi.internal.config.SSLConfiguration +import net.corda.nodeapi.internal.config.CertificateStore +import net.corda.nodeapi.internal.config.CertificateStoreSupplier import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509KeyStore import net.corda.nodeapi.internal.crypto.X509Utilities @@ -33,7 +34,8 @@ import javax.security.auth.x500.X500Principal * needed. */ // TODO: Use content signer instead of keypairs. -open class NetworkRegistrationHelper(private val config: SSLConfiguration, +open class NetworkRegistrationHelper(private val certificatesDirectory: Path, + private val signingCertificateStore: CertificateStoreSupplier, private val myLegalName: CordaX500Name, private val emailAddress: String, private val certService: NetworkRegistrationService, @@ -48,9 +50,7 @@ open class NetworkRegistrationHelper(private val config: SSLConfiguration, val logger = contextLogger() } - private val requestIdStore = config.certificatesDirectory / "certificate-request-id.txt" - // TODO: Use different password for private key. - private val privateKeyPassword = config.keyStorePassword + private val requestIdStore = certificatesDirectory / "certificate-request-id.txt" private val rootTrustStore: X509KeyStore protected val rootCert: X509Certificate @@ -75,12 +75,14 @@ open class NetworkRegistrationHelper(private val config: SSLConfiguration, * @throws CertificateRequestException if the certificate retrieved by doorman is invalid. */ fun buildKeystore() { - config.certificatesDirectory.createDirectories() - val nodeKeyStore = config.loadNodeKeyStore(createNew = true) + certificatesDirectory.createDirectories() + val nodeKeyStore = signingCertificateStore.get(createNew = true) if (keyAlias in nodeKeyStore) { println("Certificate already exists, Corda node will now terminate...") return } + // TODO: Use different password for private key. + val privateKeyPassword = nodeKeyStore.password val tlsCrlIssuerCert = validateAndGetTlsCrlIssuerCert() if (tlsCrlIssuerCert == null && isTlsCrlIssuerCertRequired()) { System.err.println("""tlsCrlIssuerCert config does not match the root certificate issuer and nor is there any other certificate in the trust store with a matching issuer. @@ -89,7 +91,7 @@ open class NetworkRegistrationHelper(private val config: SSLConfiguration, throw IllegalArgumentException("TLS CRL issuer certificate not found in the trust store.") } - val keyPair = nodeKeyStore.loadOrCreateKeyPair(SELF_SIGNED_PRIVATE_KEY) + val keyPair = nodeKeyStore.loadOrCreateKeyPair(SELF_SIGNED_PRIVATE_KEY, privateKeyPassword) val requestId = try { submitOrResumeCertificateSigningRequest(keyPair) @@ -110,7 +112,7 @@ open class NetworkRegistrationHelper(private val config: SSLConfiguration, throw certificateRequestException } validateCertificates(keyPair.public, certificates) - storePrivateKeyWithCertificates(nodeKeyStore, keyPair, certificates, keyAlias) + storePrivateKeyWithCertificates(nodeKeyStore, keyPair, certificates, keyAlias, privateKeyPassword) onSuccess(keyPair, certificates, tlsCrlIssuerCert?.subjectX500Principal?.toX500Name()) // All done, clean up temp files. requestIdStore.deleteIfExists() @@ -148,25 +150,29 @@ open class NetworkRegistrationHelper(private val config: SSLConfiguration, println("Certificate signing request approved, storing private key with the certificate chain.") } - private fun storePrivateKeyWithCertificates(nodeKeystore: X509KeyStore, keyPair: KeyPair, certificates: List<X509Certificate>, keyAlias: String) { + private fun storePrivateKeyWithCertificates(nodeKeystore: CertificateStore, keyPair: KeyPair, certificates: List<X509Certificate>, keyAlias: String, keyPassword: String) { // Save private key and certificate chain to the key store. - nodeKeystore.setPrivateKey(keyAlias, keyPair.private, certificates, keyPassword = config.keyStorePassword) - nodeKeystore.internal.deleteEntry(SELF_SIGNED_PRIVATE_KEY) - nodeKeystore.save() - println("Private key '$keyAlias' and certificate stored in ${config.nodeKeystore}.") + with(nodeKeystore.value) { + setPrivateKey(keyAlias, keyPair.private, certificates, keyPassword = keyPassword) + internal.deleteEntry(SELF_SIGNED_PRIVATE_KEY) + save() + } + println("Private key '$keyAlias' and certificate stored in node signing keystore.") } - private fun X509KeyStore.loadOrCreateKeyPair(alias: String): KeyPair { + private fun CertificateStore.loadOrCreateKeyPair(alias: String, privateKeyPassword: String = password): KeyPair { // Create or load self signed keypair from the key store. // We use the self sign certificate to store the key temporarily in the keystore while waiting for the request approval. if (alias !in this) { val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val selfSignCert = X509Utilities.createSelfSignedCACertificate(myLegalName.x500Principal, keyPair) // Save to the key store. - setPrivateKey(alias, keyPair.private, listOf(selfSignCert), keyPassword = privateKeyPassword) - save() + with(value) { + setPrivateKey(alias, keyPair.private, listOf(selfSignCert), keyPassword = privateKeyPassword) + save() + } } - return getCertificateAndKeyPair(alias, privateKeyPassword).keyPair + return query { getCertificateAndKeyPair(alias, privateKeyPassword) }.keyPair } /** @@ -243,7 +249,9 @@ open class NetworkRegistrationHelper(private val config: SSLConfiguration, class NodeRegistrationException(cause: Throwable?) : IOException("Unable to contact node registration service", cause) class NodeRegistrationHelper(private val config: NodeConfiguration, certService: NetworkRegistrationService, regConfig: NodeRegistrationOption, computeNextIdleDoormanConnectionPollInterval: (Duration?) -> Duration? = FixedPeriodLimitedRetrialStrategy(10, Duration.ofMinutes(1))) : - NetworkRegistrationHelper(config, + NetworkRegistrationHelper( + config.certificatesDirectory, + config.signingCertificateStore, config.myLegalName, config.emailAddress, certService, @@ -263,7 +271,7 @@ class NodeRegistrationHelper(private val config: NodeConfiguration, certService: } private fun createSSLKeystore(nodeCAKeyPair: KeyPair, certificates: List<X509Certificate>, tlsCertCrlIssuer: X500Name?) { - config.loadSslKeyStore(createNew = true).update { + config.p2pSslOptions.keyStore.get(createNew = true).update { println("Generating SSL certificate for node messaging service.") val sslKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val sslCert = X509Utilities.createCertificate( @@ -277,17 +285,17 @@ class NodeRegistrationHelper(private val config: NodeConfiguration, certService: logger.info("Generated TLS certificate: $sslCert") setPrivateKey(CORDA_CLIENT_TLS, sslKeyPair.private, listOf(sslCert) + certificates) } - println("SSL private key and certificate stored in ${config.sslKeystore}.") + println("SSL private key and certificate stored in ${config.p2pSslOptions.keyStore.path}.") } private fun createTruststore(rootCertificate: X509Certificate) { // Save root certificates to trust store. - config.loadTrustStore(createNew = true).update { + config.p2pSslOptions.trustStore.get(createNew = true).update { println("Generating trust store for corda node.") // Assumes certificate chain always starts with client certificate and end with root certificate. setCertificate(CORDA_ROOT_CA, rootCertificate) } - println("Node trust store stored in ${config.trustStoreFile}.") + println("Node trust store stored in ${config.p2pSslOptions.trustStore.path}.") } override fun validateAndGetTlsCrlIssuerCert(): X509Certificate? { @@ -296,8 +304,9 @@ class NodeRegistrationHelper(private val config: NodeConfiguration, certService: if (principalMatchesCertificatePrincipal(tlsCertCrlIssuer, rootCert)) { return rootCert } - return if (config.trustStoreFile.exists()) { - findMatchingCertificate(tlsCertCrlIssuer, config.loadTrustStore()) + val trustStore = config.p2pSslOptions.trustStore.getOptional() + return if (trustStore != null) { + findMatchingCertificate(tlsCertCrlIssuer, trustStore.value) } else { null } diff --git a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt index eaae962161..09d61e64a6 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt @@ -22,6 +22,7 @@ import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509KeyStore import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.testing.core.ALICE_NAME +import net.corda.testing.internal.stubs.CertificateStoreStubs import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.testing.internal.rigorousMock import org.assertj.core.api.Assertions.* @@ -52,10 +53,12 @@ class NetworkRegistrationHelperTest { val baseDirectory = fs.getPath("/baseDir").createDirectories() abstract class AbstractNodeConfiguration : NodeConfiguration + val certificatesDirectory = baseDirectory / "certificates" config = rigorousMock<AbstractNodeConfiguration>().also { doReturn(baseDirectory).whenever(it).baseDirectory - doReturn("trustpass").whenever(it).trustStorePassword - doReturn("cordacadevpass").whenever(it).keyStorePassword + doReturn(certificatesDirectory).whenever(it).certificatesDirectory + doReturn(CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)).whenever(it).p2pSslOptions + doReturn(CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)).whenever(it).signingCertificateStore doReturn(nodeLegalName).whenever(it).myLegalName doReturn("").whenever(it).emailAddress doReturn(null).whenever(it).tlsCertCrlDistPoint @@ -71,30 +74,30 @@ class NetworkRegistrationHelperTest { @Test fun `successful registration`() { - assertThat(config.nodeKeystore).doesNotExist() - assertThat(config.sslKeystore).doesNotExist() - assertThat(config.trustStoreFile).doesNotExist() + assertThat(config.signingCertificateStore.getOptional()).isNull() + assertThat(config.p2pSslOptions.keyStore.getOptional()).isNull() + assertThat(config.p2pSslOptions.trustStore.getOptional()).isNull() val rootAndIntermediateCA = createDevIntermediateCaCertPath().also { saveNetworkTrustStore(it.first.certificate) } createRegistrationHelper(rootAndIntermediateCA = rootAndIntermediateCA).buildKeystore() - val nodeKeystore = config.loadNodeKeyStore() - val sslKeystore = config.loadSslKeyStore() - val trustStore = config.loadTrustStore() + val nodeKeystore = config.signingCertificateStore.get() + val sslKeystore = config.p2pSslOptions.keyStore.get() + val trustStore = config.p2pSslOptions.trustStore.get() nodeKeystore.run { assertFalse(contains(X509Utilities.CORDA_INTERMEDIATE_CA)) assertFalse(contains(X509Utilities.CORDA_ROOT_CA)) assertFalse(contains(X509Utilities.CORDA_CLIENT_TLS)) - assertThat(CertRole.extract(getCertificate(X509Utilities.CORDA_CLIENT_CA))).isEqualTo(CertRole.NODE_CA) + assertThat(CertRole.extract(this[X509Utilities.CORDA_CLIENT_CA])).isEqualTo(CertRole.NODE_CA) } sslKeystore.run { assertFalse(contains(X509Utilities.CORDA_CLIENT_CA)) assertFalse(contains(X509Utilities.CORDA_INTERMEDIATE_CA)) assertFalse(contains(X509Utilities.CORDA_ROOT_CA)) - val nodeTlsCertChain = getCertificateChain(X509Utilities.CORDA_CLIENT_TLS) + val nodeTlsCertChain = query { getCertificateChain(X509Utilities.CORDA_CLIENT_TLS) } assertThat(nodeTlsCertChain).hasSize(4) // The TLS cert has the same subject as the node CA cert assertThat(CordaX500Name.build(nodeTlsCertChain[0].subjectX500Principal)).isEqualTo(nodeLegalName) @@ -104,7 +107,7 @@ class NetworkRegistrationHelperTest { trustStore.run { assertFalse(contains(X509Utilities.CORDA_CLIENT_CA)) assertFalse(contains(X509Utilities.CORDA_INTERMEDIATE_CA)) - assertThat(getCertificate(X509Utilities.CORDA_ROOT_CA)).isEqualTo(rootAndIntermediateCA.first.certificate) + assertThat(this[X509Utilities.CORDA_ROOT_CA]).isEqualTo(rootAndIntermediateCA.first.certificate) } } @@ -152,18 +155,18 @@ class NetworkRegistrationHelperTest { @Test fun `create service identity cert`() { - assertThat(config.nodeKeystore).doesNotExist() - assertThat(config.sslKeystore).doesNotExist() - assertThat(config.trustStoreFile).doesNotExist() + assertThat(config.signingCertificateStore.getOptional()).isNull() + assertThat(config.p2pSslOptions.keyStore.getOptional()).isNull() + assertThat(config.p2pSslOptions.trustStore.getOptional()).isNull() val rootAndIntermediateCA = createDevIntermediateCaCertPath().also { saveNetworkTrustStore(it.first.certificate) } createRegistrationHelper(CertRole.SERVICE_IDENTITY, rootAndIntermediateCA).buildKeystore() - val nodeKeystore = config.loadNodeKeyStore() + val nodeKeystore = config.signingCertificateStore.get() - assertThat(config.sslKeystore).doesNotExist() - assertThat(config.trustStoreFile).doesNotExist() + assertThat(config.p2pSslOptions.keyStore.getOptional()).isNull() + assertThat(config.p2pSslOptions.trustStore.getOptional()).isNull() val serviceIdentityAlias = "${DevIdentityGenerator.DISTRIBUTED_NOTARY_ALIAS_PREFIX}-private-key" @@ -172,7 +175,7 @@ class NetworkRegistrationHelperTest { assertFalse(contains(X509Utilities.CORDA_ROOT_CA)) assertFalse(contains(X509Utilities.CORDA_CLIENT_TLS)) assertFalse(contains(X509Utilities.CORDA_CLIENT_CA)) - assertThat(CertRole.extract(getCertificate(serviceIdentityAlias))).isEqualTo(CertRole.SERVICE_IDENTITY) + assertThat(CertRole.extract(this[serviceIdentityAlias])).isEqualTo(CertRole.SERVICE_IDENTITY) } } @@ -223,7 +226,8 @@ class NetworkRegistrationHelperTest { return when (certRole) { CertRole.NODE_CA -> NodeRegistrationHelper(config, certService, NodeRegistrationOption(config.certificatesDirectory / networkRootTrustStoreFileName, networkRootTrustStorePassword)) CertRole.SERVICE_IDENTITY -> NetworkRegistrationHelper( - config, + config.certificatesDirectory, + config.signingCertificateStore, config.myLegalName, config.emailAddress, certService, diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt index 504dac7262..69fab8f129 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt @@ -5,7 +5,7 @@ import com.typesafe.config.Config import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigRenderOptions import com.typesafe.config.ConfigValueFactory -import net.corda.client.rpc.internal.createCordaRPCClientWithInternalSslAndClassLoader +import net.corda.client.rpc.internal.createCordaRPCClientWithSslAndClassLoader import net.corda.core.concurrent.CordaFuture import net.corda.core.concurrent.firstOf import net.corda.core.identity.CordaX500Name @@ -20,6 +20,7 @@ import net.corda.node.NodeRegistrationOption import net.corda.node.VersionInfo import net.corda.node.internal.Node import net.corda.node.internal.NodeWithInfo +import net.corda.node.internal.clientSslOptionsCompatibleWith import net.corda.node.services.Permissions import net.corda.node.services.config.* import net.corda.node.utilities.registration.HTTPNetworkRegistrationService @@ -27,9 +28,6 @@ import net.corda.node.utilities.registration.NodeRegistrationHelper import net.corda.nodeapi.internal.DevIdentityGenerator import net.corda.nodeapi.internal.SignedNodeInfo import net.corda.nodeapi.internal.addShutdownHook - -import net.corda.nodeapi.internal.config.NodeSSLConfiguration -import net.corda.nodeapi.internal.config.parseAs import net.corda.nodeapi.internal.config.toConfig import net.corda.nodeapi.internal.crypto.X509KeyStore import net.corda.nodeapi.internal.crypto.X509Utilities @@ -45,6 +43,7 @@ import net.corda.testing.driver.internal.InProcessImpl import net.corda.testing.driver.internal.NodeHandleInternal import net.corda.testing.driver.internal.OutOfProcessImpl import net.corda.testing.internal.setGlobalSerialization +import net.corda.testing.internal.stubs.CertificateStoreStubs import net.corda.testing.node.ClusterSpec import net.corda.testing.node.NotarySpec import net.corda.testing.node.User @@ -172,7 +171,8 @@ class DriverDSLImpl( private fun establishRpc(config: NodeConfig, processDeathFuture: CordaFuture<out Process>): CordaFuture<CordaRPCOps> { val rpcAddress = config.corda.rpcOptions.address - val client = createCordaRPCClientWithInternalSslAndClassLoader(config.corda.rpcOptions.adminAddress, sslConfiguration = config.corda) + val clientRpcSslOptions = clientSslOptionsCompatibleWith(config.corda.rpcOptions) + val client = createCordaRPCClientWithSslAndClassLoader(rpcAddress, sslConfiguration = clientRpcSslOptions) val connectionFuture = poll(executorService, "RPC connection") { try { config.corda.rpcUsers[0].run { client.start(username, password) } @@ -842,8 +842,13 @@ class DriverDSLImpl( config += "rpcUsers" to configuration.toConfig().getValue("rpcUsers") config += "useHTTPS" to useHTTPS config += "baseDirectory" to configuration.baseDirectory.toAbsolutePath().toString() - config += "keyStorePassword" to configuration.keyStorePassword - config += "trustStorePassword" to configuration.trustStorePassword + + config += "keyStorePath" to configuration.p2pSslOptions.keyStore.path.toString() + config += "keyStorePassword" to configuration.p2pSslOptions.keyStore.password + + config += "trustStorePath" to configuration.p2pSslOptions.trustStore.path.toString() + config += "trustStorePassword" to configuration.p2pSslOptions.trustStore.password + return config } @@ -1165,20 +1170,18 @@ private fun Config.toNodeOnly(): Config { return if (hasPath("webAddress")) withoutPath("webAddress").withoutPath("useHTTPS") else this } -internal fun DriverParameters.cordappsForAllNodes(): Set<TestCorDapp> = cordappsForAllNodes ?: cordappsInCurrentAndAdditionalPackages(extraCordappPackagesToScan) +internal fun DriverParameters.cordappsForAllNodes(): Set<TestCorDapp> = cordappsForAllNodes + ?: cordappsInCurrentAndAdditionalPackages(extraCordappPackagesToScan) fun DriverDSL.startNode(providedName: CordaX500Name, devMode: Boolean, parameters: NodeParameters = NodeParameters()): CordaFuture<NodeHandle> { var customOverrides = emptyMap<String, String>() if (!devMode) { val nodeDir = baseDirectory(providedName) - val nodeSslConfig = object : NodeSSLConfiguration { - override val baseDirectory = nodeDir - override val keyStorePassword = "cordacadevpass" - override val trustStorePassword = "trustpass" - override val crlCheckSoftFail = true - } - nodeSslConfig.configureDevKeyAndTrustStores(providedName) + val certificatesDirectory = nodeDir / "certificates" + val signingCertStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory) + val p2pSslConfig = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory) + p2pSslConfig.configureDevKeyAndTrustStores(providedName, signingCertStore, certificatesDirectory) customOverrides = mapOf("devMode" to "false") } return startNode(parameters, providedName = providedName, customOverrides = customOverrides) -} +} \ No newline at end of file diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt index 7214fd450c..a514351492 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt @@ -54,6 +54,7 @@ import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.driver.TestCorDapp +import net.corda.testing.internal.stubs.CertificateStoreStubs import net.corda.testing.internal.rigorousMock import net.corda.testing.internal.setGlobalSerialization import net.corda.testing.internal.testThreadFactory @@ -456,8 +457,11 @@ open class InternalMockNetwork(defaultParameters: MockNetworkParameters = MockNe private fun createNodeImpl(parameters: InternalMockNodeParameters, nodeFactory: (MockNodeArgs, CordappLoader?) -> MockNode, start: Boolean): MockNode { val id = parameters.forcedID ?: nextNodeId++ - val config = mockNodeConfiguration().also { - doReturn(baseDirectory(id).createDirectories()).whenever(it).baseDirectory + val baseDirectory = baseDirectory(id) + val certificatesDirectory = baseDirectory / "certificates" + certificatesDirectory.createDirectories() + val config = mockNodeConfiguration(certificatesDirectory).also { + doReturn(baseDirectory).whenever(it).baseDirectory doReturn(parameters.legalName ?: CordaX500Name("Mock Company $id", "London", "GB")).whenever(it).myLegalName doReturn(makeInternalTestDataSourceProperties("node_$id", "net_$networkId")).whenever(it).dataSourceProperties doReturn(makeTestDatabaseProperties("node_$id")).whenever(it).database @@ -562,12 +566,17 @@ abstract class MessagingServiceSpy { abstract fun send(message: Message, target: MessageRecipients, sequenceKey: Any) } -private fun mockNodeConfiguration(): NodeConfiguration { +private fun mockNodeConfiguration(certificatesDirectory: Path): NodeConfiguration { @DoNotImplement abstract class AbstractNodeConfiguration : NodeConfiguration + + val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory) + val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory) + return rigorousMock<AbstractNodeConfiguration>().also { - doReturn("cordacadevpass").whenever(it).keyStorePassword - doReturn("trustpass").whenever(it).trustStorePassword + doReturn(certificatesDirectory.createDirectories()).whenever(it).certificatesDirectory + doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions + doReturn(signingCertificateStore).whenever(it).signingCertificateStore doReturn(emptyList<User>()).whenever(it).rpcUsers doReturn(null).whenever(it).notary doReturn(DatabaseConfig()).whenever(it).database diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt index 8409d98d59..9a315bb2f1 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt @@ -10,15 +10,15 @@ import net.corda.core.node.NodeInfo import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.loggerFor import net.corda.node.VersionInfo -import net.corda.node.internal.EnterpriseNode import net.corda.node.internal.NodeWithInfo +import net.corda.node.internal.EnterpriseNode import net.corda.node.services.config.* import net.corda.nodeapi.internal.config.toConfig import net.corda.nodeapi.internal.network.NetworkParametersCopier import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.core.SerializationEnvironmentRule -import net.corda.testing.driver.PortAllocation import net.corda.testing.internal.IntegrationTest +import net.corda.testing.driver.PortAllocation import net.corda.testing.internal.testThreadFactory import net.corda.testing.node.User import org.apache.logging.log4j.Level @@ -86,9 +86,9 @@ abstract class NodeBasedTest(private val cordappPackages: List<String> = emptyLi @JvmOverloads fun initNode(legalName: CordaX500Name, - platformVersion: Int = 4, - rpcUsers: List<User> = emptyList(), - configOverrides: Map<String, Any> = emptyMap()): InProcessNode { + platformVersion: Int = 4, + rpcUsers: List<User> = emptyList(), + configOverrides: Map<String, Any> = emptyMap()): InProcessNode { val baseDirectory = baseDirectory(legalName).createDirectories() val p2pAddress = configOverrides["p2pAddress"] ?: portAllocation.nextHostAndPort().toString() val config = ConfigHelper.loadConfig( diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/RPCDriver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/RPCDriver.kt index ceda937104..89d01a0a31 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/RPCDriver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/RPCDriver.kt @@ -21,8 +21,8 @@ import net.corda.core.utilities.seconds import net.corda.node.internal.security.RPCSecurityManagerImpl import net.corda.node.services.messaging.RPCServer import net.corda.node.services.messaging.RPCServerConfiguration -import net.corda.nodeapi.ArtemisTcpTransport import net.corda.nodeapi.RPCApi +import net.corda.nodeapi.internal.InternalArtemisTcpTransport import net.corda.serialization.internal.AMQP_RPC_CLIENT_CONTEXT import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.core.MAX_MESSAGE_SIZE @@ -220,14 +220,14 @@ data class RPCDriverDSL( bindingsDirectory = "$artemisDir/bindings" journalDirectory = "$artemisDir/journal" largeMessagesDirectory = "$artemisDir/large-messages" - acceptorConfigurations = setOf(ArtemisTcpTransport.rpcAcceptorTcpTransport(hostAndPort, null)) + acceptorConfigurations = setOf(InternalArtemisTcpTransport.rpcAcceptorTcpTransport(hostAndPort, null)) configureCommonSettings(maxFileSize, maxBufferedBytesPerClient) } } val inVmClientTransportConfiguration = TransportConfiguration(InVMConnectorFactory::class.java.name) fun createNettyClientTransportConfiguration(hostAndPort: NetworkHostAndPort): TransportConfiguration { - return ArtemisTcpTransport.rpcConnectorTcpTransport(hostAndPort, null) + return InternalArtemisTcpTransport.rpcConnectorTcpTransport(hostAndPort, null) } } @@ -339,7 +339,7 @@ data class RPCDriverDSL( configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT ): CordaFuture<I> { return driverDSL.executorService.fork { - val client = RPCClient<I>(ArtemisTcpTransport.rpcConnectorTcpTransport(rpcAddress, null), configuration) + val client = RPCClient<I>(InternalArtemisTcpTransport.rpcConnectorTcpTransport(rpcAddress, null), configuration) val connection = client.start(rpcOpsClass, username, password, externalTrace) driverDSL.shutdownManager.registerShutdown { connection.close() diff --git a/testing/qa/behave/tools/rpc-proxy/src/main/kotlin/net/corda/behave/service/proxy/CordaRPCProxyClient.kt b/testing/qa/behave/tools/rpc-proxy/src/main/kotlin/net/corda/behave/service/proxy/CordaRPCProxyClient.kt index 008d035f2d..eae71e575e 100644 --- a/testing/qa/behave/tools/rpc-proxy/src/main/kotlin/net/corda/behave/service/proxy/CordaRPCProxyClient.kt +++ b/testing/qa/behave/tools/rpc-proxy/src/main/kotlin/net/corda/behave/service/proxy/CordaRPCProxyClient.kt @@ -29,15 +29,11 @@ import java.security.PublicKey import java.time.Instant import javax.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM -// TODO: Make a shared implementation of CordaRPCOps where every method is unimplemented? - class CordaRPCProxyClient(private val targetHostAndPort: NetworkHostAndPort) : CordaRPCOps { companion object { val log = contextLogger() } - override val protocolVersion: Int = 1000 - init { try { AMQPClientSerializationScheme.initialiseSerialization() @@ -79,51 +75,51 @@ class CordaRPCProxyClient(private val targetHostAndPort: NetworkHostAndPort) : C } override fun stateMachinesSnapshot(): List<StateMachineInfo> { - TODO("not implemented") + TODO("not implemented") } override fun stateMachinesFeed(): DataFeed<List<StateMachineInfo>, StateMachineUpdate> { - TODO("not implemented") + TODO("not implemented") } override fun <T : ContractState> vaultQueryBy(criteria: QueryCriteria, paging: PageSpecification, sorting: Sort, contractStateType: Class<out T>): Vault.Page<T> { - TODO("not implemented") + TODO("not implemented") } override fun <T : ContractState> vaultQueryByCriteria(criteria: QueryCriteria, contractStateType: Class<out T>): Vault.Page<T> { - TODO("not implemented") + TODO("not implemented") } override fun <T : ContractState> vaultQueryByWithPagingSpec(contractStateType: Class<out T>, criteria: QueryCriteria, paging: PageSpecification): Vault.Page<T> { - TODO("not implemented") + TODO("not implemented") } override fun <T : ContractState> vaultQueryByWithSorting(contractStateType: Class<out T>, criteria: QueryCriteria, sorting: Sort): Vault.Page<T> { - TODO("not implemented") + TODO("not implemented") } override fun <T : ContractState> vaultTrackBy(criteria: QueryCriteria, paging: PageSpecification, sorting: Sort, contractStateType: Class<out T>): DataFeed<Vault.Page<T>, Vault.Update<T>> { - TODO("not implemented") + TODO("not implemented") } override fun <T : ContractState> vaultTrack(contractStateType: Class<out T>): DataFeed<Vault.Page<T>, Vault.Update<T>> { - TODO("not implemented") + TODO("not implemented") } override fun <T : ContractState> vaultTrackByCriteria(contractStateType: Class<out T>, criteria: QueryCriteria): DataFeed<Vault.Page<T>, Vault.Update<T>> { - TODO("not implemented") + TODO("not implemented") } override fun <T : ContractState> vaultTrackByWithPagingSpec(contractStateType: Class<out T>, criteria: QueryCriteria, paging: PageSpecification): DataFeed<Vault.Page<T>, Vault.Update<T>> { - TODO("not implemented") + TODO("not implemented") } override fun <T : ContractState> vaultTrackByWithSorting(contractStateType: Class<out T>, criteria: QueryCriteria, sorting: Sort): DataFeed<Vault.Page<T>, Vault.Update<T>> { - TODO("not implemented") + TODO("not implemented") } override fun internalVerifiedTransactionsSnapshot(): List<SignedTransaction> { - TODO("not implemented") + TODO("not implemented") } override fun internalFindVerifiedTransaction(txnId: SecureHash): SignedTransaction? { @@ -131,107 +127,107 @@ class CordaRPCProxyClient(private val targetHostAndPort: NetworkHostAndPort) : C } override fun internalVerifiedTransactionsFeed(): DataFeed<List<SignedTransaction>, SignedTransaction> { - TODO("not implemented") + TODO("not implemented") } override fun stateMachineRecordedTransactionMappingSnapshot(): List<StateMachineTransactionMapping> { - TODO("not implemented") + TODO("not implemented") } override fun stateMachineRecordedTransactionMappingFeed(): DataFeed<List<StateMachineTransactionMapping>, StateMachineTransactionMapping> { - TODO("not implemented") + TODO("not implemented") } override fun networkMapFeed(): DataFeed<List<NodeInfo>, NetworkMapCache.MapChange> { - TODO("not implemented") + TODO("not implemented") } override fun networkParametersFeed(): DataFeed<ParametersUpdateInfo?, ParametersUpdateInfo> { - TODO("not implemented") + TODO("not implemented") } override fun acceptNewNetworkParameters(parametersHash: SecureHash) { - TODO("not implemented") + TODO("not implemented") } override fun <T> startTrackedFlowDynamic(logicType: Class<out FlowLogic<T>>, vararg args: Any?): FlowProgressHandle<T> { - TODO("not implemented") + TODO("not implemented") } override fun addVaultTransactionNote(txnId: SecureHash, txnNote: String) { - TODO("not implemented") + TODO("not implemented") } override fun getVaultTransactionNotes(txnId: SecureHash): Iterable<String> { - TODO("not implemented") + TODO("not implemented") } override fun attachmentExists(id: SecureHash): Boolean { - TODO("not implemented") + TODO("not implemented") } override fun openAttachment(id: SecureHash): InputStream { - TODO("not implemented") + TODO("not implemented") } override fun uploadAttachment(jar: InputStream): SecureHash { - TODO("not implemented") + TODO("not implemented") } override fun uploadAttachmentWithMetadata(jar: InputStream, uploader: String, filename: String): SecureHash { - TODO("not implemented") + TODO("not implemented") } override fun queryAttachments(query: AttachmentQueryCriteria, sorting: AttachmentSort?): List<AttachmentId> { - TODO("not implemented") + TODO("not implemented") } override fun currentNodeTime(): Instant { - TODO("not implemented") + TODO("not implemented") } override fun waitUntilNetworkReady(): CordaFuture<Void?> { - TODO("not implemented") + TODO("not implemented") } override fun wellKnownPartyFromAnonymous(party: AbstractParty): Party? { - TODO("not implemented") + TODO("not implemented") } override fun partyFromKey(key: PublicKey): Party? { - TODO("not implemented") + TODO("not implemented") } override fun wellKnownPartyFromX500Name(x500Name: CordaX500Name): Party? { - TODO("not implemented") + TODO("not implemented") } override fun notaryPartyFromX500Name(x500Name: CordaX500Name): Party? { - TODO("not implemented") + TODO("not implemented") } override fun nodeInfoFromParty(party: AbstractParty): NodeInfo? { - TODO("not implemented") + TODO("not implemented") } override fun clearNetworkMapCache() { - TODO("not implemented") + TODO("not implemented") } override fun setFlowsDrainingModeEnabled(enabled: Boolean) { - TODO("not implemented") + TODO("not implemented") } override fun isFlowsDrainingModeEnabled(): Boolean { - TODO("not implemented") + TODO("not implemented") } override fun shutdown() { - TODO("not implemented") + TODO("not implemented") } override fun killFlow(id: StateMachineRunId): Boolean { - TODO("not implemented") + TODO("not implemented") } override fun refreshNetworkMapCache() { diff --git a/testing/test-common/src/main/kotlin/net/corda/testing/common/internal/UnsafeCertificatesFactory.kt b/testing/test-common/src/main/kotlin/net/corda/testing/common/internal/UnsafeCertificatesFactory.kt index 4ce15b78af..bce2152d57 100644 --- a/testing/test-common/src/main/kotlin/net/corda/testing/common/internal/UnsafeCertificatesFactory.kt +++ b/testing/test-common/src/main/kotlin/net/corda/testing/common/internal/UnsafeCertificatesFactory.kt @@ -4,7 +4,9 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.internal.createFile import net.corda.core.internal.deleteIfExists import net.corda.core.internal.div -import net.corda.nodeapi.internal.config.SSLConfiguration +import net.corda.nodeapi.internal.config.FileBasedCertificateStoreSupplier +import net.corda.nodeapi.internal.config.SslConfiguration +import net.corda.nodeapi.internal.config.MutualSslConfiguration import net.corda.nodeapi.internal.crypto.* import org.apache.commons.io.FileUtils import sun.security.tools.keytool.CertAndKeyGen @@ -65,7 +67,7 @@ class KeyStores(val keyStore: UnsafeKeyStore, val trustStore: UnsafeKeyStore) { val keyStoreFile = keyStore.toTemporaryFile("sslkeystore", directory = directory) val trustStoreFile = trustStore.toTemporaryFile("truststore", directory = directory) - val sslConfiguration = sslConfiguration(directory) + val sslConfiguration = sslConfiguration(keyStoreFile, trustStoreFile) return object : AutoClosableSSLConfiguration { override val value = sslConfiguration @@ -77,16 +79,16 @@ class KeyStores(val keyStore: UnsafeKeyStore, val trustStore: UnsafeKeyStore) { } } - data class TestSslOptions(override val certificatesDirectory: Path, - override val keyStorePassword: String, - override val trustStorePassword: String, - override val crlCheckSoftFail: Boolean) : SSLConfiguration + private fun sslConfiguration(keyStoreFile: TemporaryFile, trustStoreFile: TemporaryFile): MutualSslConfiguration { - private fun sslConfiguration(directory: Path) = TestSslOptions(directory, keyStore.password, trustStore.password, true) + val keyStore = FileBasedCertificateStoreSupplier(keyStoreFile.file, keyStore.password) + val trustStore = FileBasedCertificateStoreSupplier(trustStoreFile.file, trustStore.password) + return SslConfiguration.mutual(keyStore, trustStore) + } } interface AutoClosableSSLConfiguration : AutoCloseable { - val value: SSLConfiguration + val value: MutualSslConfiguration } typealias KeyStoreEntry = Pair<String, UnsafeCertificate> @@ -189,7 +191,7 @@ private fun newKeyStore(type: String, password: String): KeyStore { return keyStore } -fun withKeyStores(server: KeyStores, client: KeyStores, action: (brokerSslOptions: SSLConfiguration, clientSslOptions: SSLConfiguration) -> Unit) { +fun withKeyStores(server: KeyStores, client: KeyStores, action: (brokerSslOptions: MutualSslConfiguration, clientSslOptions: MutualSslConfiguration) -> Unit) { val serverDir = Files.createTempDirectory(null) FileUtils.forceDeleteOnExit(serverDir.toFile()) diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt index 5b4555e547..4ddca172af 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt @@ -9,15 +9,15 @@ import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate import net.corda.core.node.NodeInfo import net.corda.core.transactions.WireTransaction -import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.loggerFor -import net.corda.node.services.config.configureDevKeyAndTrustStores import net.corda.nodeapi.BrokerRpcSslOptions -import net.corda.nodeapi.internal.config.SSLConfiguration -import net.corda.nodeapi.internal.createDevKeyStores +import net.corda.nodeapi.internal.config.MutualSslConfiguration +import net.corda.nodeapi.internal.registerDevP2pCertificates import net.corda.nodeapi.internal.createDevNodeCa import net.corda.nodeapi.internal.crypto.* +import net.corda.nodeapi.internal.loadDevCaTrustStore import net.corda.serialization.internal.amqp.AMQP_ENABLED +import net.corda.testing.internal.stubs.CertificateStoreStubs import java.nio.file.Files import java.nio.file.Path import java.security.KeyPair @@ -37,17 +37,17 @@ inline fun <reified T : Any> T.amqpSpecific(reason: String, function: () -> Unit loggerFor<T>().info("Ignoring AMQP specific test, reason: $reason") } -fun configureTestSSL(legalName: CordaX500Name): SSLConfiguration { - return object : SSLConfiguration { - override val certificatesDirectory = Files.createTempDirectory("certs") - override val keyStorePassword: String get() = "cordacadevpass" - override val trustStorePassword: String get() = "trustpass" - override val crlCheckSoftFail: Boolean = true +fun configureTestSSL(legalName: CordaX500Name): MutualSslConfiguration { - init { - configureDevKeyAndTrustStores(legalName) - } + val certificatesDirectory = Files.createTempDirectory("certs") + val config = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory) + if (config.trustStore.getOptional() == null) { + loadDevCaTrustStore().copyTo(config.trustStore.get(true)) } + if (config.keyStore.getOptional() == null) { + config.keyStore.get(true).registerDevP2pCertificates(legalName) + } + return config } private val defaultRootCaName = X500Principal("CN=Corda Root CA,O=R3 Ltd,L=London,C=GB") @@ -103,17 +103,6 @@ fun BrokerRpcSslOptions.useSslRpcOverrides(): Map<String, String> { ) } -fun SSLConfiguration.noSslRpcOverrides(rpcAdminAddress: NetworkHostAndPort): Map<String, Any> { - return mapOf( - "rpcSettings.adminAddress" to rpcAdminAddress.toString(), - "rpcSettings.useSsl" to "false", - "rpcSettings.ssl.certificatesDirectory" to certificatesDirectory.toString(), - "rpcSettings.ssl.keyStorePassword" to keyStorePassword, - "rpcSettings.ssl.trustStorePassword" to trustStorePassword, - "rpcSettings.ssl.crlCheckSoftFail" to true - ) -} - /** * Until we have proper handling of multiple identities per node, for tests we use the first identity as special one. * TODO: Should be removed after multiple identities are introduced. @@ -127,19 +116,12 @@ fun NodeInfo.chooseIdentityAndCert(): PartyAndCertificate = legalIdentitiesAndCe */ fun NodeInfo.chooseIdentity(): Party = chooseIdentityAndCert().party -fun createNodeSslConfig(path: Path, name: CordaX500Name = CordaX500Name("MegaCorp", "London", "GB")): SSLConfiguration { - val sslConfig = object : SSLConfiguration { - override val crlCheckSoftFail = true - override val certificatesDirectory = path - override val keyStorePassword = "serverstorepass" - override val trustStorePassword = "trustpass" - } +fun p2pSslOptions(path: Path, name: CordaX500Name = CordaX500Name("MegaCorp", "London", "GB")): MutualSslConfiguration { + val sslConfig = CertificateStoreStubs.P2P.withCertificatesDirectory(path, keyStorePassword = "serverstorepass") val (rootCa, intermediateCa) = createDevIntermediateCaCertPath() - sslConfig.createDevKeyStores(name, rootCa.certificate, intermediateCa) - val trustStore = loadOrCreateKeyStore(sslConfig.trustStoreFile, sslConfig.trustStorePassword) - trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCa.certificate) - trustStore.save(sslConfig.trustStoreFile, sslConfig.trustStorePassword) - + sslConfig.keyStore.get(true).registerDevP2pCertificates(name, rootCa.certificate, intermediateCa) + val trustStore = sslConfig.trustStore.get(true) + trustStore[X509Utilities.CORDA_ROOT_CA] = rootCa.certificate return sslConfig } diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/stubs/CertificateStoreStubs.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/stubs/CertificateStoreStubs.kt new file mode 100644 index 0000000000..1eba7b9aa5 --- /dev/null +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/stubs/CertificateStoreStubs.kt @@ -0,0 +1,101 @@ +package net.corda.testing.internal.stubs + +import net.corda.core.internal.div +import net.corda.nodeapi.internal.config.FileBasedCertificateStoreSupplier +import net.corda.nodeapi.internal.config.SslConfiguration +import net.corda.nodeapi.internal.config.MutualSslConfiguration +import java.nio.file.Path + +class CertificateStoreStubs { + + companion object { + + const val DEFAULT_CERTIFICATES_DIRECTORY_NAME = "certificates" + + @JvmStatic + fun withStoreAt(certificateStorePath: Path, password: String): FileBasedCertificateStoreSupplier = FileBasedCertificateStoreSupplier(certificateStorePath, password) + } + + class Signing { + + companion object { + + const val DEFAULT_STORE_FILE_NAME = "nodekeystore.jks" + const val DEFAULT_STORE_PASSWORD = "cordacadevpass" + + @JvmStatic + fun withCertificatesDirectory(certificatesDirectory: Path, password: String = DEFAULT_STORE_PASSWORD, certificateStoreFileName: String = DEFAULT_STORE_FILE_NAME): FileBasedCertificateStoreSupplier { + + return FileBasedCertificateStoreSupplier(certificatesDirectory / certificateStoreFileName, password) + } + + @JvmStatic + fun withBaseDirectory(baseDirectory: Path, password: String = DEFAULT_STORE_PASSWORD, certificatesDirectoryName: String = DEFAULT_CERTIFICATES_DIRECTORY_NAME, certificateStoreFileName: String = DEFAULT_STORE_FILE_NAME): FileBasedCertificateStoreSupplier { + + return FileBasedCertificateStoreSupplier(baseDirectory / certificatesDirectoryName / certificateStoreFileName, password) + } + } + } + + class P2P { + + companion object { + + @JvmStatic + fun withCertificatesDirectory(certificatesDirectory: Path, keyStoreFileName: String = KeyStore.DEFAULT_STORE_FILE_NAME, keyStorePassword: String = KeyStore.DEFAULT_STORE_PASSWORD, trustStoreFileName: String = TrustStore.DEFAULT_STORE_FILE_NAME, trustStorePassword: String = TrustStore.DEFAULT_STORE_PASSWORD): MutualSslConfiguration { + + val keyStore = FileBasedCertificateStoreSupplier(certificatesDirectory / keyStoreFileName, keyStorePassword) + val trustStore = FileBasedCertificateStoreSupplier(certificatesDirectory / trustStoreFileName, trustStorePassword) + return SslConfiguration.mutual(keyStore, trustStore) + } + + @JvmStatic + fun withBaseDirectory(baseDirectory: Path, certificatesDirectoryName: String = DEFAULT_CERTIFICATES_DIRECTORY_NAME, keyStoreFileName: String = KeyStore.DEFAULT_STORE_FILE_NAME, keyStorePassword: String = KeyStore.DEFAULT_STORE_PASSWORD, trustStoreFileName: String = TrustStore.DEFAULT_STORE_FILE_NAME, trustStorePassword: String = TrustStore.DEFAULT_STORE_PASSWORD): MutualSslConfiguration { + + return withCertificatesDirectory(baseDirectory / certificatesDirectoryName, keyStorePassword, trustStorePassword, keyStoreFileName, trustStoreFileName) + } + } + + class KeyStore { + + companion object { + + const val DEFAULT_STORE_FILE_NAME = "sslkeystore.jks" + const val DEFAULT_STORE_PASSWORD = "cordacadevpass" + + @JvmStatic + fun withCertificatesDirectory(certificatesDirectory: Path, password: String = DEFAULT_STORE_PASSWORD, certificateStoreFileName: String = DEFAULT_STORE_FILE_NAME): FileBasedCertificateStoreSupplier { + + return FileBasedCertificateStoreSupplier(certificatesDirectory / certificateStoreFileName, password) + } + + @JvmStatic + fun withBaseDirectory(baseDirectory: Path, password: String = DEFAULT_STORE_PASSWORD, certificatesDirectoryName: String = DEFAULT_CERTIFICATES_DIRECTORY_NAME, certificateStoreFileName: String = DEFAULT_STORE_FILE_NAME): FileBasedCertificateStoreSupplier { + + return FileBasedCertificateStoreSupplier(baseDirectory / certificatesDirectoryName / certificateStoreFileName, password) + } + } + } + + class TrustStore { + + companion object { + + const val DEFAULT_STORE_FILE_NAME = "truststore.jks" + const val DEFAULT_STORE_PASSWORD = "trustpass" + + @JvmStatic + fun withCertificatesDirectory(certificatesDirectory: Path, password: String = DEFAULT_STORE_PASSWORD, certificateStoreFileName: String = DEFAULT_STORE_FILE_NAME): FileBasedCertificateStoreSupplier { + + return FileBasedCertificateStoreSupplier(certificatesDirectory / certificateStoreFileName, password) + } + + @JvmStatic + fun withBaseDirectory(baseDirectory: Path, password: String = DEFAULT_STORE_PASSWORD, certificatesDirectoryName: String = DEFAULT_CERTIFICATES_DIRECTORY_NAME, certificateStoreFileName: String = DEFAULT_STORE_FILE_NAME): FileBasedCertificateStoreSupplier { + + return FileBasedCertificateStoreSupplier(baseDirectory / certificatesDirectoryName / certificateStoreFileName, password) + } + } + } + } +} \ No newline at end of file diff --git a/tools/shell/src/integration-test/kotlin/net/corda/tools/shell/InteractiveShellIntegrationTest.kt b/tools/shell/src/integration-test/kotlin/net/corda/tools/shell/InteractiveShellIntegrationTest.kt index cf8882a6b5..dbae24f6ff 100644 --- a/tools/shell/src/integration-test/kotlin/net/corda/tools/shell/InteractiveShellIntegrationTest.kt +++ b/tools/shell/src/integration-test/kotlin/net/corda/tools/shell/InteractiveShellIntegrationTest.kt @@ -145,7 +145,7 @@ class InteractiveShellIntegrationTest : IntegrationTest() { driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList())) { startNode().getOrThrow().use { node -> val conf = (node as NodeHandleInternal).configuration.toShellConfig() - InteractiveShell.startShellInternal(conf) + InteractiveShell.startShell(conf) assertThatThrownBy { InteractiveShell.nodeInfo() }.isInstanceOf(ActiveMQSecurityException::class.java) } } diff --git a/tools/shell/src/main/kotlin/net/corda/tools/shell/InteractiveShell.kt b/tools/shell/src/main/kotlin/net/corda/tools/shell/InteractiveShell.kt index ed1b5e94cd..9b3f926c97 100644 --- a/tools/shell/src/main/kotlin/net/corda/tools/shell/InteractiveShell.kt +++ b/tools/shell/src/main/kotlin/net/corda/tools/shell/InteractiveShell.kt @@ -9,7 +9,6 @@ import net.corda.client.jackson.StringToMethodCallParser import net.corda.client.rpc.CordaRPCClientConfiguration import net.corda.client.rpc.CordaRPCConnection import net.corda.client.rpc.PermissionException -import net.corda.client.rpc.internal.createCordaRPCClientWithInternalSslAndClassLoader import net.corda.client.rpc.internal.createCordaRPCClientWithSslAndClassLoader import net.corda.core.CordaException import net.corda.core.concurrent.CordaFuture @@ -93,24 +92,6 @@ object InteractiveShell { _startShell(configuration, classLoader) } - /** - * Starts an interactive shell connected to the local terminal. This shell gives administrator access to the node - * internals. - */ - fun startShellInternal(configuration: ShellConfiguration, classLoader: ClassLoader? = null) { - rpcOps = { username: String, credentials: String -> - val client = createCordaRPCClientWithInternalSslAndClassLoader(hostAndPort = configuration.hostAndPort, - configuration = CordaRPCClientConfiguration.DEFAULT.copy( - maxReconnectAttempts = 1 - ), - sslConfiguration = configuration.nodeSslConfig, - classLoader = classLoader) - this.connection = client.start(username, credentials) - connection.proxy - } - _startShell(configuration, classLoader) - } - private fun _startShell(configuration: ShellConfiguration, classLoader: ClassLoader? = null) { shellConfiguration = configuration InteractiveShell.classLoader = classLoader diff --git a/tools/shell/src/main/kotlin/net/corda/tools/shell/ShellConfiguration.kt b/tools/shell/src/main/kotlin/net/corda/tools/shell/ShellConfiguration.kt index 90d5f8407c..4877b520cc 100644 --- a/tools/shell/src/main/kotlin/net/corda/tools/shell/ShellConfiguration.kt +++ b/tools/shell/src/main/kotlin/net/corda/tools/shell/ShellConfiguration.kt @@ -2,7 +2,6 @@ package net.corda.tools.shell import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.messaging.ClientRpcSslOptions -import net.corda.nodeapi.internal.config.SSLConfiguration import java.nio.file.Path data class ShellConfiguration( @@ -12,7 +11,6 @@ data class ShellConfiguration( var password: String = "", val hostAndPort: NetworkHostAndPort, val ssl: ClientRpcSslOptions? = null, - val nodeSslConfig: SSLConfiguration? = null, val sshdPort: Int? = null, val sshHostKeyDirectory: Path? = null, val noLocalShell: Boolean = false) { diff --git a/webserver/src/main/kotlin/net/corda/webserver/WebServerConfig.kt b/webserver/src/main/kotlin/net/corda/webserver/WebServerConfig.kt index 5ba08b691d..69569e8dbf 100644 --- a/webserver/src/main/kotlin/net/corda/webserver/WebServerConfig.kt +++ b/webserver/src/main/kotlin/net/corda/webserver/WebServerConfig.kt @@ -1,8 +1,8 @@ package net.corda.webserver import com.typesafe.config.Config +import net.corda.core.internal.div import net.corda.core.utilities.NetworkHostAndPort -import net.corda.nodeapi.internal.config.NodeSSLConfiguration import net.corda.nodeapi.internal.config.User import net.corda.nodeapi.internal.config.getValue import net.corda.nodeapi.internal.config.parseAs @@ -11,10 +11,13 @@ import java.nio.file.Path /** * [baseDirectory] is not retrieved from the config file but rather from a command line argument. */ -class WebServerConfig(override val baseDirectory: Path, val config: Config) : NodeSSLConfiguration { - override val keyStorePassword: String by config - override val trustStorePassword: String by config - override val crlCheckSoftFail: Boolean by config +class WebServerConfig(val baseDirectory: Path, val config: Config) { + + val keyStorePath: String by config + val keyStorePassword: String by config + val trustStorePath: String by config + val trustStorePassword: String by config + val useHTTPS: Boolean by config val myLegalName: String by config val rpcAddress: NetworkHostAndPort by lazy { diff --git a/webserver/src/main/kotlin/net/corda/webserver/internal/NodeWebServer.kt b/webserver/src/main/kotlin/net/corda/webserver/internal/NodeWebServer.kt index 5bf16e4a7a..9ca4e03546 100644 --- a/webserver/src/main/kotlin/net/corda/webserver/internal/NodeWebServer.kt +++ b/webserver/src/main/kotlin/net/corda/webserver/internal/NodeWebServer.kt @@ -66,10 +66,10 @@ class NodeWebServer(val config: WebServerConfig) { httpsConfiguration.outputBufferSize = 32768 httpsConfiguration.addCustomizer(SecureRequestCustomizer()) val sslContextFactory = SslContextFactory() - sslContextFactory.keyStorePath = config.sslKeystore.toString() + sslContextFactory.keyStorePath = config.keyStorePath sslContextFactory.setKeyStorePassword(config.keyStorePassword) sslContextFactory.setKeyManagerPassword(config.keyStorePassword) - sslContextFactory.setTrustStorePath(config.trustStoreFile.toString()) + sslContextFactory.setTrustStorePath(config.trustStorePath) sslContextFactory.setTrustStorePassword(config.trustStorePassword) sslContextFactory.setExcludeProtocols("SSL.*", "TLSv1", "TLSv1.1") sslContextFactory.setIncludeProtocols("TLSv1.2")