From 8756b49794896e1c73144436181ff9ff6b5d0fb8 Mon Sep 17 00:00:00 2001 From: Matthew Nesbit Date: Fri, 26 Aug 2016 18:08:48 +0100 Subject: [PATCH] Add switchable HTTPS to Node web server Explain change of cipher Include extracted Dev Corda Root certificate so that it can be installed by HTTPS users. --- config/dev/corda_dev_ca.cer | 13 +++++++ config/dev/generalnodea.conf | 1 + config/dev/generalnodeb.conf | 1 + config/dev/nameservernode.conf | 3 +- .../com/r3corda/core/crypto/X509Utilities.kt | 4 +- .../kotlin/com/r3corda/node/internal/Node.kt | 39 ++++++++++++++++--- .../node/services/config/NodeConfiguration.kt | 1 + node/src/main/resources/reference.conf | 3 +- 8 files changed, 56 insertions(+), 9 deletions(-) create mode 100644 config/dev/corda_dev_ca.cer diff --git a/config/dev/corda_dev_ca.cer b/config/dev/corda_dev_ca.cer new file mode 100644 index 0000000000..9b43475c71 --- /dev/null +++ b/config/dev/corda_dev_ca.cer @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIICCjCCAbCgAwIBAgIINo1Qd4LJ1RUwCgYIKoZIzj0EAwIwWDEbMBkGA1UEAwwS +Q29yZGEgTm9kZSBSb290IENBMQswCQYDVQQKDAJSMzEOMAwGA1UECwwFY29yZGEx +DzANBgNVBAcMBkxvbmRvbjELMAkGA1UEBhMCVUswHhcNMTYwNzE5MDAwMDAwWhcN +MjYwNzE3MDAwMDAwWjBYMRswGQYDVQQDDBJDb3JkYSBOb2RlIFJvb3QgQ0ExCzAJ +BgNVBAoMAlIzMQ4wDAYDVQQLDAVjb3JkYTEPMA0GA1UEBwwGTG9uZG9uMQswCQYD +VQQGEwJVSzBWMBAGByqGSM49AgEGBSuBBAAKA0IABHtaVzCVCvNqp+Jhy5/hC25h +yHomwW5gJpNCPUdgVpLnSUXm+NRzf0ia+1SevkaEPSf5kzk47K1Po6KBWVTPbUaj +ZzBlMB0GA1UdDgQWBBSNU2aUShmUQCadxpzs+WWMQTSMJTASBgNVHRMBAf8ECDAG +AQH/AgECMAsGA1UdDwQEAwIBtjAjBgNVHSUEHDAaBggrBgEFBQcDAQYIKwYBBQUH +AwIGBFUdJQAwCgYIKoZIzj0EAwIDSAAwRQIgaHrsbm0ZZ5uFXCfntTcDddJmttiX +IXi6aN7mIIsl/5kCIQC4RCyclxoeZD/TaraTDzOVeSaooxLA/SrwQMzd0pxCcg== +-----END CERTIFICATE----- diff --git a/config/dev/generalnodea.conf b/config/dev/generalnodea.conf index dd4b2217f0..0926b8410c 100644 --- a/config/dev/generalnodea.conf +++ b/config/dev/generalnodea.conf @@ -12,3 +12,4 @@ mapService : { address : "localhost:12345", identity : "Notary Service" } +useHTTPS : false diff --git a/config/dev/generalnodeb.conf b/config/dev/generalnodeb.conf index 7ba9f13f7f..61fd9472b7 100644 --- a/config/dev/generalnodeb.conf +++ b/config/dev/generalnodeb.conf @@ -12,3 +12,4 @@ mapService : { address : "localhost:12345", identity : "Notary Service" } +useHTTPS : false diff --git a/config/dev/nameservernode.conf b/config/dev/nameservernode.conf index 740b5b066d..23665e07b9 100644 --- a/config/dev/nameservernode.conf +++ b/config/dev/nameservernode.conf @@ -11,4 +11,5 @@ mapService : { hostServiceLocally : true, address : ${artemisAddress}, identity : ${myLegalName} -} \ No newline at end of file +} +useHTTPS : false \ No newline at end of file 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 1d16a20b0f..3c7164800c 100644 --- a/core/src/main/kotlin/com/r3corda/core/crypto/X509Utilities.kt +++ b/core/src/main/kotlin/com/r3corda/core/crypto/X509Utilities.kt @@ -36,7 +36,9 @@ object X509Utilities { val SIGNATURE_ALGORITHM = "SHA256withECDSA" val KEY_GENERATION_ALGORITHM = "ECDSA" - val ECDSA_CURVE = "secp256k1" // TLS implementations only support standard SEC2 curves, although internally Corda uses newer EDDSA keys + // TLS implementations only support standard SEC2 curves, although internally Corda uses newer EDDSA keys. + // Also browsers like Chrome don't seem to support the secp256k1, only the secp256r1 curve. + val ECDSA_CURVE = "secp256r1" val KEYSTORE_TYPE = "JKS" val CA_CERT_ALIAS = "CA Cert" 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 0b7828e04e..9257056cfb 100644 --- a/node/src/main/kotlin/com/r3corda/node/internal/Node.kt +++ b/node/src/main/kotlin/com/r3corda/node/internal/Node.kt @@ -9,6 +9,7 @@ import com.r3corda.core.node.services.ServiceType import com.r3corda.core.utilities.loggerFor import com.r3corda.node.serialization.NodeClock import com.r3corda.node.services.api.MessagingServiceInternal +import com.r3corda.node.services.config.FullNodeConfiguration import com.r3corda.node.services.config.NodeConfiguration import com.r3corda.node.services.messaging.ArtemisMessagingClient import com.r3corda.node.services.messaging.ArtemisMessagingServer @@ -17,11 +18,12 @@ import com.r3corda.node.servlets.Config import com.r3corda.node.servlets.DataUploadServlet import com.r3corda.node.servlets.ResponseFilter import com.r3corda.node.utilities.AffinityExecutor -import org.eclipse.jetty.server.Server +import org.eclipse.jetty.server.* import org.eclipse.jetty.server.handler.HandlerCollection import org.eclipse.jetty.servlet.DefaultServlet import org.eclipse.jetty.servlet.ServletContextHandler import org.eclipse.jetty.servlet.ServletHolder +import org.eclipse.jetty.util.ssl.SslContextFactory import org.eclipse.jetty.webapp.WebAppContext import org.glassfish.jersey.server.ResourceConfig import org.glassfish.jersey.server.ServerProperties @@ -117,9 +119,7 @@ class Node(dir: Path, val p2pAddr: HostAndPort, val webServerAddr: HostAndPort, }() if (networkMapService != null) { return ArtemisMessagingClient(dir, configuration, serverAddr, services.storageService.myLegalIdentityKey.public, serverThread) - } - else - { + } else { return ArtemisMessagingClient(dir, configuration, serverAddr, null, serverThread) } } @@ -141,8 +141,6 @@ class Node(dir: Path, val p2pAddr: HostAndPort, val webServerAddr: HostAndPort, private fun initWebServer(): Server { // Note that the web server handlers will all run concurrently, and not on the node thread. - val server = Server(InetSocketAddress(webServerAddr.hostText, webServerAddr.port)) - val handlerCollection = HandlerCollection() // Export JMX monitoring statistics and data over REST/JSON. @@ -159,6 +157,35 @@ class Node(dir: Path, val p2pAddr: HostAndPort, val webServerAddr: HostAndPort, // API, data upload and download to services (attachments, rates oracles etc) handlerCollection.addHandler(buildServletContextHandler()) + val server = Server() + + if (configuration is FullNodeConfiguration && configuration.useHTTPS) { + val httpsConfiguration = HttpConfiguration() + httpsConfiguration.outputBufferSize = 32768 + httpsConfiguration.addCustomizer(SecureRequestCustomizer()) + val sslContextFactory = SslContextFactory() + val keyStorePath = dir.resolve("certificates").resolve("sslkeystore.jks") + val trustStorePath = dir.resolve("certificates").resolve("truststore.jks") + sslContextFactory.setKeyStorePath(keyStorePath.toString()) + sslContextFactory.setKeyStorePassword(configuration.keyStorePassword) + sslContextFactory.setKeyManagerPassword(configuration.keyStorePassword) + sslContextFactory.setTrustStorePath(trustStorePath.toString()) + sslContextFactory.setTrustStorePassword(configuration.trustStorePassword) + sslContextFactory.setExcludeProtocols("SSL.*", "TLSv1", "TLSv1.1") + sslContextFactory.setIncludeProtocols("TLSv1.2") + sslContextFactory.setExcludeCipherSuites(".*NULL.*", ".*RC4.*", ".*MD5.*", ".*DES.*", ".*DSS.*") + sslContextFactory.setIncludeCipherSuites(".*AES.*GCM.*") + val sslConnector = ServerConnector(server, SslConnectionFactory(sslContextFactory, "http/1.1"), HttpConnectionFactory(httpsConfiguration)) + sslConnector.port = webServerAddr.port + server.connectors = arrayOf(sslConnector) + } else { + val httpConfiguration = HttpConfiguration() + httpConfiguration.outputBufferSize = 32768 + val httpConnector = ServerConnector(server, HttpConnectionFactory(httpConfiguration)) + httpConnector.port = webServerAddr.port + server.connectors = arrayOf(httpConnector) + } + server.handler = handlerCollection server.start() return server 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 71c7361b2c..b797f74b35 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 @@ -103,6 +103,7 @@ class FullNodeConfiguration(conf: Config) : NodeConfiguration { override val keyStorePassword: String by conf override val trustStorePassword: String by conf override val dataSourceProperties: Properties by conf + val useHTTPS: Boolean by conf val artemisAddress: HostAndPort by conf val webAddress: HostAndPort by conf val messagingServerAddress: HostAndPort? = if (conf.hasPath("messagingServerAddress")) HostAndPort.fromString(conf.getString("messagingServerAddress")) else null diff --git a/node/src/main/resources/reference.conf b/node/src/main/resources/reference.conf index f4a9758acc..49bd6dd12c 100644 --- a/node/src/main/resources/reference.conf +++ b/node/src/main/resources/reference.conf @@ -8,4 +8,5 @@ dataSourceProperties = { "dataSource.url" = "jdbc:h2:"${basedir}"/persistence" "dataSource.user" = sa "dataSource.password" = "" -} \ No newline at end of file +} +useHTTPS = false \ No newline at end of file