Merge pull request #1052 from corda/tudor-merge-21-06

Tudor merge 21 06
This commit is contained in:
Tudor Malene 2018-06-22 09:33:46 +01:00 committed by GitHub
commit d0300261cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 172 additions and 133 deletions

View File

@ -124,21 +124,25 @@ absolute path to the node's base directory.
:maxRestartCount: Maximum number of times the flow will restart before resulting in an error.
:backoffBase: The base of the exponential backoff, `t_{wait} = timeout * backoffBase^{retryCount}`.
:rpcAddress: The address of the RPC system on which RPC requests can be made to the node. If not provided then the node will run without RPC. This is now deprecated in favour of the ``rpcSettings`` block.
:rpcAddress: (Deprecated) The address of the RPC system on which RPC requests can be made to the node. If not provided then the node will run without RPC. This is now deprecated in favour of the ``rpcSettings`` block.
:rpcSettings: Options for the RPC server.
:rpcSettings: Options for the RPC server exposed by the Node.
:useSsl: (optional) boolean, indicates whether the node should require clients to use SSL for RPC connections, defaulted to ``false``.
:address: host and port for the RPC server binding.
:adminAddress: host and port for the RPC admin binding (this is the endpoint that the node process will connect to).
:standAloneBroker: (optional) boolean, indicates whether the node will connect to a standalone broker for RPC, defaulted to ``false``.
:address: (optional) host and port for the RPC server binding, if any.
:adminAddress: (optional) host and port for the RPC admin binding (only required when ``useSsl`` is ``false``, because the node connects to Artemis using SSL to ensure admin privileges are not accessible outside the node).
:ssl: (optional) SSL settings for the RPC server.
:useSsl: (optional) boolean, indicates whether or not the node should require clients to use SSL for RPC connections, defaulted to ``false``.
:ssl: (mandatory if ``useSsl=true``) SSL settings for the RPC server.
:keyStorePath: Absolute path to the key store containing the RPC SSL certificate.
:keyStorePassword: Password for the key store.
.. note:: The RPC SSL certificate is used by RPC clients to authenticate the connection.
The Node operator must provide RPC clients with a truststore containing the certificate they can trust.
We advise Node operators to not use the P2P keystore for RPC.
The node ships with a command line argument "--just-generate-rpc-ssl-settings", which generates a secure keystore
and truststore that can be used to secure the RPC connection. You can use this if you have no special requirements.
:keyStorePassword: password for the key store.
:trustStorePassword: password for the trust store.
:certificatesDirectory: directory in which the stores will be searched, unless absolute paths are provided.
:sslKeystore: absolute path to the ssl key store, defaulted to ``certificatesDirectory / "sslkeystore.jks"``.
:trustStoreFile: absolute path to the trust store, defaulted to ``certificatesDirectory / "truststore.jks"``.
:security: Contains various nested fields controlling user authentication/authorization, in particular for RPC accesses. See
:doc:`clientrpc` for details.
@ -307,76 +311,16 @@ Simple notary configuration file:
devMode : false
compatibilityZoneURL : "https://cz.corda.net"
An example ``web-server.conf`` file is as follow:
.. parsed-literal::
myLegalName : "O=Notary Service,OU=corda,L=London,C=GB"
keyStorePassword : "cordacadevpass"
trustStorePassword : "trustpass"
rpcSettings = {
useSsl = false
standAloneBroker = false
address : "my-corda-node:10003"
adminAddress : "my-corda-node:10004"
}
rpcUsers : [{ username=user1, password=letmein, permissions=[ StartFlow.net.corda.protocols.CashProtocol ] }]
Configuring a node where the Corda Comatability Zone's registration and Network Map services exist on different URLs
Configuring a node where the Corda Compatibility Zone's registration and Network Map services exist on different URLs
.. literalinclude:: example-code/src/main/resources/example-node-with-networkservices.conf
Fields
------
The available config fields are listed below. ``baseDirectory`` is available as a substitution value, containing the absolute
path to the node's base directory.
:myLegalName: The legal identity of the node acts as a human readable alias to the node's public key and several demos use
this to lookup the NodeInfo.
:keyStorePassword: The password to unlock the KeyStore file (``<workspace>/certificates/sslkeystore.jks``) containing the
node certificate and private key.
.. note:: This is the non-secret value for the development certificates automatically generated during the first node run.
Longer term these keys will be managed in secure hardware devices.
:trustStorePassword: The password to unlock the Trust store file (``<workspace>/certificates/truststore.jks``) containing
the Corda network root certificate. This is the non-secret value for the development certificates automatically
generated during the first node run.
.. note:: Longer term these keys will be managed in secure hardware devices.
:rpcSettings: Options for the RPC server.
:useSsl: (optional) boolean, indicates whether the node should require clients to use SSL for RPC connections, defaulted to ``false``.
:standAloneBroker: (optional) boolean, indicates whether the node will connect to a standalone broker for RPC, defaulted to ``false``.
:address: (optional) host and port for the RPC server binding, if any.
:adminAddress: (optional) host and port for the RPC admin binding (only required when ``useSsl`` is ``false``, because the node connects to Artemis using SSL to ensure admin privileges are not accessible outside the node).
:ssl: (optional) SSL settings for the RPC client.
:keyStorePassword: password for the key store.
:trustStorePassword: password for the trust store.
:certificatesDirectory: directory in which the stores will be searched, unless absolute paths are provided.
:sslKeystore: absolute path to the ssl key store, defaulted to ``certificatesDirectory / "sslkeystore.jks"``.
:trustStoreFile: absolute path to the trust store, defaulted to ``certificatesDirectory / "truststore.jks"``.
:trustStoreFile: absolute path to the trust store, defaulted to ``certificatesDirectory / "truststore.jks"``.
:rpcUsers: A list of users who are authorised to access the RPC system. Each user in the list is a config object with the
following fields:
:username: Username consisting only of word characters (a-z, A-Z, 0-9 and _)
:password: The password
:permissions: A list of permissions for starting flows via RPC. To give the user the permission to start the flow
``foo.bar.FlowClass``, add the string ``StartFlow.foo.bar.FlowClass`` to the list. If the list
contains the string ``ALL``, the user can start any flow via RPC. This value is intended for administrator
users and for development.
Fields Override
---------------
JVM options or environmental variables prefixed ``corda.`` can override ``node.conf`` fields.
Provided system properties also can set value for absent fields in ``node.conf``.
Example adding/overriding keyStore password when starting Corda node:
JVM options or environmental variables prefixed with ``corda.`` can override ``node.conf`` fields.
Provided system properties can also set values for absent fields in ``node.conf``.
This is an example of adding/overriding the keyStore password :
.. sourcecode:: shell

