[CORDA-1937]: Refactor NodeConfiguration hierarchy. (#3856)

This commit is contained in:
Michele Sollecito
2018-09-04 10:26:10 +01:00
committed by GitHub
parent f6ee263db1
commit d01dd22419
55 changed files with 961 additions and 605 deletions

View File

@ -7,12 +7,11 @@ import net.corda.core.context.Trace
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.core.serialization.internal.effectiveSerializationEnv import net.corda.core.serialization.internal.effectiveSerializationEnv
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.nodeapi.ArtemisTcpTransport.Companion.rpcConnectorTcpTransport
import net.corda.core.messaging.ClientRpcSslOptions import net.corda.core.messaging.ClientRpcSslOptions
import net.corda.core.utilities.days import net.corda.core.utilities.days
import net.corda.core.utilities.minutes import net.corda.core.utilities.minutes
import net.corda.core.utilities.seconds 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 net.corda.serialization.internal.AMQP_RPC_CLIENT_CONTEXT
import java.time.Duration import java.time.Duration
@ -243,10 +242,8 @@ class CordaRPCClient private constructor(
private val hostAndPort: NetworkHostAndPort, private val hostAndPort: NetworkHostAndPort,
private val configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT, private val configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT,
private val sslConfiguration: ClientRpcSslOptions? = null, private val sslConfiguration: ClientRpcSslOptions? = null,
private val nodeSslConfiguration: SSLConfiguration? = null,
private val classLoader: ClassLoader? = null, private val classLoader: ClassLoader? = null,
private val haAddressPool: List<NetworkHostAndPort> = emptyList(), private val haAddressPool: List<NetworkHostAndPort> = emptyList()
private val internalConnection: Boolean = false
) { ) {
@JvmOverloads @JvmOverloads
constructor(hostAndPort: NetworkHostAndPort, constructor(hostAndPort: NetworkHostAndPort,
@ -260,7 +257,7 @@ class CordaRPCClient private constructor(
* @param configuration An optional configuration used to tweak client behaviour. * @param configuration An optional configuration used to tweak client behaviour.
*/ */
@JvmOverloads @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 { companion object {
fun createWithSsl( fun createWithSsl(
@ -285,16 +282,7 @@ class CordaRPCClient private constructor(
sslConfiguration: ClientRpcSslOptions? = null, sslConfiguration: ClientRpcSslOptions? = null,
classLoader: ClassLoader? = null classLoader: ClassLoader? = null
): CordaRPCClient { ): CordaRPCClient {
return CordaRPCClient(hostAndPort, configuration, sslConfiguration, null, classLoader) return CordaRPCClient(hostAndPort, configuration, sslConfiguration, 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)
} }
} }
@ -312,9 +300,6 @@ class CordaRPCClient private constructor(
private fun getRpcClient(): RPCClient<CordaRPCOps> { private fun getRpcClient(): RPCClient<CordaRPCOps> {
return when { 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( haAddressPool.isEmpty() -> RPCClient(
rpcConnectorTcpTransport(hostAndPort, config = sslConfiguration), rpcConnectorTcpTransport(hostAndPort, config = sslConfiguration),

View File

@ -6,7 +6,6 @@ import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.pendingFlowsCount import net.corda.core.messaging.pendingFlowsCount
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.messaging.ClientRpcSslOptions import net.corda.core.messaging.ClientRpcSslOptions
import net.corda.nodeapi.internal.config.SSLConfiguration
import rx.Observable import rx.Observable
/** Utility which exposes the internal Corda RPC constructor to other internal Corda components */ /** Utility which exposes the internal Corda RPC constructor to other internal Corda components */
@ -17,13 +16,6 @@ fun createCordaRPCClientWithSslAndClassLoader(
classLoader: ClassLoader? = null classLoader: ClassLoader? = null
) = CordaRPCClient.createWithSslAndClassLoader(hostAndPort, configuration, sslConfiguration, classLoader) ) = 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 CordaRPCOps.drainAndShutdown(): Observable<Unit> { fun CordaRPCOps.drainAndShutdown(): Observable<Unit> {
setFlowsDrainingModeEnabled(true) setFlowsDrainingModeEnabled(true)

View File

@ -15,11 +15,11 @@ import net.corda.core.serialization.SerializationDefaults
import net.corda.core.serialization.internal.nodeSerializationEnv import net.corda.core.serialization.internal.nodeSerializationEnv
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.contextLogger 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.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.SimpleString
import org.apache.activemq.artemis.api.core.TransportConfiguration import org.apache.activemq.artemis.api.core.TransportConfiguration
import org.apache.activemq.artemis.api.core.client.ActiveMQClient import org.apache.activemq.artemis.api.core.client.ActiveMQClient
@ -43,7 +43,7 @@ class RPCClient<I : RPCOps>(
constructor( constructor(
hostAndPort: NetworkHostAndPort, hostAndPort: NetworkHostAndPort,
sslConfiguration: SSLConfiguration, sslConfiguration: SslConfiguration,
configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT, configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT,
serializationContext: SerializationContext = SerializationDefaults.RPC_CLIENT_CONTEXT serializationContext: SerializationContext = SerializationDefaults.RPC_CLIENT_CONTEXT
) : this(rpcInternalClientTcpTransport(hostAndPort, sslConfiguration), configuration, serializationContext) ) : this(rpcInternalClientTcpTransport(hostAndPort, sslConfiguration), configuration, serializationContext)

View File

@ -4,21 +4,11 @@ package net.corda.core.internal
import net.corda.core.DeleteForDJVM import net.corda.core.DeleteForDJVM
import net.corda.core.KeepForDJVM 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.crypto.*
import net.corda.core.flows.FlowLogic
import net.corda.core.node.ServicesForResolution
import net.corda.core.serialization.* 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.OpaqueBytes
import net.corda.core.utilities.UntrustworthyData import net.corda.core.utilities.UntrustworthyData
import org.slf4j.Logger import org.slf4j.Logger
import org.slf4j.MDC
import rx.Observable import rx.Observable
import rx.Observer import rx.Observer
import rx.subjects.PublishSubject import rx.subjects.PublishSubject

View File

@ -1,12 +1,16 @@
package net.corda.nodeapi package net.corda.nodeapi
import net.corda.core.messaging.ClientRpcSslOptions import net.corda.core.messaging.ClientRpcSslOptions
import net.corda.core.serialization.internal.nodeSerializationEnv
import net.corda.core.utilities.NetworkHostAndPort 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.config.SslConfiguration
import net.corda.nodeapi.internal.requireOnDefaultFileSystem import net.corda.nodeapi.internal.requireOnDefaultFileSystem
import org.apache.activemq.artemis.api.core.TransportConfiguration 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 org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants
import java.nio.file.Path import java.nio.file.Path
@ -38,26 +42,6 @@ class ArtemisTcpTransport {
/** Supported TLS versions, currently TLSv1.2 only. */ /** Supported TLS versions, currently TLSv1.2 only. */
val TLS_VERSIONS = listOf("TLSv1.2") 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( private fun SSLConfiguration.toTransportOptions() = mapOf(
TransportConstants.SSL_ENABLED_PROP_NAME to true, TransportConstants.SSL_ENABLED_PROP_NAME to true,
TransportConstants.KEYSTORE_PROVIDER_PROP_NAME to "JKS", TransportConstants.KEYSTORE_PROVIDER_PROP_NAME to "JKS",
@ -68,22 +52,6 @@ class ArtemisTcpTransport {
TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME to trustStorePassword, TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME to trustStorePassword,
TransportConstants.NEED_CLIENT_AUTH_PROP_NAME to true) 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 { fun p2pAcceptorTcpTransport(hostAndPort: NetworkHostAndPort, config: SSLConfiguration?, enableSSL: Boolean = true): TransportConfiguration {
val options = defaultArtemisOptions(hostAndPort).toMutableMap() val options = defaultArtemisOptions(hostAndPort).toMutableMap()
@ -110,27 +78,13 @@ class ArtemisTcpTransport {
/** [TransportConfiguration] for RPC TCP communication - server side. */ /** [TransportConfiguration] for RPC TCP communication - server side. */
fun rpcAcceptorTcpTransport(hostAndPort: NetworkHostAndPort, config: BrokerRpcSslOptions?, enableSSL: Boolean = true): TransportConfiguration { fun rpcAcceptorTcpTransport(hostAndPort: NetworkHostAndPort, config: BrokerRpcSslOptions?, enableSSL: Boolean = true): TransportConfiguration {
val options = defaultArtemisOptions(hostAndPort).toMutableMap() return InternalArtemisTcpTransport.rpcAcceptorTcpTransport(hostAndPort, config, enableSSL)
if (config != null && enableSSL) {
config.keyStorePath.requireOnDefaultFileSystem()
options.putAll(config.toTransportOptions())
options.putAll(defaultSSLOptions)
}
return TransportConfiguration(acceptorFactoryClassName, options)
} }
/** [TransportConfiguration] for RPC TCP communication /** [TransportConfiguration] for RPC TCP communication
* This is the Transport that connects the client JVM to the broker. */ * This is the Transport that connects the client JVM to the broker. */
fun rpcConnectorTcpTransport(hostAndPort: NetworkHostAndPort, config: ClientRpcSslOptions?, enableSSL: Boolean = true): TransportConfiguration { fun rpcConnectorTcpTransport(hostAndPort: NetworkHostAndPort, config: ClientRpcSslOptions?, enableSSL: Boolean = true): TransportConfiguration {
val options = defaultArtemisOptions(hostAndPort).toMutableMap() return InternalArtemisTcpTransport.rpcConnectorTcpTransport(hostAndPort, config, enableSSL)
if (config != null && enableSSL) {
config.trustStorePath.requireOnDefaultFileSystem()
options.putAll(config.toTransportOptions())
options.putAll(defaultSSLOptions)
}
return TransportConfiguration(connectorFactoryClassName, options)
} }
/** Create as list of [TransportConfiguration]. **/ /** Create as list of [TransportConfiguration]. **/
@ -138,12 +92,12 @@ class ArtemisTcpTransport {
rpcConnectorTcpTransport(it, config, enableSSL) rpcConnectorTcpTransport(it, config, enableSSL)
} }
fun rpcInternalClientTcpTransport(hostAndPort: NetworkHostAndPort, config: SSLConfiguration): TransportConfiguration { fun rpcInternalClientTcpTransport(hostAndPort: NetworkHostAndPort, config: SslConfiguration): TransportConfiguration {
return TransportConfiguration(connectorFactoryClassName, defaultArtemisOptions(hostAndPort) + defaultSSLOptions + config.toTransportOptions()) return InternalArtemisTcpTransport.rpcInternalClientTcpTransport(hostAndPort, config)
} }
fun rpcInternalAcceptorTcpTransport(hostAndPort: NetworkHostAndPort, config: SSLConfiguration): TransportConfiguration { fun rpcInternalAcceptorTcpTransport(hostAndPort: NetworkHostAndPort, config: SslConfiguration): TransportConfiguration {
return TransportConfiguration(acceptorFactoryClassName, defaultArtemisOptions(hostAndPort) + defaultSSLOptions + config.toTransportOptions()) return InternalArtemisTcpTransport.rpcInternalAcceptorTcpTransport(hostAndPort, config)
} }
} }
} }

View File

@ -3,9 +3,8 @@ package net.corda.nodeapi.internal
import net.corda.core.serialization.internal.nodeSerializationEnv import net.corda.core.serialization.internal.nodeSerializationEnv
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.loggerFor 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.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.ActiveMQClient
import org.apache.activemq.artemis.api.core.client.ActiveMQClient.DEFAULT_ACK_BATCH_SIZE import org.apache.activemq.artemis.api.core.client.ActiveMQClient.DEFAULT_ACK_BATCH_SIZE
import org.apache.activemq.artemis.api.core.client.ClientProducer import org.apache.activemq.artemis.api.core.client.ClientProducer
@ -18,7 +17,7 @@ interface ArtemisSessionProvider {
val started: ArtemisMessagingClient.Started? val started: ArtemisMessagingClient.Started?
} }
class ArtemisMessagingClient(private val config: SSLConfiguration, class ArtemisMessagingClient(private val config: MutualSslConfiguration,
private val serverAddress: NetworkHostAndPort, private val serverAddress: NetworkHostAndPort,
private val maxMessageSize: Int) : ArtemisSessionProvider { private val maxMessageSize: Int) : ArtemisSessionProvider {
companion object { companion object {
@ -34,7 +33,7 @@ class ArtemisMessagingClient(private val config: SSLConfiguration,
check(started == null) { "start can't be called twice" } check(started == null) { "start can't be called twice" }
log.info("Connecting to message broker: $serverAddress") log.info("Connecting to message broker: $serverAddress")
// TODO Add broker CN to config for host verification in case the embedded broker isn't used // 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 { val locator = ActiveMQClient.createServerLocatorWithoutHA(tcpTransport).apply {
// Never time out on our loopback Artemis connections. If we switch back to using the InVM transport this // 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. // would be the default and the two lines below can be deleted.

View File

@ -7,7 +7,8 @@ import net.corda.core.identity.Party
import net.corda.core.internal.createDirectories import net.corda.core.internal.createDirectories
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.utilities.trace 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.CertificateType
import net.corda.nodeapi.internal.crypto.X509KeyStore import net.corda.nodeapi.internal.crypto.X509KeyStore
import net.corda.nodeapi.internal.crypto.X509Utilities 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. */ /** Install a node key store for the given node directory using the given legal name. */
fun installKeyStoreWithNodeIdentity(nodeDir: Path, legalName: CordaX500Name): Party { fun installKeyStoreWithNodeIdentity(nodeDir: Path, legalName: CordaX500Name): Party {
val nodeSslConfig = object : NodeSSLConfiguration { val certificatesDirectory = nodeDir / "certificates"
override val baseDirectory = nodeDir val signingCertStore = FileBasedCertificateStoreSupplier(certificatesDirectory / "nodekeystore.jks", "cordacadevpass")
override val keyStorePassword: String = "cordacadevpass" val p2pKeyStore = FileBasedCertificateStoreSupplier(certificatesDirectory / "sslkeystore.jks", "cordacadevpass")
override val trustStorePassword get() = throw NotImplementedError("Not expected to be called") val p2pTrustStore = FileBasedCertificateStoreSupplier(certificatesDirectory / "truststore.jks", "trustpass")
override val crlCheckSoftFail: Boolean = true val p2pSslConfig = SslConfiguration.mutual(p2pKeyStore, p2pTrustStore)
}
nodeSslConfig.certificatesDirectory.createDirectories() certificatesDirectory.createDirectories()
val (nodeKeyStore) = nodeSslConfig.createDevKeyStores(legalName) 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") val identity = nodeKeyStore.storeLegalIdentity("$NODE_IDENTITY_ALIAS_PREFIX-private-key")
return identity.party return identity.party

View File

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

View File

@ -5,7 +5,7 @@ import net.corda.core.crypto.Crypto.generateKeyPair
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.PartyAndCertificate import net.corda.core.identity.PartyAndCertificate
import net.corda.core.internal.toX500Name 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 net.corda.nodeapi.internal.crypto.*
import org.bouncycastle.asn1.x509.GeneralName import org.bouncycastle.asn1.x509.GeneralName
import org.bouncycastle.asn1.x509.GeneralSubtree 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 * 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. * 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) fun CertificateStore.registerDevSigningCertificates(legalName: CordaX500Name,
nodeKeyStore.update { rootCert: X509Certificate = DEV_ROOT_CA.certificate,
setPrivateKey( intermediateCa: CertificateAndKeyPair = DEV_INTERMEDIATE_CA,
X509Utilities.CORDA_CLIENT_CA, devNodeCa: CertificateAndKeyPair = createDevNodeCa(intermediateCa, legalName)) {
nodeCaKeyPair.private,
listOf(nodeCaCert, intermediateCa.certificate, rootCert)) 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 { fun CertificateStore.registerDevP2pCertificates(legalName: CordaX500Name,
val nodeCaCertPath = getCertificateChain(X509Utilities.CORDA_CLIENT_CA) rootCert: X509Certificate = DEV_ROOT_CA.certificate,
// Assume key password = store password. intermediateCa: CertificateAndKeyPair = DEV_INTERMEDIATE_CA,
val nodeCaCertAndKeyPair = getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA) devNodeCa: CertificateAndKeyPair = createDevNodeCa(intermediateCa, legalName)) {
// Create new keys and store in keystore.
val identityCert = X509Utilities.createCertificate( update {
CertificateType.LEGAL_IDENTITY, val tlsKeyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
nodeCaCertAndKeyPair.certificate, val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, devNodeCa.certificate, devNodeCa.keyPair, legalName.x500Principal, tlsKeyPair.public)
nodeCaCertAndKeyPair.keyPair, setPrivateKey(X509Utilities.CORDA_CLIENT_TLS, tlsKeyPair.private, listOf(tlsCert, devNodeCa.certificate, intermediateCa.certificate, rootCert))
nodeCaCertAndKeyPair.certificate.subjectX500Principal, }
keyPair.public) }
// TODO: X509Utilities.validateCertificateChain()
// Assume key password = store password. fun CertificateStore.storeLegalIdentity(alias: String, keyPair: KeyPair = Crypto.generateKeyPair()): PartyAndCertificate {
val identityCertPath = listOf(identityCert) + nodeCaCertPath val identityCertPath = query {
setPrivateKey(alias, keyPair.private, identityCertPath) val nodeCaCertPath = getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
save() // 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)) 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 // We need a class so that we can get hold of the class loader
internal object DevCaHelper { internal object DevCaHelper {
fun loadDevCa(alias: String): CertificateAndKeyPair { fun loadDevCa(alias: String): CertificateAndKeyPair {
// TODO: Should be identity scheme return loadDevCaKeyStore().query { getCertificateAndKeyPair(alias, DEV_CA_PRIVATE_KEY_PASS) }
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)
} }
} }
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)

View File

@ -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.ArtemisMessagingComponent.RemoteInboxAddress.Companion.translateLocalQueueToInboxAddress
import net.corda.nodeapi.internal.ArtemisSessionProvider import net.corda.nodeapi.internal.ArtemisSessionProvider
import net.corda.nodeapi.internal.bridging.AMQPBridgeManager.AMQPBridge.Companion.getBridgeName 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.messages.MessageStatus
import net.corda.nodeapi.internal.protonwrapper.netty.AMQPClient import net.corda.nodeapi.internal.protonwrapper.netty.AMQPClient
import net.corda.nodeapi.internal.protonwrapper.netty.AMQPConfiguration import net.corda.nodeapi.internal.protonwrapper.netty.AMQPConfiguration
@ -25,7 +26,6 @@ import org.apache.activemq.artemis.api.core.client.ClientMessage
import org.apache.activemq.artemis.api.core.client.ClientSession import org.apache.activemq.artemis.api.core.client.ClientSession
import org.slf4j.MDC import org.slf4j.MDC
import rx.Subscription import rx.Subscription
import java.security.KeyStore
import java.util.concurrent.locks.ReentrantLock import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock import kotlin.concurrent.withLock
@ -37,26 +37,22 @@ import kotlin.concurrent.withLock
* The Netty thread pool used by the AMQPBridges is also shared and managed by the AMQPBridgeManager. * The Netty thread pool used by the AMQPBridges is also shared and managed by the AMQPBridgeManager.
*/ */
@VisibleForTesting @VisibleForTesting
class AMQPBridgeManager(config: NodeSSLConfiguration, maxMessageSize: Int, private val artemisMessageClientFactory: () -> ArtemisSessionProvider) : BridgeManager { class AMQPBridgeManager(config: MutualSslConfiguration, maxMessageSize: Int, private val artemisMessageClientFactory: () -> ArtemisSessionProvider) : BridgeManager {
private val lock = ReentrantLock() private val lock = ReentrantLock()
private val bridgeNameToBridgeMap = mutableMapOf<String, AMQPBridge>() private val bridgeNameToBridgeMap = mutableMapOf<String, AMQPBridge>()
private class AMQPConfigurationImpl private constructor(override val keyStore: KeyStore, private class AMQPConfigurationImpl private constructor(override val keyStore: CertificateStore,
override val keyStorePrivateKeyPassword: CharArray, override val trustStore: CertificateStore,
override val trustStore: KeyStore,
override val maxMessageSize: Int) : AMQPConfiguration { override val maxMessageSize: Int) : AMQPConfiguration {
constructor(config: NodeSSLConfiguration, maxMessageSize: Int) : this(config.loadSslKeyStore().internal, constructor(config: MutualSslConfiguration, maxMessageSize: Int) : this(config.keyStore.get(), config.trustStore.get(), maxMessageSize)
config.keyStorePassword.toCharArray(),
config.loadTrustStore().internal,
maxMessageSize)
} }
private val amqpConfig: AMQPConfiguration = AMQPConfigurationImpl(config, maxMessageSize) private val amqpConfig: AMQPConfiguration = AMQPConfigurationImpl(config, maxMessageSize)
private var sharedEventLoopGroup: EventLoopGroup? = null private var sharedEventLoopGroup: EventLoopGroup? = null
private var artemis: ArtemisSessionProvider? = null private var artemis: ArtemisSessionProvider? = null
constructor(config: NodeSSLConfiguration, p2pAddress: NetworkHostAndPort, maxMessageSize: Int) : this(config, maxMessageSize, { ArtemisMessagingClient(config, p2pAddress, maxMessageSize) }) constructor(config: MutualSslConfiguration, p2pAddress: NetworkHostAndPort, maxMessageSize: Int) : this(config, maxMessageSize, { ArtemisMessagingClient(config, p2pAddress, maxMessageSize) })
companion object { companion object {
private const val NUM_BRIDGE_THREADS = 0 // Default sized pool private const val NUM_BRIDGE_THREADS = 0 // Default sized pool

View File

@ -11,14 +11,14 @@ 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.P2P_PREFIX
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEERS_PREFIX import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEERS_PREFIX
import net.corda.nodeapi.internal.ArtemisSessionProvider import net.corda.nodeapi.internal.ArtemisSessionProvider
import net.corda.nodeapi.internal.config.NodeSSLConfiguration import net.corda.nodeapi.internal.config.MutualSslConfiguration
import org.apache.activemq.artemis.api.core.RoutingType import org.apache.activemq.artemis.api.core.RoutingType
import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.api.core.SimpleString
import org.apache.activemq.artemis.api.core.client.ClientConsumer import org.apache.activemq.artemis.api.core.client.ClientConsumer
import org.apache.activemq.artemis.api.core.client.ClientMessage import org.apache.activemq.artemis.api.core.client.ClientMessage
import java.util.* import java.util.*
class BridgeControlListener(val config: NodeSSLConfiguration, class BridgeControlListener(val config: MutualSslConfiguration,
maxMessageSize: Int, maxMessageSize: Int,
val artemisMessageClientFactory: () -> ArtemisSessionProvider) : AutoCloseable { val artemisMessageClientFactory: () -> ArtemisSessionProvider) : AutoCloseable {
private val bridgeId: String = UUID.randomUUID().toString() private val bridgeId: String = UUID.randomUUID().toString()
@ -27,7 +27,7 @@ class BridgeControlListener(val config: NodeSSLConfiguration,
private var artemis: ArtemisSessionProvider? = null private var artemis: ArtemisSessionProvider? = null
private var controlConsumer: ClientConsumer? = null private var controlConsumer: ClientConsumer? = null
constructor(config: NodeSSLConfiguration, constructor(config: MutualSslConfiguration,
p2pAddress: NetworkHostAndPort, p2pAddress: NetworkHostAndPort,
maxMessageSize: Int) : this(config, maxMessageSize, { ArtemisMessagingClient(config, p2pAddress, maxMessageSize) }) maxMessageSize: Int) : this(config, maxMessageSize, { ArtemisMessagingClient(config, p2pAddress, maxMessageSize) })

View File

@ -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

View File

@ -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)
}

View File

@ -4,12 +4,34 @@ import net.corda.core.internal.div
import net.corda.nodeapi.internal.crypto.X509KeyStore import net.corda.nodeapi.internal.crypto.X509KeyStore
import java.nio.file.Path 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 { interface SSLConfiguration {
val keyStorePassword: String val keyStorePassword: String
val trustStorePassword: String val trustStorePassword: String
val certificatesDirectory: Path val certificatesDirectory: Path
val sslKeystore: Path get() = certificatesDirectory / "sslkeystore.jks" val sslKeystore: Path get() = certificatesDirectory / "sslkeystore.jks"
// TODO This looks like it should be in NodeSSLConfiguration
val nodeKeystore: Path get() = certificatesDirectory / "nodekeystore.jks" val nodeKeystore: Path get() = certificatesDirectory / "nodekeystore.jks"
val trustStoreFile: Path get() = certificatesDirectory / "truststore.jks" val trustStoreFile: Path get() = certificatesDirectory / "truststore.jks"
val crlCheckSoftFail: Boolean val crlCheckSoftFail: Boolean
@ -25,9 +47,4 @@ interface SSLConfiguration {
fun loadSslKeyStore(createNew: Boolean = false): X509KeyStore { fun loadSslKeyStore(createNew: Boolean = false): X509KeyStore {
return X509KeyStore.fromFile(sslKeystore, keyStorePassword, createNew) return X509KeyStore.fromFile(sslKeystore, keyStorePassword, createNew)
} }
} }
interface NodeSSLConfiguration : SSLConfiguration {
val baseDirectory: Path
override val certificatesDirectory: Path get() = baseDirectory / "certificates"
}

View File

@ -2,6 +2,7 @@ package net.corda.nodeapi.internal.crypto
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.internal.uncheckedCast import net.corda.core.internal.uncheckedCast
import java.io.InputStream
import java.nio.file.Path import java.nio.file.Path
import java.security.KeyPair import java.security.KeyPair
import java.security.KeyStore 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) val internal: KeyStore = if (createNew) loadOrCreateKeyStore(keyStoreFile, storePassword) else loadKeyStore(keyStoreFile, storePassword)
return X509KeyStore(internal, storePassword, keyStoreFile) 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) operator fun contains(alias: String): Boolean = internal.containsAlias(alias)

View File

@ -116,7 +116,7 @@ class AMQPClient(val targets: List<NetworkHostAndPort>,
private val conf = parent.configuration private val conf = parent.configuration
init { init {
keyManagerFactory.init(conf.keyStore, conf.keyStorePrivateKeyPassword) keyManagerFactory.init(conf.keyStore)
trustManagerFactory.init(initialiseTrustStoreAndEnableCrlChecking(conf.trustStore, conf.crlCheckSoftFail)) trustManagerFactory.init(initialiseTrustStoreAndEnableCrlChecking(conf.trustStore, conf.crlCheckSoftFail))
} }

View File

@ -1,6 +1,7 @@
package net.corda.nodeapi.internal.protonwrapper.netty package net.corda.nodeapi.internal.protonwrapper.netty
import net.corda.nodeapi.internal.ArtemisMessagingComponent import net.corda.nodeapi.internal.ArtemisMessagingComponent
import net.corda.nodeapi.internal.config.CertificateStore
import java.security.KeyStore import java.security.KeyStore
interface AMQPConfiguration { interface AMQPConfiguration {
@ -21,19 +22,14 @@ interface AMQPConfiguration {
get() = ArtemisMessagingComponent.PEER_USER 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 val trustStore: CertificateStore
/**
* The trust root KeyStore to validate the peer certificates against
*/
val trustStore: KeyStore
/** /**
* Setting crlCheckSoftFail to true allows certificate paths where some leaf certificates do not contain cRLDistributionPoints * Setting crlCheckSoftFail to true allows certificate paths where some leaf certificates do not contain cRLDistributionPoints

View File

@ -60,7 +60,7 @@ class AMQPServer(val hostName: String,
private val conf = parent.configuration private val conf = parent.configuration
init { init {
keyManagerFactory.init(conf.keyStore, conf.keyStorePrivateKeyPassword) keyManagerFactory.init(conf.keyStore)
trustManagerFactory.init(initialiseTrustStoreAndEnableCrlChecking(conf.trustStore, conf.crlCheckSoftFail)) trustManagerFactory.init(initialiseTrustStoreAndEnableCrlChecking(conf.trustStore, conf.crlCheckSoftFail))
} }

View File

@ -5,14 +5,13 @@ import net.corda.core.crypto.newSecureRandom
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.contextLogger import net.corda.core.utilities.contextLogger
import net.corda.core.utilities.toHex 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 net.corda.nodeapi.internal.crypto.toBc
import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier
import org.bouncycastle.asn1.x509.Extension import org.bouncycastle.asn1.x509.Extension
import org.bouncycastle.asn1.x509.SubjectKeyIdentifier import org.bouncycastle.asn1.x509.SubjectKeyIdentifier
import java.net.Socket import java.net.Socket
import java.security.KeyStore
import java.security.SecureRandom
import java.security.cert.* import java.security.cert.*
import java.util.* import java.util.*
import javax.net.ssl.* import javax.net.ssl.*
@ -111,8 +110,8 @@ internal fun createClientSslHelper(target: NetworkHostAndPort,
sslContext.init(keyManagers, trustManagers, newSecureRandom()) sslContext.init(keyManagers, trustManagers, newSecureRandom())
val sslEngine = sslContext.createSSLEngine(target.host, target.port) val sslEngine = sslContext.createSSLEngine(target.host, target.port)
sslEngine.useClientMode = true sslEngine.useClientMode = true
sslEngine.enabledProtocols = ArtemisTcpTransport.TLS_VERSIONS.toTypedArray() sslEngine.enabledProtocols = InternalArtemisTcpTransport.TLS_VERSIONS.toTypedArray()
sslEngine.enabledCipherSuites = ArtemisTcpTransport.CIPHER_SUITES.toTypedArray() sslEngine.enabledCipherSuites = InternalArtemisTcpTransport.CIPHER_SUITES.toTypedArray()
sslEngine.enableSessionCreation = true sslEngine.enableSessionCreation = true
return SslHandler(sslEngine) return SslHandler(sslEngine)
} }
@ -126,13 +125,13 @@ internal fun createServerSslHelper(keyManagerFactory: KeyManagerFactory,
val sslEngine = sslContext.createSSLEngine() val sslEngine = sslContext.createSSLEngine()
sslEngine.useClientMode = false sslEngine.useClientMode = false
sslEngine.needClientAuth = true sslEngine.needClientAuth = true
sslEngine.enabledProtocols = ArtemisTcpTransport.TLS_VERSIONS.toTypedArray() sslEngine.enabledProtocols = InternalArtemisTcpTransport.TLS_VERSIONS.toTypedArray()
sslEngine.enabledCipherSuites = ArtemisTcpTransport.CIPHER_SUITES.toTypedArray() sslEngine.enabledCipherSuites = InternalArtemisTcpTransport.CIPHER_SUITES.toTypedArray()
sslEngine.enableSessionCreation = true sslEngine.enableSessionCreation = true
return SslHandler(sslEngine) 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 certPathBuilder = CertPathBuilder.getInstance("PKIX")
val revocationChecker = certPathBuilder.revocationChecker as PKIXRevocationChecker val revocationChecker = certPathBuilder.revocationChecker as PKIXRevocationChecker
revocationChecker.options = EnumSet.of( 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. // the following reasons: The CRL or OCSP response cannot be obtained because of a network error.
revocationChecker.options = revocationChecker.options + PKIXRevocationChecker.Option.SOFT_FAIL revocationChecker.options = revocationChecker.options + PKIXRevocationChecker.Option.SOFT_FAIL
} }
val pkixParams = PKIXBuilderParameters(trustStore, X509CertSelector()) val pkixParams = PKIXBuilderParameters(trustStore.value.internal, X509CertSelector())
pkixParams.addCertPathChecker(revocationChecker) pkixParams.addCertPathChecker(revocationChecker)
return CertPathTrustManagerParameters(pkixParams) return CertPathTrustManagerParameters(pkixParams)
} }
fun KeyManagerFactory.init(keyStore: CertificateStore) = init(keyStore.value.internal, keyStore.password.toCharArray())
fun TrustManagerFactory.init(trustStore: CertificateStore) = init(trustStore.value.internal)

View File

@ -1,8 +1,7 @@
package net.corda.nodeapi.internal.crypto package net.corda.nodeapi.internal.crypto
import net.corda.core.internal.validate import net.corda.core.internal.validate
import net.corda.nodeapi.internal.DEV_CA_TRUST_STORE_FILE import net.corda.nodeapi.internal.loadDevCaTrustStore
import net.corda.nodeapi.internal.DEV_CA_TRUST_STORE_PASS
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.TemporaryFolder import org.junit.rules.TemporaryFolder
@ -22,8 +21,8 @@ class DevCertificatesTest {
@Test @Test
fun `create server certificate in keystore for SSL`() { fun `create server certificate in keystore for SSL`() {
// given // given
val newTrustStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/$DEV_CA_TRUST_STORE_FILE"), DEV_CA_TRUST_STORE_PASS) val newTrustStore = loadDevCaTrustStore()
val newTrustRoot = newTrustStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA) val newTrustRoot = newTrustStore[X509Utilities.CORDA_ROOT_CA]
val newTrustAnchor = TrustAnchor(newTrustRoot, null) val newTrustAnchor = TrustAnchor(newTrustRoot, null)
val oldNodeCaKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("regression-test/$OLD_NODE_DEV_KEYSTORE_FILE_NAME"), OLD_DEV_KEYSTORE_PASS) val oldNodeCaKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("regression-test/$OLD_NODE_DEV_KEYSTORE_FILE_NAME"), OLD_DEV_KEYSTORE_PASS)

View File

@ -10,8 +10,11 @@ import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.deserialize import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.node.serialization.amqp.AMQPServerSerializationScheme import net.corda.node.serialization.amqp.AMQPServerSerializationScheme
import net.corda.nodeapi.internal.config.SSLConfiguration import net.corda.nodeapi.internal.config.MutualSslConfiguration
import net.corda.nodeapi.internal.createDevKeyStores 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.AllWhitelist
import net.corda.serialization.internal.SerializationContextImpl import net.corda.serialization.internal.SerializationContextImpl
import net.corda.serialization.internal.SerializationFactoryImpl 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.ALICE_NAME
import net.corda.testing.core.BOB_NAME import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.TestIdentity import net.corda.testing.core.TestIdentity
import net.corda.testing.internal.stubs.CertificateStoreStubs
import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.testing.internal.createDevIntermediateCaCertPath
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.bouncycastle.asn1.x509.* import org.bouncycastle.asn1.x509.*
@ -31,7 +35,6 @@ import java.io.IOException
import java.net.InetAddress import java.net.InetAddress
import java.net.InetSocketAddress import java.net.InetSocketAddress
import java.nio.file.Path import java.nio.file.Path
import java.security.SecureRandom
import java.security.cert.CertPath import java.security.cert.CertPath
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import java.util.* import java.util.*
@ -180,29 +183,27 @@ class X509UtilitiesTest {
@Test @Test
fun `create server certificate in keystore for SSL`() { fun `create server certificate in keystore for SSL`() {
val sslConfig = object : SSLConfiguration { val certificatesDirectory = tempFolder.root.toPath()
override val certificatesDirectory = tempFolder.root.toPath() val signingCertStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory, "serverstorepass")
override val keyStorePassword = "serverstorepass" val p2pSslConfig = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory, keyStorePassword = "serverstorepass")
override val trustStorePassword = "trustpass"
override val crlCheckSoftFail: Boolean = true
}
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath() val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
// Generate server cert and private key and populate another keystore suitable for SSL // 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 // Load back server certificate
val serverKeyStore = loadKeyStore(sslConfig.nodeKeystore, sslConfig.keyStorePassword) val serverKeyStore = signingCertStore.get().value
val (serverCert, serverKeyPair) = serverKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, sslConfig.keyStorePassword) val (serverCert, serverKeyPair) = serverKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)
serverCert.checkValidity() serverCert.checkValidity()
serverCert.verify(intermediateCa.certificate.publicKey) serverCert.verify(intermediateCa.certificate.publicKey)
assertThat(CordaX500Name.build(serverCert.subjectX500Principal)).isEqualTo(MEGA_CORP.name) assertThat(CordaX500Name.build(serverCert.subjectX500Principal)).isEqualTo(MEGA_CORP.name)
// Load back SSL certificate // Load back SSL certificate
val sslKeyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword) val sslKeyStoreReloaded = p2pSslConfig.keyStore.get()
val (sslCert) = sslKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_TLS, sslConfig.keyStorePassword) val (sslCert) = sslKeyStoreReloaded.query { getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_TLS, p2pSslConfig.keyStore.password) }
sslCert.checkValidity() sslCert.checkValidity()
sslCert.verify(serverCert.publicKey) sslCert.verify(serverCert.publicKey)
@ -216,25 +217,20 @@ class X509UtilitiesTest {
@Test @Test
fun `create server cert and use in SSL socket`() { fun `create server cert and use in SSL socket`() {
val sslConfig = object : SSLConfiguration { val sslConfig = CertificateStoreStubs.P2P.withCertificatesDirectory(tempFolder.root.toPath(), keyStorePassword = "serverstorepass")
override val certificatesDirectory = tempFolder.root.toPath()
override val keyStorePassword = "serverstorepass"
override val trustStorePassword = "trustpass"
override val crlCheckSoftFail: Boolean = true
}
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath() val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
// Generate server cert and private key and populate another keystore suitable for SSL // 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) sslConfig.createTrustStore(rootCa.certificate)
val keyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword) val keyStore = sslConfig.keyStore.get()
val trustStore = loadKeyStore(sslConfig.trustStoreFile, sslConfig.trustStorePassword) val trustStore = sslConfig.trustStore.get()
val context = SSLContext.getInstance("TLS") val context = SSLContext.getInstance("TLS")
val keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()) val keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
keyManagerFactory.init(keyStore, sslConfig.keyStorePassword.toCharArray()) keyManagerFactory.init(keyStore)
val keyManagers = keyManagerFactory.keyManagers val keyManagers = keyManagerFactory.keyManagers
val trustMgrFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) val trustMgrFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
trustMgrFactory.init(trustStore) trustMgrFactory.init(trustStore)
@ -313,10 +309,9 @@ class X509UtilitiesTest {
private fun tempFile(name: String): Path = tempFolder.root.toPath() / name private fun tempFile(name: String): Path = tempFolder.root.toPath() / name
private fun SSLConfiguration.createTrustStore(rootCert: X509Certificate) { private fun MutualSslConfiguration.createTrustStore(rootCert: X509Certificate) {
val trustStore = loadOrCreateKeyStore(trustStoreFile, trustStorePassword) val trustStore = this.trustStore.get(true)
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCert) trustStore[X509Utilities.CORDA_ROOT_CA] = rootCert
trustStore.save(trustStoreFile, trustStorePassword)
} }
@Test @Test

View File

@ -4,15 +4,14 @@ import net.corda.core.crypto.Crypto
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.node.services.config.configureDevKeyAndTrustStores 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.CertificateType
import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.ALICE_NAME
import net.corda.testing.driver.DriverParameters import net.corda.testing.driver.DriverParameters
import net.corda.testing.driver.driver import net.corda.testing.driver.driver
import net.corda.testing.internal.stubs.CertificateStoreStubs
import org.assertj.core.api.Assertions.assertThatThrownBy import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Test import org.junit.Test
import java.nio.file.Path
import javax.security.auth.x500.X500Principal import javax.security.auth.x500.X500Principal
class NodeKeystoreCheckTest { class NodeKeystoreCheckTest {
@ -30,13 +29,11 @@ class NodeKeystoreCheckTest {
driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList())) { driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList())) {
// Create keystores // Create keystores
val keystorePassword = "password" val keystorePassword = "password"
val config = object : SSLConfiguration { val certificatesDirectory = baseDirectory(ALICE_NAME) / "certificates"
override val keyStorePassword: String = keystorePassword val signingCertStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory, keystorePassword)
override val trustStorePassword: String = keystorePassword val p2pSslConfig = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory, keyStorePassword = keystorePassword, trustStorePassword = keystorePassword)
override val certificatesDirectory: Path = baseDirectory(ALICE_NAME) / "certificates"
override val crlCheckSoftFail: Boolean = true p2pSslConfig.configureDevKeyAndTrustStores(ALICE_NAME, signingCertStore, certificatesDirectory)
}
config.configureDevKeyAndTrustStores(ALICE_NAME)
// This should pass with correct keystore. // This should pass with correct keystore.
val node = startNode( val node = startNode(
@ -48,7 +45,7 @@ class NodeKeystoreCheckTest {
node.stop() node.stop()
// Fiddle with node keystore. // Fiddle with node keystore.
config.loadNodeKeyStore().update { signingCertStore.get().update {
// Self signed root // Self signed root
val badRootKeyPair = Crypto.generateKeyPair() val badRootKeyPair = Crypto.generateKeyPair()
val badRoot = X509Utilities.createSelfSignedCACertificate(X500Principal("O=Bad Root,L=Lodnon,C=GB"), badRootKeyPair) val badRoot = X509Utilities.createSelfSignedCACertificate(X500Principal("O=Bad Root,L=Lodnon,C=GB"), badRootKeyPair)

View File

@ -19,6 +19,7 @@ import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.MAX_MESSAGE_SIZE import net.corda.testing.core.MAX_MESSAGE_SIZE
import net.corda.testing.core.TestIdentity import net.corda.testing.core.TestIdentity
import net.corda.testing.driver.PortAllocation import net.corda.testing.driver.PortAllocation
import net.corda.testing.internal.stubs.CertificateStoreStubs
import net.corda.testing.internal.rigorousMock 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.Message.HDR_DUPLICATE_DETECTION_ID
import org.apache.activemq.artemis.api.core.RoutingType import org.apache.activemq.artemis.api.core.RoutingType
@ -27,7 +28,6 @@ import org.junit.Assert.assertArrayEquals
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.TemporaryFolder import org.junit.rules.TemporaryFolder
import java.security.KeyStore
import java.util.* import java.util.*
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -168,21 +168,26 @@ class AMQPBridgeTest {
} }
private fun createArtemis(sourceQueueName: String?): Triple<ArtemisMessagingServer, ArtemisMessagingClient, BridgeManager> { 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 { 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(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(true).whenever(it).crlCheckSoftFail
doReturn("cordacadevpass").whenever(it).keyStorePassword
doReturn(artemisAddress).whenever(it).p2pAddress doReturn(artemisAddress).whenever(it).p2pAddress
doReturn(null).whenever(it).jmxMonitoringHttpPort doReturn(null).whenever(it).jmxMonitoringHttpPort
} }
artemisConfig.configureWithDevSSLCertificate() artemisConfig.configureWithDevSSLCertificate()
val artemisServer = ArtemisMessagingServer(artemisConfig, artemisAddress.copy(host = "0.0.0.0"), MAX_MESSAGE_SIZE) 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() artemisServer.start()
artemisClient.start() artemisClient.start()
val bridgeManager = AMQPBridgeManager(artemisConfig, artemisAddress, MAX_MESSAGE_SIZE) val bridgeManager = AMQPBridgeManager(artemisConfig.p2pSslOptions, artemisAddress, MAX_MESSAGE_SIZE)
bridgeManager.start() bridgeManager.start()
val artemis = artemisClient.started!! val artemis = artemisClient.started!!
if (sourceQueueName != null) { if (sourceQueueName != null) {
@ -194,18 +199,23 @@ class AMQPBridgeTest {
} }
private fun createAMQPServer(maxMessageSize: Int = MAX_MESSAGE_SIZE): AMQPServer { 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 { val serverConfig = rigorousMock<AbstractNodeConfiguration>().also {
doReturn(temporaryFolder.root.toPath() / "server").whenever(it).baseDirectory doReturn(temporaryFolder.root.toPath() / "server").whenever(it).baseDirectory
doReturn(BOB_NAME).whenever(it).myLegalName doReturn(BOB_NAME).whenever(it).myLegalName
doReturn("trustpass").whenever(it).trustStorePassword doReturn(certificatesDirectory).whenever(it).certificatesDirectory
doReturn("cordacadevpass").whenever(it).keyStorePassword doReturn(signingCertificateStore).whenever(it).signingCertificateStore
doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
} }
serverConfig.configureWithDevSSLCertificate() serverConfig.configureWithDevSSLCertificate()
val keyStore = serverConfig.p2pSslOptions.keyStore.get()
val amqpConfig = object : AMQPConfiguration { val amqpConfig = object : AMQPConfiguration {
override val keyStore: KeyStore = serverConfig.loadSslKeyStore().internal override val keyStore = keyStore
override val keyStorePrivateKeyPassword: CharArray = serverConfig.keyStorePassword.toCharArray() override val trustStore = serverConfig.p2pSslOptions.trustStore.get()
override val trustStore: KeyStore = serverConfig.loadTrustStore().internal
override val trace: Boolean = true override val trace: Boolean = true
override val maxMessageSize: Int = maxMessageSize override val maxMessageSize: Int = maxMessageSize
} }

View File

@ -13,7 +13,8 @@ import net.corda.core.utilities.seconds
import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NodeConfiguration
import net.corda.node.services.config.configureWithDevSSLCertificate import net.corda.node.services.config.configureWithDevSSLCertificate
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_PREFIX 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.crypto.*
import net.corda.nodeapi.internal.protonwrapper.messages.MessageStatus import net.corda.nodeapi.internal.protonwrapper.messages.MessageStatus
import net.corda.nodeapi.internal.protonwrapper.netty.AMQPClient 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.CHARLIE_NAME
import net.corda.testing.core.MAX_MESSAGE_SIZE import net.corda.testing.core.MAX_MESSAGE_SIZE
import net.corda.testing.driver.PortAllocation 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_INTERMEDIATE_CA
import net.corda.testing.internal.DEV_ROOT_CA import net.corda.testing.internal.DEV_ROOT_CA
import net.corda.testing.internal.rigorousMock import net.corda.testing.internal.rigorousMock
@ -50,7 +52,6 @@ import java.io.Closeable
import java.math.BigInteger import java.math.BigInteger
import java.net.InetSocketAddress import java.net.InetSocketAddress
import java.security.KeyPair import java.security.KeyPair
import java.security.KeyStore
import java.security.PrivateKey import java.security.PrivateKey
import java.security.Security import java.security.Security
import java.security.cert.X509CRL import java.security.cert.X509CRL
@ -330,22 +331,25 @@ class CertificateRevocationListNodeTests {
nodeCrlDistPoint: String = "http://${server.hostAndPort}/crl/node.crl", nodeCrlDistPoint: String = "http://${server.hostAndPort}/crl/node.crl",
tlsCrlDistPoint: String? = "http://${server.hostAndPort}/crl/empty.crl", tlsCrlDistPoint: String? = "http://${server.hostAndPort}/crl/empty.crl",
maxMessageSize: Int = MAX_MESSAGE_SIZE): Pair<AMQPClient, X509Certificate> { 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 { 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(BOB_NAME).whenever(it).myLegalName
doReturn("trustpass").whenever(it).trustStorePassword doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
doReturn("cordacadevpass").whenever(it).keyStorePassword doReturn(signingCertificateStore).whenever(it).signingCertificateStore
doReturn(crlCheckSoftFail).whenever(it).crlCheckSoftFail doReturn(crlCheckSoftFail).whenever(it).crlCheckSoftFail
} }
clientConfig.configureWithDevSSLCertificate() clientConfig.configureWithDevSSLCertificate()
val nodeCert = clientConfig.recreateNodeCaAndTlsCertificates(nodeCrlDistPoint, tlsCrlDistPoint) val nodeCert = (signingCertificateStore to p2pSslConfiguration).recreateNodeCaAndTlsCertificates(nodeCrlDistPoint, tlsCrlDistPoint)
val clientTruststore = clientConfig.loadTrustStore().internal val keyStore = clientConfig.p2pSslOptions.keyStore.get()
val clientKeystore = clientConfig.loadSslKeyStore().internal
val amqpConfig = object : AMQPConfiguration { val amqpConfig = object : AMQPConfiguration {
override val keyStore: KeyStore = clientKeystore override val keyStore = keyStore
override val keyStorePrivateKeyPassword: CharArray = clientConfig.keyStorePassword.toCharArray() override val trustStore = clientConfig.p2pSslOptions.trustStore.get()
override val trustStore: KeyStore = clientTruststore
override val crlCheckSoftFail: Boolean = crlCheckSoftFail override val crlCheckSoftFail: Boolean = crlCheckSoftFail
override val maxMessageSize: Int = maxMessageSize override val maxMessageSize: Int = maxMessageSize
} }
@ -360,21 +364,24 @@ class CertificateRevocationListNodeTests {
nodeCrlDistPoint: String = "http://${server.hostAndPort}/crl/node.crl", nodeCrlDistPoint: String = "http://${server.hostAndPort}/crl/node.crl",
tlsCrlDistPoint: String? = "http://${server.hostAndPort}/crl/empty.crl", tlsCrlDistPoint: String? = "http://${server.hostAndPort}/crl/empty.crl",
maxMessageSize: Int = MAX_MESSAGE_SIZE): Pair<AMQPServer, X509Certificate> { 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 { 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(name).whenever(it).myLegalName
doReturn("trustpass").whenever(it).trustStorePassword doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
doReturn("cordacadevpass").whenever(it).keyStorePassword doReturn(signingCertificateStore).whenever(it).signingCertificateStore
doReturn(crlCheckSoftFail).whenever(it).crlCheckSoftFail doReturn(crlCheckSoftFail).whenever(it).crlCheckSoftFail
} }
serverConfig.configureWithDevSSLCertificate() serverConfig.configureWithDevSSLCertificate()
val nodeCert = serverConfig.recreateNodeCaAndTlsCertificates(nodeCrlDistPoint, tlsCrlDistPoint) val nodeCert = (signingCertificateStore to p2pSslConfiguration).recreateNodeCaAndTlsCertificates(nodeCrlDistPoint, tlsCrlDistPoint)
val serverTruststore = serverConfig.loadTrustStore().internal val keyStore = serverConfig.p2pSslOptions.keyStore.get()
val serverKeystore = serverConfig.loadSslKeyStore().internal
val amqpConfig = object : AMQPConfiguration { val amqpConfig = object : AMQPConfiguration {
override val keyStore: KeyStore = serverKeystore override val keyStore = keyStore
override val keyStorePrivateKeyPassword: CharArray = serverConfig.keyStorePassword.toCharArray() override val trustStore = serverConfig.p2pSslOptions.trustStore.get()
override val trustStore: KeyStore = serverTruststore
override val crlCheckSoftFail: Boolean = crlCheckSoftFail override val crlCheckSoftFail: Boolean = crlCheckSoftFail
override val maxMessageSize: Int = maxMessageSize override val maxMessageSize: Int = maxMessageSize
} }
@ -384,22 +391,28 @@ class CertificateRevocationListNodeTests {
amqpConfig), nodeCert) amqpConfig), nodeCert)
} }
private fun SSLConfiguration.recreateNodeCaAndTlsCertificates(nodeCaCrlDistPoint: String, tlsCrlDistPoint: String?): X509Certificate { private fun Pair<CertificateStoreSupplier, MutualSslConfiguration>.recreateNodeCaAndTlsCertificates(nodeCaCrlDistPoint: String, tlsCrlDistPoint: String?): X509Certificate {
val nodeKeyStore = loadNodeKeyStore()
val (nodeCert, nodeKeys) = nodeKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA) 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 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()) val nodeCertChain = listOf(newNodeCert, INTERMEDIATE_CA.certificate, *nodeKeyStore.query { getCertificateChain(X509Utilities.CORDA_CLIENT_CA) }.drop(2).toTypedArray())
nodeKeyStore.internal.deleteEntry(X509Utilities.CORDA_CLIENT_CA) nodeKeyStore.update {
nodeKeyStore.save() internal.deleteEntry(X509Utilities.CORDA_CLIENT_CA)
}
nodeKeyStore.update { nodeKeyStore.update {
setPrivateKey(X509Utilities.CORDA_CLIENT_CA, nodeKeys.private, nodeCertChain) setPrivateKey(X509Utilities.CORDA_CLIENT_CA, nodeKeys.private, nodeCertChain)
} }
val sslKeyStore = loadSslKeyStore() val sslKeyStore = p2pSslConfiguration.keyStore.get()
val (tlsCert, tlsKeys) = sslKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_TLS) 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 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()) val sslCertChain = listOf(newTlsCert, newNodeCert, INTERMEDIATE_CA.certificate, *sslKeyStore.query { getCertificateChain(X509Utilities.CORDA_CLIENT_TLS) }.drop(3).toTypedArray())
sslKeyStore.internal.deleteEntry(X509Utilities.CORDA_CLIENT_TLS)
sslKeyStore.save() sslKeyStore.update {
internal.deleteEntry(X509Utilities.CORDA_CLIENT_TLS)
}
sslKeyStore.update { sslKeyStore.update {
setPrivateKey(X509Utilities.CORDA_CLIENT_TLS, tlsKeys.private, sslCertChain) setPrivateKey(X509Utilities.CORDA_CLIENT_TLS, tlsKeys.private, sslCertChain)
} }

View File

@ -12,16 +12,18 @@ import net.corda.core.utilities.NetworkHostAndPort
import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NodeConfiguration
import net.corda.node.services.config.configureWithDevSSLCertificate import net.corda.node.services.config.configureWithDevSSLCertificate
import net.corda.node.services.messaging.ArtemisMessagingServer 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.ArtemisMessagingClient
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_PREFIX import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_PREFIX
import net.corda.nodeapi.internal.config.SSLConfiguration import net.corda.nodeapi.internal.InternalArtemisTcpTransport
import net.corda.nodeapi.internal.createDevKeyStores import net.corda.nodeapi.internal.config.MutualSslConfiguration
import net.corda.nodeapi.internal.registerDevP2pCertificates
import net.corda.nodeapi.internal.crypto.* import net.corda.nodeapi.internal.crypto.*
import net.corda.nodeapi.internal.protonwrapper.messages.MessageStatus import net.corda.nodeapi.internal.protonwrapper.messages.MessageStatus
import net.corda.nodeapi.internal.protonwrapper.netty.AMQPClient import net.corda.nodeapi.internal.protonwrapper.netty.AMQPClient
import net.corda.nodeapi.internal.protonwrapper.netty.AMQPConfiguration import net.corda.nodeapi.internal.protonwrapper.netty.AMQPConfiguration
import net.corda.nodeapi.internal.protonwrapper.netty.AMQPServer 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.ALICE_NAME
import net.corda.testing.core.BOB_NAME import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.CHARLIE_NAME import net.corda.testing.core.CHARLIE_NAME
@ -29,13 +31,13 @@ import net.corda.testing.core.MAX_MESSAGE_SIZE
import net.corda.testing.driver.PortAllocation import net.corda.testing.driver.PortAllocation
import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.testing.internal.createDevIntermediateCaCertPath
import net.corda.testing.internal.rigorousMock 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.RoutingType
import org.assertj.core.api.Assertions.assertThatThrownBy import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Assert.assertArrayEquals import org.junit.Assert.assertArrayEquals
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.TemporaryFolder import org.junit.rules.TemporaryFolder
import java.security.KeyStore
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import javax.net.ssl.* import javax.net.ssl.*
import kotlin.concurrent.thread import kotlin.concurrent.thread
@ -102,34 +104,30 @@ class ProtonWrapperTests {
} }
} }
private fun SSLConfiguration.createTrustStore(rootCert: X509Certificate) { private fun MutualSslConfiguration.createTrustStore(rootCert: X509Certificate) {
val trustStore = loadOrCreateKeyStore(trustStoreFile, trustStorePassword)
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCert)
trustStore.save(trustStoreFile, trustStorePassword)
}
trustStore.get(true)[X509Utilities.CORDA_ROOT_CA] = rootCert
}
@Test @Test
fun `Test AMQP Client with invalid root certificate`() { fun `Test AMQP Client with invalid root certificate`() {
val sslConfig = object : SSLConfiguration { val certificatesDirectory = temporaryFolder.root.toPath()
override val certificatesDirectory = temporaryFolder.root.toPath() val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory, "serverstorepass")
override val keyStorePassword = "serverstorepass" val sslConfig = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory, keyStorePassword = "serverstorepass")
override val trustStorePassword = "trustpass"
override val crlCheckSoftFail: Boolean = true
}
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath() val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
// Generate server cert and private key and populate another keystore suitable for SSL // 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) sslConfig.createTrustStore(rootCa.certificate)
val keyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword) val keyStore = sslConfig.keyStore.get()
val trustStore = loadKeyStore(sslConfig.trustStoreFile, sslConfig.trustStorePassword) val trustStore = sslConfig.trustStore.get()
val context = SSLContext.getInstance("TLS") val context = SSLContext.getInstance("TLS")
val keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()) val keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
keyManagerFactory.init(keyStore, sslConfig.keyStorePassword.toCharArray()) keyManagerFactory.init(keyStore)
val keyManagers = keyManagerFactory.keyManagers val keyManagers = keyManagerFactory.keyManagers
val trustMgrFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) val trustMgrFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
trustMgrFactory.init(trustStore) trustMgrFactory.init(trustStore)
@ -139,7 +137,7 @@ class ProtonWrapperTests {
val serverSocketFactory = context.serverSocketFactory val serverSocketFactory = context.serverSocketFactory
val serverSocket = serverSocketFactory.createServerSocket(serverPort) as SSLServerSocket val serverSocket = serverSocketFactory.createServerSocket(serverPort) as SSLServerSocket
val serverParams = SSLParameters(CIPHER_SUITES.toTypedArray(), val serverParams = SSLParameters(InternalArtemisTcpTransport.CIPHER_SUITES.toTypedArray(),
arrayOf("TLSv1.2")) arrayOf("TLSv1.2"))
serverParams.wantClientAuth = true serverParams.wantClientAuth = true
serverParams.needClientAuth = true serverParams.needClientAuth = true
@ -388,11 +386,16 @@ class ProtonWrapperTests {
} }
private fun createArtemisServerAndClient(maxMessageSize: Int = MAX_MESSAGE_SIZE): Pair<ArtemisMessagingServer, ArtemisMessagingClient> { 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 { 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(CHARLIE_NAME).whenever(it).myLegalName
doReturn("trustpass").whenever(it).trustStorePassword doReturn(signingCertificateStore).whenever(it).signingCertificateStore
doReturn("cordacadevpass").whenever(it).keyStorePassword doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
doReturn(NetworkHostAndPort("0.0.0.0", artemisPort)).whenever(it).p2pAddress doReturn(NetworkHostAndPort("0.0.0.0", artemisPort)).whenever(it).p2pAddress
doReturn(null).whenever(it).jmxMonitoringHttpPort doReturn(null).whenever(it).jmxMonitoringHttpPort
doReturn(true).whenever(it).crlCheckSoftFail doReturn(true).whenever(it).crlCheckSoftFail
@ -400,28 +403,32 @@ class ProtonWrapperTests {
artemisConfig.configureWithDevSSLCertificate() artemisConfig.configureWithDevSSLCertificate()
val server = ArtemisMessagingServer(artemisConfig, NetworkHostAndPort("0.0.0.0", artemisPort), maxMessageSize) 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() server.start()
client.start() client.start()
return Pair(server, client) return Pair(server, client)
} }
private fun createClient(maxMessageSize: Int = MAX_MESSAGE_SIZE): AMQPClient { 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 { 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(BOB_NAME).whenever(it).myLegalName
doReturn("trustpass").whenever(it).trustStorePassword doReturn(signingCertificateStore).whenever(it).signingCertificateStore
doReturn("cordacadevpass").whenever(it).keyStorePassword doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
doReturn(true).whenever(it).crlCheckSoftFail doReturn(true).whenever(it).crlCheckSoftFail
} }
clientConfig.configureWithDevSSLCertificate() clientConfig.configureWithDevSSLCertificate()
val clientTruststore = clientConfig.loadTrustStore().internal val clientTruststore = clientConfig.p2pSslOptions.trustStore.get()
val clientKeystore = clientConfig.loadSslKeyStore().internal val clientKeystore = clientConfig.p2pSslOptions.keyStore.get()
val amqpConfig = object : AMQPConfiguration { val amqpConfig = object : AMQPConfiguration {
override val keyStore: KeyStore = clientKeystore override val keyStore = clientKeystore
override val keyStorePrivateKeyPassword: CharArray = clientConfig.keyStorePassword.toCharArray() override val trustStore = clientTruststore
override val trustStore: KeyStore = clientTruststore
override val trace: Boolean = true override val trace: Boolean = true
override val maxMessageSize: Int = maxMessageSize override val maxMessageSize: Int = maxMessageSize
} }
@ -434,21 +441,25 @@ class ProtonWrapperTests {
} }
private fun createSharedThreadsClient(sharedEventGroup: EventLoopGroup, id: Int, maxMessageSize: Int = MAX_MESSAGE_SIZE): AMQPClient { 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 { 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(CordaX500Name(null, "client $id", "Corda", "London", null, "GB")).whenever(it).myLegalName
doReturn("trustpass").whenever(it).trustStorePassword doReturn(signingCertificateStore).whenever(it).signingCertificateStore
doReturn("cordacadevpass").whenever(it).keyStorePassword doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
doReturn(true).whenever(it).crlCheckSoftFail doReturn(true).whenever(it).crlCheckSoftFail
} }
clientConfig.configureWithDevSSLCertificate() clientConfig.configureWithDevSSLCertificate()
val clientTruststore = clientConfig.loadTrustStore().internal val clientTruststore = clientConfig.p2pSslOptions.trustStore.get()
val clientKeystore = clientConfig.loadSslKeyStore().internal val clientKeystore = clientConfig.p2pSslOptions.keyStore.get()
val amqpConfig = object : AMQPConfiguration { val amqpConfig = object : AMQPConfiguration {
override val keyStore: KeyStore = clientKeystore override val keyStore = clientKeystore
override val keyStorePrivateKeyPassword: CharArray = clientConfig.keyStorePassword.toCharArray() override val trustStore = clientTruststore
override val trustStore: KeyStore = clientTruststore
override val trace: Boolean = true override val trace: Boolean = true
override val maxMessageSize: Int = maxMessageSize override val maxMessageSize: Int = maxMessageSize
} }
@ -460,21 +471,25 @@ class ProtonWrapperTests {
} }
private fun createServer(port: Int, name: CordaX500Name = ALICE_NAME, maxMessageSize: Int = MAX_MESSAGE_SIZE): AMQPServer { private fun createServer(port: Int, name: CordaX500Name = ALICE_NAME, maxMessageSize: Int = MAX_MESSAGE_SIZE): 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 { 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(name).whenever(it).myLegalName
doReturn("trustpass").whenever(it).trustStorePassword doReturn(signingCertificateStore).whenever(it).signingCertificateStore
doReturn("cordacadevpass").whenever(it).keyStorePassword doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
doReturn(true).whenever(it).crlCheckSoftFail doReturn(true).whenever(it).crlCheckSoftFail
} }
serverConfig.configureWithDevSSLCertificate() serverConfig.configureWithDevSSLCertificate()
val serverTruststore = serverConfig.loadTrustStore().internal val serverTruststore = serverConfig.p2pSslOptions.trustStore.get()
val serverKeystore = serverConfig.loadSslKeyStore().internal val serverKeystore = serverConfig.p2pSslOptions.keyStore.get()
val amqpConfig = object : AMQPConfiguration { val amqpConfig = object : AMQPConfiguration {
override val keyStore: KeyStore = serverKeystore override val keyStore = serverKeystore
override val keyStorePrivateKeyPassword: CharArray = serverConfig.keyStorePassword.toCharArray() override val trustStore = serverTruststore
override val trustStore: KeyStore = serverTruststore
override val trace: Boolean = true override val trace: Boolean = true
override val maxMessageSize: Int = maxMessageSize override val maxMessageSize: Int = maxMessageSize
} }

View File

@ -3,6 +3,7 @@ package net.corda.node.services.messaging
import com.nhaarman.mockito_kotlin.doReturn import com.nhaarman.mockito_kotlin.doReturn
import com.nhaarman.mockito_kotlin.whenever import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.generateKeyPair
import net.corda.core.internal.div
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.seconds import net.corda.core.utilities.seconds
import net.corda.node.internal.configureDatabase 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.MAX_MESSAGE_SIZE
import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.core.SerializationEnvironmentRule
import net.corda.testing.driver.PortAllocation import net.corda.testing.driver.PortAllocation
import net.corda.testing.internal.stubs.CertificateStoreStubs
import net.corda.testing.internal.LogHelper import net.corda.testing.internal.LogHelper
import net.corda.testing.internal.rigorousMock import net.corda.testing.internal.rigorousMock
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
@ -69,11 +71,18 @@ class ArtemisMessagingTest {
@Before @Before
fun setUp() { fun setUp() {
abstract class AbstractNodeConfiguration : NodeConfiguration 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 { config = rigorousMock<AbstractNodeConfiguration>().also {
doReturn(temporaryFolder.root.toPath()).whenever(it).baseDirectory doReturn(temporaryFolder.root.toPath()).whenever(it).baseDirectory
doReturn(ALICE_NAME).whenever(it).myLegalName doReturn(ALICE_NAME).whenever(it).myLegalName
doReturn("trustpass").whenever(it).trustStorePassword doReturn(certificatesDirectory).whenever(it).certificatesDirectory
doReturn("cordacadevpass").whenever(it).keyStorePassword doReturn(signingCertificateStore).whenever(it).signingCertificateStore
doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
doReturn(NetworkHostAndPort("0.0.0.0", serverPort)).whenever(it).p2pAddress doReturn(NetworkHostAndPort("0.0.0.0", serverPort)).whenever(it).p2pAddress
doReturn(null).whenever(it).jmxMonitoringHttpPort doReturn(null).whenever(it).jmxMonitoringHttpPort
doReturn(FlowTimeoutConfiguration(5.seconds, 3, backoffBase = 1.0)).whenever(it).flowTimeout doReturn(FlowTimeoutConfiguration(5.seconds, 3, backoffBase = 1.0)).whenever(it).flowTimeout

View File

@ -12,7 +12,6 @@ import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.seconds import net.corda.core.utilities.seconds
import net.corda.node.services.config.configureDevKeyAndTrustStores import net.corda.node.services.config.configureDevKeyAndTrustStores
import net.corda.nodeapi.internal.NODE_INFO_DIRECTORY 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_FILE_NAME
import net.corda.nodeapi.internal.network.NETWORK_PARAMS_UPDATE_FILE_NAME import net.corda.nodeapi.internal.network.NETWORK_PARAMS_UPDATE_FILE_NAME
import net.corda.nodeapi.internal.network.SignedNetworkParameters import net.corda.nodeapi.internal.network.SignedNetworkParameters
@ -21,6 +20,7 @@ import net.corda.testing.core.*
import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.NodeHandle
import net.corda.testing.driver.PortAllocation import net.corda.testing.driver.PortAllocation
import net.corda.testing.driver.internal.NodeHandleInternal import net.corda.testing.driver.internal.NodeHandleInternal
import net.corda.testing.internal.stubs.CertificateStoreStubs
import net.corda.testing.node.internal.* import net.corda.testing.node.internal.*
import net.corda.testing.node.internal.network.NetworkMapServer import net.corda.testing.node.internal.network.NetworkMapServer
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
@ -247,13 +247,10 @@ private fun DriverDSLImpl.startNode(providedName: CordaX500Name, devMode: Boolea
var customOverrides = emptyMap<String, String>() var customOverrides = emptyMap<String, String>()
if (!devMode) { if (!devMode) {
val nodeDir = baseDirectory(providedName) val nodeDir = baseDirectory(providedName)
val nodeSslConfig = object : NodeSSLConfiguration { val certificatesDirectory = nodeDir / "certificates"
override val baseDirectory = nodeDir val signingCertStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)
override val keyStorePassword = "cordacadevpass" val p2pSslConfig = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)
override val trustStorePassword = "trustpass" p2pSslConfig.configureDevKeyAndTrustStores(providedName, signingCertStore, certificatesDirectory)
override val crlCheckSoftFail = true
}
nodeSslConfig.configureDevKeyAndTrustStores(providedName)
customOverrides = mapOf("devMode" to "false") customOverrides = mapOf("devMode" to "false")
} }
return startNode(providedName = providedName, customOverrides = customOverrides) return startNode(providedName = providedName, customOverrides = customOverrides)

View File

@ -17,13 +17,13 @@ import net.corda.node.services.messaging.RPCServerConfiguration
import net.corda.node.utilities.createKeyPairAndSelfSignedTLSCertificate import net.corda.node.utilities.createKeyPairAndSelfSignedTLSCertificate
import net.corda.node.utilities.saveToKeyStore import net.corda.node.utilities.saveToKeyStore
import net.corda.node.utilities.saveToTrustStore import net.corda.node.utilities.saveToTrustStore
import net.corda.nodeapi.ArtemisTcpTransport.Companion.rpcConnectorTcpTransport
import net.corda.nodeapi.BrokerRpcSslOptions 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.nodeapi.internal.config.User
import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.core.SerializationEnvironmentRule
import net.corda.testing.driver.PortAllocation 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.ActiveMQConnectionTimedOutException
import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
@ -58,12 +58,12 @@ class ArtemisRpcTests {
val brokerSslOptions = BrokerRpcSslOptions(keyStorePath, "password") val brokerSslOptions = BrokerRpcSslOptions(keyStorePath, "password")
val trustStorePath = saveToTrustStore(tempFile("rpcTruststore.jks"), selfSignCert) val trustStorePath = saveToTrustStore(tempFile("rpcTruststore.jks"), selfSignCert)
val clientSslOptions = ClientRpcSslOptions(trustStorePath, "password") val clientSslOptions = ClientRpcSslOptions(trustStorePath, "password")
testSslCommunication(createNodeSslConfig(tempFolder.root.toPath()), brokerSslOptions, true, clientSslOptions) testSslCommunication(p2pSslOptions(tempFolder.root.toPath()), brokerSslOptions, true, clientSslOptions)
} }
@Test @Test
fun rpc_with_ssl_disabled() { fun rpc_with_ssl_disabled() {
testSslCommunication(createNodeSslConfig(tempFolder.root.toPath()), null, false, null) testSslCommunication(p2pSslOptions(tempFolder.root.toPath()), null, false, null)
} }
@Test @Test
@ -73,7 +73,7 @@ class ArtemisRpcTests {
val brokerSslOptions = BrokerRpcSslOptions(keyStorePath, "password") val brokerSslOptions = BrokerRpcSslOptions(keyStorePath, "password")
// here client sslOptions are passed null (as in, do not use SSL) // here client sslOptions are passed null (as in, do not use SSL)
assertThatThrownBy { assertThatThrownBy {
testSslCommunication(createNodeSslConfig(tempFolder.root.toPath()), brokerSslOptions, true, null) testSslCommunication(p2pSslOptions(tempFolder.root.toPath()), brokerSslOptions, true, null)
}.isInstanceOf(ActiveMQConnectionTimedOutException::class.java) }.isInstanceOf(ActiveMQConnectionTimedOutException::class.java)
} }
@ -91,11 +91,11 @@ class ArtemisRpcTests {
val clientSslOptions = ClientRpcSslOptions(trustStorePath, "password") val clientSslOptions = ClientRpcSslOptions(trustStorePath, "password")
assertThatThrownBy { assertThatThrownBy {
testSslCommunication(createNodeSslConfig(tempFolder.root.toPath()), brokerSslOptions, true, clientSslOptions) testSslCommunication(p2pSslOptions(tempFolder.root.toPath()), brokerSslOptions, true, clientSslOptions)
}.isInstanceOf(RPCException::class.java) }.isInstanceOf(RPCException::class.java)
} }
private fun testSslCommunication(nodeSSlconfig: SSLConfiguration, private fun testSslCommunication(nodeSSlconfig: MutualSslConfiguration,
brokerSslOptions: BrokerRpcSslOptions?, brokerSslOptions: BrokerRpcSslOptions?,
useSslForBroker: Boolean, useSslForBroker: Boolean,
clientSslOptions: ClientRpcSslOptions?, clientSslOptions: ClientRpcSslOptions?,

View File

@ -2,7 +2,6 @@ package net.corda.services.messaging
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.copyTo
import net.corda.core.internal.createDirectories import net.corda.core.internal.createDirectories
import net.corda.core.internal.exists import net.corda.core.internal.exists
import net.corda.core.internal.toX500Name 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.ArtemisMessagingComponent.Companion.PEER_USER
import net.corda.nodeapi.internal.DEV_INTERMEDIATE_CA import net.corda.nodeapi.internal.DEV_INTERMEDIATE_CA
import net.corda.nodeapi.internal.DEV_ROOT_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.CertificateType
import net.corda.nodeapi.internal.crypto.X509Utilities 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.config.ActiveMQDefaultConfiguration
import org.apache.activemq.artemis.api.core.ActiveMQClusterSecurityException import org.apache.activemq.artemis.api.core.ActiveMQClusterSecurityException
import org.apache.activemq.artemis.api.core.ActiveMQNotConnectedException import org.apache.activemq.artemis.api.core.ActiveMQNotConnectedException
@ -84,57 +84,35 @@ class MQSecurityAsNodeTest : P2PMQSecurityTest() {
@Test @Test
fun `login with invalid certificate chain`() { fun `login with invalid certificate chain`() {
val sslConfig = object : SSLConfiguration { val certsDir = Files.createTempDirectory("certs")
override val certificatesDirectory = Files.createTempDirectory("certs") certsDir.createDirectories()
override val keyStorePassword: String get() = "cordacadevpass" val signingCertStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certsDir)
override val trustStorePassword: String get() = "trustpass" val p2pSslConfig = CertificateStoreStubs.P2P.withCertificatesDirectory(certsDir)
override val crlCheckSoftFail: Boolean = true
init { val legalName = CordaX500Name("MegaCorp", "London", "GB")
val legalName = CordaX500Name("MegaCorp", "London", "GB") if (!p2pSslConfig.trustStore.path.exists()) {
certificatesDirectory.createDirectories() val trustStore = p2pSslConfig.trustStore.get(true)
if (!trustStoreFile.exists()) { loadDevCaTrustStore().copyTo(trustStore)
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 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 { assertThatExceptionOfType(ActiveMQNotConnectedException::class.java).isThrownBy {
attacker.start(PEER_USER, PEER_USER) attacker.start(PEER_USER, PEER_USER)
} }

View File

@ -18,12 +18,12 @@ import net.corda.node.internal.NodeWithInfo
import net.corda.nodeapi.RPCApi import net.corda.nodeapi.RPCApi
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.INTERNAL_PREFIX import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.INTERNAL_PREFIX
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NOTIFICATIONS_ADDRESS 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.ALICE_NAME
import net.corda.testing.core.BOB_NAME import net.corda.testing.core.BOB_NAME
import net.corda.testing.node.User
import net.corda.testing.core.singleIdentity import net.corda.testing.core.singleIdentity
import net.corda.testing.internal.configureTestSSL import net.corda.testing.internal.configureTestSSL
import net.corda.testing.node.User
import net.corda.testing.node.internal.NodeBasedTest import net.corda.testing.node.internal.NodeBasedTest
import net.corda.testing.node.internal.startFlow import net.corda.testing.node.internal.startFlow
import org.apache.activemq.artemis.api.core.ActiveMQNonExistentQueueException import org.apache.activemq.artemis.api.core.ActiveMQNonExistentQueueException
@ -94,7 +94,7 @@ abstract class MQSecurityTest : NodeBasedTest() {
assertAllQueueCreationAttacksFail(randomQueue) 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) val client = SimpleMQClient(target, sslConfiguration)
clients += client clients += client
return client return client

View File

@ -3,8 +3,8 @@ package net.corda.services.messaging
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.serialization.internal.nodeSerializationEnv import net.corda.core.serialization.internal.nodeSerializationEnv
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.nodeapi.ArtemisTcpTransport import net.corda.nodeapi.internal.InternalArtemisTcpTransport.Companion.p2pConnectorTcpTransport
import net.corda.nodeapi.internal.config.SSLConfiguration import net.corda.nodeapi.internal.config.MutualSslConfiguration
import net.corda.testing.internal.configureTestSSL import net.corda.testing.internal.configureTestSSL
import org.apache.activemq.artemis.api.core.client.* 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. * As the name suggests this is a simple client for connecting to MQ brokers.
*/ */
class SimpleMQClient(val target: NetworkHostAndPort, 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 { companion object {
val DEFAULT_MQ_LEGAL_NAME = CordaX500Name(organisation = "SimpleMQClient", locality = "London", country = "GB") 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 lateinit var producer: ClientProducer
fun start(username: String? = null, password: String? = null, enableSSL: Boolean = true) { 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 { val locator = ActiveMQClient.createServerLocatorWithoutHA(tcpTransport).apply {
isBlockOnNonDurableSend = true isBlockOnNonDurableSend = true
threadPoolMaxSize = 1 threadPoolMaxSize = 1

View File

@ -44,6 +44,7 @@ import net.corda.node.services.FinalityHandler
import net.corda.node.services.NotaryChangeHandler import net.corda.node.services.NotaryChangeHandler
import net.corda.node.services.api.* import net.corda.node.services.api.*
import net.corda.node.services.config.* 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.config.shell.toShellConfig
import net.corda.node.services.events.NodeSchedulerService import net.corda.node.services.events.NodeSchedulerService
import net.corda.node.services.events.ScheduledActivityObserver import net.corda.node.services.events.ScheduledActivityObserver
@ -248,7 +249,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
val trustRoot = initKeyStore() val trustRoot = initKeyStore()
val (identity, identityKeyPair) = obtainIdentity(notaryConfig = null) val (identity, identityKeyPair) = obtainIdentity(notaryConfig = null)
startDatabase() 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)) identityService.start(trustRoot, listOf(identity.certificate, nodeCa))
return database.use { return database.use {
it.transaction { it.transaction {
@ -278,7 +279,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
log.info("Node starting up ...") log.info("Node starting up ...")
val trustRoot = initKeyStore() val trustRoot = initKeyStore()
val nodeCa = configuration.loadNodeKeyStore().getCertificate(X509Utilities.CORDA_CLIENT_CA) val nodeCa = configuration.signingCertificateStore.get()[X509Utilities.CORDA_CLIENT_CA]
initialiseJVMAgents() initialiseJVMAgents()
schemaService.mappedSchemasWarnings().forEach { schemaService.mappedSchemasWarnings().forEach {
@ -368,10 +369,10 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
open fun startShell() { open fun startShell() {
if (configuration.shouldInitCrashShell()) { if (configuration.shouldInitCrashShell()) {
val shellConfiguration = configuration.toShellConfig() val shellConfiguration = configuration.toShellConfig()
shellConfiguration.sshHostKeyDirectory?.let { shellConfiguration.sshdPort?.let {
log.info("Binding Shell SSHD server on port $it.") log.info("Binding Shell SSHD server on port $it.")
} }
InteractiveShell.startShellInternal(shellConfiguration, cordappLoader.appClassLoader) InteractiveShell.startShell(shellConfiguration, cordappLoader.appClassLoader)
} }
} }
@ -696,8 +697,8 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
private fun validateKeyStore(): X509Certificate { private fun validateKeyStore(): X509Certificate {
val containCorrectKeys = try { val containCorrectKeys = try {
// This will throw IOException if key file not found or KeyStoreException if keystore password is incorrect. // This will throw IOException if key file not found or KeyStoreException if keystore password is incorrect.
val sslKeystore = configuration.loadSslKeyStore() val sslKeystore = configuration.p2pSslOptions.keyStore.get()
val identitiesKeystore = configuration.loadNodeKeyStore() val identitiesKeystore = configuration.signingCertificateStore.get()
X509Utilities.CORDA_CLIENT_TLS in sslKeystore && X509Utilities.CORDA_CLIENT_CA in identitiesKeystore X509Utilities.CORDA_CLIENT_TLS in sslKeystore && X509Utilities.CORDA_CLIENT_CA in identitiesKeystore
} catch (e: KeyStoreException) { } catch (e: KeyStoreException) {
log.warn("Certificate key store found but key store password does not match configuration.") log.warn("Certificate key store found but key store password does not match configuration.")
@ -714,9 +715,9 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
} }
// Check all cert path chain to the trusted root // Check all cert path chain to the trusted root
val sslCertChainRoot = configuration.loadSslKeyStore().getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).last() val sslCertChainRoot = configuration.p2pSslOptions.keyStore.get().query { getCertificateChain(X509Utilities.CORDA_CLIENT_TLS) }.last()
val nodeCaCertChainRoot = configuration.loadNodeKeyStore().getCertificateChain(X509Utilities.CORDA_CLIENT_CA).last() val nodeCaCertChainRoot = configuration.signingCertificateStore.get().query { getCertificateChain(X509Utilities.CORDA_CLIENT_CA) }.last()
val trustRoot = configuration.loadTrustStore().getCertificate(X509Utilities.CORDA_ROOT_CA) val trustRoot = configuration.p2pSslOptions.trustStore.get()[X509Utilities.CORDA_ROOT_CA]
require(sslCertChainRoot == trustRoot) { "TLS certificate must chain to the trusted root." } require(sslCertChainRoot == trustRoot) { "TLS certificate must chain to the trusted root." }
require(nodeCaCertChainRoot == trustRoot) { "Client CA certificate must chain to the trusted root." } require(nodeCaCertChainRoot == trustRoot) { "Client CA certificate must chain to the trusted root." }
@ -760,7 +761,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
return notaryConfig.run { return notaryConfig.run {
when { when {
raft != null -> { 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) (if (validating) ::RaftValidatingNotaryService else ::RaftNonValidatingNotaryService)(services, notaryKey, uniquenessProvider)
} }
bftSMaRt != null -> { bftSMaRt != null -> {
@ -804,7 +805,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
networkParameters: NetworkParameters) networkParameters: NetworkParameters)
private fun obtainIdentity(notaryConfig: NotaryConfig?): Pair<PartyAndCertificate, KeyPair> { 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) { val (id, singleName) = if (notaryConfig == null || !notaryConfig.isClusterConfig) {
// Node's main identity or if it's a single node notary // Node's main identity or if it's a single node notary
@ -819,25 +820,25 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
if (privateKeyAlias !in keyStore) { if (privateKeyAlias !in keyStore) {
singleName ?: throw IllegalArgumentException( singleName ?: throw IllegalArgumentException(
"Unable to find in the key store the identity of the distributed notary the node is part of") "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 // TODO This check shouldn't be needed
check(singleName == configuration.myLegalName) check(singleName == configuration.myLegalName)
keyStore.storeLegalIdentity(privateKeyAlias, generateKeyPair()) 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. // TODO: Use configuration to indicate composite key should be used instead of public key for the identity.
val compositeKeyAlias = "$id-composite-key" val compositeKeyAlias = "$id-composite-key"
val certificates = if (compositeKeyAlias in keyStore) { val certificates = if (compositeKeyAlias in keyStore) {
// Use composite key instead if it exists // 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 // 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 + // 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. // 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 { } else {
keyStore.getCertificateChain(privateKeyAlias).let { keyStore.query { getCertificateChain(privateKeyAlias) }.let {
check(it[0] == x509Cert) { "Certificates from key store do not line up!" } check(it[0] == x509Cert) { "Certificates from key store do not line up!" }
it it
} }
@ -1027,4 +1028,13 @@ fun CordaPersistence.startHikariPool(hikariProperties: Properties, databaseConfi
else -> throw CouldNotCreateDataSourceException("Could not create the DataSource: ${ex.message}", ex) 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)
} }

View File

@ -227,12 +227,12 @@ open class Node(configuration: NodeConfiguration,
startLocalRpcBroker(securityManager) startLocalRpcBroker(securityManager)
} }
val bridgeControlListener = BridgeControlListener(configuration, network.serverAddress, networkParameters.maxMessageSize) val bridgeControlListener = BridgeControlListener(configuration.p2pSslOptions, network.serverAddress, networkParameters.maxMessageSize)
printBasicNodeInfo("Advertised P2P messaging addresses", nodeInfo.addresses.joinToString()) printBasicNodeInfo("Advertised P2P messaging addresses", nodeInfo.addresses.joinToString())
val rpcServerConfiguration = RPCServerConfiguration.DEFAULT val rpcServerConfiguration = RPCServerConfiguration.DEFAULT
rpcServerAddresses?.let { 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 connection address", it.primary.toString())
printBasicNodeInfo("RPC admin connection address", it.admin.toString()) printBasicNodeInfo("RPC admin connection address", it.admin.toString())
} }
@ -271,9 +271,9 @@ open class Node(configuration: NodeConfiguration,
val rpcBrokerDirectory: Path = baseDirectory / "brokers" / "rpc" val rpcBrokerDirectory: Path = baseDirectory / "brokers" / "rpc"
with(rpcOptions) { with(rpcOptions) {
rpcBroker = if (useSsl) { 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 { } 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() rpcBroker!!.closeOnStop()

View File

@ -8,11 +8,10 @@ import net.corda.core.internal.createDirectories
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.internal.exists import net.corda.core.internal.exists
import net.corda.nodeapi.internal.* 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.config.toProperties
import net.corda.nodeapi.internal.crypto.X509KeyStore 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 org.slf4j.LoggerFactory
import java.nio.file.Path import java.nio.file.Path
@ -68,22 +67,33 @@ object ConfigHelper {
* the CA certs in Node resources. Then provision KeyStores into certificates folder under node path. * the CA certs in Node resources. Then provision KeyStores into certificates folder under node path.
*/ */
// TODO Move this to KeyStoreConfigHelpers // TODO Move this to KeyStoreConfigHelpers
fun NodeConfiguration.configureWithDevSSLCertificate() = configureDevKeyAndTrustStores(myLegalName) fun NodeConfiguration.configureWithDevSSLCertificate() = p2pSslOptions.configureDevKeyAndTrustStores(myLegalName, signingCertificateStore, certificatesDirectory)
// TODO Move this to KeyStoreConfigHelpers // 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() 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. // Move distributed service composite key (generated by IdentityGenerator.generateToDisk) to keystore if exists.
val distributedServiceKeystore = certificatesDirectory / "distributedService.jks" val distributedServiceKeystore = certificatesDirectory / "distributedService.jks"
if (distributedServiceKeystore.exists()) { if (distributedServiceKeystore.exists()) {
val serviceKeystore = X509KeyStore.fromFile(distributedServiceKeystore, DEV_CA_KEY_STORE_PASS) val serviceKeystore = X509KeyStore.fromFile(distributedServiceKeystore, DEV_CA_KEY_STORE_PASS)
nodeKeyStore.update { signingKeyStore.update {
serviceKeystore.aliases().forEach { serviceKeystore.aliases().forEach {
if (serviceKeystore.internal.isKeyEntry(it)) { if (serviceKeystore.internal.isKeyEntry(it)) {
setPrivateKey(it, serviceKeystore.getPrivateKey(it, DEV_CA_PRIVATE_KEY_PASS), serviceKeystore.getCertificateChain(it)) setPrivateKey(it, serviceKeystore.getPrivateKey(it, DEV_CA_PRIVATE_KEY_PASS), serviceKeystore.getCertificateChain(it))

View File

@ -11,7 +11,9 @@ import net.corda.core.utilities.loggerFor
import net.corda.core.utilities.seconds import net.corda.core.utilities.seconds
import net.corda.node.services.config.rpc.NodeRpcOptions import net.corda.node.services.config.rpc.NodeRpcOptions
import net.corda.nodeapi.BrokerRpcSslOptions 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.UnknownConfigKeysPolicy
import net.corda.nodeapi.internal.config.User import net.corda.nodeapi.internal.config.User
import net.corda.nodeapi.internal.config.parseAs import net.corda.nodeapi.internal.config.parseAs
@ -30,7 +32,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 val DEFAULT_FLOW_MONITOR_SUSPENSION_LOGGING_THRESHOLD_MILLIS: Duration = Duration.ofMinutes(1)
private const val CORDAPPS_DIR_NAME_DEFAULT = "cordapps" private const val CORDAPPS_DIR_NAME_DEFAULT = "cordapps"
interface NodeConfiguration : NodeSSLConfiguration { interface NodeConfiguration {
val myLegalName: CordaX500Name val myLegalName: CordaX500Name
val emailAddress: String val emailAddress: String
val jmxMonitoringHttpPort: Int? val jmxMonitoringHttpPort: Int?
@ -69,9 +71,16 @@ interface NodeConfiguration : NodeSSLConfiguration {
val effectiveH2Settings: NodeH2Settings? val effectiveH2Settings: NodeH2Settings?
val flowMonitorPeriodMillis: Duration get() = DEFAULT_FLOW_MONITOR_PERIOD_MILLIS val flowMonitorPeriodMillis: Duration get() = DEFAULT_FLOW_MONITOR_PERIOD_MILLIS
val flowMonitorSuspensionLoggingThresholdMillis: Duration get() = DEFAULT_FLOW_MONITOR_SUSPENSION_LOGGING_THRESHOLD_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 jmxReporterType : JmxReporterType? get() = defaultJmxReporterType
val baseDirectory: Path
val certificatesDirectory: Path
val signingCertificateStore: FileBasedCertificateStoreSupplier
val p2pSslOptions: MutualSslConfiguration
val cordappDirectories: List<Path>
fun validate(): List<String> fun validate(): List<String>
companion object { companion object {
@ -176,8 +185,8 @@ data class NodeConfigurationImpl(
override val myLegalName: CordaX500Name, override val myLegalName: CordaX500Name,
override val jmxMonitoringHttpPort: Int? = null, override val jmxMonitoringHttpPort: Int? = null,
override val emailAddress: String, override val emailAddress: String,
override val keyStorePassword: String, private val keyStorePassword: String,
override val trustStorePassword: String, private val trustStorePassword: String,
override val crlCheckSoftFail: Boolean, override val crlCheckSoftFail: Boolean,
override val dataSourceProperties: Properties, override val dataSourceProperties: Properties,
override val compatibilityZoneURL: URL? = null, override val compatibilityZoneURL: URL? = null,
@ -243,6 +252,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 override val rpcOptions: NodeRpcOptions
get() { get() {
return actualRpcSettings.asOptions() return actualRpcSettings.asOptions()
@ -356,8 +376,6 @@ data class NodeConfigurationImpl(
} }
} }
data class NodeRpcSettings( data class NodeRpcSettings(
val address: NetworkHostAndPort?, val address: NetworkHostAndPort?,
val adminAddress: NetworkHostAndPort?, val adminAddress: NetworkHostAndPort?,

View File

@ -1,6 +1,7 @@
package net.corda.node.services.config.shell package net.corda.node.services.config.shell
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.node.internal.clientSslOptionsCompatibleWith
import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NodeConfiguration
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.INTERNAL_SHELL_USER import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.INTERNAL_SHELL_USER
import net.corda.tools.shell.ShellConfiguration import net.corda.tools.shell.ShellConfiguration
@ -14,8 +15,8 @@ fun NodeConfiguration.toShellConfig() = ShellConfiguration(
cordappsDirectory = this.baseDirectory.toString() / CORDAPPS_DIR, cordappsDirectory = this.baseDirectory.toString() / CORDAPPS_DIR,
user = INTERNAL_SHELL_USER, user = INTERNAL_SHELL_USER,
password = INTERNAL_SHELL_USER, password = INTERNAL_SHELL_USER,
hostAndPort = this.rpcOptions.adminAddress, hostAndPort = this.rpcOptions.address,
nodeSslConfig = this, ssl = clientSslOptionsCompatibleWith(this.rpcOptions),
sshdPort = this.sshd?.port, sshdPort = this.sshd?.port,
sshHostKeyDirectory = this.baseDirectory / SSHD_HOSTKEY_DIR, sshHostKeyDirectory = this.baseDirectory / SSHD_HOSTKEY_DIR,
noLocalShell = this.noLocalShell) noLocalShell = this.noLocalShell)

View File

@ -1,6 +1,5 @@
package net.corda.node.services.messaging package net.corda.node.services.messaging
import io.netty.channel.unix.Errors
import net.corda.core.internal.ThreadBox import net.corda.core.internal.ThreadBox
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.serialization.SingletonSerializeAsToken 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.node.internal.artemis.BrokerJaasLoginModule.Companion.PEER_ROLE
import net.corda.core.internal.errors.AddressBindingException import net.corda.core.internal.errors.AddressBindingException
import net.corda.node.services.config.NodeConfiguration 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.AmqpMessageSizeChecksInterceptor
import net.corda.nodeapi.internal.ArtemisMessageSizeChecksInterceptor import net.corda.nodeapi.internal.ArtemisMessageSizeChecksInterceptor
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.INTERNAL_PREFIX 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.JOURNAL_HEADER_SIZE
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NOTIFICATIONS_ADDRESS import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NOTIFICATIONS_ADDRESS
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_PREFIX import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_PREFIX
import net.corda.nodeapi.internal.InternalArtemisTcpTransport.Companion.p2pAcceptorTcpTransport
import net.corda.nodeapi.internal.requireOnDefaultFileSystem import net.corda.nodeapi.internal.requireOnDefaultFileSystem
import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.api.core.SimpleString
import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl
@ -120,7 +119,7 @@ class ArtemisMessagingServer(private val config: NodeConfiguration,
bindingsDirectory = (artemisDir / "bindings").toString() bindingsDirectory = (artemisDir / "bindings").toString()
journalDirectory = (artemisDir / "journal").toString() journalDirectory = (artemisDir / "journal").toString()
largeMessagesDirectory = (artemisDir / "large-messages").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 // 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. // 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 idCacheSize = 2000 // Artemis Default duplicate cache size i.e. a guess
@ -162,8 +161,8 @@ class ArtemisMessagingServer(private val config: NodeConfiguration,
@Throws(IOException::class, KeyStoreException::class) @Throws(IOException::class, KeyStoreException::class)
private fun createArtemisSecurityManager(): ActiveMQJAASSecurityManager { private fun createArtemisSecurityManager(): ActiveMQJAASSecurityManager {
val keyStore = config.loadSslKeyStore().internal val keyStore = config.p2pSslOptions.keyStore.get().value.internal
val trustStore = config.loadTrustStore().internal val trustStore = config.p2pSslOptions.trustStore.get().value.internal
val securityConfig = object : SecurityConfiguration() { val securityConfig = object : SecurityConfiguration() {
// Override to make it work with our login module // Override to make it work with our login module

View File

@ -6,9 +6,9 @@ import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.serialization.internal.nodeSerializationEnv import net.corda.core.serialization.internal.nodeSerializationEnv
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.node.internal.security.RPCSecurityManager 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.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.ActiveMQClient
import org.apache.activemq.artemis.api.core.client.ServerLocator import org.apache.activemq.artemis.api.core.client.ServerLocator
import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl 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. * 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 locator: ServerLocator? = null
private var rpcServer: RPCServer? = null private var rpcServer: RPCServer? = null
fun init(rpcOps: RPCOps, securityManager: RPCSecurityManager) = synchronized(this) { 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 { locator = ActiveMQClient.createServerLocatorWithoutHA(tcpTransport).apply {
// Never time out on our loopback Artemis connections. If we switch back to using the InVM transport this // 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. // would be the default and the two lines below can be deleted.

View File

@ -26,7 +26,6 @@ import net.corda.node.services.statemachine.DeduplicationId
import net.corda.node.services.statemachine.ExternalEvent import net.corda.node.services.statemachine.ExternalEvent
import net.corda.node.services.statemachine.SenderDeduplicationId import net.corda.node.services.statemachine.SenderDeduplicationId
import net.corda.node.utilities.AffinityExecutor 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.* import net.corda.nodeapi.internal.ArtemisMessagingComponent.*
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.BRIDGE_CONTROL import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.BRIDGE_CONTROL
@ -34,6 +33,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.JOURNAL_HEADER_SIZE
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2PMessagingHeaders import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2PMessagingHeaders
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEERS_PREFIX 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.BridgeControl
import net.corda.nodeapi.internal.bridging.BridgeEntry import net.corda.nodeapi.internal.bridging.BridgeEntry
import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.CordaPersistence
@ -149,7 +149,7 @@ class P2PMessagingClient(val config: NodeConfiguration,
started = true started = true
log.info("Connecting to message broker: $serverAddress") log.info("Connecting to message broker: $serverAddress")
// TODO Add broker CN to config for host verification in case the embedded broker isn't used // 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 { locator = ActiveMQClient.createServerLocatorWithoutHA(tcpTransport).apply {
// Never time out on our loopback Artemis connections. If we switch back to using the InVM transport this // 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. // would be the default and the two lines below can be deleted.

View File

@ -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.artemis.BrokerJaasLoginModule.Companion.RPC_SECURITY_CONFIG
import net.corda.node.internal.security.RPCSecurityManager import net.corda.node.internal.security.RPCSecurityManager
import net.corda.nodeapi.BrokerRpcSslOptions 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.api.core.management.ActiveMQServerControl
import org.apache.activemq.artemis.core.config.impl.SecurityConfiguration import org.apache.activemq.artemis.core.config.impl.SecurityConfiguration
import org.apache.activemq.artemis.core.server.ActiveMQServer import org.apache.activemq.artemis.core.server.ActiveMQServer
@ -28,17 +28,17 @@ class ArtemisRpcBroker internal constructor(
private val maxMessageSize: Int, private val maxMessageSize: Int,
private val jmxEnabled: Boolean = false, private val jmxEnabled: Boolean = false,
private val baseDirectory: Path, private val baseDirectory: Path,
private val nodeConfiguration: SSLConfiguration, private val nodeConfiguration: MutualSslConfiguration,
private val shouldStartLocalShell: Boolean) : ArtemisBroker { private val shouldStartLocalShell: Boolean) : ArtemisBroker {
companion object { companion object {
private val logger = loggerFor<ArtemisRpcBroker>() 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) 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) 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) @Throws(IOException::class, KeyStoreException::class)
private fun createArtemisSecurityManager(loginListener: LoginListener): ActiveMQJAASSecurityManager { private fun createArtemisSecurityManager(loginListener: LoginListener): ActiveMQJAASSecurityManager {
val keyStore = nodeConfiguration.loadSslKeyStore().internal val keyStore = nodeConfiguration.keyStore.get()
val trustStore = nodeConfiguration.loadTrustStore().internal val trustStore = nodeConfiguration.trustStore.get()
val securityConfig = object : SecurityConfiguration() { val securityConfig = object : SecurityConfiguration() {
override fun getAppConfigurationEntry(name: String): Array<AppConfigurationEntry> { override fun getAppConfigurationEntry(name: String): Array<AppConfigurationEntry> {
val options = mapOf( val options = mapOf(
RPC_SECURITY_CONFIG to RPCJaasConfig(securityManager, loginListener, useSsl), 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)) return arrayOf(AppConfigurationEntry(name, AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options))
} }

View File

@ -4,12 +4,12 @@ import net.corda.core.internal.div
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.node.internal.artemis.BrokerJaasLoginModule import net.corda.node.internal.artemis.BrokerJaasLoginModule
import net.corda.node.internal.artemis.SecureArtemisConfiguration 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.BrokerRpcSslOptions
import net.corda.nodeapi.RPCApi import net.corda.nodeapi.RPCApi
import net.corda.nodeapi.internal.ArtemisMessagingComponent 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.api.core.SimpleString
import org.apache.activemq.artemis.core.config.CoreQueueConfiguration import org.apache.activemq.artemis.core.config.CoreQueueConfiguration
import org.apache.activemq.artemis.core.security.Role 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 org.apache.activemq.artemis.core.settings.impl.AddressSettings
import java.nio.file.Path 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 val loginListener: (String) -> Unit
init { init {

View File

@ -28,8 +28,7 @@ import net.corda.core.utilities.debug
import net.corda.node.services.config.RaftConfig import net.corda.node.services.config.RaftConfig
import net.corda.node.services.transactions.RaftTransactionCommitLog.Commands.CommitTransaction import net.corda.node.services.transactions.RaftTransactionCommitLog.Commands.CommitTransaction
import net.corda.node.utilities.AppendOnlyPersistentMap import net.corda.node.utilities.AppendOnlyPersistentMap
import net.corda.nodeapi.internal.config.NodeSSLConfiguration import net.corda.nodeapi.internal.config.MutualSslConfiguration
import net.corda.nodeapi.internal.config.SSLConfiguration
import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
import java.nio.file.Path import java.nio.file.Path
@ -51,7 +50,8 @@ import javax.persistence.Table
*/ */
@ThreadSafe @ThreadSafe
class RaftUniquenessProvider( class RaftUniquenessProvider(
private val transportConfiguration: NodeSSLConfiguration, private val storagePath: Path,
private val transportConfiguration: MutualSslConfiguration,
private val db: CordaPersistence, private val db: CordaPersistence,
private val clock: Clock, private val clock: Clock,
private val metrics: MetricRegistry, private val metrics: MetricRegistry,
@ -96,8 +96,6 @@ class RaftUniquenessProvider(
var index: Long = 0 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 _clientFuture: CompletableFuture<CopycatClient>
private lateinit var server: CopycatServer private lateinit var server: CopycatServer
@ -155,14 +153,14 @@ class RaftUniquenessProvider(
.build() .build()
} }
private fun buildTransport(config: SSLConfiguration): Transport? { private fun buildTransport(config: MutualSslConfiguration): Transport? {
return NettyTransport.builder() return NettyTransport.builder()
.withSsl() .withSsl()
.withSslProtocol(SslProtocol.TLSv1_2) .withSslProtocol(SslProtocol.TLSv1_2)
.withKeyStorePath(config.sslKeystore.toString()) .withKeyStorePath(config.keyStore.path.toString())
.withKeyStorePassword(config.keyStorePassword) .withKeyStorePassword(config.keyStore.password)
.withTrustStorePath(config.trustStoreFile.toString()) .withTrustStorePath(config.trustStore.path.toString())
.withTrustStorePassword(config.trustStorePassword) .withTrustStorePassword(config.trustStore.password)
.build() .build()
} }

View File

@ -6,7 +6,8 @@ import net.corda.core.internal.*
import net.corda.core.utilities.contextLogger import net.corda.core.utilities.contextLogger
import net.corda.node.NodeRegistrationOption import net.corda.node.NodeRegistrationOption
import net.corda.node.services.config.NodeConfiguration 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.CertificateType
import net.corda.nodeapi.internal.crypto.X509KeyStore import net.corda.nodeapi.internal.crypto.X509KeyStore
import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities
@ -33,7 +34,8 @@ import javax.security.auth.x500.X500Principal
* needed. * needed.
*/ */
// TODO: Use content signer instead of keypairs. // 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 myLegalName: CordaX500Name,
private val emailAddress: String, private val emailAddress: String,
private val certService: NetworkRegistrationService, private val certService: NetworkRegistrationService,
@ -48,9 +50,7 @@ open class NetworkRegistrationHelper(private val config: SSLConfiguration,
val logger = contextLogger() val logger = contextLogger()
} }
private val requestIdStore = config.certificatesDirectory / "certificate-request-id.txt" private val requestIdStore = certificatesDirectory / "certificate-request-id.txt"
// TODO: Use different password for private key.
private val privateKeyPassword = config.keyStorePassword
private val rootTrustStore: X509KeyStore private val rootTrustStore: X509KeyStore
protected val rootCert: X509Certificate 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. * @throws CertificateRequestException if the certificate retrieved by doorman is invalid.
*/ */
fun buildKeystore() { fun buildKeystore() {
config.certificatesDirectory.createDirectories() certificatesDirectory.createDirectories()
val nodeKeyStore = config.loadNodeKeyStore(createNew = true) val nodeKeyStore = signingCertificateStore.get(createNew = true)
if (keyAlias in nodeKeyStore) { if (keyAlias in nodeKeyStore) {
println("Certificate already exists, Corda node will now terminate...") println("Certificate already exists, Corda node will now terminate...")
return return
} }
// TODO: Use different password for private key.
val privateKeyPassword = nodeKeyStore.password
val tlsCrlIssuerCert = validateAndGetTlsCrlIssuerCert() val tlsCrlIssuerCert = validateAndGetTlsCrlIssuerCert()
if (tlsCrlIssuerCert == null && isTlsCrlIssuerCertRequired()) { 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. 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.") 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 { val requestId = try {
submitOrResumeCertificateSigningRequest(keyPair) submitOrResumeCertificateSigningRequest(keyPair)
@ -110,7 +112,7 @@ open class NetworkRegistrationHelper(private val config: SSLConfiguration,
throw certificateRequestException throw certificateRequestException
} }
validateCertificates(keyPair.public, certificates) validateCertificates(keyPair.public, certificates)
storePrivateKeyWithCertificates(nodeKeyStore, keyPair, certificates, keyAlias) storePrivateKeyWithCertificates(nodeKeyStore, keyPair, certificates, keyAlias, privateKeyPassword)
onSuccess(keyPair, certificates, tlsCrlIssuerCert?.subjectX500Principal?.toX500Name()) onSuccess(keyPair, certificates, tlsCrlIssuerCert?.subjectX500Principal?.toX500Name())
// All done, clean up temp files. // All done, clean up temp files.
requestIdStore.deleteIfExists() 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.") 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. // Save private key and certificate chain to the key store.
nodeKeystore.setPrivateKey(keyAlias, keyPair.private, certificates, keyPassword = config.keyStorePassword) with(nodeKeystore.value) {
nodeKeystore.internal.deleteEntry(SELF_SIGNED_PRIVATE_KEY) setPrivateKey(keyAlias, keyPair.private, certificates, keyPassword = keyPassword)
nodeKeystore.save() internal.deleteEntry(SELF_SIGNED_PRIVATE_KEY)
println("Private key '$keyAlias' and certificate stored in ${config.nodeKeystore}.") 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. // 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. // We use the self sign certificate to store the key temporarily in the keystore while waiting for the request approval.
if (alias !in this) { if (alias !in this) {
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val selfSignCert = X509Utilities.createSelfSignedCACertificate(myLegalName.x500Principal, keyPair) val selfSignCert = X509Utilities.createSelfSignedCACertificate(myLegalName.x500Principal, keyPair)
// Save to the key store. // Save to the key store.
setPrivateKey(alias, keyPair.private, listOf(selfSignCert), keyPassword = privateKeyPassword) with(value) {
save() 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 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))) : 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.myLegalName,
config.emailAddress, config.emailAddress,
certService, certService,
@ -263,7 +271,7 @@ class NodeRegistrationHelper(private val config: NodeConfiguration, certService:
} }
private fun createSSLKeystore(nodeCAKeyPair: KeyPair, certificates: List<X509Certificate>, tlsCertCrlIssuer: X500Name?) { 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.") println("Generating SSL certificate for node messaging service.")
val sslKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val sslKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val sslCert = X509Utilities.createCertificate( val sslCert = X509Utilities.createCertificate(
@ -277,17 +285,17 @@ class NodeRegistrationHelper(private val config: NodeConfiguration, certService:
logger.info("Generated TLS certificate: $sslCert") logger.info("Generated TLS certificate: $sslCert")
setPrivateKey(CORDA_CLIENT_TLS, sslKeyPair.private, listOf(sslCert) + certificates) 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) { private fun createTruststore(rootCertificate: X509Certificate) {
// Save root certificates to trust store. // 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.") println("Generating trust store for corda node.")
// Assumes certificate chain always starts with client certificate and end with root certificate. // Assumes certificate chain always starts with client certificate and end with root certificate.
setCertificate(CORDA_ROOT_CA, rootCertificate) 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? { override fun validateAndGetTlsCrlIssuerCert(): X509Certificate? {
@ -296,8 +304,9 @@ class NodeRegistrationHelper(private val config: NodeConfiguration, certService:
if (principalMatchesCertificatePrincipal(tlsCertCrlIssuer, rootCert)) { if (principalMatchesCertificatePrincipal(tlsCertCrlIssuer, rootCert)) {
return rootCert return rootCert
} }
return if (config.trustStoreFile.exists()) { val trustStore = config.p2pSslOptions.trustStore.getOptional()
findMatchingCertificate(tlsCertCrlIssuer, config.loadTrustStore()) return if (trustStore != null) {
findMatchingCertificate(tlsCertCrlIssuer, trustStore.value)
} else { } else {
null null
} }

View File

@ -22,6 +22,7 @@ import net.corda.nodeapi.internal.crypto.CertificateType
import net.corda.nodeapi.internal.crypto.X509KeyStore import net.corda.nodeapi.internal.crypto.X509KeyStore
import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.testing.core.ALICE_NAME 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.createDevIntermediateCaCertPath
import net.corda.testing.internal.rigorousMock import net.corda.testing.internal.rigorousMock
import org.assertj.core.api.Assertions.* import org.assertj.core.api.Assertions.*
@ -52,10 +53,12 @@ class NetworkRegistrationHelperTest {
val baseDirectory = fs.getPath("/baseDir").createDirectories() val baseDirectory = fs.getPath("/baseDir").createDirectories()
abstract class AbstractNodeConfiguration : NodeConfiguration abstract class AbstractNodeConfiguration : NodeConfiguration
val certificatesDirectory = baseDirectory / "certificates"
config = rigorousMock<AbstractNodeConfiguration>().also { config = rigorousMock<AbstractNodeConfiguration>().also {
doReturn(baseDirectory).whenever(it).baseDirectory doReturn(baseDirectory).whenever(it).baseDirectory
doReturn("trustpass").whenever(it).trustStorePassword doReturn(certificatesDirectory).whenever(it).certificatesDirectory
doReturn("cordacadevpass").whenever(it).keyStorePassword doReturn(CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)).whenever(it).p2pSslOptions
doReturn(CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)).whenever(it).signingCertificateStore
doReturn(nodeLegalName).whenever(it).myLegalName doReturn(nodeLegalName).whenever(it).myLegalName
doReturn("").whenever(it).emailAddress doReturn("").whenever(it).emailAddress
doReturn(null).whenever(it).tlsCertCrlDistPoint doReturn(null).whenever(it).tlsCertCrlDistPoint
@ -71,30 +74,30 @@ class NetworkRegistrationHelperTest {
@Test @Test
fun `successful registration`() { fun `successful registration`() {
assertThat(config.nodeKeystore).doesNotExist() assertThat(config.signingCertificateStore.getOptional()).isNull()
assertThat(config.sslKeystore).doesNotExist() assertThat(config.p2pSslOptions.keyStore.getOptional()).isNull()
assertThat(config.trustStoreFile).doesNotExist() assertThat(config.p2pSslOptions.trustStore.getOptional()).isNull()
val rootAndIntermediateCA = createDevIntermediateCaCertPath().also { saveNetworkTrustStore(it.first.certificate) } val rootAndIntermediateCA = createDevIntermediateCaCertPath().also { saveNetworkTrustStore(it.first.certificate) }
createRegistrationHelper(rootAndIntermediateCA = rootAndIntermediateCA).buildKeystore() createRegistrationHelper(rootAndIntermediateCA = rootAndIntermediateCA).buildKeystore()
val nodeKeystore = config.loadNodeKeyStore() val nodeKeystore = config.signingCertificateStore.get()
val sslKeystore = config.loadSslKeyStore() val sslKeystore = config.p2pSslOptions.keyStore.get()
val trustStore = config.loadTrustStore() val trustStore = config.p2pSslOptions.trustStore.get()
nodeKeystore.run { nodeKeystore.run {
assertFalse(contains(X509Utilities.CORDA_INTERMEDIATE_CA)) assertFalse(contains(X509Utilities.CORDA_INTERMEDIATE_CA))
assertFalse(contains(X509Utilities.CORDA_ROOT_CA)) assertFalse(contains(X509Utilities.CORDA_ROOT_CA))
assertFalse(contains(X509Utilities.CORDA_CLIENT_TLS)) 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 { sslKeystore.run {
assertFalse(contains(X509Utilities.CORDA_CLIENT_CA)) assertFalse(contains(X509Utilities.CORDA_CLIENT_CA))
assertFalse(contains(X509Utilities.CORDA_INTERMEDIATE_CA)) assertFalse(contains(X509Utilities.CORDA_INTERMEDIATE_CA))
assertFalse(contains(X509Utilities.CORDA_ROOT_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) assertThat(nodeTlsCertChain).hasSize(4)
// The TLS cert has the same subject as the node CA cert // The TLS cert has the same subject as the node CA cert
assertThat(CordaX500Name.build(nodeTlsCertChain[0].subjectX500Principal)).isEqualTo(nodeLegalName) assertThat(CordaX500Name.build(nodeTlsCertChain[0].subjectX500Principal)).isEqualTo(nodeLegalName)
@ -104,7 +107,7 @@ class NetworkRegistrationHelperTest {
trustStore.run { trustStore.run {
assertFalse(contains(X509Utilities.CORDA_CLIENT_CA)) assertFalse(contains(X509Utilities.CORDA_CLIENT_CA))
assertFalse(contains(X509Utilities.CORDA_INTERMEDIATE_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 @Test
fun `create service identity cert`() { fun `create service identity cert`() {
assertThat(config.nodeKeystore).doesNotExist() assertThat(config.signingCertificateStore.getOptional()).isNull()
assertThat(config.sslKeystore).doesNotExist() assertThat(config.p2pSslOptions.keyStore.getOptional()).isNull()
assertThat(config.trustStoreFile).doesNotExist() assertThat(config.p2pSslOptions.trustStore.getOptional()).isNull()
val rootAndIntermediateCA = createDevIntermediateCaCertPath().also { saveNetworkTrustStore(it.first.certificate) } val rootAndIntermediateCA = createDevIntermediateCaCertPath().also { saveNetworkTrustStore(it.first.certificate) }
createRegistrationHelper(CertRole.SERVICE_IDENTITY, rootAndIntermediateCA).buildKeystore() createRegistrationHelper(CertRole.SERVICE_IDENTITY, rootAndIntermediateCA).buildKeystore()
val nodeKeystore = config.loadNodeKeyStore() val nodeKeystore = config.signingCertificateStore.get()
assertThat(config.sslKeystore).doesNotExist() assertThat(config.p2pSslOptions.keyStore.getOptional()).isNull()
assertThat(config.trustStoreFile).doesNotExist() assertThat(config.p2pSslOptions.trustStore.getOptional()).isNull()
val serviceIdentityAlias = "${DevIdentityGenerator.DISTRIBUTED_NOTARY_ALIAS_PREFIX}-private-key" 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_ROOT_CA))
assertFalse(contains(X509Utilities.CORDA_CLIENT_TLS)) assertFalse(contains(X509Utilities.CORDA_CLIENT_TLS))
assertFalse(contains(X509Utilities.CORDA_CLIENT_CA)) 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) { return when (certRole) {
CertRole.NODE_CA -> NodeRegistrationHelper(config, certService, NodeRegistrationOption(config.certificatesDirectory / networkRootTrustStoreFileName, networkRootTrustStorePassword)) CertRole.NODE_CA -> NodeRegistrationHelper(config, certService, NodeRegistrationOption(config.certificatesDirectory / networkRootTrustStoreFileName, networkRootTrustStorePassword))
CertRole.SERVICE_IDENTITY -> NetworkRegistrationHelper( CertRole.SERVICE_IDENTITY -> NetworkRegistrationHelper(
config, config.certificatesDirectory,
config.signingCertificateStore,
config.myLegalName, config.myLegalName,
config.emailAddress, config.emailAddress,
certService, certService,

View File

@ -5,7 +5,7 @@ import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigFactory
import com.typesafe.config.ConfigRenderOptions import com.typesafe.config.ConfigRenderOptions
import com.typesafe.config.ConfigValueFactory 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.CordaFuture
import net.corda.core.concurrent.firstOf import net.corda.core.concurrent.firstOf
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
@ -23,6 +23,7 @@ import net.corda.node.NodeRegistrationOption
import net.corda.node.VersionInfo import net.corda.node.VersionInfo
import net.corda.node.internal.Node import net.corda.node.internal.Node
import net.corda.node.internal.NodeWithInfo import net.corda.node.internal.NodeWithInfo
import net.corda.node.internal.clientSslOptionsCompatibleWith
import net.corda.node.services.Permissions import net.corda.node.services.Permissions
import net.corda.node.services.config.* import net.corda.node.services.config.*
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
@ -160,7 +161,8 @@ class DriverDSLImpl(
private fun establishRpc(config: NodeConfig, processDeathFuture: CordaFuture<out Process>): CordaFuture<CordaRPCOps> { private fun establishRpc(config: NodeConfig, processDeathFuture: CordaFuture<out Process>): CordaFuture<CordaRPCOps> {
val rpcAddress = config.corda.rpcOptions.address 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") { val connectionFuture = poll(executorService, "RPC connection") {
try { try {
config.corda.rpcUsers[0].run { client.start(username, password) } config.corda.rpcUsers[0].run { client.start(username, password) }
@ -798,8 +800,13 @@ class DriverDSLImpl(
config += "rpcUsers" to configuration.toConfig().getValue("rpcUsers") config += "rpcUsers" to configuration.toConfig().getValue("rpcUsers")
config += "useHTTPS" to useHTTPS config += "useHTTPS" to useHTTPS
config += "baseDirectory" to configuration.baseDirectory.toAbsolutePath().toString() 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 return config
} }

View File

@ -53,6 +53,7 @@ import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.nodeapi.internal.persistence.DatabaseConfig
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.driver.TestCorDapp import net.corda.testing.driver.TestCorDapp
import net.corda.testing.internal.stubs.CertificateStoreStubs
import net.corda.testing.internal.rigorousMock import net.corda.testing.internal.rigorousMock
import net.corda.testing.internal.setGlobalSerialization import net.corda.testing.internal.setGlobalSerialization
import net.corda.testing.internal.testThreadFactory 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 { private fun createNodeImpl(parameters: InternalMockNodeParameters, nodeFactory: (MockNodeArgs, CordappLoader?) -> MockNode, start: Boolean): MockNode {
val id = parameters.forcedID ?: nextNodeId++ val id = parameters.forcedID ?: nextNodeId++
val config = mockNodeConfiguration().also { val baseDirectory = baseDirectory(id)
doReturn(baseDirectory(id).createDirectories()).whenever(it).baseDirectory 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(parameters.legalName ?: CordaX500Name("Mock Company $id", "London", "GB")).whenever(it).myLegalName
doReturn(makeTestDataSourceProperties("node_${id}_net_$networkId")).whenever(it).dataSourceProperties doReturn(makeTestDataSourceProperties("node_${id}_net_$networkId")).whenever(it).dataSourceProperties
doReturn(emptyList<SecureHash>()).whenever(it).extraNetworkMapKeys doReturn(emptyList<SecureHash>()).whenever(it).extraNetworkMapKeys
@ -561,12 +565,17 @@ abstract class MessagingServiceSpy {
abstract fun send(message: Message, target: MessageRecipients, sequenceKey: Any) abstract fun send(message: Message, target: MessageRecipients, sequenceKey: Any)
} }
private fun mockNodeConfiguration(): NodeConfiguration { private fun mockNodeConfiguration(certificatesDirectory: Path): NodeConfiguration {
@DoNotImplement @DoNotImplement
abstract class AbstractNodeConfiguration : NodeConfiguration abstract class AbstractNodeConfiguration : NodeConfiguration
val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)
val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)
return rigorousMock<AbstractNodeConfiguration>().also { return rigorousMock<AbstractNodeConfiguration>().also {
doReturn("cordacadevpass").whenever(it).keyStorePassword doReturn(certificatesDirectory.createDirectories()).whenever(it).certificatesDirectory
doReturn("trustpass").whenever(it).trustStorePassword doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
doReturn(signingCertificateStore).whenever(it).signingCertificateStore
doReturn(emptyList<User>()).whenever(it).rpcUsers doReturn(emptyList<User>()).whenever(it).rpcUsers
doReturn(null).whenever(it).notary doReturn(null).whenever(it).notary
doReturn(DatabaseConfig()).whenever(it).database doReturn(DatabaseConfig()).whenever(it).database

View File

@ -21,8 +21,8 @@ import net.corda.core.utilities.seconds
import net.corda.node.internal.security.RPCSecurityManagerImpl import net.corda.node.internal.security.RPCSecurityManagerImpl
import net.corda.node.services.messaging.RPCServer import net.corda.node.services.messaging.RPCServer
import net.corda.node.services.messaging.RPCServerConfiguration import net.corda.node.services.messaging.RPCServerConfiguration
import net.corda.nodeapi.ArtemisTcpTransport
import net.corda.nodeapi.RPCApi import net.corda.nodeapi.RPCApi
import net.corda.nodeapi.internal.InternalArtemisTcpTransport
import net.corda.serialization.internal.AMQP_RPC_CLIENT_CONTEXT import net.corda.serialization.internal.AMQP_RPC_CLIENT_CONTEXT
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.core.MAX_MESSAGE_SIZE import net.corda.testing.core.MAX_MESSAGE_SIZE
@ -220,14 +220,14 @@ data class RPCDriverDSL(
bindingsDirectory = "$artemisDir/bindings" bindingsDirectory = "$artemisDir/bindings"
journalDirectory = "$artemisDir/journal" journalDirectory = "$artemisDir/journal"
largeMessagesDirectory = "$artemisDir/large-messages" largeMessagesDirectory = "$artemisDir/large-messages"
acceptorConfigurations = setOf(ArtemisTcpTransport.rpcAcceptorTcpTransport(hostAndPort, null)) acceptorConfigurations = setOf(InternalArtemisTcpTransport.rpcAcceptorTcpTransport(hostAndPort, null))
configureCommonSettings(maxFileSize, maxBufferedBytesPerClient) configureCommonSettings(maxFileSize, maxBufferedBytesPerClient)
} }
} }
val inVmClientTransportConfiguration = TransportConfiguration(InVMConnectorFactory::class.java.name) val inVmClientTransportConfiguration = TransportConfiguration(InVMConnectorFactory::class.java.name)
fun createNettyClientTransportConfiguration(hostAndPort: NetworkHostAndPort): TransportConfiguration { 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 configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT
): CordaFuture<I> { ): CordaFuture<I> {
return driverDSL.executorService.fork { 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) val connection = client.start(rpcOpsClass, username, password, externalTrace)
driverDSL.shutdownManager.registerShutdown { driverDSL.shutdownManager.registerShutdown {
connection.close() connection.close()

View File

@ -4,7 +4,9 @@ import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.createFile import net.corda.core.internal.createFile
import net.corda.core.internal.deleteIfExists import net.corda.core.internal.deleteIfExists
import net.corda.core.internal.div 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 net.corda.nodeapi.internal.crypto.*
import org.apache.commons.io.FileUtils import org.apache.commons.io.FileUtils
import sun.security.tools.keytool.CertAndKeyGen 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 keyStoreFile = keyStore.toTemporaryFile("sslkeystore", directory = directory)
val trustStoreFile = trustStore.toTemporaryFile("truststore", directory = directory) val trustStoreFile = trustStore.toTemporaryFile("truststore", directory = directory)
val sslConfiguration = sslConfiguration(directory) val sslConfiguration = sslConfiguration(keyStoreFile, trustStoreFile)
return object : AutoClosableSSLConfiguration { return object : AutoClosableSSLConfiguration {
override val value = sslConfiguration override val value = sslConfiguration
@ -77,16 +79,16 @@ class KeyStores(val keyStore: UnsafeKeyStore, val trustStore: UnsafeKeyStore) {
} }
} }
data class TestSslOptions(override val certificatesDirectory: Path, private fun sslConfiguration(keyStoreFile: TemporaryFile, trustStoreFile: TemporaryFile): MutualSslConfiguration {
override val keyStorePassword: String,
override val trustStorePassword: String,
override val crlCheckSoftFail: Boolean) : SSLConfiguration
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 { interface AutoClosableSSLConfiguration : AutoCloseable {
val value: SSLConfiguration val value: MutualSslConfiguration
} }
typealias KeyStoreEntry = Pair<String, UnsafeCertificate> typealias KeyStoreEntry = Pair<String, UnsafeCertificate>
@ -189,7 +191,7 @@ private fun newKeyStore(type: String, password: String): KeyStore {
return 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) val serverDir = Files.createTempDirectory(null)
FileUtils.forceDeleteOnExit(serverDir.toFile()) FileUtils.forceDeleteOnExit(serverDir.toFile())

View File

@ -9,15 +9,15 @@ import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.node.services.config.configureDevKeyAndTrustStores
import net.corda.nodeapi.BrokerRpcSslOptions import net.corda.nodeapi.BrokerRpcSslOptions
import net.corda.nodeapi.internal.config.SSLConfiguration import net.corda.nodeapi.internal.config.MutualSslConfiguration
import net.corda.nodeapi.internal.createDevKeyStores import net.corda.nodeapi.internal.registerDevP2pCertificates
import net.corda.nodeapi.internal.createDevNodeCa import net.corda.nodeapi.internal.createDevNodeCa
import net.corda.nodeapi.internal.crypto.* import net.corda.nodeapi.internal.crypto.*
import net.corda.nodeapi.internal.loadDevCaTrustStore
import net.corda.serialization.internal.amqp.AMQP_ENABLED import net.corda.serialization.internal.amqp.AMQP_ENABLED
import net.corda.testing.internal.stubs.CertificateStoreStubs
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.security.KeyPair 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") loggerFor<T>().info("Ignoring AMQP specific test, reason: $reason")
} }
fun configureTestSSL(legalName: CordaX500Name): SSLConfiguration { fun configureTestSSL(legalName: CordaX500Name): MutualSslConfiguration {
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
init { val certificatesDirectory = Files.createTempDirectory("certs")
configureDevKeyAndTrustStores(legalName) 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") 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. * 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. * 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 NodeInfo.chooseIdentity(): Party = chooseIdentityAndCert().party
fun createNodeSslConfig(path: Path, name: CordaX500Name = CordaX500Name("MegaCorp", "London", "GB")): SSLConfiguration { fun p2pSslOptions(path: Path, name: CordaX500Name = CordaX500Name("MegaCorp", "London", "GB")): MutualSslConfiguration {
val sslConfig = object : SSLConfiguration { val sslConfig = CertificateStoreStubs.P2P.withCertificatesDirectory(path, keyStorePassword = "serverstorepass")
override val crlCheckSoftFail = true
override val certificatesDirectory = path
override val keyStorePassword = "serverstorepass"
override val trustStorePassword = "trustpass"
}
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath() val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
sslConfig.createDevKeyStores(name, rootCa.certificate, intermediateCa) sslConfig.keyStore.get(true).registerDevP2pCertificates(name, rootCa.certificate, intermediateCa)
val trustStore = loadOrCreateKeyStore(sslConfig.trustStoreFile, sslConfig.trustStorePassword) val trustStore = sslConfig.trustStore.get(true)
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCa.certificate) trustStore[X509Utilities.CORDA_ROOT_CA] = rootCa.certificate
trustStore.save(sslConfig.trustStoreFile, sslConfig.trustStorePassword)
return sslConfig return sslConfig
} }

View File

@ -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)
}
}
}
}
}

View File

@ -131,7 +131,7 @@ class InteractiveShellIntegrationTest {
driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList())) { driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList())) {
startNode().getOrThrow().use { node -> startNode().getOrThrow().use { node ->
val conf = (node as NodeHandleInternal).configuration.toShellConfig() val conf = (node as NodeHandleInternal).configuration.toShellConfig()
InteractiveShell.startShellInternal(conf) InteractiveShell.startShell(conf)
assertThatThrownBy { InteractiveShell.nodeInfo() }.isInstanceOf(ActiveMQSecurityException::class.java) assertThatThrownBy { InteractiveShell.nodeInfo() }.isInstanceOf(ActiveMQSecurityException::class.java)
} }
} }

View File

@ -9,7 +9,6 @@ import net.corda.client.jackson.StringToMethodCallParser
import net.corda.client.rpc.CordaRPCClientConfiguration import net.corda.client.rpc.CordaRPCClientConfiguration
import net.corda.client.rpc.CordaRPCConnection import net.corda.client.rpc.CordaRPCConnection
import net.corda.client.rpc.PermissionException import net.corda.client.rpc.PermissionException
import net.corda.client.rpc.internal.createCordaRPCClientWithInternalSslAndClassLoader
import net.corda.client.rpc.internal.createCordaRPCClientWithSslAndClassLoader import net.corda.client.rpc.internal.createCordaRPCClientWithSslAndClassLoader
import net.corda.core.CordaException import net.corda.core.CordaException
import net.corda.core.concurrent.CordaFuture import net.corda.core.concurrent.CordaFuture
@ -93,24 +92,6 @@ object InteractiveShell {
_startShell(configuration, classLoader) _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) { private fun _startShell(configuration: ShellConfiguration, classLoader: ClassLoader? = null) {
shellConfiguration = configuration shellConfiguration = configuration
InteractiveShell.classLoader = classLoader InteractiveShell.classLoader = classLoader

View File

@ -2,7 +2,6 @@ package net.corda.tools.shell
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.messaging.ClientRpcSslOptions import net.corda.core.messaging.ClientRpcSslOptions
import net.corda.nodeapi.internal.config.SSLConfiguration
import java.nio.file.Path import java.nio.file.Path
data class ShellConfiguration( data class ShellConfiguration(
@ -12,7 +11,6 @@ data class ShellConfiguration(
var password: String = "", var password: String = "",
val hostAndPort: NetworkHostAndPort, val hostAndPort: NetworkHostAndPort,
val ssl: ClientRpcSslOptions? = null, val ssl: ClientRpcSslOptions? = null,
val nodeSslConfig: SSLConfiguration? = null,
val sshdPort: Int? = null, val sshdPort: Int? = null,
val sshHostKeyDirectory: Path? = null, val sshHostKeyDirectory: Path? = null,
val noLocalShell: Boolean = false) { val noLocalShell: Boolean = false) {

View File

@ -1,8 +1,8 @@
package net.corda.webserver package net.corda.webserver
import com.typesafe.config.Config import com.typesafe.config.Config
import net.corda.core.internal.div
import net.corda.core.utilities.NetworkHostAndPort 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.User
import net.corda.nodeapi.internal.config.getValue import net.corda.nodeapi.internal.config.getValue
import net.corda.nodeapi.internal.config.parseAs 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. * [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 { class WebServerConfig(val baseDirectory: Path, val config: Config) {
override val keyStorePassword: String by config
override val trustStorePassword: String by config val keyStorePath: String by config
override val crlCheckSoftFail: Boolean by config val keyStorePassword: String by config
val trustStorePath: String by config
val trustStorePassword: String by config
val useHTTPS: Boolean by config val useHTTPS: Boolean by config
val myLegalName: String by config val myLegalName: String by config
val rpcAddress: NetworkHostAndPort by lazy { val rpcAddress: NetworkHostAndPort by lazy {

View File

@ -66,10 +66,10 @@ class NodeWebServer(val config: WebServerConfig) {
httpsConfiguration.outputBufferSize = 32768 httpsConfiguration.outputBufferSize = 32768
httpsConfiguration.addCustomizer(SecureRequestCustomizer()) httpsConfiguration.addCustomizer(SecureRequestCustomizer())
val sslContextFactory = SslContextFactory() val sslContextFactory = SslContextFactory()
sslContextFactory.keyStorePath = config.sslKeystore.toString() sslContextFactory.keyStorePath = config.keyStorePath
sslContextFactory.setKeyStorePassword(config.keyStorePassword) sslContextFactory.setKeyStorePassword(config.keyStorePassword)
sslContextFactory.setKeyManagerPassword(config.keyStorePassword) sslContextFactory.setKeyManagerPassword(config.keyStorePassword)
sslContextFactory.setTrustStorePath(config.trustStoreFile.toString()) sslContextFactory.setTrustStorePath(config.trustStorePath)
sslContextFactory.setTrustStorePassword(config.trustStorePassword) sslContextFactory.setTrustStorePassword(config.trustStorePassword)
sslContextFactory.setExcludeProtocols("SSL.*", "TLSv1", "TLSv1.1") sslContextFactory.setExcludeProtocols("SSL.*", "TLSv1", "TLSv1.1")
sslContextFactory.setIncludeProtocols("TLSv1.2") sslContextFactory.setIncludeProtocols("TLSv1.2")