diff --git a/core/src/main/kotlin/com/r3corda/core/crypto/X509Utilities.kt b/core/src/main/kotlin/com/r3corda/core/crypto/X509Utilities.kt index 3696db7cc8..1d16a20b0f 100644 --- a/core/src/main/kotlin/com/r3corda/core/crypto/X509Utilities.kt +++ b/core/src/main/kotlin/com/r3corda/core/crypto/X509Utilities.kt @@ -54,6 +54,8 @@ object X509Utilities { * @param daysAfter number of days to roll forward returned end date relative to current date * @param parentNotBefore if provided is used to lower bound the date interval returned * @param parentNotAfter if provided is used to upper bound the date interval returned + * Note we use Date rather than LocalDate as the consuming java.security and BouncyCastle certificate apis all use Date + * Thus we avoid too many round trip conversions. */ private fun getCertificateValidityWindow(daysBefore: Int, daysAfter: Int, parentNotBefore: Date? = null, parentNotAfter: Date? = null): Pair { val startOfDayUTC = Instant.now().truncatedTo(ChronoUnit.DAYS) diff --git a/node/src/main/kotlin/com/r3corda/node/internal/Node.kt b/node/src/main/kotlin/com/r3corda/node/internal/Node.kt index 3c5ba06ae0..489d116eda 100644 --- a/node/src/main/kotlin/com/r3corda/node/internal/Node.kt +++ b/node/src/main/kotlin/com/r3corda/node/internal/Node.kt @@ -67,7 +67,7 @@ class Node(dir: Path, val p2pAddr: HostAndPort, val webServerAddr: HostAndPort, // when our process shuts down, but we try in stop() anyway just to be nice. private var nodeFileLock: FileLock? = null - override fun makeMessagingService(): MessagingService = ArtemisMessagingService(dir, p2pAddr, serverThread) + override fun makeMessagingService(): MessagingService = ArtemisMessagingService(dir, p2pAddr, configuration, serverThread) override fun startMessagingService() { // Start up the MQ service. diff --git a/node/src/main/kotlin/com/r3corda/node/internal/testing/MockNode.kt b/node/src/main/kotlin/com/r3corda/node/internal/testing/MockNode.kt index cdb5699b91..ce8b0eae3a 100644 --- a/node/src/main/kotlin/com/r3corda/node/internal/testing/MockNode.kt +++ b/node/src/main/kotlin/com/r3corda/node/internal/testing/MockNode.kt @@ -119,6 +119,8 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, override val myLegalName: String = legalName ?: "Mock Company $id" override val exportJMXto: String = "" override val nearestCity: String = "Atlantis" + override val keyStorePassword: String = "dummy" + override val trustStorePassword: String = "trustpass" } val node = nodeFactory.create(path, config, this, networkMapAddress, advertisedServices.toSet(), id, keyPair) if (start) { diff --git a/node/src/main/kotlin/com/r3corda/node/internal/testing/Simulation.kt b/node/src/main/kotlin/com/r3corda/node/internal/testing/Simulation.kt index 8227c1b885..72ce1bb6fe 100644 --- a/node/src/main/kotlin/com/r3corda/node/internal/testing/Simulation.kt +++ b/node/src/main/kotlin/com/r3corda/node/internal/testing/Simulation.kt @@ -59,6 +59,8 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, override val myLegalName: String = "Bank $letter" override val exportJMXto: String = "" override val nearestCity: String = city + override val keyStorePassword: String = "dummy" + override val trustStorePassword: String = "trustpass" } return SimulatedNode(dir, cfg, network, networkMapAddr, advertisedServices, id, keyPair) } @@ -77,6 +79,8 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, override val myLegalName: String = "Network coordination center" override val exportJMXto: String = "" override val nearestCity: String = "Amsterdam" + override val keyStorePassword: String = "dummy" + override val trustStorePassword: String = "trustpass" } return object : SimulatedNode(dir, cfg, network, networkMapAddr, advertisedServices, id, keyPair) {} @@ -91,6 +95,8 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, override val myLegalName: String = "Notary Service" override val exportJMXto: String = "" override val nearestCity: String = "Zurich" + override val keyStorePassword: String = "dummy" + override val trustStorePassword: String = "trustpass" } return SimulatedNode(dir, cfg, network, networkMapAddr, advertisedServices, id, keyPair) } @@ -104,6 +110,8 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, override val myLegalName: String = "Rates Service Provider" override val exportJMXto: String = "" override val nearestCity: String = "Madrid" + override val keyStorePassword: String = "dummy" + override val trustStorePassword: String = "trustpass" } return object : SimulatedNode(dir, cfg, network, networkMapAddr, advertisedServices, id, keyPair) { @@ -122,6 +130,8 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, override val myLegalName: String = "Regulator A" override val exportJMXto: String = "" override val nearestCity: String = "Paris" + override val keyStorePassword: String = "dummy" + override val trustStorePassword: String = "trustpass" } val n = object : SimulatedNode(dir, cfg, network, networkMapAddr, advertisedServices, id, keyPair) { diff --git a/node/src/main/kotlin/com/r3corda/node/services/config/NodeConfiguration.kt b/node/src/main/kotlin/com/r3corda/node/services/config/NodeConfiguration.kt index 3cd30b29a8..4eb104aca6 100644 --- a/node/src/main/kotlin/com/r3corda/node/services/config/NodeConfiguration.kt +++ b/node/src/main/kotlin/com/r3corda/node/services/config/NodeConfiguration.kt @@ -8,6 +8,8 @@ interface NodeConfiguration { val myLegalName: String val exportJMXto: String val nearestCity: String + val keyStorePassword: String + val trustStorePassword: String } // Allow the use of "String by config" syntax. TODO: Make it more flexible. @@ -17,4 +19,6 @@ class NodeConfigurationFromConfig(val config: Config = ConfigFactory.load()) : N override val myLegalName: String by config override val exportJMXto: String by config override val nearestCity: String by config + override val keyStorePassword: String by config + override val trustStorePassword: String by config } \ No newline at end of file diff --git a/node/src/main/kotlin/com/r3corda/node/services/messaging/ArtemisMessagingService.kt b/node/src/main/kotlin/com/r3corda/node/services/messaging/ArtemisMessagingService.kt index 393484698a..b38f6c0fc6 100644 --- a/node/src/main/kotlin/com/r3corda/node/services/messaging/ArtemisMessagingService.kt +++ b/node/src/main/kotlin/com/r3corda/node/services/messaging/ArtemisMessagingService.kt @@ -9,6 +9,7 @@ import com.r3corda.core.messaging.* import com.r3corda.core.serialization.SingletonSerializeAsToken import com.r3corda.core.utilities.loggerFor import com.r3corda.node.internal.Node +import com.r3corda.node.services.config.NodeConfiguration import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.api.core.TransportConfiguration import org.apache.activemq.artemis.api.core.client.* @@ -49,11 +50,13 @@ import javax.annotation.concurrent.ThreadSafe * * @param directory A place where Artemis can stash its message journal and other files. * @param myHostPort What host and port to bind to for receiving inbound connections. + * @param config The config object is used to pass in the passwords for the certificate KeyStore and TrustStore * @param defaultExecutor This will be used as the default executor to run message handlers on, if no other is specified. */ @ThreadSafe class ArtemisMessagingService(val directory: Path, val myHostPort: HostAndPort, + val config: NodeConfiguration, val defaultExecutor: Executor = RunOnCallerThread) : SingletonSerializeAsToken(), MessagingService { // In future: can contain onion routing info, etc. @@ -98,7 +101,10 @@ class ArtemisMessagingService(val directory: Path, private val keyStorePath = directory.resolve("certificates").resolve("sslkeystore.jks") private val trustStorePath = directory.resolve("certificates").resolve("truststore.jks") - private val KEYSTORE_PASSWORD = "cordacadevpass" // TODO we need a proper way of managing keystores and passwords + + // Restrict enabled Cipher Suites to AES and GCM as minimum for the bulk cipher. + // Our self-generated certificates all use ECDSA for handshakes, but we allow classical RSA certificates to work + // in case we need to use keytool certificates in some demos private val CIPHER_SUITES = listOf( "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", @@ -340,10 +346,10 @@ class ArtemisMessagingService(val directory: Path, SSL_ENABLED_PROP_NAME to true, KEYSTORE_PROVIDER_PROP_NAME to "JKS", KEYSTORE_PATH_PROP_NAME to keyStorePath, - KEYSTORE_PASSWORD_PROP_NAME to KEYSTORE_PASSWORD, // TODO proper management of keystores and password + KEYSTORE_PASSWORD_PROP_NAME to config.keyStorePassword, // TODO proper management of keystores and password TRUSTSTORE_PROVIDER_PROP_NAME to "JKS", TRUSTSTORE_PATH_PROP_NAME to trustStorePath, - TRUSTSTORE_PASSWORD_PROP_NAME to "trustpass", + TRUSTSTORE_PASSWORD_PROP_NAME to config.trustStorePassword, ENABLED_CIPHER_SUITES_PROP_NAME to CIPHER_SUITES.joinToString(","), ENABLED_PROTOCOLS_PROP_NAME to "TLSv1.2", NEED_CLIENT_AUTH_PROP_NAME to true @@ -364,7 +370,7 @@ class ArtemisMessagingService(val directory: Path, val caKeyStore = X509Utilities.loadKeyStore( javaClass.classLoader.getResourceAsStream("com/r3corda/node/internal/certificates/cordadevcakeys.jks"), "cordacadevpass") - X509Utilities.createKeystoreForSSL(keyStorePath, KEYSTORE_PASSWORD, KEYSTORE_PASSWORD, caKeyStore, "cordacadevkeypass") + X509Utilities.createKeystoreForSSL(keyStorePath, config.keyStorePassword, config.keyStorePassword, caKeyStore, "cordacadevkeypass") } } diff --git a/node/src/test/kotlin/com/r3corda/node/services/ArtemisMessagingServiceTests.kt b/node/src/test/kotlin/com/r3corda/node/services/ArtemisMessagingServiceTests.kt index 92479eba53..e2cd96e023 100644 --- a/node/src/test/kotlin/com/r3corda/node/services/ArtemisMessagingServiceTests.kt +++ b/node/src/test/kotlin/com/r3corda/node/services/ArtemisMessagingServiceTests.kt @@ -2,6 +2,7 @@ package com.r3corda.node.services import com.r3corda.core.messaging.Message import com.r3corda.core.testing.freeLocalHostAndPort +import com.r3corda.node.services.config.NodeConfiguration import com.r3corda.node.services.messaging.ArtemisMessagingService import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy @@ -55,7 +56,15 @@ class ArtemisMessagingServiceTests { } private fun createMessagingService(): ArtemisMessagingService { - return ArtemisMessagingService(temporaryFolder.newFolder().toPath(), hostAndPort).apply { + val config = object: NodeConfiguration { + override val myLegalName: String = "me" + override val exportJMXto: String = "" + override val nearestCity: String = "London" + override val keyStorePassword: String = "testpass" + override val trustStorePassword: String = "trustpass" + + } + return ArtemisMessagingService(temporaryFolder.newFolder().toPath(), hostAndPort, config).apply { configureWithDevSSLCertificate() messagingNetwork = this } diff --git a/src/main/kotlin/com/r3corda/demos/RateFixDemo.kt b/src/main/kotlin/com/r3corda/demos/RateFixDemo.kt index 08960dbfa2..99037c5957 100644 --- a/src/main/kotlin/com/r3corda/demos/RateFixDemo.kt +++ b/src/main/kotlin/com/r3corda/demos/RateFixDemo.kt @@ -76,6 +76,8 @@ fun main(args: Array) { override val myLegalName: String = "Rate fix demo node" override val exportJMXto: String = "http" override val nearestCity: String = "Atlantis" + override val keyStorePassword: String = "cordacadevpass" + override val trustStorePassword: String = "trustpass" } val apiAddr = HostAndPort.fromParts(myNetAddr.hostText, myNetAddr.port + 1) diff --git a/src/main/resources/reference.conf b/src/main/resources/reference.conf index 65c0d10700..27851335e1 100644 --- a/src/main/resources/reference.conf +++ b/src/main/resources/reference.conf @@ -1,3 +1,5 @@ myLegalName = "Vast Global MegaCorp, Ltd" exportJMXto = "http" -nearestCity = "The Moon" \ No newline at end of file +nearestCity = "The Moon" +keyStorePassword = "cordacadevpass" +trustStorePassword = "trustpass" \ No newline at end of file