From b042fac905c7f625c0769e1733cc74ed3cf1d39f Mon Sep 17 00:00:00 2001 From: Matthew Nesbit Date: Fri, 22 Jul 2016 15:13:46 +0100 Subject: [PATCH] Tidy up based upon comments on PR --- .../com/r3corda/core/crypto/X509Utilities.kt | 30 +++++++++++++------ .../r3corda/core/crypto/X509UtilitiesTest.kt | 2 +- .../kotlin/com/r3corda/node/internal/Node.kt | 2 +- .../messaging/ArtemisMessagingService.kt | 21 +++++++++---- 4 files changed, 38 insertions(+), 17 deletions(-) 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 1f51c836e6..3696db7cc8 100644 --- a/core/src/main/kotlin/com/r3corda/core/crypto/X509Utilities.kt +++ b/core/src/main/kotlin/com/r3corda/core/crypto/X509Utilities.kt @@ -55,7 +55,7 @@ object X509Utilities { * @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 */ - private fun GetCertificateValidityWindow(daysBefore: Int, daysAfter: Int, parentNotBefore: Date? = null, parentNotAfter: Date? = null): Pair { + private fun getCertificateValidityWindow(daysBefore: Int, daysAfter: Int, parentNotBefore: Date? = null, parentNotAfter: Date? = null): Pair { val startOfDayUTC = Instant.now().truncatedTo(ChronoUnit.DAYS) var notBefore = Date.from(startOfDayUTC.minus(daysBefore.toLong(), ChronoUnit.DAYS)) @@ -94,7 +94,7 @@ object X509Utilities { /** * Helper method to create Subject field contents */ - fun GetX509Name(domain: String): X500Name { + fun getDevX509Name(domain: String): X500Name { val nameBuilder = X500NameBuilder(BCStyle.INSTANCE) nameBuilder.addRDN(BCStyle.CN, domain) nameBuilder.addRDN(BCStyle.O, "R3") @@ -121,6 +121,10 @@ object X509Utilities { } } else { keyStore.load(null, pass) + val output = FileOutputStream(keyStoreFilePath.toFile()) + output.use { + keyStore.store(output, pass) + } } return keyStore } @@ -231,11 +235,14 @@ object X509Utilities { fun createSelfSignedCACert(domain: String): CACertAndKey { val keyPair = generateECDSAKeyPairForSSL() - val issuer = GetX509Name(domain) + val issuer = getDevX509Name(domain) val serial = BigInteger.valueOf(random63BitValue()) val subject = issuer val pubKey = keyPair.public - val window = GetCertificateValidityWindow(0, 365 * 10) + + // Ten year certificate validity + // TODO how do we manage certificate expiry, revocation and loss + val window = getCertificateValidityWindow(0, 365 * 10) val builder = JcaX509v3CertificateBuilder( issuer, serial, window.first, window.second, subject, pubKey) @@ -276,10 +283,12 @@ object X509Utilities { val issuer = X509CertificateHolder(certificateAuthority.certificate.encoded).subject val serial = BigInteger.valueOf(random63BitValue()) - val subject = GetX509Name(domain) + val subject = getDevX509Name(domain) val pubKey = keyPair.public - // One year certificate validity - val window = GetCertificateValidityWindow(0, 365, certificateAuthority.certificate.notBefore, certificateAuthority.certificate.notAfter) + + // Ten year certificate validity + // TODO how do we manage certificate expiry, revocation and loss + val window = getCertificateValidityWindow(0, 365*10, certificateAuthority.certificate.notBefore, certificateAuthority.certificate.notAfter) val builder = JcaX509v3CertificateBuilder( issuer, serial, window.first, window.second, subject, pubKey) @@ -325,7 +334,10 @@ object X509Utilities { val issuer = X509CertificateHolder(certificateAuthority.certificate.encoded).subject val serial = BigInteger.valueOf(random63BitValue()) - val window = GetCertificateValidityWindow(0, 365, certificateAuthority.certificate.notBefore, certificateAuthority.certificate.notAfter) + + // Ten year certificate validity + // TODO how do we manage certificate expiry, revocation and loss + val window = getCertificateValidityWindow(0, 365*10, certificateAuthority.certificate.notBefore, certificateAuthority.certificate.notAfter) val builder = JcaX509v3CertificateBuilder(issuer, serial, window.first, window.second, subject, publicKey) builder.addExtension(Extension.subjectKeyIdentifier, false, createSubjectKeyIdentifier(publicKey)) @@ -518,7 +530,7 @@ object X509Utilities { val serverKey = X509Utilities.generateECDSAKeyPairForSSL() val host = InetAddress.getLocalHost() - val subject = GetX509Name(host.canonicalHostName) + val subject = getDevX509Name(host.canonicalHostName) val serverCert = X509Utilities.createServerCert(subject, serverKey.public, intermediateCA, diff --git a/core/src/test/kotlin/com/r3corda/core/crypto/X509UtilitiesTest.kt b/core/src/test/kotlin/com/r3corda/core/crypto/X509UtilitiesTest.kt index 4f14c0a748..ff4168c6ce 100644 --- a/core/src/test/kotlin/com/r3corda/core/crypto/X509UtilitiesTest.kt +++ b/core/src/test/kotlin/com/r3corda/core/crypto/X509UtilitiesTest.kt @@ -54,7 +54,7 @@ class X509UtilitiesTest { @Test fun `create valid server certificate chain`() { val caCertAndKey = X509Utilities.createSelfSignedCACert("Test CA Cert") - val subjectDN = X509Utilities.GetX509Name("Server Cert") + val subjectDN = X509Utilities.getDevX509Name("Server Cert") val keypair = X509Utilities.generateECDSAKeyPairForSSL() val serverCert = X509Utilities.createServerCert(subjectDN, keypair.public, caCertAndKey, listOf("alias name"), listOf("10.0.0.54")) assertTrue { serverCert.subjectDN.name.contains("CN=Server Cert") } // using our subject common name 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 2171d1515c..3c5ba06ae0 100644 --- a/node/src/main/kotlin/com/r3corda/node/internal/Node.kt +++ b/node/src/main/kotlin/com/r3corda/node/internal/Node.kt @@ -72,7 +72,7 @@ class Node(dir: Path, val p2pAddr: HostAndPort, val webServerAddr: HostAndPort, override fun startMessagingService() { // Start up the MQ service. (net as ArtemisMessagingService).apply { - configureWithDevSSLCertificate() //Provision a dev certificate and private key if required + configureWithDevSSLCertificate() // TODO Create proper certificate provisioning process start() } } 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 9eb2c56552..393484698a 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 @@ -40,7 +40,7 @@ import javax.annotation.concurrent.ThreadSafe /** * This class implements the [MessagingService] API using Apache Artemis, the successor to their ActiveMQ product. * Artemis is a message queue broker and here, we embed the entire server inside our own process. Nodes communicate - * with each other using an Artemis specific protocol, but it supports other protocols like AQMP/1.0 + * with each other using an Artemis specific protocol, but it supports other protocols like AMQP/1.0 * as well for interop. * * The current implementation is skeletal and lacks features like security or firewall tunnelling (that is, you must @@ -99,6 +99,14 @@ 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 + private val CIPHER_SUITES = listOf( + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256", + "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256") init { require(directory.fileSystem == FileSystems.getDefault()) { "Artemis only uses the default file system" } @@ -321,9 +329,10 @@ class ArtemisMessagingService(val directory: Path, HOST_PROP_NAME to host, PORT_PROP_NAME to port.toInt(), - // Turn on AMQP support, which needs the protoclo jar on the classpath. + // 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 + // 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 PROTOCOLS_PROP_NAME to "CORE,AMQP", // Enable TLS transport layer with client certs and restrict to at least SHA256 in handshake @@ -335,15 +344,15 @@ class ArtemisMessagingService(val directory: Path, TRUSTSTORE_PROVIDER_PROP_NAME to "JKS", TRUSTSTORE_PATH_PROP_NAME to trustStorePath, TRUSTSTORE_PASSWORD_PROP_NAME to "trustpass", - ENABLED_CIPHER_SUITES_PROP_NAME to "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256,TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_DSS_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256,TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,TLS_DHE_DSS_WITH_AES_128_GCM_SHA256", + ENABLED_CIPHER_SUITES_PROP_NAME to CIPHER_SUITES.joinToString(","), ENABLED_PROTOCOLS_PROP_NAME to "TLSv1.2", NEED_CLIENT_AUTH_PROP_NAME to true ) ) /** - * Strictly for dev only automatically construct a server certificate\private key signed from - * the CA certs in Node resources. + * Strictly for dev only automatically construct a server certificate/private key signed from + * the CA certs in Node resources. Then provision KeyStores into certificates folder under node path. */ fun configureWithDevSSLCertificate() { Files.createDirectories(directory.resolve("certificates"))