View File

@ -58,7 +58,7 @@ object X509Utilities {
const val CORDA_CLIENT_TLS = "cordaclienttls"
const val CORDA_CLIENT_CA = "cordaclientca"
private val DEFAULT_VALIDITY_WINDOW = Pair(0.millis, 3650.days)
val DEFAULT_VALIDITY_WINDOW = Pair(0.millis, 3650.days)
/**
* Helper function to return the latest out of an instant and an optional date.

View File

@ -17,6 +17,9 @@ import net.corda.core.utilities.getOrThrow
import net.corda.node.services.Permissions.Companion.all
import net.corda.nodeapi.BrokerRpcSslOptions
import net.corda.core.messaging.ClientRpcSslOptions
import net.corda.node.utilities.createKeyPairAndSelfSignedTLSCertificate
import net.corda.node.utilities.saveToKeyStore
import net.corda.node.utilities.saveToTrustStore
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_RPC_USER
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME
@ -28,9 +31,6 @@ import net.corda.testing.driver.internal.RandomFree
import net.corda.testing.internal.IntegrationTest
import net.corda.testing.internal.IntegrationTestSchemas
import net.corda.testing.internal.toDatabaseSchemaName
import net.corda.testing.internal.createKeyPairAndSelfSignedCertificate
import net.corda.testing.internal.saveToKeyStore
import net.corda.testing.internal.saveToTrustStore
import net.corda.testing.internal.useSslRpcOverrides
import net.corda.testing.node.User
import org.apache.activemq.artemis.api.core.ActiveMQException
@ -41,6 +41,7 @@ import org.junit.ClassRule
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import javax.security.auth.x500.X500Principal
class RpcSslTest : IntegrationTest() {
companion object {
@ -54,6 +55,7 @@ class RpcSslTest : IntegrationTest() {
@JvmField
val tempFolder = TemporaryFolder()
val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB")
@Test
fun `RPC client using ssl is able to run a command`() {
@ -61,7 +63,7 @@ class RpcSslTest : IntegrationTest() {
var successfulLogin = false
var failedLogin = false
val (keyPair, cert) = createKeyPairAndSelfSignedCertificate()
val (keyPair, cert) = createKeyPairAndSelfSignedTLSCertificate(testName)
val keyStorePath = saveToKeyStore(tempFolder.root.toPath() / "keystore.jks", keyPair, cert)
val brokerSslOptions = BrokerRpcSslOptions(keyStorePath, "password")
@ -98,11 +100,11 @@ class RpcSslTest : IntegrationTest() {
val user = User("mark", "dadada", setOf(all()))
var successful = false
val (keyPair, cert) = createKeyPairAndSelfSignedCertificate()
val (keyPair, cert) = createKeyPairAndSelfSignedTLSCertificate(testName)
val keyStorePath = saveToKeyStore(tempFolder.root.toPath() / "keystore.jks", keyPair, cert)
val brokerSslOptions = BrokerRpcSslOptions(keyStorePath, "password")
val (_, cert1) = createKeyPairAndSelfSignedCertificate()
val (_, cert1) = createKeyPairAndSelfSignedTLSCertificate(testName)
val trustStorePath = saveToTrustStore(tempFolder.root.toPath() / "truststore.jks", cert1)
val clientSslOptions = ClientRpcSslOptions(trustStorePath, "password")
@ -140,7 +142,7 @@ class RpcSslTest : IntegrationTest() {
@Test
fun `The system RPC user can not connect to the rpc broker without the node's key`() {
val (keyPair, cert) = createKeyPairAndSelfSignedCertificate()
val (keyPair, cert) = createKeyPairAndSelfSignedTLSCertificate(testName)
val keyStorePath = saveToKeyStore(tempFolder.root.toPath() / "keystore.jks", keyPair, cert)
val brokerSslOptions = BrokerRpcSslOptions(keyStorePath, "password")
val trustStorePath = saveToTrustStore(tempFolder.root.toPath() / "truststore.jks", cert)

View File

@ -64,6 +64,8 @@ class NodeArgsParser : AbstractArgsParser<CmdLineOptions>() {
private val isVersionArg = optionParser.accepts("version", "Print the version and exit")
private val justGenerateNodeInfoArg = optionParser.accepts("just-generate-node-info",
"Perform the node start-up task necessary to generate its nodeInfo, save it to disk, then quit")
private val justGenerateRpcSslCertsArg = optionParser.accepts("just-generate-rpc-ssl-settings",
"Generate the ssl keystore and truststore for a secure RPC connection.")
private val bootstrapRaftClusterArg = optionParser.accepts("bootstrap-raft-cluster", "Bootstraps Raft cluster. The node forms a single node cluster (ignoring otherwise configured peer addresses), acting as a seed for other nodes to join the cluster.")
private val clearNetworkMapCache = optionParser.accepts("clear-network-map-cache", "Clears local copy of network map, on node startup it will be restored from server or file system.")
@ -85,6 +87,7 @@ class NodeArgsParser : AbstractArgsParser<CmdLineOptions>() {
val noLocalShell = optionSet.has(noLocalShellArg)
val sshdServer = optionSet.has(sshdServerArg)
val justGenerateNodeInfo = optionSet.has(justGenerateNodeInfoArg)
val justGenerateRpcSslCerts = optionSet.has(justGenerateRpcSslCertsArg)
val bootstrapRaftCluster = optionSet.has(bootstrapRaftClusterArg)
val networkRootTrustStorePath = optionSet.valueOf(networkRootTrustStorePathArg)
val networkRootTrustStorePassword = optionSet.valueOf(networkRootTrustStorePasswordArg)
@ -109,6 +112,7 @@ class NodeArgsParser : AbstractArgsParser<CmdLineOptions>() {
noLocalShell,
sshdServer,
justGenerateNodeInfo,
justGenerateRpcSslCerts,
bootstrapRaftCluster,
unknownConfigKeysPolicy,
devMode,
@ -127,6 +131,7 @@ data class CmdLineOptions(val baseDirectory: Path,
val noLocalShell: Boolean,
val sshdServer: Boolean,
val justGenerateNodeInfo: Boolean,
val justGenerateRpcSslCerts: Boolean,
val bootstrapRaftCluster: Boolean,
val unknownConfigKeysPolicy: UnknownConfigKeysPolicy,
val devMode: Boolean,

View File

@ -261,7 +261,7 @@ 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, 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())
}

View File

@ -16,11 +16,8 @@ import com.typesafe.config.ConfigRenderOptions
import io.netty.channel.unix.Errors
import net.corda.core.cordapp.Cordapp
import net.corda.core.crypto.Crypto
import net.corda.core.internal.Emoji
import net.corda.core.internal.*
import net.corda.core.internal.concurrent.thenMatch
import net.corda.core.internal.createDirectories
import net.corda.core.internal.div
import net.corda.core.internal.randomOrNull
import net.corda.core.utilities.Try
import net.corda.core.utilities.loggerFor
import net.corda.node.CmdLineOptions
@ -34,8 +31,11 @@ import net.corda.node.services.config.NodeConfigurationImpl
import net.corda.node.services.config.shouldStartLocalShell
import net.corda.node.services.config.shouldStartSSHDaemon
import net.corda.node.services.transactions.bftSMaRtSerialFilter
import net.corda.node.utilities.createKeyPairAndSelfSignedTLSCertificate
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
import net.corda.node.utilities.registration.NodeRegistrationHelper
import net.corda.node.utilities.saveToKeyStore
import net.corda.node.utilities.saveToTrustStore
import net.corda.node.utilities.registration.UnableToRegisterNodeWithDoormanException
import net.corda.nodeapi.internal.addShutdownHook
import net.corda.nodeapi.internal.config.UnknownConfigurationKeysException
@ -47,12 +47,14 @@ import org.fusesource.jansi.Ansi
import org.fusesource.jansi.AnsiConsole
import org.slf4j.bridge.SLF4JBridgeHandler
import sun.misc.VMSupport
import java.io.Console
import java.io.RandomAccessFile
import java.lang.management.ManagementFactory
import java.net.InetAddress
import java.nio.file.Path
import java.nio.file.Paths
import java.util.*
import kotlin.system.exitProcess
/** This class is responsible for starting a Node from command line arguments. */
open class NodeStartup(val args: Array<String>) {
@ -188,6 +190,70 @@ open class NodeStartup(val args: Array<String>) {
node.generateAndSaveNodeInfo()
return
}
if (cmdlineOptions.justGenerateRpcSslCerts) {
val (keyPair, cert) = createKeyPairAndSelfSignedTLSCertificate(conf.myLegalName.x500Principal)
val keyStorePath = conf.baseDirectory / "certificates" / "rpcsslkeystore.jks"
val trustStorePath = conf.baseDirectory / "certificates" / "export" / "rpcssltruststore.jks"
if (keyStorePath.exists() || trustStorePath.exists()) {
println("Found existing RPC SSL keystores. Command was already run. Exiting..")
exitProcess(0)
}
val console: Console? = System.console()
when (console) {
// In this case, the JVM is not connected to the console so we need to exit
null -> {
println("Not connected to console. Exiting")
exitProcess(1)
}
// Otherwise we can proceed normally
else -> {
while (true) {
val keystorePassword1 = console.readPassword("Enter the keystore password => ")
val keystorePassword2 = console.readPassword("Re-enter the keystore password => ")
if (!keystorePassword1.contentEquals(keystorePassword2)) {
println("The keystore passwords don't match.")
continue
}
saveToKeyStore(keyStorePath, keyPair, cert, String(keystorePassword1), "rpcssl")
println("The keystore was saved to: $keyStorePath .")
break
}
while (true) {
val trustStorePassword1 = console.readPassword("Enter the truststore password => ")
val trustStorePassword2 = console.readPassword("Re-enter the truststore password => ")
if (!trustStorePassword1.contentEquals(trustStorePassword2)) {
println("The truststore passwords don't match.")
continue
}
saveToTrustStore(trustStorePath, cert, String(trustStorePassword1), "rpcssl")
println("The truststore was saved to: $trustStorePath .")
println("You need to distribute this file along with the password in a secure way to all RPC clients.")
break
}
val dollar = '$'
println("""
|
|The SSL certificates were generated successfully.
|
|Add this snippet to the "rpcSettings" section of your node.conf:
| useSsl=true
| ssl {
| keyStorePath=$dollar{baseDirectory}/certificates/rpcsslkeystore.jks
| keyStorePassword=the_above_password
| }
|""".trimMargin())
}
}
return
}
val startedNode = node.start()
logLoadedCorDapps(startedNode.services.cordappProvider.cordapps)
startedNode.internals.nodeReadyFuture.thenMatch({

View File

@ -286,7 +286,7 @@ data class NodeConfigurationImpl(
override val rpcOptions: NodeRpcOptions
get() {
return actualRpcSettings.asOptions(BrokerRpcSslOptions(baseDirectory / "certificates" / "nodekeystore.jks", keyStorePassword))
return actualRpcSettings.asOptions()
}
private fun validateTlsCertCrlConfig(): List<String> {
@ -332,10 +332,11 @@ data class NodeConfigurationImpl(
private fun validateRpcSettings(options: NodeRpcSettings): List<String> {
val errors = mutableListOf<String>()
if (options.address != null) {
if (!options.useSsl && options.adminAddress == null) {
errors += "'rpcSettings.adminAddress': missing. Property is mandatory when 'rpcSettings.useSsl' is false (default)."
}
if (options.adminAddress == null) {
errors += "'rpcSettings.adminAddress': missing"
}
if (options.useSsl && options.ssl == null) {
errors += "'rpcSettings.ssl': missing (rpcSettings.useSsl was set to true)."
}
return errors
}
@ -434,13 +435,13 @@ data class NodeRpcSettings(
val useSsl: Boolean = false,
val ssl: BrokerRpcSslOptions?
) {
fun asOptions(fallbackSslOptions: BrokerRpcSslOptions): NodeRpcOptions {
fun asOptions(): NodeRpcOptions {
return object : NodeRpcOptions {
override val address = this@NodeRpcSettings.address!!
override val adminAddress = this@NodeRpcSettings.adminAddress!!
override val standAloneBroker = this@NodeRpcSettings.standAloneBroker
override val useSsl = this@NodeRpcSettings.useSsl
override val sslConfig = this@NodeRpcSettings.ssl ?: fallbackSslOptions
override val sslConfig = this@NodeRpcSettings.ssl
override fun toString(): String {
return "address: $address, adminAddress: $adminAddress, standAloneBroker: $standAloneBroker, useSsl: $useSsl, sslConfig: $sslConfig"

View File

@ -18,5 +18,5 @@ interface NodeRpcOptions {
val adminAddress: NetworkHostAndPort
val standAloneBroker: Boolean
val useSsl: Boolean
val sslConfig: BrokerRpcSslOptions
val sslConfig: BrokerRpcSslOptions?
}

View File

@ -0,0 +1,36 @@
package net.corda.node.utilities
import net.corda.core.crypto.Crypto
import net.corda.nodeapi.internal.crypto.*
import java.nio.file.Path
import java.security.KeyPair
import java.security.cert.X509Certificate
import java.time.Duration
import javax.security.auth.x500.X500Principal
fun createKeyPairAndSelfSignedTLSCertificate(x500Principal: X500Principal): Pair<KeyPair, X509Certificate> {
val rpcKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val selfSignCert = createSelfSignedTLSCertificate(x500Principal, rpcKeyPair)
return Pair(rpcKeyPair, selfSignCert)
}
fun createSelfSignedTLSCertificate(subject: X500Principal,
keyPair: KeyPair,
validityWindow: Pair<Duration, Duration> = X509Utilities.DEFAULT_VALIDITY_WINDOW): X509Certificate {
val window = X509Utilities.getCertificateValidityWindow(validityWindow.first, validityWindow.second)
return X509Utilities.createCertificate(CertificateType.TLS, subject, keyPair, subject, keyPair.public, window)
}
fun saveToKeyStore(keyStorePath: Path, rpcKeyPair: KeyPair, selfSignCert: X509Certificate, password: String = "password", alias: String = "Key"): Path {
val keyStore = loadOrCreateKeyStore(keyStorePath, password)
keyStore.addOrReplaceKey(alias, rpcKeyPair.private, password.toCharArray(), arrayOf(selfSignCert))
keyStore.save(keyStorePath, password)
return keyStorePath
}
fun saveToTrustStore(trustStorePath: Path, selfSignCert: X509Certificate, password: String = "password", alias: String = "Key"): Path {
val trustStore = loadOrCreateKeyStore(trustStorePath, password)
trustStore.addOrReplaceCertificate(alias, selfSignCert)
trustStore.save(trustStorePath, password)
return trustStorePath
}

View File

@ -52,6 +52,7 @@ class NodeArgsParserTest {
noLocalShell = false,
sshdServer = false,
justGenerateNodeInfo = false,
justGenerateRpcSslCerts = false,
bootstrapRaftCluster = false,
unknownConfigKeysPolicy = UnknownConfigKeysPolicy.FAIL,
devMode = false,

View File

@ -24,6 +24,9 @@ import net.corda.node.internal.security.RPCSecurityManagerImpl
import net.corda.node.services.Permissions.Companion.all
import net.corda.node.services.messaging.InternalRPCMessagingClient
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
@ -31,10 +34,7 @@ import net.corda.nodeapi.internal.config.User
import net.corda.testing.core.SerializationEnvironmentRule
import net.corda.testing.driver.PortAllocation
import net.corda.testing.driver.internal.RandomFree
import net.corda.testing.internal.createKeyPairAndSelfSignedCertificate
import net.corda.testing.internal.createNodeSslConfig
import net.corda.testing.internal.saveToKeyStore
import net.corda.testing.internal.saveToTrustStore
import org.apache.activemq.artemis.api.core.ActiveMQConnectionTimedOutException
import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl
import org.assertj.core.api.Assertions.assertThat
@ -43,6 +43,7 @@ import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import java.nio.file.Path
import javax.security.auth.x500.X500Principal
class ArtemisRpcTests {
private val ports: PortAllocation = RandomFree
@ -59,9 +60,11 @@ class ArtemisRpcTests {
@JvmField
val tempFolder = TemporaryFolder()
val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB")
@Test
fun rpc_with_ssl_enabled() {
val (rpcKeyPair, selfSignCert) = createKeyPairAndSelfSignedCertificate()
val (rpcKeyPair, selfSignCert) = createKeyPairAndSelfSignedTLSCertificate(testName)
val keyStorePath = saveToKeyStore(tempFile("rpcKeystore.jks"), rpcKeyPair, selfSignCert)
val brokerSslOptions = BrokerRpcSslOptions(keyStorePath, "password")
val trustStorePath = saveToTrustStore(tempFile("rpcTruststore.jks"), selfSignCert)
@ -76,7 +79,7 @@ class ArtemisRpcTests {
@Test
fun rpc_with_no_ssl_on_client_side_and_ssl_on_server_side() {
val (rpcKeyPair, selfSignCert) = createKeyPairAndSelfSignedCertificate()
val (rpcKeyPair, selfSignCert) = createKeyPairAndSelfSignedTLSCertificate(testName)
val keyStorePath = saveToKeyStore(tempFile("rpcKeystore.jks"), rpcKeyPair, selfSignCert)
val brokerSslOptions = BrokerRpcSslOptions(keyStorePath, "password")
// here client sslOptions are passed null (as in, do not use SSL)
@ -87,12 +90,12 @@ class ArtemisRpcTests {
@Test
fun rpc_client_certificate_untrusted_to_server() {
val (rpcKeyPair, selfSignCert) = createKeyPairAndSelfSignedCertificate()
val (rpcKeyPair, selfSignCert) = createKeyPairAndSelfSignedTLSCertificate(testName)
val keyStorePath = saveToKeyStore(tempFile("rpcKeystore.jks"), rpcKeyPair, selfSignCert)
// create another keypair and certificate and add that to the client truststore
// the ssl connection should not
val (_, selfSignCert1) = createKeyPairAndSelfSignedCertificate()
val (_, selfSignCert1) = createKeyPairAndSelfSignedTLSCertificate(testName)
val trustStorePath = saveToTrustStore(tempFile("rpcTruststore.jks"), selfSignCert1)
val brokerSslOptions = BrokerRpcSslOptions(keyStorePath, "password")

View File

@ -28,7 +28,6 @@ import net.corda.serialization.internal.amqp.AMQP_ENABLED
import java.nio.file.Files
import java.nio.file.Path
import java.security.KeyPair
import java.security.cert.X509Certificate
import javax.security.auth.x500.X500Principal
@Suppress("unused")
@ -150,24 +149,3 @@ fun createNodeSslConfig(path: Path, name: CordaX500Name = CordaX500Name("MegaCor
return sslConfig
}
fun createKeyPairAndSelfSignedCertificate(): Pair<KeyPair, X509Certificate> {
val rpcKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB")
val selfSignCert = X509Utilities.createSelfSignedCACertificate(testName, rpcKeyPair)
return Pair(rpcKeyPair, selfSignCert)
}
fun saveToKeyStore(keyStorePath: Path, rpcKeyPair: KeyPair, selfSignCert: X509Certificate, password: String = "password"): Path {
val keyStore = loadOrCreateKeyStore(keyStorePath, password)
keyStore.addOrReplaceKey("Key", rpcKeyPair.private, password.toCharArray(), arrayOf(selfSignCert))
keyStore.save(keyStorePath, password)
return keyStorePath
}
fun saveToTrustStore(trustStorePath: Path, selfSignCert: X509Certificate, password: String = "password"): Path {
val trustStore = loadOrCreateKeyStore(trustStorePath, password)
trustStore.addOrReplaceCertificate("Key", selfSignCert)
trustStore.save(trustStorePath, password)
return trustStorePath
}

View File

@ -22,6 +22,9 @@ import net.corda.node.services.Permissions.Companion.all
import net.corda.node.services.config.shell.toShellConfig
import net.corda.nodeapi.BrokerRpcSslOptions
import net.corda.core.messaging.ClientRpcSslOptions
import net.corda.node.utilities.createKeyPairAndSelfSignedTLSCertificate
import net.corda.node.utilities.saveToKeyStore
import net.corda.node.utilities.saveToTrustStore
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.DUMMY_BANK_A_NAME
@ -33,9 +36,6 @@ import net.corda.testing.driver.internal.RandomFree
import net.corda.testing.internal.IntegrationTest
import net.corda.testing.internal.IntegrationTestSchemas
import net.corda.testing.internal.toDatabaseSchemaName
import net.corda.testing.internal.createKeyPairAndSelfSignedCertificate
import net.corda.testing.internal.saveToKeyStore
import net.corda.testing.internal.saveToTrustStore
import net.corda.testing.internal.useSslRpcOverrides
import net.corda.testing.node.User
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
@ -47,6 +47,7 @@ import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import javax.security.auth.x500.X500Principal
import kotlin.test.assertTrue
class InteractiveShellIntegrationTest : IntegrationTest() {
@ -61,6 +62,8 @@ class InteractiveShellIntegrationTest : IntegrationTest() {
@JvmField
val tempFolder = TemporaryFolder()
val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB")
@Test
fun `shell should not log in with invalid credentials`() {
val user = User("u", "p", setOf())
@ -98,7 +101,7 @@ class InteractiveShellIntegrationTest : IntegrationTest() {
val user = User("mark", "dadada", setOf(all()))
var successful = false
val (keyPair, cert) = createKeyPairAndSelfSignedCertificate()
val (keyPair, cert) = createKeyPairAndSelfSignedTLSCertificate(testName)
val keyStorePath = saveToKeyStore(tempFolder.root.toPath() / "keystore.jks", keyPair, cert)
val brokerSslOptions = BrokerRpcSslOptions(keyStorePath, "password")
@ -125,11 +128,11 @@ class InteractiveShellIntegrationTest : IntegrationTest() {
@Test
fun `shell shoud not log in with invalid truststore`() {
val user = User("mark", "dadada", setOf("ALL"))
val (keyPair, cert) = createKeyPairAndSelfSignedCertificate()
val (keyPair, cert) = createKeyPairAndSelfSignedTLSCertificate(testName)
val keyStorePath = saveToKeyStore(tempFolder.root.toPath() / "keystore.jks", keyPair, cert)
val brokerSslOptions = BrokerRpcSslOptions(keyStorePath, "password")
val (_, cert1) = createKeyPairAndSelfSignedCertificate()
val (_, cert1) = createKeyPairAndSelfSignedTLSCertificate(testName)
val trustStorePath = saveToTrustStore(tempFolder.root.toPath() / "truststore.jks", cert1)
val clientSslOptions = ClientRpcSslOptions(trustStorePath, "password")
@ -209,7 +212,7 @@ class InteractiveShellIntegrationTest : IntegrationTest() {
Permissions.invokeRpc(CordaRPCOps::registeredFlows),
Permissions.invokeRpc(CordaRPCOps::nodeInfo)/*all()*/))
val (keyPair, cert) = createKeyPairAndSelfSignedCertificate()
val (keyPair, cert) = createKeyPairAndSelfSignedTLSCertificate(testName)
val keyStorePath = saveToKeyStore(tempFolder.root.toPath() / "keystore.jks", keyPair, cert)
val brokerSslOptions = BrokerRpcSslOptions(keyStorePath, "password")
val trustStorePath = saveToTrustStore(tempFolder.root.toPath() / "truststore.jks", cert)