mirror of
https://github.com/corda/corda.git
synced 2025-04-13 22:23:31 +00:00
Merge pull request #1368 from corda/os_ent_merges/CORDA-1937
OS -> ENT merges: CORDA-1937
This commit is contained in:
commit
b4a64625fc
@ -17,12 +17,12 @@ import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.node.services.messaging.ArtemisMessagingServer
|
||||
import net.corda.nodeapi.internal.*
|
||||
import net.corda.nodeapi.internal.bridging.BridgeControl
|
||||
import net.corda.nodeapi.internal.config.NodeSSLConfiguration
|
||||
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.network.NetworkParametersCopier
|
||||
import net.corda.testing.core.*
|
||||
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.SimpleString
|
||||
import org.apache.curator.test.TestingServer
|
||||
@ -67,22 +67,20 @@ class BridgeSmokeTest {
|
||||
|
||||
@Test
|
||||
fun `Run full features bridge from jar to ensure everything works`() {
|
||||
val artemisConfig = object : NodeSSLConfiguration {
|
||||
override val baseDirectory: Path = tempFolder.root.toPath()
|
||||
override val keyStorePassword: String = "cordacadevpass"
|
||||
override val trustStorePassword: String = "trustpass"
|
||||
override val crlCheckSoftFail: Boolean = true
|
||||
}
|
||||
|
||||
val baseDirectory = tempFolder.root.toPath().createDirectories()
|
||||
val artemisConfig = CertificateStoreStubs.P2P.withBaseDirectory(baseDirectory)
|
||||
|
||||
artemisConfig.createBridgeKeyStores(DUMMY_BANK_A_NAME)
|
||||
copyBridgeResource("corda-firewall.jar")
|
||||
copyBridgeResource("firewall.conf")
|
||||
createNetworkParams(tempFolder.root.toPath())
|
||||
createNetworkParams(baseDirectory)
|
||||
val (artemisServer, artemisClient) = createArtemis()
|
||||
val zkServer = TestingServer(11105, false)
|
||||
try {
|
||||
installBridgeControlResponder(artemisClient)
|
||||
zkServer.start()
|
||||
val bridge = startBridge(tempFolder.root.toPath())
|
||||
val bridge = startBridge(baseDirectory)
|
||||
waitForBridge(bridge)
|
||||
} finally {
|
||||
zkServer.close()
|
||||
@ -115,18 +113,18 @@ class BridgeSmokeTest {
|
||||
copier.install(baseDirectory)
|
||||
}
|
||||
|
||||
private fun SSLConfiguration.createBridgeKeyStores(legalName: CordaX500Name,
|
||||
rootCert: X509Certificate = DEV_ROOT_CA.certificate,
|
||||
intermediateCa: CertificateAndKeyPair = DEV_INTERMEDIATE_CA) {
|
||||
private fun MutualSslConfiguration.createBridgeKeyStores(legalName: CordaX500Name,
|
||||
rootCert: X509Certificate = DEV_ROOT_CA.certificate,
|
||||
intermediateCa: CertificateAndKeyPair = DEV_INTERMEDIATE_CA) {
|
||||
|
||||
certificatesDirectory.createDirectories()
|
||||
if (!trustStoreFile.exists()) {
|
||||
loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/$DEV_CA_TRUST_STORE_FILE"), DEV_CA_TRUST_STORE_PASS).save(trustStoreFile, trustStorePassword)
|
||||
if (!trustStore.path.exists()) {
|
||||
val trustStore = trustStore.get(true)
|
||||
loadDevCaTrustStore().copyTo(trustStore)
|
||||
}
|
||||
|
||||
val (nodeCaCert, nodeCaKeyPair) = createDevNodeCa(intermediateCa, legalName)
|
||||
|
||||
val sslKeyStore = loadSslKeyStore(createNew = true)
|
||||
val sslKeyStore = keyStore.get(createNew = true)
|
||||
sslKeyStore.update {
|
||||
val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, legalName.x500Principal, tlsKeyPair.public)
|
||||
@ -203,17 +201,23 @@ class BridgeSmokeTest {
|
||||
}
|
||||
|
||||
private fun createArtemis(): Pair<ArtemisMessagingServer, ArtemisMessagingClient> {
|
||||
val baseDirectory = tempFolder.root.toPath()
|
||||
val certificatesDirectory = baseDirectory / "certificates"
|
||||
val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)
|
||||
val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)
|
||||
|
||||
val artemisConfig = rigorousMock<AbstractNodeConfiguration>().also {
|
||||
doReturn(tempFolder.root.toPath()).whenever(it).baseDirectory
|
||||
doReturn(baseDirectory).whenever(it).baseDirectory
|
||||
doReturn(certificatesDirectory).whenever(it).certificatesDirectory
|
||||
doReturn(ALICE_NAME).whenever(it).myLegalName
|
||||
doReturn("trustpass").whenever(it).trustStorePassword
|
||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||
doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
|
||||
doReturn(signingCertificateStore).whenever(it).signingCertificateStore
|
||||
doReturn(NetworkHostAndPort("localhost", 11005)).whenever(it).p2pAddress
|
||||
doReturn(null).whenever(it).jmxMonitoringHttpPort
|
||||
doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000), externalBridge = true)).whenever(it).enterpriseConfiguration
|
||||
}
|
||||
val artemisServer = ArtemisMessagingServer(artemisConfig, NetworkHostAndPort("0.0.0.0", 11005), MAX_MESSAGE_SIZE)
|
||||
val artemisClient = ArtemisMessagingClient(artemisConfig, NetworkHostAndPort("localhost", 11005), MAX_MESSAGE_SIZE)
|
||||
val artemisClient = ArtemisMessagingClient(artemisConfig.p2pSslOptions, NetworkHostAndPort("localhost", 11005), MAX_MESSAGE_SIZE)
|
||||
artemisServer.start()
|
||||
artemisClient.start()
|
||||
return Pair(artemisServer, artemisClient)
|
||||
|
@ -28,6 +28,7 @@ import net.corda.testing.core.DUMMY_BANK_A_NAME
|
||||
import net.corda.testing.core.MAX_MESSAGE_SIZE
|
||||
import net.corda.testing.core.SerializationEnvironmentRule
|
||||
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.SimpleString
|
||||
import org.apache.curator.test.TestingServer
|
||||
@ -387,61 +388,76 @@ class BridgeIntegrationTest {
|
||||
|
||||
|
||||
private fun createArtemis(): Pair<ArtemisMessagingServer, ArtemisMessagingClient> {
|
||||
val baseDirectory = tempFolder.root.toPath()
|
||||
val certificatesDirectory = baseDirectory / "certificates"
|
||||
val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)
|
||||
val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)
|
||||
val artemisConfig = rigorousMock<AbstractNodeConfiguration>().also {
|
||||
doReturn(tempFolder.root.toPath()).whenever(it).baseDirectory
|
||||
doReturn(baseDirectory).whenever(it).baseDirectory
|
||||
doReturn(certificatesDirectory).whenever(it).certificatesDirectory
|
||||
doReturn(ALICE_NAME).whenever(it).myLegalName
|
||||
doReturn("trustpass").whenever(it).trustStorePassword
|
||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||
doReturn(signingCertificateStore).whenever(it).signingCertificateStore
|
||||
doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
|
||||
doReturn(NetworkHostAndPort("localhost", 11005)).whenever(it).p2pAddress
|
||||
doReturn(null).whenever(it).jmxMonitoringHttpPort
|
||||
doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000), externalBridge = true)).whenever(it).enterpriseConfiguration
|
||||
}
|
||||
val artemisServer = ArtemisMessagingServer(artemisConfig, NetworkHostAndPort("0.0.0.0", 11005), MAX_MESSAGE_SIZE)
|
||||
val artemisClient = ArtemisMessagingClient(artemisConfig, NetworkHostAndPort("localhost", 11005), MAX_MESSAGE_SIZE)
|
||||
val artemisClient = ArtemisMessagingClient(artemisConfig.p2pSslOptions, NetworkHostAndPort("localhost", 11005), MAX_MESSAGE_SIZE)
|
||||
artemisServer.start()
|
||||
artemisClient.start()
|
||||
return Pair(artemisServer, artemisClient)
|
||||
}
|
||||
|
||||
private fun createArtemis2(): Pair<ArtemisMessagingServer, ArtemisMessagingClient> {
|
||||
val originalCertsFolderPath = tempFolder.root.toPath() / "certificates"
|
||||
val folderPath = tempFolder.root.toPath() / "artemis2"
|
||||
val baseDirectory = tempFolder.root.toPath()
|
||||
val originalCertsFolderPath = baseDirectory / "certificates"
|
||||
val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(originalCertsFolderPath)
|
||||
val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(originalCertsFolderPath)
|
||||
|
||||
val folderPath = baseDirectory / "artemis2"
|
||||
val newCertsFolderPath = folderPath / "certificates"
|
||||
newCertsFolderPath.createDirectories()
|
||||
(originalCertsFolderPath / "truststore.jks").copyToDirectory(newCertsFolderPath)
|
||||
(originalCertsFolderPath / "sslkeystore.jks").copyToDirectory(newCertsFolderPath)
|
||||
val artemisConfig = rigorousMock<AbstractNodeConfiguration>().also {
|
||||
doReturn(folderPath).whenever(it).baseDirectory
|
||||
doReturn(originalCertsFolderPath).whenever(it).certificatesDirectory
|
||||
doReturn(ALICE_NAME).whenever(it).myLegalName
|
||||
doReturn("trustpass").whenever(it).trustStorePassword
|
||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||
doReturn(signingCertificateStore).whenever(it).signingCertificateStore
|
||||
doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
|
||||
doReturn(NetworkHostAndPort("localhost", 12005)).whenever(it).p2pAddress
|
||||
doReturn(null).whenever(it).jmxMonitoringHttpPort
|
||||
doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000), externalBridge = true)).whenever(it).enterpriseConfiguration
|
||||
}
|
||||
val artemisServer = ArtemisMessagingServer(artemisConfig, NetworkHostAndPort("0.0.0.0", 12005), MAX_MESSAGE_SIZE)
|
||||
val artemisClient = ArtemisMessagingClient(artemisConfig, NetworkHostAndPort("localhost", 12005), MAX_MESSAGE_SIZE)
|
||||
val artemisClient = ArtemisMessagingClient(artemisConfig.p2pSslOptions, NetworkHostAndPort("localhost", 12005), MAX_MESSAGE_SIZE)
|
||||
return Pair(artemisServer, artemisClient)
|
||||
}
|
||||
|
||||
private fun createDummyPeerArtemis(): Pair<ArtemisMessagingServer, ArtemisMessagingClient> {
|
||||
val originalCertsFolderPath = tempFolder.root.toPath() / "certificates"
|
||||
val folderPath = tempFolder.root.toPath() / "artemis3"
|
||||
val baseDirectory = tempFolder.root.toPath()
|
||||
val originalCertsFolderPath = baseDirectory / "certificates"
|
||||
val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(originalCertsFolderPath)
|
||||
val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(originalCertsFolderPath)
|
||||
|
||||
val folderPath = baseDirectory / "artemis3"
|
||||
val newCertsFolderPath = folderPath / "certificates"
|
||||
newCertsFolderPath.createDirectories()
|
||||
(originalCertsFolderPath / "truststore.jks").copyToDirectory(newCertsFolderPath)
|
||||
(originalCertsFolderPath / "sslkeystore.jks").copyToDirectory(newCertsFolderPath)
|
||||
val artemisConfig = rigorousMock<AbstractNodeConfiguration>().also {
|
||||
doReturn(folderPath).whenever(it).baseDirectory
|
||||
doReturn(newCertsFolderPath).whenever(it).certificatesDirectory
|
||||
doReturn(DUMMY_BANK_A_NAME).whenever(it).myLegalName
|
||||
doReturn("trustpass").whenever(it).trustStorePassword
|
||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||
doReturn(signingCertificateStore).whenever(it).signingCertificateStore
|
||||
doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
|
||||
doReturn(NetworkHostAndPort("localhost", 7890)).whenever(it).p2pAddress
|
||||
doReturn(null).whenever(it).jmxMonitoringHttpPort
|
||||
doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000), externalBridge = true)).whenever(it).enterpriseConfiguration
|
||||
}
|
||||
val artemisServer = ArtemisMessagingServer(artemisConfig, NetworkHostAndPort("0.0.0.0", 7890), MAX_MESSAGE_SIZE)
|
||||
val artemisClient = ArtemisMessagingClient(artemisConfig, NetworkHostAndPort("localhost", 7890), MAX_MESSAGE_SIZE)
|
||||
val artemisClient = ArtemisMessagingClient(artemisConfig.p2pSslOptions, NetworkHostAndPort("localhost", 7890), MAX_MESSAGE_SIZE)
|
||||
artemisServer.start()
|
||||
artemisClient.start()
|
||||
artemisClient.started!!.session.createQueue(SimpleString("${P2P_PREFIX}12345"), RoutingType.ANYCAST, SimpleString("${P2P_PREFIX}12345"), true)
|
||||
|
@ -12,6 +12,7 @@ import net.corda.core.internal.div
|
||||
import net.corda.core.internal.readAll
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEERS_PREFIX
|
||||
import net.corda.nodeapi.internal.config.CertificateStore
|
||||
import net.corda.nodeapi.internal.crypto.X509KeyStore
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
import net.corda.nodeapi.internal.protonwrapper.messages.MessageStatus
|
||||
@ -25,7 +26,6 @@ import org.junit.Assert.assertArrayEquals
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import java.security.KeyStore
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class AMQPListenerTest {
|
||||
@ -58,14 +58,14 @@ class AMQPListenerTest {
|
||||
assertEquals(true, stateFollower.next())
|
||||
assertEquals(true, amqpListenerService.active)
|
||||
assertEquals(false, serverListening("localhost", 10005))
|
||||
val keyStoreBytes = bridgeConfig.sslKeystore.readAll()
|
||||
val trustStoreBytes = bridgeConfig.trustStoreFile.readAll()
|
||||
val keyStoreBytes = bridgeConfig.p2pSslOptions.keyStore.path.readAll()
|
||||
val trustStoreBytes = bridgeConfig.p2pSslOptions.trustStore.path.readAll()
|
||||
// start listening
|
||||
amqpListenerService.provisionKeysAndActivate(keyStoreBytes,
|
||||
bridgeConfig.keyStorePassword.toCharArray(),
|
||||
bridgeConfig.keyStorePassword.toCharArray(),
|
||||
bridgeConfig.p2pSslOptions.keyStore.password.toCharArray(),
|
||||
bridgeConfig.p2pSslOptions.keyStore.password.toCharArray(),
|
||||
trustStoreBytes,
|
||||
bridgeConfig.trustStorePassword.toCharArray())
|
||||
bridgeConfig.p2pSslOptions.trustStore.password.toCharArray())
|
||||
// Fire lots of activity to prove we are good
|
||||
assertEquals(TestAuditService.AuditEvent.STATUS_CHANGE, auditFollower.next())
|
||||
assertEquals(true, amqpListenerService.active)
|
||||
@ -76,12 +76,11 @@ class AMQPListenerTest {
|
||||
assertEquals(TestAuditService.AuditEvent.FAILED_CONNECTION, auditFollower.next())
|
||||
val clientConfig = createAndLoadConfigFromResource(tempFolder.root.toPath() / "client", configResource)
|
||||
clientConfig.createBridgeKeyStores(DUMMY_BANK_B_NAME)
|
||||
val clientKeyStore = clientConfig.loadSslKeyStore().internal
|
||||
val clientTrustStore = clientConfig.loadTrustStore().internal
|
||||
val clientKeyStore = clientConfig.p2pSslOptions.keyStore.get()
|
||||
val clientTrustStore = clientConfig.p2pSslOptions.trustStore.get()
|
||||
val amqpConfig = object : AMQPConfiguration {
|
||||
override val keyStore: KeyStore = clientKeyStore
|
||||
override val keyStorePrivateKeyPassword: CharArray = clientConfig.keyStorePassword.toCharArray()
|
||||
override val trustStore: KeyStore = clientTrustStore
|
||||
override val keyStore = clientKeyStore
|
||||
override val trustStore = clientTrustStore
|
||||
override val maxMessageSize: Int = maxMessageSize
|
||||
override val trace: Boolean = true
|
||||
}
|
||||
@ -134,26 +133,28 @@ class AMQPListenerTest {
|
||||
val amqpListenerService = BridgeAMQPListenerServiceImpl(bridgeConfig, maxMessageSize, auditService)
|
||||
amqpListenerService.start()
|
||||
auditService.start()
|
||||
val keyStoreBytes = bridgeConfig.sslKeystore.readAll()
|
||||
val trustStoreBytes = bridgeConfig.trustStoreFile.readAll()
|
||||
val keyStoreBytes = bridgeConfig.p2pSslOptions.keyStore.path.readAll()
|
||||
val trustStoreBytes = bridgeConfig.p2pSslOptions.trustStore.path.readAll()
|
||||
// start listening
|
||||
amqpListenerService.provisionKeysAndActivate(keyStoreBytes,
|
||||
bridgeConfig.keyStorePassword.toCharArray(),
|
||||
bridgeConfig.keyStorePassword.toCharArray(),
|
||||
bridgeConfig.p2pSslOptions.keyStore.password.toCharArray(),
|
||||
bridgeConfig.p2pSslOptions.keyStore.password.toCharArray(),
|
||||
trustStoreBytes,
|
||||
bridgeConfig.trustStorePassword.toCharArray())
|
||||
bridgeConfig.p2pSslOptions.trustStore.password.toCharArray())
|
||||
val connectionFollower = amqpListenerService.onConnection.toBlocking().iterator
|
||||
val auditFollower = auditService.onAuditEvent.toBlocking().iterator
|
||||
val clientKeys = Crypto.generateKeyPair(ECDSA_SECP256R1_SHA256)
|
||||
val clientCert = X509Utilities.createSelfSignedCACertificate(ALICE_NAME.x500Principal, clientKeys)
|
||||
val clientKeyStore = X509KeyStore("password")
|
||||
clientKeyStore.setPrivateKey("TLS_CERT", clientKeys.private, listOf(clientCert))
|
||||
val clientTrustStore = X509KeyStore("password")
|
||||
clientTrustStore.setCertificate("TLS_ROOT", clientCert)
|
||||
val clientKeyStoreRaw = X509KeyStore("password")
|
||||
clientKeyStoreRaw.setPrivateKey("TLS_CERT", clientKeys.private, listOf(clientCert))
|
||||
val clientKeyStore = CertificateStore.of(clientKeyStoreRaw, "password")
|
||||
|
||||
val clientTrustStoreRaw = X509KeyStore("password")
|
||||
clientTrustStoreRaw.setCertificate("TLS_ROOT", clientCert)
|
||||
val clientTrustStore = CertificateStore.of(clientTrustStoreRaw, "password")
|
||||
val amqpConfig = object : AMQPConfiguration {
|
||||
override val keyStore: KeyStore = clientKeyStore.internal
|
||||
override val keyStorePrivateKeyPassword: CharArray = "password".toCharArray()
|
||||
override val trustStore: KeyStore = clientTrustStore.internal
|
||||
override val keyStore = clientKeyStore
|
||||
override val trustStore = clientTrustStore
|
||||
override val maxMessageSize: Int = maxMessageSize
|
||||
override val trace: Boolean = true
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import net.corda.bridge.createAndLoadConfigFromResource
|
||||
import net.corda.bridge.createBridgeKeyStores
|
||||
import net.corda.bridge.createNetworkParams
|
||||
import net.corda.bridge.services.artemis.BridgeArtemisConnectionServiceImpl
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.node.services.config.EnterpriseConfiguration
|
||||
import net.corda.node.services.config.MutualExclusionConfiguration
|
||||
@ -15,6 +16,7 @@ import net.corda.testing.core.DUMMY_BANK_A_NAME
|
||||
import net.corda.testing.core.MAX_MESSAGE_SIZE
|
||||
import net.corda.testing.core.SerializationEnvironmentRule
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
import net.corda.testing.internal.stubs.CertificateStoreStubs
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
@ -84,11 +86,17 @@ class ArtemisConnectionTest {
|
||||
|
||||
|
||||
private fun createArtemis(): ArtemisMessagingServer {
|
||||
|
||||
val baseDirectory = tempFolder.root.toPath()
|
||||
val certificatesDirectory = baseDirectory / "certificates"
|
||||
val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)
|
||||
val p2pSslOptions = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)
|
||||
val artemisConfig = rigorousMock<AbstractNodeConfiguration>().also {
|
||||
doReturn(tempFolder.root.toPath()).whenever(it).baseDirectory
|
||||
doReturn(baseDirectory).whenever(it).baseDirectory
|
||||
doReturn(certificatesDirectory).whenever(it).certificatesDirectory
|
||||
doReturn(DUMMY_BANK_A_NAME).whenever(it).myLegalName
|
||||
doReturn("trustpass").whenever(it).trustStorePassword
|
||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||
doReturn(signingCertificateStore).whenever(it).signingCertificateStore
|
||||
doReturn(p2pSslOptions).whenever(it).p2pSslOptions
|
||||
doReturn(NetworkHostAndPort("localhost", 11005)).whenever(it).p2pAddress
|
||||
doReturn(null).whenever(it).jmxMonitoringHttpPort
|
||||
doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000), externalBridge = true)).whenever(it).enterpriseConfiguration
|
||||
|
@ -2,8 +2,7 @@ package net.corda.bridge.services.api
|
||||
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.nodeapi.internal.config.NodeSSLConfiguration
|
||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
||||
import net.corda.nodeapi.internal.config.MutualSslConfiguration
|
||||
import net.corda.nodeapi.internal.protonwrapper.netty.SocksProxyConfig
|
||||
import java.nio.file.Path
|
||||
|
||||
@ -28,12 +27,7 @@ enum class FirewallMode {
|
||||
FloatOuter
|
||||
}
|
||||
|
||||
interface BridgeSSLConfiguration : SSLConfiguration {
|
||||
override val keyStorePassword: String
|
||||
override val trustStorePassword: String
|
||||
override val sslKeystore: Path
|
||||
override val trustStoreFile: Path
|
||||
}
|
||||
interface BridgeSSLConfiguration : MutualSslConfiguration
|
||||
|
||||
|
||||
/**
|
||||
@ -91,7 +85,8 @@ interface FloatOuterConfiguration {
|
||||
val customSSLConfiguration: BridgeSSLConfiguration?
|
||||
}
|
||||
|
||||
interface FirewallConfiguration : NodeSSLConfiguration {
|
||||
interface FirewallConfiguration {
|
||||
val baseDirectory: Path
|
||||
val firewallMode: FirewallMode
|
||||
val outboundConfig: BridgeOutboundConfiguration?
|
||||
val inboundConfig: BridgeInboundConfiguration?
|
||||
@ -115,4 +110,6 @@ interface FirewallConfiguration : NodeSSLConfiguration {
|
||||
// This is relevant to bridges, because we push messages into the inbox and use the async acknowledgement responses to reply to sender.
|
||||
val p2pConfirmationWindowSize: Int
|
||||
val whitelistedHeaders: List<String>
|
||||
val crlCheckSoftFail: Boolean
|
||||
val p2pSslOptions: MutualSslConfiguration
|
||||
}
|
@ -1,15 +1,15 @@
|
||||
package net.corda.bridge.services.artemis
|
||||
|
||||
import net.corda.bridge.services.api.*
|
||||
import net.corda.bridge.services.config.BridgeSSLConfigurationImpl
|
||||
import net.corda.bridge.services.util.ServiceStateCombiner
|
||||
import net.corda.bridge.services.util.ServiceStateHelper
|
||||
import net.corda.core.internal.ThreadBox
|
||||
import net.corda.core.serialization.internal.nodeSerializationEnv
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.nodeapi.ArtemisTcpTransport
|
||||
import net.corda.nodeapi.internal.ArtemisMessagingClient
|
||||
import net.corda.nodeapi.internal.ArtemisMessagingComponent
|
||||
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.FailoverEventType
|
||||
import org.apache.activemq.artemis.api.core.client.ServerLocator
|
||||
@ -34,13 +34,13 @@ class BridgeArtemisConnectionServiceImpl(val conf: FirewallConfiguration,
|
||||
}
|
||||
|
||||
private val state = ThreadBox(InnerState())
|
||||
private val sslConfiguration: BridgeSSLConfiguration
|
||||
private val sslConfiguration: MutualSslConfiguration
|
||||
private val statusFollower: ServiceStateCombiner
|
||||
private var statusSubscriber: Subscription? = null
|
||||
|
||||
init {
|
||||
statusFollower = ServiceStateCombiner(listOf(auditService))
|
||||
sslConfiguration = conf.outboundConfig?.customSSLConfiguration ?: BridgeSSLConfigurationImpl(conf)
|
||||
sslConfiguration = conf.outboundConfig?.customSSLConfiguration ?: conf.p2pSslOptions
|
||||
}
|
||||
|
||||
override fun start() {
|
||||
@ -61,7 +61,7 @@ class BridgeArtemisConnectionServiceImpl(val conf: FirewallConfiguration,
|
||||
log.info("Connecting to message broker: ${outboundConf.artemisBrokerAddress}")
|
||||
val brokerAddresses = listOf(outboundConf.artemisBrokerAddress) + outboundConf.alternateArtemisBrokerAddresses
|
||||
// TODO Add broker CN to config for host verification in case the embedded broker isn't used
|
||||
val tcpTransports = brokerAddresses.map { ArtemisTcpTransport.p2pConnectorTcpTransport(it, sslConfiguration) }
|
||||
val tcpTransports = brokerAddresses.map { InternalArtemisTcpTransport.p2pConnectorTcpTransport(it, sslConfiguration) }
|
||||
locator = ActiveMQClient.createServerLocatorWithoutHA(*tcpTransports.toTypedArray()).apply {
|
||||
// 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.
|
||||
|
@ -6,22 +6,23 @@ import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.nodeapi.internal.ArtemisMessagingComponent
|
||||
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.parseAs
|
||||
import net.corda.nodeapi.internal.protonwrapper.netty.SocksProxyConfig
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
|
||||
|
||||
fun Config.parseAsFirewallConfiguration(): FirewallConfiguration = parseAs<FirewallConfigurationImpl>()
|
||||
|
||||
data class BridgeSSLConfigurationImpl(override val keyStorePassword: String,
|
||||
override val trustStorePassword: String,
|
||||
override val certificatesDirectory: Path = Paths.get("certificates"),
|
||||
override val sslKeystore: Path = certificatesDirectory / "sslkeystore.jks",
|
||||
override val trustStoreFile: Path = certificatesDirectory / "truststore.jks",
|
||||
override val crlCheckSoftFail: Boolean) : BridgeSSLConfiguration {
|
||||
constructor(config: NodeSSLConfiguration) : this(config.keyStorePassword, config.trustStorePassword, config.certificatesDirectory, config.sslKeystore, config.trustStoreFile, config.crlCheckSoftFail)
|
||||
data class BridgeSSLConfigurationImpl(private val sslKeystore: Path,
|
||||
private val keyStorePassword: String,
|
||||
private val trustStoreFile: Path,
|
||||
private val trustStorePassword: String,
|
||||
private val crlCheckSoftFail: Boolean) : BridgeSSLConfiguration {
|
||||
|
||||
override val keyStore = FileBasedCertificateStoreSupplier(sslKeystore, keyStorePassword)
|
||||
override val trustStore = FileBasedCertificateStoreSupplier(trustStoreFile, trustStorePassword)
|
||||
}
|
||||
|
||||
data class BridgeOutboundConfigurationImpl(override val artemisBrokerAddress: NetworkHostAndPort,
|
||||
@ -45,12 +46,12 @@ data class BridgeHAConfigImpl(override val haConnectionString: String, override
|
||||
|
||||
data class FirewallConfigurationImpl(
|
||||
override val baseDirectory: Path,
|
||||
override val certificatesDirectory: Path = baseDirectory / "certificates",
|
||||
override val sslKeystore: Path = certificatesDirectory / "sslkeystore.jks",
|
||||
override val trustStoreFile: Path = certificatesDirectory / "truststore.jks",
|
||||
private val certificatesDirectory: Path = baseDirectory / "certificates",
|
||||
private val sslKeystore: Path = certificatesDirectory / "sslkeystore.jks",
|
||||
private val trustStoreFile: Path = certificatesDirectory / "truststore.jks",
|
||||
override val crlCheckSoftFail: Boolean,
|
||||
override val keyStorePassword: String,
|
||||
override val trustStorePassword: String,
|
||||
private val keyStorePassword: String,
|
||||
private val trustStorePassword: String,
|
||||
override val firewallMode: FirewallMode,
|
||||
override val networkParametersPath: Path,
|
||||
override val outboundConfig: BridgeOutboundConfigurationImpl?,
|
||||
@ -74,6 +75,12 @@ data class FirewallConfigurationImpl(
|
||||
require(inboundConfig != null && floatOuterConfig != null) { "Missing required configuration" }
|
||||
}
|
||||
}
|
||||
|
||||
private val p2pKeystorePath = sslKeystore
|
||||
private val p2pKeyStore = FileBasedCertificateStoreSupplier(p2pKeystorePath, keyStorePassword)
|
||||
private val p2pTrustStoreFilePath = trustStoreFile
|
||||
private val p2pTrustStore = FileBasedCertificateStoreSupplier(p2pTrustStoreFilePath, trustStorePassword)
|
||||
override val p2pSslOptions: MutualSslConfiguration = SslConfiguration.mutual(p2pKeyStore, p2pTrustStore)
|
||||
}
|
||||
|
||||
|
||||
|
@ -7,7 +7,9 @@ import net.corda.bridge.services.api.ServiceStateSupport
|
||||
import net.corda.bridge.services.util.ServiceStateCombiner
|
||||
import net.corda.bridge.services.util.ServiceStateHelper
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.nodeapi.internal.config.CertificateStore
|
||||
import net.corda.nodeapi.internal.crypto.KEYSTORE_TYPE
|
||||
import net.corda.nodeapi.internal.crypto.X509KeyStore
|
||||
import net.corda.nodeapi.internal.protonwrapper.messages.ReceivedMessage
|
||||
import net.corda.nodeapi.internal.protonwrapper.netty.AMQPConfiguration
|
||||
import net.corda.nodeapi.internal.protonwrapper.netty.AMQPServer
|
||||
@ -48,13 +50,13 @@ class BridgeAMQPListenerServiceImpl(val conf: FirewallConfiguration,
|
||||
trustStorePassword: CharArray) {
|
||||
require(active) { "AuditService must be active" }
|
||||
require(keyStorePassword !== keyStorePrivateKeyPassword) { "keyStorePassword and keyStorePrivateKeyPassword must reference distinct arrays!" }
|
||||
val keyStore = loadKeyStoreAndWipeKeys(keyStoreBytes, keyStorePassword)
|
||||
val trustStore = loadKeyStoreAndWipeKeys(trustStoreBytes, trustStorePassword)
|
||||
|
||||
val keyStore = CertificateStore.of(loadKeyStore(keyStoreBytes, keyStorePassword), java.lang.String.valueOf(keyStorePrivateKeyPassword)).also { wipeKeys(keyStoreBytes, keyStorePassword) }
|
||||
val trustStore = CertificateStore.of(loadKeyStore(trustStoreBytes, trustStorePassword), java.lang.String.valueOf(trustStorePassword)).also { wipeKeys(trustStoreBytes, trustStorePassword) }
|
||||
val bindAddress = conf.inboundConfig!!.listeningAddress
|
||||
val amqpConfiguration = object : AMQPConfiguration {
|
||||
override val keyStore: KeyStore = keyStore
|
||||
override val keyStorePrivateKeyPassword: CharArray = keyStorePrivateKeyPassword
|
||||
override val trustStore: KeyStore = trustStore
|
||||
override val keyStore = keyStore
|
||||
override val trustStore = trustStore
|
||||
override val crlCheckSoftFail: Boolean = conf.crlCheckSoftFail
|
||||
override val maxMessageSize: Int = maximumMessageSize
|
||||
override val trace: Boolean = conf.enableAMQPPacketTrace
|
||||
@ -80,15 +82,18 @@ class BridgeAMQPListenerServiceImpl(val conf: FirewallConfiguration,
|
||||
consoleLogger.info(msg)
|
||||
}
|
||||
|
||||
private fun loadKeyStoreAndWipeKeys(keyStoreBytes: ByteArray, keyStorePassword: CharArray): KeyStore {
|
||||
private fun wipeKeys(keyStoreBytes: ByteArray, keyStorePassword: CharArray) {
|
||||
// We overwrite the keys we don't need anymore
|
||||
Arrays.fill(keyStoreBytes, 0xAA.toByte())
|
||||
Arrays.fill(keyStorePassword, 0xAA55.toChar())
|
||||
}
|
||||
|
||||
private fun loadKeyStore(keyStoreBytes: ByteArray, keyStorePassword: CharArray): X509KeyStore {
|
||||
val keyStore = KeyStore.getInstance(KEYSTORE_TYPE)
|
||||
ByteArrayInputStream(keyStoreBytes).use {
|
||||
keyStore.load(it, keyStorePassword)
|
||||
}
|
||||
// We overwrite the keys we don't need anymore
|
||||
Arrays.fill(keyStoreBytes, 0xAA.toByte())
|
||||
Arrays.fill(keyStorePassword, 0xAA55.toChar())
|
||||
return keyStore
|
||||
return X509KeyStore(keyStore, java.lang.String.valueOf(keyStorePassword))
|
||||
}
|
||||
|
||||
override fun wipeKeysAndDeactivate() {
|
||||
|
@ -1,7 +1,6 @@
|
||||
package net.corda.bridge.services.receiver
|
||||
|
||||
import net.corda.bridge.services.api.*
|
||||
import net.corda.bridge.services.config.BridgeSSLConfigurationImpl
|
||||
import net.corda.bridge.services.receiver.FloatControlTopics.FLOAT_DATA_TOPIC
|
||||
import net.corda.bridge.services.util.ServiceStateCombiner
|
||||
import net.corda.bridge.services.util.ServiceStateHelper
|
||||
@ -12,13 +11,13 @@ import net.corda.core.serialization.serialize
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_PREFIX
|
||||
import net.corda.nodeapi.internal.config.MutualSslConfiguration
|
||||
import net.corda.nodeapi.internal.protonwrapper.messages.MessageStatus
|
||||
import net.corda.nodeapi.internal.protonwrapper.messages.ReceivedMessage
|
||||
import net.corda.nodeapi.internal.protonwrapper.netty.AMQPConfiguration
|
||||
import net.corda.nodeapi.internal.protonwrapper.netty.AMQPServer
|
||||
import net.corda.nodeapi.internal.protonwrapper.netty.ConnectionChange
|
||||
import rx.Subscription
|
||||
import java.security.KeyStore
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import kotlin.concurrent.withLock
|
||||
|
||||
@ -39,7 +38,7 @@ class FloatControlListenerService(val conf: FirewallConfiguration,
|
||||
private var connectSubscriber: Subscription? = null
|
||||
private var receiveSubscriber: Subscription? = null
|
||||
private var amqpControlServer: AMQPServer? = null
|
||||
private val sslConfiguration: BridgeSSLConfiguration
|
||||
private val sslConfiguration: MutualSslConfiguration
|
||||
private val floatControlAddress = conf.floatOuterConfig!!.floatAddress
|
||||
private val floatClientName = conf.floatOuterConfig!!.expectedCertificateSubject
|
||||
private var activeConnectionInfo: ConnectionChange? = null
|
||||
@ -48,7 +47,7 @@ class FloatControlListenerService(val conf: FirewallConfiguration,
|
||||
|
||||
init {
|
||||
statusFollower = ServiceStateCombiner(listOf(auditService, amqpListener))
|
||||
sslConfiguration = conf.floatOuterConfig?.customSSLConfiguration ?: BridgeSSLConfigurationImpl(conf)
|
||||
sslConfiguration = conf.floatOuterConfig?.customSSLConfiguration ?: conf.p2pSslOptions
|
||||
}
|
||||
|
||||
|
||||
@ -68,15 +67,13 @@ class FloatControlListenerService(val conf: FirewallConfiguration,
|
||||
|
||||
private fun startControlListener() {
|
||||
lock.withLock {
|
||||
val keyStore = sslConfiguration.loadSslKeyStore().internal
|
||||
val keyStorePrivateKeyPassword = sslConfiguration.keyStorePassword
|
||||
val trustStore = sslConfiguration.loadTrustStore().internal
|
||||
val keyStore = sslConfiguration.keyStore.get()
|
||||
val trustStore = sslConfiguration.trustStore.get()
|
||||
val amqpConfig = object : AMQPConfiguration {
|
||||
override val userName: String? = null
|
||||
override val password: String? = null
|
||||
override val keyStore: KeyStore = keyStore
|
||||
override val keyStorePrivateKeyPassword: CharArray = keyStorePrivateKeyPassword.toCharArray()
|
||||
override val trustStore: KeyStore = trustStore
|
||||
override val keyStore = keyStore
|
||||
override val trustStore = trustStore
|
||||
override val crlCheckSoftFail: Boolean = conf.crlCheckSoftFail
|
||||
override val maxMessageSize: Int = maximumMessageSize
|
||||
override val trace: Boolean = conf.enableAMQPPacketTrace
|
||||
|
@ -5,7 +5,7 @@ import net.corda.bridge.services.util.ServiceStateCombiner
|
||||
import net.corda.bridge.services.util.ServiceStateHelper
|
||||
import net.corda.core.internal.readAll
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
||||
import net.corda.nodeapi.internal.config.MutualSslConfiguration
|
||||
import net.corda.nodeapi.internal.protonwrapper.messages.ReceivedMessage
|
||||
import rx.Subscription
|
||||
|
||||
@ -22,23 +22,23 @@ class InProcessBridgeReceiverService(val conf: FirewallConfiguration,
|
||||
private val statusFollower: ServiceStateCombiner
|
||||
private var statusSubscriber: Subscription? = null
|
||||
private var receiveSubscriber: Subscription? = null
|
||||
private val sslConfiguration: SSLConfiguration
|
||||
private val sslConfiguration: MutualSslConfiguration
|
||||
|
||||
init {
|
||||
statusFollower = ServiceStateCombiner(listOf(auditService, haService, amqpListenerService, filterService))
|
||||
sslConfiguration = conf.inboundConfig?.customSSLConfiguration ?: conf
|
||||
sslConfiguration = conf.inboundConfig?.customSSLConfiguration ?: conf.p2pSslOptions
|
||||
}
|
||||
|
||||
override fun start() {
|
||||
statusSubscriber = statusFollower.activeChange.subscribe({
|
||||
if (it) {
|
||||
val keyStoreBytes = sslConfiguration.sslKeystore.readAll()
|
||||
val trustStoreBytes = sslConfiguration.trustStoreFile.readAll()
|
||||
val keyStoreBytes = sslConfiguration.keyStore.path.readAll()
|
||||
val trustStoreBytes = sslConfiguration.trustStore.path.readAll()
|
||||
amqpListenerService.provisionKeysAndActivate(keyStoreBytes,
|
||||
sslConfiguration.keyStorePassword.toCharArray(),
|
||||
sslConfiguration.keyStorePassword.toCharArray(),
|
||||
sslConfiguration.keyStore.password.toCharArray(),
|
||||
sslConfiguration.keyStore.password.toCharArray(),
|
||||
trustStoreBytes,
|
||||
sslConfiguration.trustStorePassword.toCharArray())
|
||||
sslConfiguration.trustStore.password.toCharArray())
|
||||
} else {
|
||||
if (amqpListenerService.running) {
|
||||
amqpListenerService.wipeKeysAndDeactivate()
|
||||
|
@ -13,7 +13,7 @@ import net.corda.core.serialization.serialize
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.core.utilities.debug
|
||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
||||
import net.corda.nodeapi.internal.config.MutualSslConfiguration
|
||||
import net.corda.nodeapi.internal.protonwrapper.messages.MessageStatus
|
||||
import net.corda.nodeapi.internal.protonwrapper.messages.ReceivedMessage
|
||||
import net.corda.nodeapi.internal.protonwrapper.netty.AMQPClient
|
||||
@ -21,7 +21,6 @@ import net.corda.nodeapi.internal.protonwrapper.netty.AMQPConfiguration
|
||||
import net.corda.nodeapi.internal.protonwrapper.netty.ConnectionChange
|
||||
import rx.Subscription
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.security.KeyStore
|
||||
import java.security.SecureRandom
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.TimeoutException
|
||||
@ -41,15 +40,15 @@ class TunnelingBridgeReceiverService(val conf: FirewallConfiguration,
|
||||
private var connectSubscriber: Subscription? = null
|
||||
private var receiveSubscriber: Subscription? = null
|
||||
private var amqpControlClient: AMQPClient? = null
|
||||
private val controlLinkSSLConfiguration: SSLConfiguration
|
||||
private val floatListenerSSLConfiguration: SSLConfiguration
|
||||
private val controlLinkSSLConfiguration: MutualSslConfiguration
|
||||
private val floatListenerSSLConfiguration: MutualSslConfiguration
|
||||
private val expectedCertificateSubject: CordaX500Name
|
||||
private val secureRandom: SecureRandom = newSecureRandom()
|
||||
|
||||
init {
|
||||
statusFollower = ServiceStateCombiner(listOf(auditService, haService, filterService))
|
||||
controlLinkSSLConfiguration = conf.bridgeInnerConfig?.customSSLConfiguration ?: conf
|
||||
floatListenerSSLConfiguration = conf.bridgeInnerConfig?.customFloatOuterSSLConfiguration ?: conf
|
||||
controlLinkSSLConfiguration = conf.bridgeInnerConfig?.customSSLConfiguration ?: conf.p2pSslOptions
|
||||
floatListenerSSLConfiguration = conf.bridgeInnerConfig?.customFloatOuterSSLConfiguration ?: conf.p2pSslOptions
|
||||
expectedCertificateSubject = conf.bridgeInnerConfig!!.expectedCertificateSubject
|
||||
}
|
||||
|
||||
@ -58,15 +57,13 @@ class TunnelingBridgeReceiverService(val conf: FirewallConfiguration,
|
||||
statusSubscriber = statusFollower.activeChange.subscribe({
|
||||
if (it) {
|
||||
val floatAddresses = conf.bridgeInnerConfig!!.floatAddresses
|
||||
val controlLinkKeyStore = controlLinkSSLConfiguration.loadSslKeyStore().internal
|
||||
val controLinkKeyStorePrivateKeyPassword = controlLinkSSLConfiguration.keyStorePassword
|
||||
val controlLinkTrustStore = controlLinkSSLConfiguration.loadTrustStore().internal
|
||||
val controlLinkKeyStore = controlLinkSSLConfiguration.keyStore.get()
|
||||
val controlLinkTrustStore = controlLinkSSLConfiguration.trustStore.get()
|
||||
val amqpConfig = object : AMQPConfiguration {
|
||||
override val userName: String? = null
|
||||
override val password: String? = null
|
||||
override val keyStore: KeyStore = controlLinkKeyStore
|
||||
override val keyStorePrivateKeyPassword: CharArray = controLinkKeyStorePrivateKeyPassword.toCharArray()
|
||||
override val trustStore: KeyStore = controlLinkTrustStore
|
||||
override val keyStore = controlLinkKeyStore
|
||||
override val trustStore = controlLinkTrustStore
|
||||
override val crlCheckSoftFail: Boolean = conf.crlCheckSoftFail
|
||||
override val maxMessageSize: Int = maximumMessageSize
|
||||
override val trace: Boolean = conf.enableAMQPPacketTrace
|
||||
@ -124,12 +121,12 @@ class TunnelingBridgeReceiverService(val conf: FirewallConfiguration,
|
||||
auditService.statusChangeEvent("Connection change on float control port $connectionChange")
|
||||
if (connectionChange.connected) {
|
||||
val (freshKeyStorePassword, freshKeyStoreKeyPassword, recodedKeyStore) = recodeKeyStore(floatListenerSSLConfiguration)
|
||||
val trustStoreBytes = floatListenerSSLConfiguration.trustStoreFile.readAll()
|
||||
val trustStoreBytes = floatListenerSSLConfiguration.trustStore.path.readAll()
|
||||
val activateMessage = ActivateFloat(recodedKeyStore,
|
||||
freshKeyStorePassword,
|
||||
freshKeyStoreKeyPassword,
|
||||
trustStoreBytes,
|
||||
floatListenerSSLConfiguration.trustStorePassword.toCharArray())
|
||||
floatListenerSSLConfiguration.trustStore.password.toCharArray())
|
||||
val amqpActivateMessage = amqpControlClient!!.createMessage(activateMessage.serialize(context = SerializationDefaults.P2P_CONTEXT).bytes,
|
||||
FLOAT_CONTROL_TOPIC,
|
||||
expectedCertificateSubject.toString(),
|
||||
@ -150,9 +147,9 @@ class TunnelingBridgeReceiverService(val conf: FirewallConfiguration,
|
||||
}
|
||||
|
||||
// Recode KeyStore to use a fresh random password for entries and overall
|
||||
private fun recodeKeyStore(sslConfiguration: SSLConfiguration): Triple<CharArray, CharArray, ByteArray> {
|
||||
val keyStoreOriginal = sslConfiguration.loadSslKeyStore().internal
|
||||
val originalKeyStorePassword = sslConfiguration.keyStorePassword.toCharArray()
|
||||
private fun recodeKeyStore(sslConfiguration: MutualSslConfiguration): Triple<CharArray, CharArray, ByteArray> {
|
||||
val keyStoreOriginal = sslConfiguration.keyStore.get().value.internal
|
||||
val originalKeyStorePassword = sslConfiguration.keyStore.password.toCharArray()
|
||||
val freshKeyStorePassword = CharArray(20) { secureRandom.nextInt(0xD800).toChar() } // Stick to single character Unicode range
|
||||
val freshPrivateKeyPassword = CharArray(20) { secureRandom.nextInt(0xD800).toChar() } // Stick to single character Unicode range
|
||||
for (alias in keyStoreOriginal.aliases()) {
|
||||
|
@ -23,7 +23,7 @@ class DirectBridgeSenderService(val conf: FirewallConfiguration,
|
||||
private val statusFollower: ServiceStateCombiner
|
||||
private var statusSubscriber: Subscription? = null
|
||||
private var listenerActiveSubscriber: Subscription? = null
|
||||
private var bridgeControlListener: BridgeControlListener = BridgeControlListener(conf, conf.outboundConfig!!.socksProxyConfig, maxMessageSize, { ForwardingArtemisMessageClient(artemisConnectionService) })
|
||||
private var bridgeControlListener: BridgeControlListener = BridgeControlListener(conf.p2pSslOptions, conf.outboundConfig!!.socksProxyConfig, maxMessageSize, { ForwardingArtemisMessageClient(artemisConnectionService) })
|
||||
|
||||
init {
|
||||
statusFollower = ServiceStateCombiner(listOf(auditService, artemisConnectionService, haService))
|
||||
|
@ -7,9 +7,8 @@ import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.exists
|
||||
import net.corda.core.node.NetworkParameters
|
||||
import net.corda.core.node.NotaryInfo
|
||||
import net.corda.core.node.services.AttachmentId
|
||||
import net.corda.nodeapi.internal.*
|
||||
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.network.NetworkParametersCopier
|
||||
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||
@ -33,7 +32,7 @@ fun createNetworkParams(baseDirectory: Path): Int {
|
||||
maxMessageSize = 10485760,
|
||||
maxTransactionSize = 40000,
|
||||
epoch = 1,
|
||||
whitelistedContractImplementations = emptyMap<String, List<AttachmentId>>()
|
||||
whitelistedContractImplementations = emptyMap()
|
||||
)
|
||||
val copier = NetworkParametersCopier(networkParameters, overwriteFile = true)
|
||||
copier.install(baseDirectory)
|
||||
@ -55,18 +54,22 @@ fun createAndLoadConfigFromResource(baseDirectory: Path, configResource: String)
|
||||
return config
|
||||
}
|
||||
|
||||
fun SSLConfiguration.createBridgeKeyStores(legalName: CordaX500Name,
|
||||
rootCert: X509Certificate = DEV_ROOT_CA.certificate,
|
||||
intermediateCa: CertificateAndKeyPair = DEV_INTERMEDIATE_CA) {
|
||||
fun FirewallConfiguration.createBridgeKeyStores(legalName: CordaX500Name,
|
||||
rootCert: X509Certificate = DEV_ROOT_CA.certificate,
|
||||
intermediateCa: CertificateAndKeyPair = DEV_INTERMEDIATE_CA) = p2pSslOptions.createBridgeKeyStores(legalName, rootCert, intermediateCa)
|
||||
|
||||
certificatesDirectory.createDirectories()
|
||||
if (!trustStoreFile.exists()) {
|
||||
loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/${DEV_CA_TRUST_STORE_FILE}"), DEV_CA_TRUST_STORE_PASS).save(trustStoreFile, trustStorePassword)
|
||||
fun MutualSslConfiguration.createBridgeKeyStores(legalName: CordaX500Name,
|
||||
rootCert: X509Certificate = DEV_ROOT_CA.certificate,
|
||||
intermediateCa: CertificateAndKeyPair = DEV_INTERMEDIATE_CA) {
|
||||
|
||||
if (!trustStore.path.exists()) {
|
||||
val trustStore = trustStore.get(true)
|
||||
loadDevCaTrustStore().copyTo(trustStore)
|
||||
}
|
||||
|
||||
val (nodeCaCert, nodeCaKeyPair) = createDevNodeCa(intermediateCa, legalName)
|
||||
|
||||
val sslKeyStore = loadSslKeyStore(createNew = true)
|
||||
val sslKeyStore = keyStore.get(createNew = true)
|
||||
sslKeyStore.update {
|
||||
val tlsKeyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, legalName.x500Principal, tlsKeyPair.public)
|
||||
|
@ -62,23 +62,23 @@ class ConfigTest {
|
||||
fun `Load overridden cert config`() {
|
||||
val configResource = "/net/corda/bridge/custombasecerts/firewall.conf"
|
||||
val config = createAndLoadConfigFromResource(tempFolder.root.toPath(), configResource)
|
||||
assertEquals(Paths.get("customcerts/mysslkeystore.jks"), config.sslKeystore)
|
||||
assertEquals(Paths.get("customcerts/mytruststore.jks"), config.trustStoreFile)
|
||||
assertEquals(Paths.get("customcerts/mysslkeystore.jks"), config.p2pSslOptions.keyStore.path)
|
||||
assertEquals(Paths.get("customcerts/mytruststore.jks"), config.p2pSslOptions.trustStore.path)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Load custom inner certificate config`() {
|
||||
val configResource = "/net/corda/bridge/separatedwithcustomcerts/bridge/firewall.conf"
|
||||
val config = createAndLoadConfigFromResource(tempFolder.root.toPath(), configResource)
|
||||
assertEquals(Paths.get("outboundcerts/outboundkeys.jks"), config.outboundConfig!!.customSSLConfiguration!!.sslKeystore)
|
||||
assertEquals(Paths.get("outboundcerts/outboundtrust.jks"), config.outboundConfig!!.customSSLConfiguration!!.trustStoreFile)
|
||||
assertEquals("outboundkeypassword", config.outboundConfig!!.customSSLConfiguration!!.keyStorePassword)
|
||||
assertEquals("outboundtrustpassword", config.outboundConfig!!.customSSLConfiguration!!.trustStorePassword)
|
||||
assertEquals(Paths.get("outboundcerts/outboundkeys.jks"), config.outboundConfig!!.customSSLConfiguration!!.keyStore.path)
|
||||
assertEquals(Paths.get("outboundcerts/outboundtrust.jks"), config.outboundConfig!!.customSSLConfiguration!!.trustStore.path)
|
||||
assertEquals("outboundkeypassword", config.outboundConfig!!.customSSLConfiguration!!.keyStore.password)
|
||||
assertEquals("outboundtrustpassword", config.outboundConfig!!.customSSLConfiguration!!.trustStore.password)
|
||||
assertNull(config.inboundConfig)
|
||||
assertEquals(Paths.get("tunnelcerts/tunnelkeys.jks"), config.bridgeInnerConfig!!.customSSLConfiguration!!.sslKeystore)
|
||||
assertEquals(Paths.get("tunnelcerts/tunneltrust.jks"), config.bridgeInnerConfig!!.customSSLConfiguration!!.trustStoreFile)
|
||||
assertEquals("tunnelkeypassword", config.bridgeInnerConfig!!.customSSLConfiguration!!.keyStorePassword)
|
||||
assertEquals("tunneltrustpassword", config.bridgeInnerConfig!!.customSSLConfiguration!!.trustStorePassword)
|
||||
assertEquals(Paths.get("tunnelcerts/tunnelkeys.jks"), config.bridgeInnerConfig!!.customSSLConfiguration!!.keyStore.path)
|
||||
assertEquals(Paths.get("tunnelcerts/tunneltrust.jks"), config.bridgeInnerConfig!!.customSSLConfiguration!!.trustStore.path)
|
||||
assertEquals("tunnelkeypassword", config.bridgeInnerConfig!!.customSSLConfiguration!!.keyStore.password)
|
||||
assertEquals("tunneltrustpassword", config.bridgeInnerConfig!!.customSSLConfiguration!!.trustStore.password)
|
||||
assertNull(config.floatOuterConfig)
|
||||
}
|
||||
|
||||
@ -86,15 +86,15 @@ class ConfigTest {
|
||||
fun `Load custom outer certificate config`() {
|
||||
val configResource = "/net/corda/bridge/separatedwithcustomcerts/float/firewall.conf"
|
||||
val config = createAndLoadConfigFromResource(tempFolder.root.toPath(), configResource)
|
||||
assertEquals(Paths.get("inboundcerts/inboundkeys.jks"), config.inboundConfig!!.customSSLConfiguration!!.sslKeystore)
|
||||
assertEquals(Paths.get("inboundcerts/inboundtrust.jks"), config.inboundConfig!!.customSSLConfiguration!!.trustStoreFile)
|
||||
assertEquals("inboundkeypassword", config.inboundConfig!!.customSSLConfiguration!!.keyStorePassword)
|
||||
assertEquals("inboundtrustpassword", config.inboundConfig!!.customSSLConfiguration!!.trustStorePassword)
|
||||
assertEquals(Paths.get("inboundcerts/inboundkeys.jks"), config.inboundConfig!!.customSSLConfiguration!!.keyStore.path)
|
||||
assertEquals(Paths.get("inboundcerts/inboundtrust.jks"), config.inboundConfig!!.customSSLConfiguration!!.trustStore.path)
|
||||
assertEquals("inboundkeypassword", config.inboundConfig!!.customSSLConfiguration!!.keyStore.password)
|
||||
assertEquals("inboundtrustpassword", config.inboundConfig!!.customSSLConfiguration!!.trustStore.password)
|
||||
assertNull(config.outboundConfig)
|
||||
assertEquals(Paths.get("tunnelcerts/tunnelkeys.jks"), config.floatOuterConfig!!.customSSLConfiguration!!.sslKeystore)
|
||||
assertEquals(Paths.get("tunnelcerts/tunneltrust.jks"), config.floatOuterConfig!!.customSSLConfiguration!!.trustStoreFile)
|
||||
assertEquals("tunnelkeypassword", config.floatOuterConfig!!.customSSLConfiguration!!.keyStorePassword)
|
||||
assertEquals("tunneltrustpassword", config.floatOuterConfig!!.customSSLConfiguration!!.trustStorePassword)
|
||||
assertEquals(Paths.get("tunnelcerts/tunnelkeys.jks"), config.floatOuterConfig!!.customSSLConfiguration!!.keyStore.path)
|
||||
assertEquals(Paths.get("tunnelcerts/tunneltrust.jks"), config.floatOuterConfig!!.customSSLConfiguration!!.trustStore.path)
|
||||
assertEquals("tunnelkeypassword", config.floatOuterConfig!!.customSSLConfiguration!!.keyStore.password)
|
||||
assertEquals("tunneltrustpassword", config.floatOuterConfig!!.customSSLConfiguration!!.trustStore.password)
|
||||
assertNull(config.bridgeInnerConfig)
|
||||
}
|
||||
|
||||
|
@ -61,7 +61,6 @@ public class CordaRPCJavaClientTest extends NodeBasedTest {
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
node = startNode(ALICE_NAME, 1000, singletonList(rpcUser));
|
||||
client = new CordaRPCClient(requireNonNull(node.getNode().getConfiguration().getRpcOptions().getAddress()));
|
||||
}
|
||||
|
@ -7,12 +7,11 @@ import net.corda.core.context.Trace
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.serialization.internal.effectiveSerializationEnv
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.nodeapi.ArtemisTcpTransport.Companion.rpcConnectorTcpTransport
|
||||
import net.corda.core.messaging.ClientRpcSslOptions
|
||||
import net.corda.core.utilities.days
|
||||
import net.corda.core.utilities.minutes
|
||||
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 java.time.Duration
|
||||
|
||||
@ -243,10 +242,8 @@ class CordaRPCClient private constructor(
|
||||
private val hostAndPort: NetworkHostAndPort,
|
||||
private val configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT,
|
||||
private val sslConfiguration: ClientRpcSslOptions? = null,
|
||||
private val nodeSslConfiguration: SSLConfiguration? = null,
|
||||
private val classLoader: ClassLoader? = null,
|
||||
private val haAddressPool: List<NetworkHostAndPort> = emptyList(),
|
||||
private val internalConnection: Boolean = false
|
||||
private val haAddressPool: List<NetworkHostAndPort> = emptyList()
|
||||
) {
|
||||
@JvmOverloads
|
||||
constructor(hostAndPort: NetworkHostAndPort,
|
||||
@ -260,7 +257,7 @@ class CordaRPCClient private constructor(
|
||||
* @param configuration An optional configuration used to tweak client behaviour.
|
||||
*/
|
||||
@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 {
|
||||
fun createWithSsl(
|
||||
@ -285,16 +282,7 @@ class CordaRPCClient private constructor(
|
||||
sslConfiguration: ClientRpcSslOptions? = null,
|
||||
classLoader: ClassLoader? = null
|
||||
): CordaRPCClient {
|
||||
return CordaRPCClient(hostAndPort, configuration, sslConfiguration, null, 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)
|
||||
return CordaRPCClient(hostAndPort, configuration, sslConfiguration, classLoader)
|
||||
}
|
||||
|
||||
internal fun createWithSslAndClassLoader(
|
||||
@ -303,7 +291,7 @@ class CordaRPCClient private constructor(
|
||||
sslConfiguration: ClientRpcSslOptions? = null,
|
||||
classLoader: ClassLoader? = null
|
||||
): CordaRPCClient {
|
||||
return CordaRPCClient(haAddressPool.first(), configuration, sslConfiguration, null, classLoader, haAddressPool)
|
||||
return CordaRPCClient(haAddressPool.first(), configuration, sslConfiguration, classLoader, haAddressPool)
|
||||
}
|
||||
}
|
||||
|
||||
@ -321,10 +309,7 @@ class CordaRPCClient private constructor(
|
||||
|
||||
private fun getRpcClient(): RPCClient<CordaRPCOps> {
|
||||
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(
|
||||
rpcConnectorTcpTransport(hostAndPort, config = sslConfiguration),
|
||||
configuration,
|
||||
|
@ -6,7 +6,6 @@ import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.messaging.pendingFlowsCount
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.messaging.ClientRpcSslOptions
|
||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
||||
import rx.Observable
|
||||
|
||||
/** Utility which exposes the internal Corda RPC constructor to other internal Corda components */
|
||||
@ -17,13 +16,6 @@ fun createCordaRPCClientWithSslAndClassLoader(
|
||||
classLoader: ClassLoader? = null
|
||||
) = 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 createCordaRPCClientWithSslAndClassLoader(
|
||||
haAddressPool: List<NetworkHostAndPort>,
|
||||
configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT,
|
||||
|
@ -15,11 +15,11 @@ import net.corda.core.serialization.SerializationDefaults
|
||||
import net.corda.core.serialization.internal.nodeSerializationEnv
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
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.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.TransportConfiguration
|
||||
import org.apache.activemq.artemis.api.core.client.ActiveMQClient
|
||||
@ -43,7 +43,7 @@ class RPCClient<I : RPCOps>(
|
||||
|
||||
constructor(
|
||||
hostAndPort: NetworkHostAndPort,
|
||||
sslConfiguration: SSLConfiguration,
|
||||
sslConfiguration: SslConfiguration,
|
||||
configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT,
|
||||
serializationContext: SerializationContext = SerializationDefaults.RPC_CLIENT_CONTEXT
|
||||
) : this(rpcInternalClientTcpTransport(hostAndPort, sslConfiguration), configuration, serializationContext)
|
||||
|
@ -4,22 +4,14 @@ package net.corda.core.internal
|
||||
|
||||
import net.corda.core.DeleteForDJVM
|
||||
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.flows.FlowLogic
|
||||
import net.corda.core.node.ServicesForResolution
|
||||
import net.corda.core.schemas.MappedSchema
|
||||
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.UntrustworthyData
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.MDC
|
||||
import rx.Observable
|
||||
import rx.Observer
|
||||
import rx.subjects.PublishSubject
|
||||
|
@ -96,6 +96,12 @@ data class StateMachineTransactionMapping(val stateMachineRunId: StateMachineRun
|
||||
|
||||
/** RPC operations that the node exposes to clients. */
|
||||
interface CordaRPCOps : RPCOps {
|
||||
/**
|
||||
* Returns the RPC protocol version, which is the same the node's Platform Version. Exists since version 1 so guaranteed
|
||||
* to be present.
|
||||
*/
|
||||
override val protocolVersion: Int get() = nodeInfo().platformVersion
|
||||
|
||||
/** Returns a list of currently in-progress state machine infos. */
|
||||
fun stateMachinesSnapshot(): List<StateMachineInfo>
|
||||
|
||||
|
@ -91,8 +91,8 @@ class FlowWorkerTest {
|
||||
// create test certificates
|
||||
config.configureWithDevSSLCertificate()
|
||||
|
||||
val trustRoot = config.loadTrustStore().getCertificate(X509Utilities.CORDA_ROOT_CA)
|
||||
val nodeCa = config.loadNodeKeyStore().getCertificate(X509Utilities.CORDA_CLIENT_CA)
|
||||
val trustRoot = config.p2pSslOptions.trustStore.get().query { getCertificate(X509Utilities.CORDA_ROOT_CA) }
|
||||
val nodeCa = config.signingCertificateStore.get().query { getCertificate(X509Utilities.CORDA_CLIENT_CA) }
|
||||
|
||||
val broker = createFlowWorkerBroker(config, networkParameters.maxMessageSize)
|
||||
val bridgeControlListener = createBridgeControlListener(config, networkParameters.maxMessageSize)
|
||||
@ -146,8 +146,8 @@ class FlowWorkerTest {
|
||||
// create test certificates
|
||||
bankAConfig.configureWithDevSSLCertificate()
|
||||
|
||||
val bankATrustRoot = bankAConfig.loadTrustStore().getCertificate(X509Utilities.CORDA_ROOT_CA)
|
||||
val bankANodeCa = bankAConfig.loadNodeKeyStore().getCertificate(X509Utilities.CORDA_CLIENT_CA)
|
||||
val bankATrustRoot = bankAConfig.p2pSslOptions.trustStore.get().query { getCertificate(X509Utilities.CORDA_ROOT_CA) }
|
||||
val bankANodeCa = bankAConfig.signingCertificateStore.get().query { getCertificate(X509Utilities.CORDA_CLIENT_CA) }
|
||||
|
||||
val bankABroker = createFlowWorkerBroker(bankAConfig, networkParameters.maxMessageSize)
|
||||
val bankABridgeControlListener = createBridgeControlListener(bankAConfig, networkParameters.maxMessageSize)
|
||||
@ -166,8 +166,8 @@ class FlowWorkerTest {
|
||||
// create test certificates
|
||||
bankBConfig.configureWithDevSSLCertificate()
|
||||
|
||||
val bankBTrustRoot = bankBConfig.loadTrustStore().getCertificate(X509Utilities.CORDA_ROOT_CA)
|
||||
val bankBNodeCa = bankBConfig.loadNodeKeyStore().getCertificate(X509Utilities.CORDA_CLIENT_CA)
|
||||
val bankBTrustRoot = bankBConfig.p2pSslOptions.trustStore.get().query { getCertificate(X509Utilities.CORDA_ROOT_CA) }
|
||||
val bankBNodeCa = bankBConfig.signingCertificateStore.get().query { getCertificate(X509Utilities.CORDA_CLIENT_CA) }
|
||||
// NetworkParametersCopier(networkParameters).install(bankBConfig.baseDirectory)
|
||||
|
||||
val bankBBroker = createFlowWorkerBroker(bankBConfig, networkParameters.maxMessageSize)
|
||||
@ -234,13 +234,13 @@ class FlowWorkerTest {
|
||||
}
|
||||
|
||||
private fun createBridgeControlListener(config: NodeConfiguration, maxMessageSize: Int): BridgeControlListener {
|
||||
val bridgeControlListener = BridgeControlListener(config, config.messagingServerAddress!!, maxMessageSize)
|
||||
val bridgeControlListener = BridgeControlListener(config.p2pSslOptions, config.messagingServerAddress!!, maxMessageSize)
|
||||
bridgeControlListener.start()
|
||||
return bridgeControlListener
|
||||
}
|
||||
|
||||
private fun createArtemisClient(config: NodeConfiguration, queueAddress: String): Triple<ClientSession, ClientConsumer, ClientProducer> {
|
||||
val artemisClient = ArtemisMessagingClient(config, config.messagingServerAddress!!, MAX_MESSAGE_SIZE)
|
||||
val artemisClient = ArtemisMessagingClient(config.p2pSslOptions, config.messagingServerAddress!!, MAX_MESSAGE_SIZE)
|
||||
val started = artemisClient.start()
|
||||
started.session.createQueue(queueAddress, RoutingType.ANYCAST, queueAddress, true)
|
||||
return Triple(started.session, started.session.createConsumer(queueAddress), started.session.createProducer())
|
||||
|
@ -43,7 +43,7 @@ class FlowWorker(flowWorkerId: String, private val flowWorkerServiceHub: FlowWor
|
||||
flowWorkerServiceHub.start()
|
||||
runOnStop += { flowWorkerServiceHub.stop() }
|
||||
|
||||
val flowWorkerMessagingClient = ArtemisMessagingClient(flowWorkerServiceHub.configuration, flowWorkerServiceHub.configuration.messagingServerAddress!!, flowWorkerServiceHub.networkParameters.maxMessageSize)
|
||||
val flowWorkerMessagingClient = ArtemisMessagingClient(flowWorkerServiceHub.configuration.p2pSslOptions, flowWorkerServiceHub.configuration.messagingServerAddress!!, flowWorkerServiceHub.networkParameters.maxMessageSize)
|
||||
runOnStop += { flowWorkerMessagingClient.stop() }
|
||||
|
||||
val session = flowWorkerMessagingClient.start().session
|
||||
|
@ -59,8 +59,8 @@ data class RpcFlowWorkerDriverDSL(private val driverDSL: DriverDSLImpl) : Intern
|
||||
fun startRpcFlowWorker(myLegalName: CordaX500Name, rpcUsers: List<net.corda.testing.node.User>, numberOfFlowWorkers: Int = 1): CordaFuture<RpcFlowWorkerHandle> {
|
||||
val (config, rpcWorkerConfig, flowWorkerConfigs) = generateConfigs(myLegalName, rpcUsers, numberOfFlowWorkers)
|
||||
|
||||
val trustRoot = rpcWorkerConfig.loadTrustStore().getCertificate(X509Utilities.CORDA_ROOT_CA)
|
||||
val nodeCa = rpcWorkerConfig.loadNodeKeyStore().getCertificate(X509Utilities.CORDA_CLIENT_CA)
|
||||
val trustRoot = rpcWorkerConfig.p2pSslOptions.trustStore.get().query { getCertificate(X509Utilities.CORDA_ROOT_CA) }
|
||||
val nodeCa = rpcWorkerConfig.signingCertificateStore.get().query { getCertificate(X509Utilities.CORDA_CLIENT_CA) }
|
||||
|
||||
val ourKeyPair = Crypto.generateKeyPair()
|
||||
val ourParty = Party(myLegalName, ourKeyPair.public)
|
||||
@ -151,9 +151,9 @@ private fun createRpcWorkerBroker(config: NodeConfiguration, maxMessageSize: Int
|
||||
val rpcOptions = config.rpcOptions
|
||||
val securityManager = RPCSecurityManagerImpl(SecurityConfiguration.AuthService.fromUsers(config.rpcUsers))
|
||||
val broker = if (rpcOptions.useSsl) {
|
||||
ArtemisRpcBroker.withSsl(config, rpcOptions.address, rpcOptions.adminAddress, rpcOptions.sslConfig!!, securityManager, maxMessageSize, false, config.baseDirectory / "artemis", false)
|
||||
ArtemisRpcBroker.withSsl(config.p2pSslOptions, rpcOptions.address, rpcOptions.adminAddress, rpcOptions.sslConfig!!, securityManager, maxMessageSize, false, config.baseDirectory / "artemis", false)
|
||||
} else {
|
||||
ArtemisRpcBroker.withoutSsl(config, rpcOptions.address, rpcOptions.adminAddress, securityManager, maxMessageSize, false, config.baseDirectory / "artemis", false)
|
||||
ArtemisRpcBroker.withoutSsl(config.p2pSslOptions, rpcOptions.address, rpcOptions.adminAddress, securityManager, maxMessageSize, false, config.baseDirectory / "artemis", false)
|
||||
}
|
||||
broker.start()
|
||||
return broker
|
||||
@ -180,7 +180,7 @@ private fun createFlowWorker(config: NodeConfiguration, myInfo: NodeInfo, networ
|
||||
}
|
||||
|
||||
private fun createBridgeControlListener(config: NodeConfiguration, maxMessageSize: Int): BridgeControlListener {
|
||||
val bridgeControlListener = BridgeControlListener(config, config.messagingServerAddress!!, maxMessageSize)
|
||||
val bridgeControlListener = BridgeControlListener(config.p2pSslOptions, config.messagingServerAddress!!, maxMessageSize)
|
||||
bridgeControlListener.start()
|
||||
return bridgeControlListener
|
||||
}
|
@ -56,14 +56,13 @@ class CordaRpcWorkerOps(
|
||||
const val RPC_WORKER_QUEUE_ADDRESS_PREFIX = "${ArtemisMessagingComponent.INTERNAL_PREFIX}rpc.worker."
|
||||
}
|
||||
|
||||
override val protocolVersion: Int = 1000
|
||||
private val flowWorkerQueueAddress = "${FlowWorker.FLOW_WORKER_QUEUE_ADDRESS_PREFIX}${services.myInfo.legalIdentities[0].owningKey.toStringShort()}"
|
||||
|
||||
private val rpcWorkerQueueAddress = "$RPC_WORKER_QUEUE_ADDRESS_PREFIX${services.myInfo.legalIdentities[0].owningKey.toStringShort()}"
|
||||
private val rpcWorkerId = UUID.randomUUID().toString()
|
||||
private val rpcWorkerQueueName = "$rpcWorkerQueueAddress.$rpcWorkerId"
|
||||
|
||||
private val artemisClient = ArtemisMessagingClient(services.configuration, services.configuration.messagingServerAddress!!, services.networkParameters.maxMessageSize)
|
||||
private val artemisClient = ArtemisMessagingClient(services.configuration.p2pSslOptions, services.configuration.messagingServerAddress!!, services.networkParameters.maxMessageSize)
|
||||
private lateinit var session: ClientSession
|
||||
private lateinit var producer: ClientProducer
|
||||
|
||||
|
@ -77,8 +77,8 @@ class Main : Runnable {
|
||||
val ourKeyPair = getIdentity()
|
||||
val myInfo = getNodeInfo()
|
||||
|
||||
val trustRoot = rpcWorkerConfig.loadTrustStore().getCertificate(X509Utilities.CORDA_ROOT_CA)
|
||||
val nodeCa = rpcWorkerConfig.loadNodeKeyStore().getCertificate(X509Utilities.CORDA_CLIENT_CA)
|
||||
val trustRoot = rpcWorkerConfig.p2pSslOptions.trustStore.get().query { getCertificate(X509Utilities.CORDA_ROOT_CA) }
|
||||
val nodeCa = rpcWorkerConfig.signingCertificateStore.get().query { getCertificate(X509Utilities.CORDA_CLIENT_CA) }
|
||||
|
||||
val signedNetworkParameters = NetworkParametersReader(trustRoot, null, rpcWorkerConfig.baseDirectory).read()
|
||||
val rpcWorkerBroker = createRpcWorkerBroker(rpcWorkerConfig, signedNetworkParameters.networkParameters.maxMessageSize)
|
||||
@ -101,9 +101,9 @@ class Main : Runnable {
|
||||
val rpcOptions = config.rpcOptions
|
||||
val securityManager = RPCSecurityManagerImpl(SecurityConfiguration.AuthService.fromUsers(config.rpcUsers))
|
||||
val broker = if (rpcOptions.useSsl) {
|
||||
ArtemisRpcBroker.withSsl(config, rpcOptions.address, rpcOptions.adminAddress, rpcOptions.sslConfig!!, securityManager, maxMessageSize, false, config.baseDirectory / "artemis", false)
|
||||
ArtemisRpcBroker.withSsl(config.p2pSslOptions, rpcOptions.address, rpcOptions.adminAddress, rpcOptions.sslConfig!!, securityManager, maxMessageSize, false, config.baseDirectory / "artemis", false)
|
||||
} else {
|
||||
ArtemisRpcBroker.withoutSsl(config, rpcOptions.address, rpcOptions.adminAddress, securityManager, maxMessageSize, false, config.baseDirectory / "artemis", false)
|
||||
ArtemisRpcBroker.withoutSsl(config.p2pSslOptions, rpcOptions.address, rpcOptions.adminAddress, securityManager, maxMessageSize, false, config.baseDirectory / "artemis", false)
|
||||
}
|
||||
broker.start()
|
||||
return broker
|
||||
@ -126,9 +126,9 @@ class RpcWorker(private val rpcWorkerServiceHub: RpcWorkerServiceHub, private va
|
||||
rpcThreadPoolSize = rpcWorkerServiceHub.configuration.enterpriseConfiguration.tuning.rpcThreadPoolSize
|
||||
)
|
||||
val securityManager = RPCSecurityManagerImpl(SecurityConfiguration.AuthService.fromUsers(rpcWorkerServiceHub.configuration.rpcUsers))
|
||||
val nodeName = CordaX500Name.build(rpcWorkerServiceHub.configuration.loadSslKeyStore().getCertificate(X509Utilities.CORDA_CLIENT_TLS).subjectX500Principal)
|
||||
val nodeName = CordaX500Name.build(rpcWorkerServiceHub.configuration.p2pSslOptions.keyStore.get().query { getCertificate(X509Utilities.CORDA_CLIENT_TLS).subjectX500Principal })
|
||||
|
||||
val internalRpcMessagingClient = InternalRPCMessagingClient(rpcWorkerServiceHub.configuration, rpcWorkerServiceHub.configuration.rpcOptions.adminAddress, Node.MAX_RPC_MESSAGE_SIZE, nodeName, rpcServerConfiguration)
|
||||
val internalRpcMessagingClient = InternalRPCMessagingClient(rpcWorkerServiceHub.configuration.p2pSslOptions, rpcWorkerServiceHub.configuration.rpcOptions.adminAddress, Node.MAX_RPC_MESSAGE_SIZE, nodeName, rpcServerConfiguration)
|
||||
internalRpcMessagingClient.init(rpcWorkerServiceHub.rpcOps, securityManager)
|
||||
internalRpcMessagingClient.start(serverControl)
|
||||
|
||||
|
@ -1,12 +1,16 @@
|
||||
package net.corda.nodeapi
|
||||
|
||||
import net.corda.core.messaging.ClientRpcSslOptions
|
||||
import net.corda.core.serialization.internal.nodeSerializationEnv
|
||||
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.requireOnDefaultFileSystem
|
||||
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
|
||||
|
||||
@ -38,26 +42,6 @@ class ArtemisTcpTransport {
|
||||
/** Supported TLS versions, currently TLSv1.2 only. */
|
||||
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(
|
||||
TransportConstants.SSL_ENABLED_PROP_NAME to true,
|
||||
TransportConstants.KEYSTORE_PROVIDER_PROP_NAME to "JKS",
|
||||
@ -68,22 +52,6 @@ class ArtemisTcpTransport {
|
||||
TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME to trustStorePassword,
|
||||
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 {
|
||||
val options = defaultArtemisOptions(hostAndPort).toMutableMap()
|
||||
|
||||
@ -110,27 +78,13 @@ class ArtemisTcpTransport {
|
||||
|
||||
/** [TransportConfiguration] for RPC TCP communication - server side. */
|
||||
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)
|
||||
return InternalArtemisTcpTransport.rpcAcceptorTcpTransport(hostAndPort, config, enableSSL)
|
||||
}
|
||||
|
||||
/** [TransportConfiguration] for RPC TCP communication
|
||||
* This is the Transport that connects the client JVM to the broker. */
|
||||
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)
|
||||
return InternalArtemisTcpTransport.rpcConnectorTcpTransport(hostAndPort, config, enableSSL)
|
||||
}
|
||||
|
||||
/** Create as list of [TransportConfiguration]. **/
|
||||
@ -138,12 +92,12 @@ class ArtemisTcpTransport {
|
||||
rpcConnectorTcpTransport(it, config, enableSSL)
|
||||
}
|
||||
|
||||
fun rpcInternalClientTcpTransport(hostAndPort: NetworkHostAndPort, config: SSLConfiguration): TransportConfiguration {
|
||||
return TransportConfiguration(connectorFactoryClassName, defaultArtemisOptions(hostAndPort) + defaultSSLOptions + config.toTransportOptions())
|
||||
fun rpcInternalClientTcpTransport(hostAndPort: NetworkHostAndPort, config: SslConfiguration): TransportConfiguration {
|
||||
return InternalArtemisTcpTransport.rpcInternalClientTcpTransport(hostAndPort, config)
|
||||
}
|
||||
|
||||
fun rpcInternalAcceptorTcpTransport(hostAndPort: NetworkHostAndPort, config: SSLConfiguration): TransportConfiguration {
|
||||
return TransportConfiguration(acceptorFactoryClassName, defaultArtemisOptions(hostAndPort) + defaultSSLOptions + config.toTransportOptions())
|
||||
fun rpcInternalAcceptorTcpTransport(hostAndPort: NetworkHostAndPort, config: SslConfiguration): TransportConfiguration {
|
||||
return InternalArtemisTcpTransport.rpcInternalAcceptorTcpTransport(hostAndPort, config)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,9 +3,9 @@ package net.corda.nodeapi.internal
|
||||
import net.corda.core.serialization.internal.nodeSerializationEnv
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
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.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.*
|
||||
import org.apache.activemq.artemis.api.core.client.ActiveMQClient.DEFAULT_ACK_BATCH_SIZE
|
||||
|
||||
@ -15,14 +15,12 @@ interface ArtemisSessionProvider {
|
||||
val started: ArtemisMessagingClient.Started?
|
||||
}
|
||||
|
||||
class ArtemisMessagingClient(
|
||||
private val config: SSLConfiguration,
|
||||
private val serverAddress: NetworkHostAndPort,
|
||||
private val maxMessageSize: Int,
|
||||
private val autoCommitSends: Boolean = true,
|
||||
private val autoCommitAcks: Boolean = true,
|
||||
private val confirmationWindowSize: Int = -1
|
||||
) : ArtemisSessionProvider {
|
||||
class ArtemisMessagingClient(private val config: MutualSslConfiguration,
|
||||
private val serverAddress: NetworkHostAndPort,
|
||||
private val maxMessageSize: Int,
|
||||
private val autoCommitSends: Boolean = true,
|
||||
private val autoCommitAcks: Boolean = true,
|
||||
private val confirmationWindowSize: Int = -1) : ArtemisSessionProvider {
|
||||
companion object {
|
||||
private val log = loggerFor<ArtemisMessagingClient>()
|
||||
}
|
||||
@ -36,7 +34,7 @@ class ArtemisMessagingClient(
|
||||
check(started == null) { "start can't be called twice" }
|
||||
log.info("Connecting to message broker: $serverAddress")
|
||||
// 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 {
|
||||
// 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.
|
||||
|
@ -7,7 +7,8 @@ import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.div
|
||||
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.X509KeyStore
|
||||
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. */
|
||||
fun installKeyStoreWithNodeIdentity(nodeDir: Path, legalName: CordaX500Name): Party {
|
||||
val nodeSslConfig = object : NodeSSLConfiguration {
|
||||
override val baseDirectory = nodeDir
|
||||
override val keyStorePassword: String = "cordacadevpass"
|
||||
override val trustStorePassword get() = throw NotImplementedError("Not expected to be called")
|
||||
override val crlCheckSoftFail: Boolean = true
|
||||
}
|
||||
val certificatesDirectory = nodeDir / "certificates"
|
||||
val signingCertStore = FileBasedCertificateStoreSupplier(certificatesDirectory / "nodekeystore.jks", "cordacadevpass")
|
||||
val p2pKeyStore = FileBasedCertificateStoreSupplier(certificatesDirectory / "sslkeystore.jks", "cordacadevpass")
|
||||
val p2pTrustStore = FileBasedCertificateStoreSupplier(certificatesDirectory / "truststore.jks", "trustpass")
|
||||
val p2pSslConfig = SslConfiguration.mutual(p2pKeyStore, p2pTrustStore)
|
||||
|
||||
nodeSslConfig.certificatesDirectory.createDirectories()
|
||||
val (nodeKeyStore) = nodeSslConfig.createDevKeyStores(legalName)
|
||||
certificatesDirectory.createDirectories()
|
||||
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")
|
||||
return identity.party
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ import net.corda.core.crypto.Crypto.generateKeyPair
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
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 org.bouncycastle.asn1.x509.GeneralName
|
||||
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
|
||||
* 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)
|
||||
nodeKeyStore.update {
|
||||
setPrivateKey(
|
||||
X509Utilities.CORDA_CLIENT_CA,
|
||||
nodeCaKeyPair.private,
|
||||
listOf(nodeCaCert, intermediateCa.certificate, rootCert))
|
||||
fun CertificateStore.registerDevSigningCertificates(legalName: CordaX500Name,
|
||||
rootCert: X509Certificate = DEV_ROOT_CA.certificate,
|
||||
intermediateCa: CertificateAndKeyPair = DEV_INTERMEDIATE_CA,
|
||||
devNodeCa: CertificateAndKeyPair = createDevNodeCa(intermediateCa, legalName)) {
|
||||
|
||||
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 {
|
||||
val nodeCaCertPath = getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
|
||||
// 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.
|
||||
val identityCertPath = listOf(identityCert) + nodeCaCertPath
|
||||
setPrivateKey(alias, keyPair.private, identityCertPath)
|
||||
save()
|
||||
fun CertificateStore.registerDevP2pCertificates(legalName: CordaX500Name,
|
||||
rootCert: X509Certificate = DEV_ROOT_CA.certificate,
|
||||
intermediateCa: CertificateAndKeyPair = DEV_INTERMEDIATE_CA,
|
||||
devNodeCa: CertificateAndKeyPair = createDevNodeCa(intermediateCa, legalName)) {
|
||||
|
||||
update {
|
||||
val tlsKeyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, devNodeCa.certificate, devNodeCa.keyPair, legalName.x500Principal, tlsKeyPair.public)
|
||||
setPrivateKey(X509Utilities.CORDA_CLIENT_TLS, tlsKeyPair.private, listOf(tlsCert, devNodeCa.certificate, intermediateCa.certificate, rootCert))
|
||||
}
|
||||
}
|
||||
|
||||
fun CertificateStore.storeLegalIdentity(alias: String, keyPair: KeyPair = Crypto.generateKeyPair()): PartyAndCertificate {
|
||||
val identityCertPath = query {
|
||||
val nodeCaCertPath = getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
|
||||
// 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))
|
||||
}
|
||||
|
||||
@ -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
|
||||
internal object DevCaHelper {
|
||||
fun loadDevCa(alias: String): CertificateAndKeyPair {
|
||||
// TODO: Should be identity scheme
|
||||
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)
|
||||
return loadDevCaKeyStore().query { 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)
|
||||
|
@ -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.ArtemisSessionProvider
|
||||
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.netty.AMQPClient
|
||||
import net.corda.nodeapi.internal.protonwrapper.netty.AMQPConfiguration
|
||||
@ -26,7 +27,6 @@ import org.apache.activemq.artemis.api.core.client.ClientMessage
|
||||
import org.apache.activemq.artemis.api.core.client.ClientSession
|
||||
import org.slf4j.MDC
|
||||
import rx.Subscription
|
||||
import java.security.KeyStore
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import kotlin.concurrent.withLock
|
||||
|
||||
@ -38,30 +38,23 @@ import kotlin.concurrent.withLock
|
||||
* The Netty thread pool used by the AMQPBridges is also shared and managed by the AMQPBridgeManager.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
class AMQPBridgeManager(config: NodeSSLConfiguration, socksProxyConfig: SocksProxyConfig? = null,
|
||||
maxMessageSize: Int, private val artemisMessageClientFactory: () -> ArtemisSessionProvider) : BridgeManager {
|
||||
class AMQPBridgeManager(config: MutualSslConfiguration, socksProxyConfig: SocksProxyConfig? = null, maxMessageSize: Int, private val artemisMessageClientFactory: () -> ArtemisSessionProvider) : BridgeManager {
|
||||
|
||||
private val lock = ReentrantLock()
|
||||
private val bridgeNameToBridgeMap = mutableMapOf<String, AMQPBridge>()
|
||||
|
||||
private class AMQPConfigurationImpl private constructor(override val keyStore: KeyStore,
|
||||
override val keyStorePrivateKeyPassword: CharArray,
|
||||
override val trustStore: KeyStore,
|
||||
private class AMQPConfigurationImpl private constructor(override val keyStore: CertificateStore,
|
||||
override val trustStore: CertificateStore,
|
||||
override val socksProxyConfig: SocksProxyConfig?,
|
||||
override val maxMessageSize: Int) : AMQPConfiguration {
|
||||
constructor(config: NodeSSLConfiguration, socksProxyConfig: SocksProxyConfig?, maxMessageSize: Int) : this(config.loadSslKeyStore().internal,
|
||||
config.keyStorePassword.toCharArray(),
|
||||
config.loadTrustStore().internal,
|
||||
socksProxyConfig,
|
||||
maxMessageSize)
|
||||
constructor(config: MutualSslConfiguration, socksProxyConfig: SocksProxyConfig?, maxMessageSize: Int) : this(config.keyStore.get(), config.trustStore.get(), socksProxyConfig, maxMessageSize)
|
||||
}
|
||||
|
||||
private val amqpConfig: AMQPConfiguration = AMQPConfigurationImpl(config, socksProxyConfig, maxMessageSize)
|
||||
private var sharedEventLoopGroup: EventLoopGroup? = null
|
||||
private var artemis: ArtemisSessionProvider? = null
|
||||
|
||||
constructor(config: NodeSSLConfiguration, p2pAddress: NetworkHostAndPort, maxMessageSize: Int, socksProxyConfig: SocksProxyConfig? = null) : this(config, socksProxyConfig, maxMessageSize, { ArtemisMessagingClient(config, p2pAddress, maxMessageSize) })
|
||||
|
||||
constructor(config: MutualSslConfiguration, p2pAddress: NetworkHostAndPort, maxMessageSize: Int, socksProxyConfig: SocksProxyConfig? = null) : this(config, socksProxyConfig, maxMessageSize, { ArtemisMessagingClient(config, p2pAddress, maxMessageSize) })
|
||||
|
||||
companion object {
|
||||
private const val NUM_BRIDGE_THREADS = 0 // Default sized pool
|
||||
|
@ -11,9 +11,9 @@ 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.PEERS_PREFIX
|
||||
import net.corda.nodeapi.internal.ArtemisSessionProvider
|
||||
import net.corda.nodeapi.internal.config.NodeSSLConfiguration
|
||||
import net.corda.nodeapi.internal.protonwrapper.netty.SocksProxyConfig
|
||||
import org.apache.activemq.artemis.api.core.ActiveMQQueueExistsException
|
||||
import net.corda.nodeapi.internal.config.MutualSslConfiguration
|
||||
import org.apache.activemq.artemis.api.core.RoutingType
|
||||
import org.apache.activemq.artemis.api.core.SimpleString
|
||||
import org.apache.activemq.artemis.api.core.client.ClientConsumer
|
||||
@ -23,8 +23,7 @@ import rx.Observable
|
||||
import rx.subjects.PublishSubject
|
||||
import java.util.*
|
||||
|
||||
class BridgeControlListener(val config: NodeSSLConfiguration,
|
||||
|
||||
class BridgeControlListener(val config: MutualSslConfiguration,
|
||||
socksProxyConfig: SocksProxyConfig? = null,
|
||||
maxMessageSize: Int,
|
||||
val artemisMessageClientFactory: () -> ArtemisSessionProvider) : AutoCloseable {
|
||||
@ -38,7 +37,7 @@ class BridgeControlListener(val config: NodeSSLConfiguration,
|
||||
private var controlConsumer: ClientConsumer? = null
|
||||
private var notifyConsumer: ClientConsumer? = null
|
||||
|
||||
constructor(config: NodeSSLConfiguration,
|
||||
constructor(config: MutualSslConfiguration,
|
||||
p2pAddress: NetworkHostAndPort,
|
||||
|
||||
maxMessageSize: Int,
|
||||
|
@ -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
|
@ -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)
|
||||
}
|
@ -4,12 +4,34 @@ import net.corda.core.internal.div
|
||||
import net.corda.nodeapi.internal.crypto.X509KeyStore
|
||||
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 {
|
||||
val keyStorePassword: String
|
||||
val trustStorePassword: String
|
||||
val certificatesDirectory: Path
|
||||
val sslKeystore: Path get() = certificatesDirectory / "sslkeystore.jks"
|
||||
// TODO This looks like it should be in NodeSSLConfiguration
|
||||
val nodeKeystore: Path get() = certificatesDirectory / "nodekeystore.jks"
|
||||
val trustStoreFile: Path get() = certificatesDirectory / "truststore.jks"
|
||||
val crlCheckSoftFail: Boolean
|
||||
@ -25,9 +47,4 @@ interface SSLConfiguration {
|
||||
fun loadSslKeyStore(createNew: Boolean = false): X509KeyStore {
|
||||
return X509KeyStore.fromFile(sslKeystore, keyStorePassword, createNew)
|
||||
}
|
||||
}
|
||||
|
||||
interface NodeSSLConfiguration : SSLConfiguration {
|
||||
val baseDirectory: Path
|
||||
override val certificatesDirectory: Path get() = baseDirectory / "certificates"
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ package net.corda.nodeapi.internal.crypto
|
||||
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.internal.uncheckedCast
|
||||
import java.io.InputStream
|
||||
import java.nio.file.Path
|
||||
import java.security.KeyPair
|
||||
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)
|
||||
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)
|
||||
|
@ -132,7 +132,7 @@ class AMQPClient(val targets: List<NetworkHostAndPort>,
|
||||
private val conf = parent.configuration
|
||||
|
||||
init {
|
||||
keyManagerFactory.init(conf.keyStore, conf.keyStorePrivateKeyPassword)
|
||||
keyManagerFactory.init(conf.keyStore)
|
||||
trustManagerFactory.init(initialiseTrustStoreAndEnableCrlChecking(conf.trustStore, conf.crlCheckSoftFail))
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.corda.nodeapi.internal.protonwrapper.netty
|
||||
|
||||
import net.corda.nodeapi.internal.ArtemisMessagingComponent
|
||||
import net.corda.nodeapi.internal.config.CertificateStore
|
||||
import java.security.KeyStore
|
||||
|
||||
interface AMQPConfiguration {
|
||||
@ -21,19 +22,14 @@ interface AMQPConfiguration {
|
||||
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
|
||||
|
||||
/**
|
||||
* The trust root KeyStore to validate the peer certificates against
|
||||
*/
|
||||
val trustStore: KeyStore
|
||||
val trustStore: CertificateStore
|
||||
|
||||
/**
|
||||
* Setting crlCheckSoftFail to true allows certificate paths where some leaf certificates do not contain cRLDistributionPoints
|
||||
|
@ -60,7 +60,7 @@ class AMQPServer(val hostName: String,
|
||||
private val conf = parent.configuration
|
||||
|
||||
init {
|
||||
keyManagerFactory.init(conf.keyStore, conf.keyStorePrivateKeyPassword)
|
||||
keyManagerFactory.init(conf.keyStore.value.internal, conf.keyStore.password.toCharArray())
|
||||
trustManagerFactory.init(initialiseTrustStoreAndEnableCrlChecking(conf.trustStore, conf.crlCheckSoftFail))
|
||||
}
|
||||
|
||||
|
@ -5,14 +5,13 @@ import net.corda.core.crypto.newSecureRandom
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.contextLogger
|
||||
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 org.bouncycastle.asn1.x509.AuthorityKeyIdentifier
|
||||
import org.bouncycastle.asn1.x509.Extension
|
||||
import org.bouncycastle.asn1.x509.SubjectKeyIdentifier
|
||||
import java.net.Socket
|
||||
import java.security.KeyStore
|
||||
import java.security.SecureRandom
|
||||
import java.security.cert.*
|
||||
import java.util.*
|
||||
import javax.net.ssl.*
|
||||
@ -111,8 +110,8 @@ internal fun createClientSslHelper(target: NetworkHostAndPort,
|
||||
sslContext.init(keyManagers, trustManagers, newSecureRandom())
|
||||
val sslEngine = sslContext.createSSLEngine(target.host, target.port)
|
||||
sslEngine.useClientMode = true
|
||||
sslEngine.enabledProtocols = ArtemisTcpTransport.TLS_VERSIONS.toTypedArray()
|
||||
sslEngine.enabledCipherSuites = ArtemisTcpTransport.CIPHER_SUITES.toTypedArray()
|
||||
sslEngine.enabledProtocols = InternalArtemisTcpTransport.TLS_VERSIONS.toTypedArray()
|
||||
sslEngine.enabledCipherSuites = InternalArtemisTcpTransport.CIPHER_SUITES.toTypedArray()
|
||||
sslEngine.enableSessionCreation = true
|
||||
return SslHandler(sslEngine)
|
||||
}
|
||||
@ -126,13 +125,13 @@ internal fun createServerSslHelper(keyManagerFactory: KeyManagerFactory,
|
||||
val sslEngine = sslContext.createSSLEngine()
|
||||
sslEngine.useClientMode = false
|
||||
sslEngine.needClientAuth = true
|
||||
sslEngine.enabledProtocols = ArtemisTcpTransport.TLS_VERSIONS.toTypedArray()
|
||||
sslEngine.enabledCipherSuites = ArtemisTcpTransport.CIPHER_SUITES.toTypedArray()
|
||||
sslEngine.enabledProtocols = InternalArtemisTcpTransport.TLS_VERSIONS.toTypedArray()
|
||||
sslEngine.enabledCipherSuites = InternalArtemisTcpTransport.CIPHER_SUITES.toTypedArray()
|
||||
sslEngine.enableSessionCreation = true
|
||||
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 revocationChecker = certPathBuilder.revocationChecker as PKIXRevocationChecker
|
||||
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.
|
||||
revocationChecker.options = revocationChecker.options + PKIXRevocationChecker.Option.SOFT_FAIL
|
||||
}
|
||||
val pkixParams = PKIXBuilderParameters(trustStore, X509CertSelector())
|
||||
val pkixParams = PKIXBuilderParameters(trustStore.value.internal, X509CertSelector())
|
||||
pkixParams.addCertPathChecker(revocationChecker)
|
||||
return CertPathTrustManagerParameters(pkixParams)
|
||||
}
|
||||
|
||||
fun KeyManagerFactory.init(keyStore: CertificateStore) = init(keyStore.value.internal, keyStore.password.toCharArray())
|
||||
|
||||
fun TrustManagerFactory.init(trustStore: CertificateStore) = init(trustStore.value.internal)
|
||||
|
@ -1,8 +1,7 @@
|
||||
package net.corda.nodeapi.internal.crypto
|
||||
|
||||
import net.corda.core.internal.validate
|
||||
import net.corda.nodeapi.internal.DEV_CA_TRUST_STORE_FILE
|
||||
import net.corda.nodeapi.internal.DEV_CA_TRUST_STORE_PASS
|
||||
import net.corda.nodeapi.internal.loadDevCaTrustStore
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
@ -22,8 +21,8 @@ class DevCertificatesTest {
|
||||
@Test
|
||||
fun `create server certificate in keystore for SSL`() {
|
||||
// given
|
||||
val newTrustStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/$DEV_CA_TRUST_STORE_FILE"), DEV_CA_TRUST_STORE_PASS)
|
||||
val newTrustRoot = newTrustStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA)
|
||||
val newTrustStore = loadDevCaTrustStore()
|
||||
val newTrustRoot = newTrustStore[X509Utilities.CORDA_ROOT_CA]
|
||||
val newTrustAnchor = TrustAnchor(newTrustRoot, null)
|
||||
|
||||
val oldNodeCaKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("regression-test/$OLD_NODE_DEV_KEYSTORE_FILE_NAME"), OLD_DEV_KEYSTORE_PASS)
|
||||
|
@ -10,8 +10,11 @@ import net.corda.core.serialization.SerializationContext
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.node.serialization.amqp.AMQPServerSerializationScheme
|
||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
||||
import net.corda.nodeapi.internal.createDevKeyStores
|
||||
import net.corda.nodeapi.internal.config.MutualSslConfiguration
|
||||
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.SerializationContextImpl
|
||||
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.BOB_NAME
|
||||
import net.corda.testing.core.TestIdentity
|
||||
import net.corda.testing.internal.stubs.CertificateStoreStubs
|
||||
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.bouncycastle.asn1.x509.*
|
||||
@ -31,7 +35,6 @@ import java.io.IOException
|
||||
import java.net.InetAddress
|
||||
import java.net.InetSocketAddress
|
||||
import java.nio.file.Path
|
||||
import java.security.SecureRandom
|
||||
import java.security.cert.CertPath
|
||||
import java.security.cert.X509Certificate
|
||||
import java.util.*
|
||||
@ -180,29 +183,27 @@ class X509UtilitiesTest {
|
||||
|
||||
@Test
|
||||
fun `create server certificate in keystore for SSL`() {
|
||||
val sslConfig = object : SSLConfiguration {
|
||||
override val certificatesDirectory = tempFolder.root.toPath()
|
||||
override val keyStorePassword = "serverstorepass"
|
||||
override val trustStorePassword = "trustpass"
|
||||
override val crlCheckSoftFail: Boolean = true
|
||||
}
|
||||
val certificatesDirectory = tempFolder.root.toPath()
|
||||
val signingCertStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory, "serverstorepass")
|
||||
val p2pSslConfig = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory, keyStorePassword = "serverstorepass")
|
||||
|
||||
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
|
||||
|
||||
// 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
|
||||
val serverKeyStore = loadKeyStore(sslConfig.nodeKeystore, sslConfig.keyStorePassword)
|
||||
val (serverCert, serverKeyPair) = serverKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, sslConfig.keyStorePassword)
|
||||
val serverKeyStore = signingCertStore.get().value
|
||||
val (serverCert, serverKeyPair) = serverKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)
|
||||
|
||||
serverCert.checkValidity()
|
||||
serverCert.verify(intermediateCa.certificate.publicKey)
|
||||
assertThat(CordaX500Name.build(serverCert.subjectX500Principal)).isEqualTo(MEGA_CORP.name)
|
||||
|
||||
// Load back SSL certificate
|
||||
val sslKeyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword)
|
||||
val (sslCert) = sslKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_TLS, sslConfig.keyStorePassword)
|
||||
val sslKeyStoreReloaded = p2pSslConfig.keyStore.get()
|
||||
val (sslCert) = sslKeyStoreReloaded.query { getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_TLS, p2pSslConfig.keyStore.password) }
|
||||
|
||||
sslCert.checkValidity()
|
||||
sslCert.verify(serverCert.publicKey)
|
||||
@ -216,25 +217,20 @@ class X509UtilitiesTest {
|
||||
|
||||
@Test
|
||||
fun `create server cert and use in SSL socket`() {
|
||||
val sslConfig = object : SSLConfiguration {
|
||||
override val certificatesDirectory = tempFolder.root.toPath()
|
||||
override val keyStorePassword = "serverstorepass"
|
||||
override val trustStorePassword = "trustpass"
|
||||
override val crlCheckSoftFail: Boolean = true
|
||||
}
|
||||
val sslConfig = CertificateStoreStubs.P2P.withCertificatesDirectory(tempFolder.root.toPath(), keyStorePassword = "serverstorepass")
|
||||
|
||||
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
|
||||
|
||||
// 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)
|
||||
|
||||
val keyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword)
|
||||
val trustStore = loadKeyStore(sslConfig.trustStoreFile, sslConfig.trustStorePassword)
|
||||
val keyStore = sslConfig.keyStore.get()
|
||||
val trustStore = sslConfig.trustStore.get()
|
||||
|
||||
val context = SSLContext.getInstance("TLS")
|
||||
val keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
|
||||
keyManagerFactory.init(keyStore, sslConfig.keyStorePassword.toCharArray())
|
||||
keyManagerFactory.init(keyStore)
|
||||
val keyManagers = keyManagerFactory.keyManagers
|
||||
val trustMgrFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
|
||||
trustMgrFactory.init(trustStore)
|
||||
@ -313,10 +309,9 @@ class X509UtilitiesTest {
|
||||
|
||||
private fun tempFile(name: String): Path = tempFolder.root.toPath() / name
|
||||
|
||||
private fun SSLConfiguration.createTrustStore(rootCert: X509Certificate) {
|
||||
val trustStore = loadOrCreateKeyStore(trustStoreFile, trustStorePassword)
|
||||
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCert)
|
||||
trustStore.save(trustStoreFile, trustStorePassword)
|
||||
private fun MutualSslConfiguration.createTrustStore(rootCert: X509Certificate) {
|
||||
val trustStore = this.trustStore.get(true)
|
||||
trustStore[X509Utilities.CORDA_ROOT_CA] = rootCert
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -12,19 +12,15 @@ import net.corda.core.messaging.startFlow
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.node.internal.NodeStartup
|
||||
import net.corda.node.services.Permissions.Companion.startFlow
|
||||
import net.corda.nodeapi.exceptions.InternalNodeException
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.core.BOB_NAME
|
||||
import net.corda.testing.core.DUMMY_BANK_A_NAME
|
||||
import net.corda.testing.driver.DriverParameters
|
||||
import net.corda.testing.driver.NodeHandle
|
||||
import net.corda.testing.driver.NodeParameters
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.internal.IntegrationTest
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.internal.toDatabaseSchemaName
|
||||
import net.corda.testing.node.User
|
||||
import net.corda.testing.node.internal.startNode
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Test
|
||||
|
@ -4,20 +4,19 @@ import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
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.X509Utilities
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||
import net.corda.testing.driver.DriverParameters
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.internal.stubs.CertificateStoreStubs
|
||||
import net.corda.testing.internal.IntegrationTest
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.internal.toDatabaseSchemaName
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Test
|
||||
import java.nio.file.Path
|
||||
import javax.security.auth.x500.X500Principal
|
||||
|
||||
class NodeKeystoreCheckTest : IntegrationTest() {
|
||||
@ -41,13 +40,11 @@ class NodeKeystoreCheckTest : IntegrationTest() {
|
||||
driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList())) {
|
||||
// Create keystores
|
||||
val keystorePassword = "password"
|
||||
val config = object : SSLConfiguration {
|
||||
override val keyStorePassword: String = keystorePassword
|
||||
override val trustStorePassword: String = keystorePassword
|
||||
override val certificatesDirectory: Path = baseDirectory(ALICE_NAME) / "certificates"
|
||||
override val crlCheckSoftFail: Boolean = true
|
||||
}
|
||||
config.configureDevKeyAndTrustStores(ALICE_NAME)
|
||||
val certificatesDirectory = baseDirectory(ALICE_NAME) / "certificates"
|
||||
val signingCertStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory, keystorePassword)
|
||||
val p2pSslConfig = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory, keyStorePassword = keystorePassword, trustStorePassword = keystorePassword)
|
||||
|
||||
p2pSslConfig.configureDevKeyAndTrustStores(ALICE_NAME, signingCertStore, certificatesDirectory)
|
||||
|
||||
// This should pass with correct keystore.
|
||||
val node = startNode(
|
||||
@ -59,7 +56,7 @@ class NodeKeystoreCheckTest : IntegrationTest() {
|
||||
node.stop()
|
||||
|
||||
// Fiddle with node keystore.
|
||||
config.loadNodeKeyStore().update {
|
||||
signingCertStore.get().update {
|
||||
// Self signed root
|
||||
val badRootKeyPair = Crypto.generateKeyPair()
|
||||
val badRoot = X509Utilities.createSelfSignedCACertificate(X500Principal("O=Bad Root,L=Lodnon,C=GB"), badRootKeyPair)
|
||||
|
@ -23,6 +23,7 @@ import net.corda.testing.core.BOB_NAME
|
||||
import net.corda.testing.core.MAX_MESSAGE_SIZE
|
||||
import net.corda.testing.core.TestIdentity
|
||||
import net.corda.testing.driver.PortAllocation
|
||||
import net.corda.testing.internal.stubs.CertificateStoreStubs
|
||||
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.RoutingType
|
||||
@ -33,7 +34,6 @@ import org.junit.Ignore
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import java.security.KeyStore
|
||||
import java.util.*
|
||||
import kotlin.system.measureNanoTime
|
||||
import kotlin.system.measureTimeMillis
|
||||
@ -242,12 +242,17 @@ class AMQPBridgeTest {
|
||||
|
||||
|
||||
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 {
|
||||
doReturn(temporaryFolder.root.toPath() / "artemis").whenever(it).baseDirectory
|
||||
doReturn(baseDir).whenever(it).baseDirectory
|
||||
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("cordacadevpass").whenever(it).keyStorePassword
|
||||
doReturn(artemisAddress).whenever(it).p2pAddress
|
||||
doReturn(null).whenever(it).jmxMonitoringHttpPort
|
||||
doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000))).whenever(it).enterpriseConfiguration
|
||||
@ -255,11 +260,11 @@ class AMQPBridgeTest {
|
||||
artemisConfig.configureWithDevSSLCertificate()
|
||||
|
||||
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()
|
||||
artemisClient.start()
|
||||
val bridgeManager = AMQPBridgeManager(artemisConfig, artemisAddress, MAX_MESSAGE_SIZE)
|
||||
val bridgeManager = AMQPBridgeManager(artemisConfig.p2pSslOptions, artemisAddress, MAX_MESSAGE_SIZE)
|
||||
bridgeManager.start()
|
||||
val artemis = artemisClient.started!!
|
||||
if (sourceQueueName != null) {
|
||||
@ -272,18 +277,23 @@ class AMQPBridgeTest {
|
||||
|
||||
|
||||
private fun createArtemisReceiver(targetAdress: NetworkHostAndPort, workingDir: String): Pair<ArtemisMessagingServer, ArtemisMessagingClient> {
|
||||
val baseDir = temporaryFolder.root.toPath() / workingDir
|
||||
val certificatesDirectory = baseDir / "certificates"
|
||||
val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)
|
||||
val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)
|
||||
val artemisConfig = rigorousMock<AbstractNodeConfiguration>().also {
|
||||
doReturn(temporaryFolder.root.toPath() / workingDir).whenever(it).baseDirectory
|
||||
doReturn(baseDir).whenever(it).baseDirectory
|
||||
doReturn(certificatesDirectory).whenever(it).certificatesDirectory
|
||||
doReturn(BOB_NAME).whenever(it).myLegalName
|
||||
doReturn("trustpass").whenever(it).trustStorePassword
|
||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||
doReturn(signingCertificateStore).whenever(it).signingCertificateStore
|
||||
doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
|
||||
doReturn(targetAdress).whenever(it).p2pAddress
|
||||
doReturn("").whenever(it).jmxMonitoringHttpPort
|
||||
doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000))).whenever(it).enterpriseConfiguration
|
||||
}
|
||||
artemisConfig.configureWithDevSSLCertificate()
|
||||
val artemisServer = ArtemisMessagingServer(artemisConfig, NetworkHostAndPort("0.0.0.0", targetAdress.port), MAX_MESSAGE_SIZE)
|
||||
val artemisClient = ArtemisMessagingClient(artemisConfig, targetAdress, MAX_MESSAGE_SIZE, confirmationWindowSize = 10 * 1024)
|
||||
val artemisClient = ArtemisMessagingClient(artemisConfig.p2pSslOptions, targetAdress, MAX_MESSAGE_SIZE, confirmationWindowSize = 10 * 1024)
|
||||
artemisServer.start()
|
||||
artemisClient.start()
|
||||
|
||||
@ -292,18 +302,23 @@ class AMQPBridgeTest {
|
||||
}
|
||||
|
||||
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 {
|
||||
doReturn(temporaryFolder.root.toPath() / "server").whenever(it).baseDirectory
|
||||
doReturn(BOB_NAME).whenever(it).myLegalName
|
||||
doReturn("trustpass").whenever(it).trustStorePassword
|
||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||
doReturn(certificatesDirectory).whenever(it).certificatesDirectory
|
||||
doReturn(signingCertificateStore).whenever(it).signingCertificateStore
|
||||
doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
|
||||
}
|
||||
serverConfig.configureWithDevSSLCertificate()
|
||||
|
||||
val keyStore = serverConfig.p2pSslOptions.keyStore.get()
|
||||
val amqpConfig = object : AMQPConfiguration {
|
||||
override val keyStore: KeyStore = serverConfig.loadSslKeyStore().internal
|
||||
override val keyStorePrivateKeyPassword: CharArray = serverConfig.keyStorePassword.toCharArray()
|
||||
override val trustStore: KeyStore = serverConfig.loadTrustStore().internal
|
||||
override val keyStore = keyStore
|
||||
override val trustStore = serverConfig.p2pSslOptions.trustStore.get()
|
||||
override val trace: Boolean = true
|
||||
override val maxMessageSize: Int = maxMessageSize
|
||||
}
|
||||
|
@ -13,7 +13,8 @@ import net.corda.core.utilities.seconds
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.node.services.config.configureWithDevSSLCertificate
|
||||
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.protonwrapper.messages.MessageStatus
|
||||
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.MAX_MESSAGE_SIZE
|
||||
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_ROOT_CA
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
@ -50,7 +52,6 @@ import java.io.Closeable
|
||||
import java.math.BigInteger
|
||||
import java.net.InetSocketAddress
|
||||
import java.security.KeyPair
|
||||
import java.security.KeyStore
|
||||
import java.security.PrivateKey
|
||||
import java.security.Security
|
||||
import java.security.cert.X509CRL
|
||||
@ -330,22 +331,25 @@ class CertificateRevocationListNodeTests {
|
||||
nodeCrlDistPoint: String = "http://${server.hostAndPort}/crl/node.crl",
|
||||
tlsCrlDistPoint: String? = "http://${server.hostAndPort}/crl/empty.crl",
|
||||
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 {
|
||||
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("trustpass").whenever(it).trustStorePassword
|
||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||
doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
|
||||
doReturn(signingCertificateStore).whenever(it).signingCertificateStore
|
||||
doReturn(crlCheckSoftFail).whenever(it).crlCheckSoftFail
|
||||
}
|
||||
clientConfig.configureWithDevSSLCertificate()
|
||||
val nodeCert = clientConfig.recreateNodeCaAndTlsCertificates(nodeCrlDistPoint, tlsCrlDistPoint)
|
||||
val clientTruststore = clientConfig.loadTrustStore().internal
|
||||
val clientKeystore = clientConfig.loadSslKeyStore().internal
|
||||
val nodeCert = (signingCertificateStore to p2pSslConfiguration).recreateNodeCaAndTlsCertificates(nodeCrlDistPoint, tlsCrlDistPoint)
|
||||
val keyStore = clientConfig.p2pSslOptions.keyStore.get()
|
||||
|
||||
val amqpConfig = object : AMQPConfiguration {
|
||||
override val keyStore: KeyStore = clientKeystore
|
||||
override val keyStorePrivateKeyPassword: CharArray = clientConfig.keyStorePassword.toCharArray()
|
||||
override val trustStore: KeyStore = clientTruststore
|
||||
override val keyStore = keyStore
|
||||
override val trustStore = clientConfig.p2pSslOptions.trustStore.get()
|
||||
override val crlCheckSoftFail: Boolean = crlCheckSoftFail
|
||||
override val maxMessageSize: Int = maxMessageSize
|
||||
}
|
||||
@ -360,21 +364,24 @@ class CertificateRevocationListNodeTests {
|
||||
nodeCrlDistPoint: String = "http://${server.hostAndPort}/crl/node.crl",
|
||||
tlsCrlDistPoint: String? = "http://${server.hostAndPort}/crl/empty.crl",
|
||||
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 {
|
||||
doReturn(temporaryFolder.root.toPath() / "server").whenever(it).baseDirectory
|
||||
doReturn(baseDirectory).whenever(it).baseDirectory
|
||||
doReturn(certificatesDirectory).whenever(it).certificatesDirectory
|
||||
doReturn(name).whenever(it).myLegalName
|
||||
doReturn("trustpass").whenever(it).trustStorePassword
|
||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||
doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
|
||||
doReturn(signingCertificateStore).whenever(it).signingCertificateStore
|
||||
doReturn(crlCheckSoftFail).whenever(it).crlCheckSoftFail
|
||||
}
|
||||
serverConfig.configureWithDevSSLCertificate()
|
||||
val nodeCert = serverConfig.recreateNodeCaAndTlsCertificates(nodeCrlDistPoint, tlsCrlDistPoint)
|
||||
val serverTruststore = serverConfig.loadTrustStore().internal
|
||||
val serverKeystore = serverConfig.loadSslKeyStore().internal
|
||||
val nodeCert = (signingCertificateStore to p2pSslConfiguration).recreateNodeCaAndTlsCertificates(nodeCrlDistPoint, tlsCrlDistPoint)
|
||||
val keyStore = serverConfig.p2pSslOptions.keyStore.get()
|
||||
val amqpConfig = object : AMQPConfiguration {
|
||||
override val keyStore: KeyStore = serverKeystore
|
||||
override val keyStorePrivateKeyPassword: CharArray = serverConfig.keyStorePassword.toCharArray()
|
||||
override val trustStore: KeyStore = serverTruststore
|
||||
override val keyStore = keyStore
|
||||
override val trustStore = serverConfig.p2pSslOptions.trustStore.get()
|
||||
override val crlCheckSoftFail: Boolean = crlCheckSoftFail
|
||||
override val maxMessageSize: Int = maxMessageSize
|
||||
}
|
||||
@ -384,22 +391,28 @@ class CertificateRevocationListNodeTests {
|
||||
amqpConfig), nodeCert)
|
||||
}
|
||||
|
||||
private fun SSLConfiguration.recreateNodeCaAndTlsCertificates(nodeCaCrlDistPoint: String, tlsCrlDistPoint: String?): X509Certificate {
|
||||
val nodeKeyStore = loadNodeKeyStore()
|
||||
val (nodeCert, nodeKeys) = nodeKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)
|
||||
private fun Pair<CertificateStoreSupplier, MutualSslConfiguration>.recreateNodeCaAndTlsCertificates(nodeCaCrlDistPoint: String, tlsCrlDistPoint: String?): X509Certificate {
|
||||
|
||||
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 nodeCertChain = listOf(newNodeCert, INTERMEDIATE_CA.certificate, *nodeKeyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA).drop(2).toTypedArray())
|
||||
nodeKeyStore.internal.deleteEntry(X509Utilities.CORDA_CLIENT_CA)
|
||||
nodeKeyStore.save()
|
||||
val nodeCertChain = listOf(newNodeCert, INTERMEDIATE_CA.certificate, *nodeKeyStore.query { getCertificateChain(X509Utilities.CORDA_CLIENT_CA) }.drop(2).toTypedArray())
|
||||
nodeKeyStore.update {
|
||||
internal.deleteEntry(X509Utilities.CORDA_CLIENT_CA)
|
||||
}
|
||||
nodeKeyStore.update {
|
||||
setPrivateKey(X509Utilities.CORDA_CLIENT_CA, nodeKeys.private, nodeCertChain)
|
||||
}
|
||||
val sslKeyStore = loadSslKeyStore()
|
||||
val (tlsCert, tlsKeys) = sslKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_TLS)
|
||||
val sslKeyStore = p2pSslConfiguration.keyStore.get()
|
||||
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 sslCertChain = listOf(newTlsCert, newNodeCert, INTERMEDIATE_CA.certificate, *sslKeyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).drop(3).toTypedArray())
|
||||
sslKeyStore.internal.deleteEntry(X509Utilities.CORDA_CLIENT_TLS)
|
||||
sslKeyStore.save()
|
||||
val sslCertChain = listOf(newTlsCert, newNodeCert, INTERMEDIATE_CA.certificate, *sslKeyStore.query { getCertificateChain(X509Utilities.CORDA_CLIENT_TLS) }.drop(3).toTypedArray())
|
||||
|
||||
sslKeyStore.update {
|
||||
internal.deleteEntry(X509Utilities.CORDA_CLIENT_TLS)
|
||||
}
|
||||
sslKeyStore.update {
|
||||
setPrivateKey(X509Utilities.CORDA_CLIENT_TLS, tlsKeys.private, sslCertChain)
|
||||
}
|
||||
|
@ -14,16 +14,18 @@ import net.corda.node.services.config.MutualExclusionConfiguration
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.node.services.config.configureWithDevSSLCertificate
|
||||
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.ArtemisMessagingComponent.Companion.P2P_PREFIX
|
||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
||||
import net.corda.nodeapi.internal.createDevKeyStores
|
||||
import net.corda.nodeapi.internal.InternalArtemisTcpTransport
|
||||
import net.corda.nodeapi.internal.config.MutualSslConfiguration
|
||||
import net.corda.nodeapi.internal.registerDevP2pCertificates
|
||||
import net.corda.nodeapi.internal.crypto.*
|
||||
import net.corda.nodeapi.internal.protonwrapper.messages.MessageStatus
|
||||
import net.corda.nodeapi.internal.protonwrapper.netty.AMQPClient
|
||||
import net.corda.nodeapi.internal.protonwrapper.netty.AMQPConfiguration
|
||||
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.BOB_NAME
|
||||
import net.corda.testing.core.CHARLIE_NAME
|
||||
@ -31,13 +33,13 @@ import net.corda.testing.core.MAX_MESSAGE_SIZE
|
||||
import net.corda.testing.driver.PortAllocation
|
||||
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
import net.corda.testing.internal.stubs.CertificateStoreStubs
|
||||
import org.apache.activemq.artemis.api.core.RoutingType
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.Assert.assertArrayEquals
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import java.security.KeyStore
|
||||
import java.security.cert.X509Certificate
|
||||
import javax.net.ssl.*
|
||||
import kotlin.concurrent.thread
|
||||
@ -120,34 +122,30 @@ class ProtonWrapperTests {
|
||||
}
|
||||
}
|
||||
|
||||
private fun SSLConfiguration.createTrustStore(rootCert: X509Certificate) {
|
||||
val trustStore = loadOrCreateKeyStore(trustStoreFile, trustStorePassword)
|
||||
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCert)
|
||||
trustStore.save(trustStoreFile, trustStorePassword)
|
||||
}
|
||||
private fun MutualSslConfiguration.createTrustStore(rootCert: X509Certificate) {
|
||||
|
||||
trustStore.get(true)[X509Utilities.CORDA_ROOT_CA] = rootCert
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Test AMQP Client with invalid root certificate`() {
|
||||
val sslConfig = object : SSLConfiguration {
|
||||
override val certificatesDirectory = temporaryFolder.root.toPath()
|
||||
override val keyStorePassword = "serverstorepass"
|
||||
override val trustStorePassword = "trustpass"
|
||||
override val crlCheckSoftFail: Boolean = true
|
||||
}
|
||||
val certificatesDirectory = temporaryFolder.root.toPath()
|
||||
val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory, "serverstorepass")
|
||||
val sslConfig = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory, keyStorePassword = "serverstorepass")
|
||||
|
||||
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
|
||||
|
||||
// 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)
|
||||
|
||||
val keyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword)
|
||||
val trustStore = loadKeyStore(sslConfig.trustStoreFile, sslConfig.trustStorePassword)
|
||||
val keyStore = sslConfig.keyStore.get()
|
||||
val trustStore = sslConfig.trustStore.get()
|
||||
|
||||
val context = SSLContext.getInstance("TLS")
|
||||
val keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
|
||||
keyManagerFactory.init(keyStore, sslConfig.keyStorePassword.toCharArray())
|
||||
keyManagerFactory.init(keyStore)
|
||||
val keyManagers = keyManagerFactory.keyManagers
|
||||
val trustMgrFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
|
||||
trustMgrFactory.init(trustStore)
|
||||
@ -157,7 +155,7 @@ class ProtonWrapperTests {
|
||||
val serverSocketFactory = context.serverSocketFactory
|
||||
|
||||
val serverSocket = serverSocketFactory.createServerSocket(serverPort) as SSLServerSocket
|
||||
val serverParams = SSLParameters(CIPHER_SUITES.toTypedArray(),
|
||||
val serverParams = SSLParameters(InternalArtemisTcpTransport.CIPHER_SUITES.toTypedArray(),
|
||||
arrayOf("TLSv1.2"))
|
||||
serverParams.wantClientAuth = true
|
||||
serverParams.needClientAuth = true
|
||||
@ -406,11 +404,16 @@ class ProtonWrapperTests {
|
||||
}
|
||||
|
||||
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 {
|
||||
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("trustpass").whenever(it).trustStorePassword
|
||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||
doReturn(signingCertificateStore).whenever(it).signingCertificateStore
|
||||
doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
|
||||
doReturn(NetworkHostAndPort("0.0.0.0", artemisPort)).whenever(it).p2pAddress
|
||||
doReturn(null).whenever(it).jmxMonitoringHttpPort
|
||||
doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000))).whenever(it).enterpriseConfiguration
|
||||
@ -419,28 +422,32 @@ class ProtonWrapperTests {
|
||||
artemisConfig.configureWithDevSSLCertificate()
|
||||
|
||||
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()
|
||||
client.start()
|
||||
return Pair(server, client)
|
||||
}
|
||||
|
||||
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 {
|
||||
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("trustpass").whenever(it).trustStorePassword
|
||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||
doReturn(signingCertificateStore).whenever(it).signingCertificateStore
|
||||
doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
|
||||
doReturn(true).whenever(it).crlCheckSoftFail
|
||||
}
|
||||
clientConfig.configureWithDevSSLCertificate()
|
||||
|
||||
val clientTruststore = clientConfig.loadTrustStore().internal
|
||||
val clientKeystore = clientConfig.loadSslKeyStore().internal
|
||||
val clientTruststore = clientConfig.p2pSslOptions.trustStore.get()
|
||||
val clientKeystore = clientConfig.p2pSslOptions.keyStore.get()
|
||||
val amqpConfig = object : AMQPConfiguration {
|
||||
override val keyStore: KeyStore = clientKeystore
|
||||
override val keyStorePrivateKeyPassword: CharArray = clientConfig.keyStorePassword.toCharArray()
|
||||
override val trustStore: KeyStore = clientTruststore
|
||||
override val keyStore = clientKeystore
|
||||
override val trustStore = clientTruststore
|
||||
override val trace: Boolean = true
|
||||
override val maxMessageSize: Int = maxMessageSize
|
||||
}
|
||||
@ -453,21 +460,25 @@ class ProtonWrapperTests {
|
||||
}
|
||||
|
||||
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 {
|
||||
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("trustpass").whenever(it).trustStorePassword
|
||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||
doReturn(signingCertificateStore).whenever(it).signingCertificateStore
|
||||
doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
|
||||
doReturn(true).whenever(it).crlCheckSoftFail
|
||||
}
|
||||
clientConfig.configureWithDevSSLCertificate()
|
||||
|
||||
val clientTruststore = clientConfig.loadTrustStore().internal
|
||||
val clientKeystore = clientConfig.loadSslKeyStore().internal
|
||||
val clientTruststore = clientConfig.p2pSslOptions.trustStore.get()
|
||||
val clientKeystore = clientConfig.p2pSslOptions.keyStore.get()
|
||||
val amqpConfig = object : AMQPConfiguration {
|
||||
override val keyStore: KeyStore = clientKeystore
|
||||
override val keyStorePrivateKeyPassword: CharArray = clientConfig.keyStorePassword.toCharArray()
|
||||
override val trustStore: KeyStore = clientTruststore
|
||||
override val keyStore = clientKeystore
|
||||
override val trustStore = clientTruststore
|
||||
override val trace: Boolean = true
|
||||
override val maxMessageSize: Int = maxMessageSize
|
||||
}
|
||||
@ -478,25 +489,26 @@ class ProtonWrapperTests {
|
||||
sharedThreadPool = sharedEventGroup)
|
||||
}
|
||||
|
||||
|
||||
private fun createServer(port: Int, name: CordaX500Name = ALICE_NAME, maxMessageSize: Int = MAX_MESSAGE_SIZE,
|
||||
crlCheckSoftFail: Boolean = true
|
||||
): AMQPServer {
|
||||
private fun createServer(port: Int, name: CordaX500Name = ALICE_NAME, maxMessageSize: Int = MAX_MESSAGE_SIZE, crlCheckSoftFail: Boolean = true): 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 {
|
||||
doReturn(temporaryFolder.root.toPath() / "server").whenever(it).baseDirectory
|
||||
doReturn(baseDirectory).whenever(it).baseDirectory
|
||||
doReturn(certificatesDirectory).whenever(it).certificatesDirectory
|
||||
doReturn(name).whenever(it).myLegalName
|
||||
doReturn("trustpass").whenever(it).trustStorePassword
|
||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||
doReturn(signingCertificateStore).whenever(it).signingCertificateStore
|
||||
doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
|
||||
doReturn(crlCheckSoftFail).whenever(it).crlCheckSoftFail
|
||||
}
|
||||
serverConfig.configureWithDevSSLCertificate()
|
||||
|
||||
val serverTruststore = serverConfig.loadTrustStore().internal
|
||||
val serverKeystore = serverConfig.loadSslKeyStore().internal
|
||||
val serverTruststore = serverConfig.p2pSslOptions.trustStore.get()
|
||||
val serverKeystore = serverConfig.p2pSslOptions.keyStore.get()
|
||||
val amqpConfig = object : AMQPConfiguration {
|
||||
override val keyStore: KeyStore = serverKeystore
|
||||
override val keyStorePrivateKeyPassword: CharArray = serverConfig.keyStorePassword.toCharArray()
|
||||
override val trustStore: KeyStore = serverTruststore
|
||||
override val keyStore = serverKeystore
|
||||
override val trustStore = serverTruststore
|
||||
override val trace: Boolean = true
|
||||
override val maxMessageSize: Int = maxMessageSize
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import net.corda.nodeapi.internal.protonwrapper.netty.*
|
||||
import net.corda.testing.core.*
|
||||
import net.corda.testing.driver.PortAllocation
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
import net.corda.testing.internal.stubs.CertificateStoreStubs
|
||||
import org.apache.activemq.artemis.api.core.RoutingType
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertArrayEquals
|
||||
@ -33,7 +34,6 @@ import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import java.security.KeyStore
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class SocksTests {
|
||||
@ -266,11 +266,17 @@ class SocksTests {
|
||||
}
|
||||
|
||||
private fun createArtemisServerAndClient(): Pair<ArtemisMessagingServer, ArtemisMessagingClient> {
|
||||
val baseDirectory = temporaryFolder.root.toPath() / "artemis"
|
||||
val certificatesDirectory = baseDirectory / "certificates"
|
||||
val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)
|
||||
val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)
|
||||
|
||||
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("trustpass").whenever(it).trustStorePassword
|
||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||
doReturn(signingCertificateStore).whenever(it).signingCertificateStore
|
||||
doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
|
||||
doReturn(NetworkHostAndPort("0.0.0.0", artemisPort)).whenever(it).p2pAddress
|
||||
doReturn(null).whenever(it).jmxMonitoringHttpPort
|
||||
doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000))).whenever(it).enterpriseConfiguration
|
||||
@ -278,27 +284,32 @@ class SocksTests {
|
||||
artemisConfig.configureWithDevSSLCertificate()
|
||||
|
||||
val server = ArtemisMessagingServer(artemisConfig, NetworkHostAndPort("0.0.0.0", artemisPort), MAX_MESSAGE_SIZE)
|
||||
val client = ArtemisMessagingClient(artemisConfig, NetworkHostAndPort("localhost", artemisPort), MAX_MESSAGE_SIZE)
|
||||
val client = ArtemisMessagingClient(artemisConfig.p2pSslOptions, NetworkHostAndPort("localhost", artemisPort), MAX_MESSAGE_SIZE)
|
||||
server.start()
|
||||
client.start()
|
||||
return Pair(server, client)
|
||||
}
|
||||
|
||||
private fun createClient(): AMQPClient {
|
||||
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 {
|
||||
doReturn(temporaryFolder.root.toPath() / "client").whenever(it).baseDirectory
|
||||
doReturn(certificatesDirectory).whenever(it).certificatesDirectory
|
||||
doReturn(BOB_NAME).whenever(it).myLegalName
|
||||
doReturn("trustpass").whenever(it).trustStorePassword
|
||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||
doReturn(signingCertificateStore).whenever(it).signingCertificateStore
|
||||
doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
|
||||
}
|
||||
clientConfig.configureWithDevSSLCertificate()
|
||||
|
||||
val clientTruststore = clientConfig.loadTrustStore().internal
|
||||
val clientKeystore = clientConfig.loadSslKeyStore().internal
|
||||
val clientTruststore = clientConfig.p2pSslOptions.trustStore.get()
|
||||
val clientKeystore = clientConfig.p2pSslOptions.keyStore.get()
|
||||
val amqpConfig = object : AMQPConfiguration {
|
||||
override val keyStore: KeyStore = clientKeystore
|
||||
override val keyStorePrivateKeyPassword: CharArray = clientConfig.keyStorePassword.toCharArray()
|
||||
override val trustStore: KeyStore = clientTruststore
|
||||
override val keyStore = clientKeystore
|
||||
override val trustStore = clientTruststore
|
||||
override val trace: Boolean = true
|
||||
override val maxMessageSize: Int = MAX_MESSAGE_SIZE
|
||||
override val socksProxyConfig: SocksProxyConfig? = SocksProxyConfig(SocksProxyVersion.SOCKS5, NetworkHostAndPort("127.0.0.1", socksPort), null, null)
|
||||
@ -312,20 +323,25 @@ class SocksTests {
|
||||
}
|
||||
|
||||
private fun createSharedThreadsClient(sharedEventGroup: EventLoopGroup, id: Int): AMQPClient {
|
||||
val baseDirectory = temporaryFolder.root.toPath() / "client_%$id"
|
||||
val certificatesDirectory = baseDirectory / "certificates"
|
||||
val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)
|
||||
val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)
|
||||
|
||||
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("trustpass").whenever(it).trustStorePassword
|
||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||
doReturn(signingCertificateStore).whenever(it).signingCertificateStore
|
||||
doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
|
||||
}
|
||||
clientConfig.configureWithDevSSLCertificate()
|
||||
|
||||
val clientTruststore = clientConfig.loadTrustStore().internal
|
||||
val clientKeystore = clientConfig.loadSslKeyStore().internal
|
||||
val clientTruststore = clientConfig.p2pSslOptions.trustStore.get()
|
||||
val clientKeystore = clientConfig.p2pSslOptions.keyStore.get()
|
||||
val amqpConfig = object : AMQPConfiguration {
|
||||
override val keyStore: KeyStore = clientKeystore
|
||||
override val keyStorePrivateKeyPassword: CharArray = clientConfig.keyStorePassword.toCharArray()
|
||||
override val trustStore: KeyStore = clientTruststore
|
||||
override val keyStore = clientKeystore
|
||||
override val trustStore = clientTruststore
|
||||
override val trace: Boolean = true
|
||||
override val maxMessageSize: Int = MAX_MESSAGE_SIZE
|
||||
override val socksProxyConfig: SocksProxyConfig? = SocksProxyConfig(SocksProxyVersion.SOCKS5, NetworkHostAndPort("127.0.0.1", socksPort), null, null)
|
||||
@ -339,20 +355,25 @@ class SocksTests {
|
||||
}
|
||||
|
||||
private fun createServer(port: Int, name: CordaX500Name = ALICE_NAME): AMQPServer {
|
||||
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 {
|
||||
doReturn(temporaryFolder.root.toPath() / "server").whenever(it).baseDirectory
|
||||
doReturn(baseDirectory).whenever(it).baseDirectory
|
||||
doReturn(certificatesDirectory).whenever(it).certificatesDirectory
|
||||
doReturn(name).whenever(it).myLegalName
|
||||
doReturn("trustpass").whenever(it).trustStorePassword
|
||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||
doReturn(signingCertificateStore).whenever(it).signingCertificateStore
|
||||
doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
|
||||
}
|
||||
serverConfig.configureWithDevSSLCertificate()
|
||||
|
||||
val serverTruststore = serverConfig.loadTrustStore().internal
|
||||
val serverKeystore = serverConfig.loadSslKeyStore().internal
|
||||
val serverTruststore = serverConfig.p2pSslOptions.trustStore.get()
|
||||
val serverKeystore = serverConfig.p2pSslOptions.keyStore.get()
|
||||
val amqpConfig = object : AMQPConfiguration {
|
||||
override val keyStore: KeyStore = serverKeystore
|
||||
override val keyStorePrivateKeyPassword: CharArray = serverConfig.keyStorePassword.toCharArray()
|
||||
override val trustStore: KeyStore = serverTruststore
|
||||
override val keyStore = serverKeystore
|
||||
override val trustStore = serverTruststore
|
||||
override val trace: Boolean = true
|
||||
override val maxMessageSize: Int = MAX_MESSAGE_SIZE
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import com.nhaarman.mockito_kotlin.doReturn
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import net.corda.core.crypto.generateKeyPair
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.seconds
|
||||
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.SerializationEnvironmentRule
|
||||
import net.corda.testing.driver.PortAllocation
|
||||
import net.corda.testing.internal.stubs.CertificateStoreStubs
|
||||
import net.corda.testing.internal.LogHelper
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
import net.corda.testing.node.internal.MOCK_VERSION_INFO
|
||||
@ -69,11 +71,18 @@ class ArtemisMessagingTest {
|
||||
@Before
|
||||
fun setUp() {
|
||||
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 {
|
||||
doReturn(temporaryFolder.root.toPath()).whenever(it).baseDirectory
|
||||
doReturn(ALICE_NAME).whenever(it).myLegalName
|
||||
doReturn("trustpass").whenever(it).trustStorePassword
|
||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||
doReturn(certificatesDirectory).whenever(it).certificatesDirectory
|
||||
doReturn(signingCertificateStore).whenever(it).signingCertificateStore
|
||||
doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
|
||||
doReturn(NetworkHostAndPort("0.0.0.0", serverPort)).whenever(it).p2pAddress
|
||||
doReturn(null).whenever(it).jmxMonitoringHttpPort
|
||||
doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000))).whenever(it).enterpriseConfiguration
|
||||
|
@ -1,7 +1,5 @@
|
||||
package net.corda.node.services.network
|
||||
|
||||
import net.corda.cordform.CordformNode
|
||||
import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.crypto.random63BitValue
|
||||
import net.corda.core.internal.*
|
||||
import net.corda.core.internal.concurrent.transpose
|
||||
@ -10,9 +8,7 @@ import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.node.services.config.configureDevKeyAndTrustStores
|
||||
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_UPDATE_FILE_NAME
|
||||
import net.corda.nodeapi.internal.network.SignedNetworkParameters
|
||||
@ -26,7 +22,6 @@ import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.internal.toDatabaseSchemaName
|
||||
import net.corda.testing.node.internal.*
|
||||
import net.corda.testing.node.internal.network.NetworkMapServer
|
||||
import net.corda.testing.node.internal.startNode
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.*
|
||||
|
@ -17,13 +17,13 @@ import net.corda.node.services.messaging.RPCServerConfiguration
|
||||
import net.corda.node.utilities.createKeyPairAndSelfSignedTLSCertificate
|
||||
import net.corda.node.utilities.saveToKeyStore
|
||||
import net.corda.node.utilities.saveToTrustStore
|
||||
import net.corda.nodeapi.ArtemisTcpTransport.Companion.rpcConnectorTcpTransport
|
||||
import net.corda.nodeapi.BrokerRpcSslOptions
|
||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
||||
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.testing.core.SerializationEnvironmentRule
|
||||
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.management.ActiveMQServerControl
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
@ -58,12 +58,12 @@ class ArtemisRpcTests {
|
||||
val brokerSslOptions = BrokerRpcSslOptions(keyStorePath, "password")
|
||||
val trustStorePath = saveToTrustStore(tempFile("rpcTruststore.jks"), selfSignCert)
|
||||
val clientSslOptions = ClientRpcSslOptions(trustStorePath, "password")
|
||||
testSslCommunication(createNodeSslConfig(tempFolder.root.toPath()), brokerSslOptions, true, clientSslOptions)
|
||||
testSslCommunication(p2pSslOptions(tempFolder.root.toPath()), brokerSslOptions, true, clientSslOptions)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun rpc_with_ssl_disabled() {
|
||||
testSslCommunication(createNodeSslConfig(tempFolder.root.toPath()), null, false, null)
|
||||
testSslCommunication(p2pSslOptions(tempFolder.root.toPath()), null, false, null)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -73,7 +73,7 @@ class ArtemisRpcTests {
|
||||
val brokerSslOptions = BrokerRpcSslOptions(keyStorePath, "password")
|
||||
// here client sslOptions are passed null (as in, do not use SSL)
|
||||
assertThatThrownBy {
|
||||
testSslCommunication(createNodeSslConfig(tempFolder.root.toPath()), brokerSslOptions, true, null)
|
||||
testSslCommunication(p2pSslOptions(tempFolder.root.toPath()), brokerSslOptions, true, null)
|
||||
}.isInstanceOf(ActiveMQConnectionTimedOutException::class.java)
|
||||
}
|
||||
|
||||
@ -91,11 +91,11 @@ class ArtemisRpcTests {
|
||||
val clientSslOptions = ClientRpcSslOptions(trustStorePath, "password")
|
||||
|
||||
assertThatThrownBy {
|
||||
testSslCommunication(createNodeSslConfig(tempFolder.root.toPath()), brokerSslOptions, true, clientSslOptions)
|
||||
testSslCommunication(p2pSslOptions(tempFolder.root.toPath()), brokerSslOptions, true, clientSslOptions)
|
||||
}.isInstanceOf(RPCException::class.java)
|
||||
}
|
||||
|
||||
private fun testSslCommunication(nodeSSlconfig: SSLConfiguration,
|
||||
private fun testSslCommunication(nodeSSlconfig: MutualSslConfiguration,
|
||||
brokerSslOptions: BrokerRpcSslOptions?,
|
||||
useSslForBroker: Boolean,
|
||||
clientSslOptions: ClientRpcSslOptions?,
|
||||
|
@ -1,29 +1,29 @@
|
||||
package net.corda.node.services.vault
|
||||
|
||||
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.testing.core.TestIdentity
|
||||
import net.corda.testing.internal.GlobalDatabaseRule
|
||||
import net.corda.testing.internal.toDatabaseSchemaName
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Rule
|
||||
import org.junit.rules.RuleChain
|
||||
|
||||
class VaultQueryIntegrationTests : VaultQueryTestsBase(), VaultQueryParties by vaultQueryTestRule {
|
||||
|
||||
companion object {
|
||||
val MEGA_CORP = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).name
|
||||
val databaseSchemas = listOf(Companion.MEGA_CORP.toDatabaseSchemaName())
|
||||
|
||||
val globalDatabaseRule = GlobalDatabaseRule(databaseSchemas)
|
||||
val vaultQueryTestRule = VaultQueryTestRule()
|
||||
|
||||
@ClassRule @JvmField
|
||||
val ruleChain = RuleChain.outerRule(globalDatabaseRule).around(vaultQueryTestRule)
|
||||
}
|
||||
|
||||
@Suppress("LeakingThis")
|
||||
@Rule
|
||||
@JvmField
|
||||
val transactionRule = VaultQueryRollbackRule(this)
|
||||
}
|
||||
//package net.corda.node.services.vault
|
||||
//
|
||||
//
|
||||
//import net.corda.core.identity.CordaX500Name
|
||||
//import net.corda.testing.core.TestIdentity
|
||||
//import net.corda.testing.internal.GlobalDatabaseRule
|
||||
//import net.corda.testing.internal.toDatabaseSchemaName
|
||||
//import org.junit.ClassRule
|
||||
//import org.junit.Rule
|
||||
//import org.junit.rules.RuleChain
|
||||
//
|
||||
//class VaultQueryIntegrationTests : VaultQueryTestsBase(), VaultQueryParties by vaultQueryTestRule {
|
||||
//
|
||||
// companion object {
|
||||
// val MEGA_CORP = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).name
|
||||
// val databaseSchemas = listOf(Companion.MEGA_CORP.toDatabaseSchemaName())
|
||||
//
|
||||
// val globalDatabaseRule = GlobalDatabaseRule(databaseSchemas)
|
||||
// val vaultQueryTestRule = VaultQueryTestRule()
|
||||
//
|
||||
// @ClassRule @JvmField
|
||||
// val ruleChain = RuleChain.outerRule(globalDatabaseRule).around(vaultQueryTestRule)
|
||||
// }
|
||||
//
|
||||
// @Suppress("LeakingThis")
|
||||
// @Rule
|
||||
// @JvmField
|
||||
// val transactionRule = VaultQueryRollbackRule(this)
|
||||
//}
|
||||
|
@ -2,7 +2,6 @@ package net.corda.services.messaging
|
||||
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.copyTo
|
||||
import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.exists
|
||||
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.DEV_INTERMEDIATE_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.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.core.ActiveMQClusterSecurityException
|
||||
import org.apache.activemq.artemis.api.core.ActiveMQNotConnectedException
|
||||
@ -84,57 +84,35 @@ class MQSecurityAsNodeTest : P2PMQSecurityTest() {
|
||||
|
||||
@Test
|
||||
fun `login with invalid certificate chain`() {
|
||||
val sslConfig = 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
|
||||
val certsDir = Files.createTempDirectory("certs")
|
||||
certsDir.createDirectories()
|
||||
val signingCertStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certsDir)
|
||||
val p2pSslConfig = CertificateStoreStubs.P2P.withCertificatesDirectory(certsDir)
|
||||
|
||||
init {
|
||||
val legalName = CordaX500Name("MegaCorp", "London", "GB")
|
||||
certificatesDirectory.createDirectories()
|
||||
if (!trustStoreFile.exists()) {
|
||||
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 legalName = CordaX500Name("MegaCorp", "London", "GB")
|
||||
if (!p2pSslConfig.trustStore.path.exists()) {
|
||||
val trustStore = p2pSslConfig.trustStore.get(true)
|
||||
loadDevCaTrustStore().copyTo(trustStore)
|
||||
}
|
||||
|
||||
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 {
|
||||
attacker.start(PEER_USER, PEER_USER)
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ import net.corda.node.internal.NodeWithInfo
|
||||
import net.corda.nodeapi.RPCApi
|
||||
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.INTERNAL_PREFIX
|
||||
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.BOB_NAME
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
@ -103,7 +103,7 @@ abstract class MQSecurityTest : NodeBasedTest() {
|
||||
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)
|
||||
clients += client
|
||||
return client
|
||||
|
@ -3,8 +3,8 @@ package net.corda.services.messaging
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.serialization.internal.nodeSerializationEnv
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.nodeapi.ArtemisTcpTransport
|
||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
||||
import net.corda.nodeapi.internal.InternalArtemisTcpTransport.Companion.p2pConnectorTcpTransport
|
||||
import net.corda.nodeapi.internal.config.MutualSslConfiguration
|
||||
import net.corda.testing.internal.configureTestSSL
|
||||
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.
|
||||
*/
|
||||
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 {
|
||||
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
|
||||
|
||||
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 {
|
||||
isBlockOnNonDurableSend = true
|
||||
threadPoolMaxSize = 1
|
||||
|
@ -46,6 +46,7 @@ import net.corda.node.services.FinalityHandler
|
||||
import net.corda.node.services.NotaryChangeHandler
|
||||
import net.corda.node.services.api.*
|
||||
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.events.NodeSchedulerService
|
||||
import net.corda.node.services.events.ScheduledActivityObserver
|
||||
@ -257,7 +258,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
||||
val trustRoot = initKeyStore()
|
||||
val (identity, identityKeyPair) = obtainIdentity(notaryConfig = null)
|
||||
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))
|
||||
return database.use {
|
||||
it.transaction {
|
||||
@ -287,7 +288,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
||||
log.info("Node starting up ...")
|
||||
|
||||
val trustRoot = initKeyStore()
|
||||
val nodeCa = configuration.loadNodeKeyStore().getCertificate(X509Utilities.CORDA_CLIENT_CA)
|
||||
val nodeCa = configuration.signingCertificateStore.get()[X509Utilities.CORDA_CLIENT_CA]
|
||||
initialiseJVMAgents()
|
||||
|
||||
schemaService.mappedSchemasWarnings().forEach {
|
||||
@ -392,10 +393,10 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
||||
open fun startShell() {
|
||||
if (configuration.shouldInitCrashShell()) {
|
||||
val shellConfiguration = configuration.toShellConfig()
|
||||
shellConfiguration.sshHostKeyDirectory?.let {
|
||||
shellConfiguration.sshdPort?.let {
|
||||
log.info("Binding Shell SSHD server on port $it.")
|
||||
}
|
||||
InteractiveShell.startShellInternal(shellConfiguration, cordappLoader.appClassLoader)
|
||||
InteractiveShell.startShell(shellConfiguration, cordappLoader.appClassLoader)
|
||||
}
|
||||
}
|
||||
|
||||
@ -720,8 +721,8 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
||||
private fun validateKeyStore(): X509Certificate {
|
||||
val containCorrectKeys = try {
|
||||
// This will throw IOException if key file not found or KeyStoreException if keystore password is incorrect.
|
||||
val sslKeystore = configuration.loadSslKeyStore()
|
||||
val identitiesKeystore = configuration.loadNodeKeyStore()
|
||||
val sslKeystore = configuration.p2pSslOptions.keyStore.get()
|
||||
val identitiesKeystore = configuration.signingCertificateStore.get()
|
||||
X509Utilities.CORDA_CLIENT_TLS in sslKeystore && X509Utilities.CORDA_CLIENT_CA in identitiesKeystore
|
||||
} catch (e: KeyStoreException) {
|
||||
log.warn("Certificate key store found but key store password does not match configuration.")
|
||||
@ -738,15 +739,15 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
||||
}
|
||||
|
||||
// Check all cert path chain to the trusted root
|
||||
val sslCertChainRoot = configuration.loadSslKeyStore().getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).last()
|
||||
val nodeCaCertChainRoot = configuration.loadNodeKeyStore().getCertificateChain(X509Utilities.CORDA_CLIENT_CA).last()
|
||||
val trustRoot = configuration.loadTrustStore().getCertificate(X509Utilities.CORDA_ROOT_CA)
|
||||
val sslCertChainRoot = configuration.p2pSslOptions.keyStore.get().query { getCertificateChain(X509Utilities.CORDA_CLIENT_TLS) }.last()
|
||||
val nodeCaCertChainRoot = configuration.signingCertificateStore.get().query { getCertificateChain(X509Utilities.CORDA_CLIENT_CA) }.last()
|
||||
val trustRoot = configuration.p2pSslOptions.trustStore.get()[X509Utilities.CORDA_ROOT_CA]
|
||||
|
||||
require(sslCertChainRoot == trustRoot) { "TLS certificate must chain to the trusted root." }
|
||||
require(nodeCaCertChainRoot == trustRoot) { "Client CA certificate must chain to the trusted root." }
|
||||
|
||||
if (configuration.devMode) {
|
||||
val blacklisted = isCRLDistributionPointBlacklisted(configuration.loadNodeKeyStore().getCertificateChain(X509Utilities.CORDA_CLIENT_CA))
|
||||
val blacklisted = isCRLDistributionPointBlacklisted(configuration.signingCertificateStore.get().query { getCertificateChain(X509Utilities.CORDA_CLIENT_CA) })
|
||||
if (blacklisted) {
|
||||
log.warn("The format of the autogenerated dev. mode certificate this system uses has been deprecated. Please contact support@r3.com for information on how to upgrade.")
|
||||
}
|
||||
@ -797,7 +798,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
||||
return notaryConfig.run {
|
||||
when {
|
||||
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)
|
||||
}
|
||||
bftSMaRt != null -> {
|
||||
@ -844,7 +845,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
||||
networkParameters: NetworkParameters)
|
||||
|
||||
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) {
|
||||
// Node's main identity or if it's a single node notary
|
||||
@ -859,25 +860,25 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
||||
if (privateKeyAlias !in keyStore) {
|
||||
singleName ?: throw IllegalArgumentException(
|
||||
"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
|
||||
check(singleName == configuration.myLegalName)
|
||||
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.
|
||||
val compositeKeyAlias = "$id-composite-key"
|
||||
val certificates = if (compositeKeyAlias in keyStore) {
|
||||
// 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
|
||||
// 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.
|
||||
listOf(certificate) + keyStore.getCertificateChain(privateKeyAlias).drop(1)
|
||||
listOf(certificate) + keyStore.query { getCertificateChain(privateKeyAlias) }.drop(1)
|
||||
} else {
|
||||
keyStore.getCertificateChain(privateKeyAlias).let {
|
||||
keyStore.query { getCertificateChain(privateKeyAlias) }.let {
|
||||
check(it[0] == x509Cert) { "Certificates from key store do not line up!" }
|
||||
it
|
||||
}
|
||||
@ -1075,4 +1076,13 @@ fun CordaPersistence.startHikariPool(hikariProperties: Properties, databaseConfi
|
||||
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)
|
||||
}
|
@ -233,7 +233,7 @@ open class Node(configuration: NodeConfiguration,
|
||||
|
||||
val externalBridge = configuration.enterpriseConfiguration.externalBridge
|
||||
val bridgeControlListener = if (externalBridge == null || !externalBridge) {
|
||||
BridgeControlListener(configuration, network.serverAddress, networkParameters.maxMessageSize)
|
||||
BridgeControlListener(configuration.p2pSslOptions, network.serverAddress, networkParameters.maxMessageSize)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
@ -244,7 +244,7 @@ open class Node(configuration: NodeConfiguration,
|
||||
rpcThreadPoolSize = configuration.enterpriseConfiguration.tuning.rpcThreadPoolSize
|
||||
)
|
||||
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 admin connection address", it.admin.toString())
|
||||
}
|
||||
@ -284,9 +284,9 @@ open class Node(configuration: NodeConfiguration,
|
||||
val rpcBrokerDirectory: Path = baseDirectory / "brokers" / "rpc"
|
||||
with(rpcOptions) {
|
||||
rpcBroker = if (useSsl) {
|
||||
ArtemisRpcBroker.withSsl(configuration, this.address, adminAddress, sslConfig!!, securityManager, MAX_RPC_MESSAGE_SIZE, jmxMonitoringHttpPort != null, rpcBrokerDirectory, shouldStartLocalShell())
|
||||
ArtemisRpcBroker.withSsl(configuration.p2pSslOptions, this.address, adminAddress, sslConfig!!, securityManager, MAX_RPC_MESSAGE_SIZE, jmxMonitoringHttpPort != null, rpcBrokerDirectory, shouldStartLocalShell())
|
||||
} else {
|
||||
ArtemisRpcBroker.withoutSsl(configuration, this.address, adminAddress, securityManager, MAX_RPC_MESSAGE_SIZE, jmxMonitoringHttpPort != null, rpcBrokerDirectory, shouldStartLocalShell())
|
||||
ArtemisRpcBroker.withoutSsl(configuration.p2pSslOptions, this.address, adminAddress, securityManager, MAX_RPC_MESSAGE_SIZE, jmxMonitoringHttpPort != null, rpcBrokerDirectory, shouldStartLocalShell())
|
||||
}
|
||||
}
|
||||
rpcBroker!!.closeOnStop()
|
||||
|
@ -10,11 +10,10 @@ import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.exists
|
||||
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.crypto.X509KeyStore
|
||||
import net.corda.nodeapi.internal.crypto.loadKeyStore
|
||||
import net.corda.nodeapi.internal.crypto.save
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.nio.file.Path
|
||||
import kotlin.math.min
|
||||
@ -83,22 +82,33 @@ object ConfigHelper {
|
||||
* the CA certs in Node resources. Then provision KeyStores into certificates folder under node path.
|
||||
*/
|
||||
// TODO Move this to KeyStoreConfigHelpers
|
||||
fun NodeConfiguration.configureWithDevSSLCertificate() = configureDevKeyAndTrustStores(myLegalName)
|
||||
fun NodeConfiguration.configureWithDevSSLCertificate() = p2pSslOptions.configureDevKeyAndTrustStores(myLegalName, signingCertificateStore, certificatesDirectory)
|
||||
|
||||
// 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()
|
||||
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.
|
||||
val distributedServiceKeystore = certificatesDirectory / "distributedService.jks"
|
||||
if (distributedServiceKeystore.exists()) {
|
||||
val serviceKeystore = X509KeyStore.fromFile(distributedServiceKeystore, DEV_CA_KEY_STORE_PASS)
|
||||
nodeKeyStore.update {
|
||||
signingKeyStore.update {
|
||||
serviceKeystore.aliases().forEach {
|
||||
if (serviceKeystore.internal.isKeyEntry(it)) {
|
||||
setPrivateKey(it, serviceKeystore.getPrivateKey(it, DEV_CA_PRIVATE_KEY_PASS), serviceKeystore.getCertificateChain(it))
|
||||
|
@ -11,7 +11,9 @@ import net.corda.core.utilities.loggerFor
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.node.services.config.rpc.NodeRpcOptions
|
||||
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.User
|
||||
import net.corda.nodeapi.internal.config.parseAs
|
||||
@ -32,7 +34,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 const val CORDAPPS_DIR_NAME_DEFAULT = "cordapps"
|
||||
|
||||
interface NodeConfiguration : NodeSSLConfiguration {
|
||||
interface NodeConfiguration {
|
||||
val myLegalName: CordaX500Name
|
||||
val emailAddress: String
|
||||
val jmxMonitoringHttpPort: Int?
|
||||
@ -75,8 +77,16 @@ interface NodeConfiguration : NodeSSLConfiguration {
|
||||
val effectiveH2Settings: NodeH2Settings?
|
||||
val flowMonitorPeriodMillis: Duration get() = DEFAULT_FLOW_MONITOR_PERIOD_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 baseDirectory: Path
|
||||
val certificatesDirectory: Path
|
||||
val signingCertificateStore: FileBasedCertificateStoreSupplier
|
||||
val p2pSslOptions: MutualSslConfiguration
|
||||
|
||||
val cordappDirectories: List<Path>
|
||||
|
||||
fun validate(): List<String>
|
||||
|
||||
companion object {
|
||||
@ -162,7 +172,7 @@ data class MySQLConfiguration(
|
||||
* further commit requests.
|
||||
*/
|
||||
val maxQueueSize: Int = 100_000
|
||||
) {
|
||||
) {
|
||||
init {
|
||||
require(connectionRetries >= 0) { "connectionRetries cannot be negative" }
|
||||
}
|
||||
@ -220,8 +230,8 @@ data class NodeConfigurationImpl(
|
||||
override val myLegalName: CordaX500Name,
|
||||
override val jmxMonitoringHttpPort: Int? = null,
|
||||
override val emailAddress: String,
|
||||
override val keyStorePassword: String,
|
||||
override val trustStorePassword: String,
|
||||
private val keyStorePassword: String,
|
||||
private val trustStorePassword: String,
|
||||
override val crlCheckSoftFail: Boolean,
|
||||
override val dataSourceProperties: Properties,
|
||||
override val compatibilityZoneURL: URL? = null,
|
||||
@ -292,6 +302,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
|
||||
get() {
|
||||
return actualRpcSettings.asOptions()
|
||||
@ -430,8 +451,6 @@ data class NodeConfigurationImpl(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
data class NodeRpcSettings(
|
||||
val address: NetworkHostAndPort?,
|
||||
val adminAddress: NetworkHostAndPort?,
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.corda.node.services.config.shell
|
||||
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.node.internal.clientSslOptionsCompatibleWith
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.INTERNAL_SHELL_USER
|
||||
import net.corda.tools.shell.ShellConfiguration
|
||||
@ -14,8 +15,8 @@ fun NodeConfiguration.toShellConfig() = ShellConfiguration(
|
||||
cordappsDirectory = this.baseDirectory.toString() / CORDAPPS_DIR,
|
||||
user = INTERNAL_SHELL_USER,
|
||||
password = INTERNAL_SHELL_USER,
|
||||
hostAndPort = this.rpcOptions.adminAddress,
|
||||
nodeSslConfig = this,
|
||||
hostAndPort = this.rpcOptions.address,
|
||||
ssl = clientSslOptionsCompatibleWith(this.rpcOptions),
|
||||
sshdPort = this.sshd?.port,
|
||||
sshHostKeyDirectory = this.baseDirectory / SSHD_HOSTKEY_DIR,
|
||||
noLocalShell = this.noLocalShell)
|
||||
|
@ -1,6 +1,5 @@
|
||||
package net.corda.node.services.messaging
|
||||
|
||||
import io.netty.channel.unix.Errors
|
||||
import net.corda.core.internal.ThreadBox
|
||||
import net.corda.core.internal.div
|
||||
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.core.internal.errors.AddressBindingException
|
||||
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.ArtemisMessageSizeChecksInterceptor
|
||||
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.NOTIFICATIONS_ADDRESS
|
||||
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_PREFIX
|
||||
import net.corda.nodeapi.internal.InternalArtemisTcpTransport.Companion.p2pAcceptorTcpTransport
|
||||
import net.corda.nodeapi.internal.requireOnDefaultFileSystem
|
||||
import org.apache.activemq.artemis.api.core.SimpleString
|
||||
import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl
|
||||
@ -120,7 +119,7 @@ class ArtemisMessagingServer(private val config: NodeConfiguration,
|
||||
bindingsDirectory = (artemisDir / "bindings").toString()
|
||||
journalDirectory = (artemisDir / "journal").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
|
||||
// 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
|
||||
@ -163,8 +162,8 @@ class ArtemisMessagingServer(private val config: NodeConfiguration,
|
||||
|
||||
@Throws(IOException::class, KeyStoreException::class)
|
||||
private fun createArtemisSecurityManager(): ActiveMQJAASSecurityManager {
|
||||
val keyStore = config.loadSslKeyStore().internal
|
||||
val trustStore = config.loadTrustStore().internal
|
||||
val keyStore = config.p2pSslOptions.keyStore.get().value.internal
|
||||
val trustStore = config.p2pSslOptions.trustStore.get().value.internal
|
||||
|
||||
val securityConfig = object : SecurityConfiguration() {
|
||||
// Override to make it work with our login module
|
||||
|
@ -6,9 +6,9 @@ import net.corda.core.serialization.SingletonSerializeAsToken
|
||||
import net.corda.core.serialization.internal.nodeSerializationEnv
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
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.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.ServerLocator
|
||||
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.
|
||||
*/
|
||||
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 rpcServer: RPCServer? = null
|
||||
|
||||
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 {
|
||||
// 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.
|
||||
|
@ -27,7 +27,6 @@ import net.corda.node.services.statemachine.DeduplicationId
|
||||
import net.corda.node.services.statemachine.ExternalEvent
|
||||
import net.corda.node.services.statemachine.SenderDeduplicationId
|
||||
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.Companion.BRIDGE_CONTROL
|
||||
@ -35,6 +34,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.P2PMessagingHeaders
|
||||
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.BridgeEntry
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
@ -152,7 +152,7 @@ class P2PMessagingClient(val config: NodeConfiguration,
|
||||
started = true
|
||||
log.info("Connecting to message broker: $serverAddress")
|
||||
// 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 {
|
||||
// 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.
|
||||
|
@ -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.security.RPCSecurityManager
|
||||
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.core.config.impl.SecurityConfiguration
|
||||
import org.apache.activemq.artemis.core.server.ActiveMQServer
|
||||
@ -28,17 +28,17 @@ class ArtemisRpcBroker internal constructor(
|
||||
private val maxMessageSize: Int,
|
||||
private val jmxEnabled: Boolean = false,
|
||||
private val baseDirectory: Path,
|
||||
private val nodeConfiguration: SSLConfiguration,
|
||||
private val nodeConfiguration: MutualSslConfiguration,
|
||||
private val shouldStartLocalShell: Boolean) : ArtemisBroker {
|
||||
|
||||
companion object {
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
@ -83,14 +83,14 @@ class ArtemisRpcBroker internal constructor(
|
||||
|
||||
@Throws(IOException::class, KeyStoreException::class)
|
||||
private fun createArtemisSecurityManager(loginListener: LoginListener): ActiveMQJAASSecurityManager {
|
||||
val keyStore = nodeConfiguration.loadSslKeyStore().internal
|
||||
val trustStore = nodeConfiguration.loadTrustStore().internal
|
||||
val keyStore = nodeConfiguration.keyStore.get()
|
||||
val trustStore = nodeConfiguration.trustStore.get()
|
||||
|
||||
val securityConfig = object : SecurityConfiguration() {
|
||||
override fun getAppConfigurationEntry(name: String): Array<AppConfigurationEntry> {
|
||||
val options = mapOf(
|
||||
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))
|
||||
}
|
||||
|
@ -4,12 +4,12 @@ import net.corda.core.internal.div
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.node.internal.artemis.BrokerJaasLoginModule
|
||||
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.RPCApi
|
||||
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.core.config.CoreQueueConfiguration
|
||||
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 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
|
||||
|
||||
init {
|
||||
|
@ -28,8 +28,7 @@ import net.corda.core.utilities.debug
|
||||
import net.corda.node.services.config.RaftConfig
|
||||
import net.corda.node.services.transactions.RaftTransactionCommitLog.Commands.CommitTransaction
|
||||
import net.corda.node.utilities.AppendOnlyPersistentMap
|
||||
import net.corda.nodeapi.internal.config.NodeSSLConfiguration
|
||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
||||
import net.corda.nodeapi.internal.config.MutualSslConfiguration
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
|
||||
import java.nio.file.Path
|
||||
@ -51,7 +50,8 @@ import javax.persistence.Table
|
||||
*/
|
||||
@ThreadSafe
|
||||
class RaftUniquenessProvider(
|
||||
private val transportConfiguration: NodeSSLConfiguration,
|
||||
private val storagePath: Path,
|
||||
private val transportConfiguration: MutualSslConfiguration,
|
||||
private val db: CordaPersistence,
|
||||
private val clock: Clock,
|
||||
private val metrics: MetricRegistry,
|
||||
@ -96,8 +96,6 @@ class RaftUniquenessProvider(
|
||||
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 server: CopycatServer
|
||||
|
||||
@ -155,14 +153,14 @@ class RaftUniquenessProvider(
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun buildTransport(config: SSLConfiguration): Transport? {
|
||||
private fun buildTransport(config: MutualSslConfiguration): Transport? {
|
||||
return NettyTransport.builder()
|
||||
.withSsl()
|
||||
.withSslProtocol(SslProtocol.TLSv1_2)
|
||||
.withKeyStorePath(config.sslKeystore.toString())
|
||||
.withKeyStorePassword(config.keyStorePassword)
|
||||
.withTrustStorePath(config.trustStoreFile.toString())
|
||||
.withTrustStorePassword(config.trustStorePassword)
|
||||
.withKeyStorePath(config.keyStore.path.toString())
|
||||
.withKeyStorePassword(config.keyStore.password)
|
||||
.withTrustStorePath(config.trustStore.path.toString())
|
||||
.withTrustStorePassword(config.trustStore.password)
|
||||
.build()
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,8 @@ import net.corda.core.internal.*
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.node.NodeRegistrationOption
|
||||
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.X509KeyStore
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
@ -33,7 +34,8 @@ import javax.security.auth.x500.X500Principal
|
||||
* needed.
|
||||
*/
|
||||
// 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 emailAddress: String,
|
||||
private val certService: NetworkRegistrationService,
|
||||
@ -48,9 +50,7 @@ open class NetworkRegistrationHelper(private val config: SSLConfiguration,
|
||||
val logger = contextLogger()
|
||||
}
|
||||
|
||||
private val requestIdStore = config.certificatesDirectory / "certificate-request-id.txt"
|
||||
// TODO: Use different password for private key.
|
||||
private val privateKeyPassword = config.keyStorePassword
|
||||
private val requestIdStore = certificatesDirectory / "certificate-request-id.txt"
|
||||
private val rootTrustStore: X509KeyStore
|
||||
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.
|
||||
*/
|
||||
fun buildKeystore() {
|
||||
config.certificatesDirectory.createDirectories()
|
||||
val nodeKeyStore = config.loadNodeKeyStore(createNew = true)
|
||||
certificatesDirectory.createDirectories()
|
||||
val nodeKeyStore = signingCertificateStore.get(createNew = true)
|
||||
if (keyAlias in nodeKeyStore) {
|
||||
println("Certificate already exists, Corda node will now terminate...")
|
||||
return
|
||||
}
|
||||
// TODO: Use different password for private key.
|
||||
val privateKeyPassword = nodeKeyStore.password
|
||||
val tlsCrlIssuerCert = validateAndGetTlsCrlIssuerCert()
|
||||
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.
|
||||
@ -89,7 +91,7 @@ open class NetworkRegistrationHelper(private val config: SSLConfiguration,
|
||||
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 {
|
||||
submitOrResumeCertificateSigningRequest(keyPair)
|
||||
@ -110,7 +112,7 @@ open class NetworkRegistrationHelper(private val config: SSLConfiguration,
|
||||
throw certificateRequestException
|
||||
}
|
||||
validateCertificates(keyPair.public, certificates)
|
||||
storePrivateKeyWithCertificates(nodeKeyStore, keyPair, certificates, keyAlias)
|
||||
storePrivateKeyWithCertificates(nodeKeyStore, keyPair, certificates, keyAlias, privateKeyPassword)
|
||||
onSuccess(keyPair, certificates, tlsCrlIssuerCert?.subjectX500Principal?.toX500Name())
|
||||
// All done, clean up temp files.
|
||||
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.")
|
||||
}
|
||||
|
||||
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.
|
||||
nodeKeystore.setPrivateKey(keyAlias, keyPair.private, certificates, keyPassword = config.keyStorePassword)
|
||||
nodeKeystore.internal.deleteEntry(SELF_SIGNED_PRIVATE_KEY)
|
||||
nodeKeystore.save()
|
||||
println("Private key '$keyAlias' and certificate stored in ${config.nodeKeystore}.")
|
||||
with(nodeKeystore.value) {
|
||||
setPrivateKey(keyAlias, keyPair.private, certificates, keyPassword = keyPassword)
|
||||
internal.deleteEntry(SELF_SIGNED_PRIVATE_KEY)
|
||||
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.
|
||||
// We use the self sign certificate to store the key temporarily in the keystore while waiting for the request approval.
|
||||
if (alias !in this) {
|
||||
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val selfSignCert = X509Utilities.createSelfSignedCACertificate(myLegalName.x500Principal, keyPair)
|
||||
// Save to the key store.
|
||||
setPrivateKey(alias, keyPair.private, listOf(selfSignCert), keyPassword = privateKeyPassword)
|
||||
save()
|
||||
with(value) {
|
||||
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 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.emailAddress,
|
||||
certService,
|
||||
@ -263,7 +271,7 @@ class NodeRegistrationHelper(private val config: NodeConfiguration, certService:
|
||||
}
|
||||
|
||||
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.")
|
||||
val sslKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val sslCert = X509Utilities.createCertificate(
|
||||
@ -277,17 +285,17 @@ class NodeRegistrationHelper(private val config: NodeConfiguration, certService:
|
||||
logger.info("Generated TLS certificate: $sslCert")
|
||||
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) {
|
||||
// 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.")
|
||||
// Assumes certificate chain always starts with client certificate and end with root certificate.
|
||||
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? {
|
||||
@ -296,8 +304,9 @@ class NodeRegistrationHelper(private val config: NodeConfiguration, certService:
|
||||
if (principalMatchesCertificatePrincipal(tlsCertCrlIssuer, rootCert)) {
|
||||
return rootCert
|
||||
}
|
||||
return if (config.trustStoreFile.exists()) {
|
||||
findMatchingCertificate(tlsCertCrlIssuer, config.loadTrustStore())
|
||||
val trustStore = config.p2pSslOptions.trustStore.getOptional()
|
||||
return if (trustStore != null) {
|
||||
findMatchingCertificate(tlsCertCrlIssuer, trustStore.value)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import net.corda.nodeapi.internal.crypto.CertificateType
|
||||
import net.corda.nodeapi.internal.crypto.X509KeyStore
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
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.rigorousMock
|
||||
import org.assertj.core.api.Assertions.*
|
||||
@ -52,10 +53,12 @@ class NetworkRegistrationHelperTest {
|
||||
val baseDirectory = fs.getPath("/baseDir").createDirectories()
|
||||
|
||||
abstract class AbstractNodeConfiguration : NodeConfiguration
|
||||
val certificatesDirectory = baseDirectory / "certificates"
|
||||
config = rigorousMock<AbstractNodeConfiguration>().also {
|
||||
doReturn(baseDirectory).whenever(it).baseDirectory
|
||||
doReturn("trustpass").whenever(it).trustStorePassword
|
||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||
doReturn(certificatesDirectory).whenever(it).certificatesDirectory
|
||||
doReturn(CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)).whenever(it).p2pSslOptions
|
||||
doReturn(CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)).whenever(it).signingCertificateStore
|
||||
doReturn(nodeLegalName).whenever(it).myLegalName
|
||||
doReturn("").whenever(it).emailAddress
|
||||
doReturn(null).whenever(it).tlsCertCrlDistPoint
|
||||
@ -71,30 +74,30 @@ class NetworkRegistrationHelperTest {
|
||||
|
||||
@Test
|
||||
fun `successful registration`() {
|
||||
assertThat(config.nodeKeystore).doesNotExist()
|
||||
assertThat(config.sslKeystore).doesNotExist()
|
||||
assertThat(config.trustStoreFile).doesNotExist()
|
||||
assertThat(config.signingCertificateStore.getOptional()).isNull()
|
||||
assertThat(config.p2pSslOptions.keyStore.getOptional()).isNull()
|
||||
assertThat(config.p2pSslOptions.trustStore.getOptional()).isNull()
|
||||
|
||||
val rootAndIntermediateCA = createDevIntermediateCaCertPath().also { saveNetworkTrustStore(it.first.certificate) }
|
||||
|
||||
createRegistrationHelper(rootAndIntermediateCA = rootAndIntermediateCA).buildKeystore()
|
||||
|
||||
val nodeKeystore = config.loadNodeKeyStore()
|
||||
val sslKeystore = config.loadSslKeyStore()
|
||||
val trustStore = config.loadTrustStore()
|
||||
val nodeKeystore = config.signingCertificateStore.get()
|
||||
val sslKeystore = config.p2pSslOptions.keyStore.get()
|
||||
val trustStore = config.p2pSslOptions.trustStore.get()
|
||||
|
||||
nodeKeystore.run {
|
||||
assertFalse(contains(X509Utilities.CORDA_INTERMEDIATE_CA))
|
||||
assertFalse(contains(X509Utilities.CORDA_ROOT_CA))
|
||||
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 {
|
||||
assertFalse(contains(X509Utilities.CORDA_CLIENT_CA))
|
||||
assertFalse(contains(X509Utilities.CORDA_INTERMEDIATE_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)
|
||||
// The TLS cert has the same subject as the node CA cert
|
||||
assertThat(CordaX500Name.build(nodeTlsCertChain[0].subjectX500Principal)).isEqualTo(nodeLegalName)
|
||||
@ -104,7 +107,7 @@ class NetworkRegistrationHelperTest {
|
||||
trustStore.run {
|
||||
assertFalse(contains(X509Utilities.CORDA_CLIENT_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
|
||||
fun `create service identity cert`() {
|
||||
assertThat(config.nodeKeystore).doesNotExist()
|
||||
assertThat(config.sslKeystore).doesNotExist()
|
||||
assertThat(config.trustStoreFile).doesNotExist()
|
||||
assertThat(config.signingCertificateStore.getOptional()).isNull()
|
||||
assertThat(config.p2pSslOptions.keyStore.getOptional()).isNull()
|
||||
assertThat(config.p2pSslOptions.trustStore.getOptional()).isNull()
|
||||
|
||||
val rootAndIntermediateCA = createDevIntermediateCaCertPath().also { saveNetworkTrustStore(it.first.certificate) }
|
||||
|
||||
createRegistrationHelper(CertRole.SERVICE_IDENTITY, rootAndIntermediateCA).buildKeystore()
|
||||
|
||||
val nodeKeystore = config.loadNodeKeyStore()
|
||||
val nodeKeystore = config.signingCertificateStore.get()
|
||||
|
||||
assertThat(config.sslKeystore).doesNotExist()
|
||||
assertThat(config.trustStoreFile).doesNotExist()
|
||||
assertThat(config.p2pSslOptions.keyStore.getOptional()).isNull()
|
||||
assertThat(config.p2pSslOptions.trustStore.getOptional()).isNull()
|
||||
|
||||
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_CLIENT_TLS))
|
||||
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) {
|
||||
CertRole.NODE_CA -> NodeRegistrationHelper(config, certService, NodeRegistrationOption(config.certificatesDirectory / networkRootTrustStoreFileName, networkRootTrustStorePassword))
|
||||
CertRole.SERVICE_IDENTITY -> NetworkRegistrationHelper(
|
||||
config,
|
||||
config.certificatesDirectory,
|
||||
config.signingCertificateStore,
|
||||
config.myLegalName,
|
||||
config.emailAddress,
|
||||
certService,
|
||||
|
@ -5,7 +5,7 @@ import com.typesafe.config.Config
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import com.typesafe.config.ConfigRenderOptions
|
||||
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.firstOf
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
@ -20,6 +20,7 @@ import net.corda.node.NodeRegistrationOption
|
||||
import net.corda.node.VersionInfo
|
||||
import net.corda.node.internal.Node
|
||||
import net.corda.node.internal.NodeWithInfo
|
||||
import net.corda.node.internal.clientSslOptionsCompatibleWith
|
||||
import net.corda.node.services.Permissions
|
||||
import net.corda.node.services.config.*
|
||||
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
|
||||
@ -27,9 +28,6 @@ import net.corda.node.utilities.registration.NodeRegistrationHelper
|
||||
import net.corda.nodeapi.internal.DevIdentityGenerator
|
||||
import net.corda.nodeapi.internal.SignedNodeInfo
|
||||
import net.corda.nodeapi.internal.addShutdownHook
|
||||
|
||||
import net.corda.nodeapi.internal.config.NodeSSLConfiguration
|
||||
import net.corda.nodeapi.internal.config.parseAs
|
||||
import net.corda.nodeapi.internal.config.toConfig
|
||||
import net.corda.nodeapi.internal.crypto.X509KeyStore
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
@ -45,6 +43,7 @@ import net.corda.testing.driver.internal.InProcessImpl
|
||||
import net.corda.testing.driver.internal.NodeHandleInternal
|
||||
import net.corda.testing.driver.internal.OutOfProcessImpl
|
||||
import net.corda.testing.internal.setGlobalSerialization
|
||||
import net.corda.testing.internal.stubs.CertificateStoreStubs
|
||||
import net.corda.testing.node.ClusterSpec
|
||||
import net.corda.testing.node.NotarySpec
|
||||
import net.corda.testing.node.User
|
||||
@ -172,7 +171,8 @@ class DriverDSLImpl(
|
||||
|
||||
private fun establishRpc(config: NodeConfig, processDeathFuture: CordaFuture<out Process>): CordaFuture<CordaRPCOps> {
|
||||
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") {
|
||||
try {
|
||||
config.corda.rpcUsers[0].run { client.start(username, password) }
|
||||
@ -842,8 +842,13 @@ class DriverDSLImpl(
|
||||
config += "rpcUsers" to configuration.toConfig().getValue("rpcUsers")
|
||||
config += "useHTTPS" to useHTTPS
|
||||
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
|
||||
}
|
||||
|
||||
@ -1165,20 +1170,18 @@ private fun Config.toNodeOnly(): Config {
|
||||
return if (hasPath("webAddress")) withoutPath("webAddress").withoutPath("useHTTPS") else this
|
||||
}
|
||||
|
||||
internal fun DriverParameters.cordappsForAllNodes(): Set<TestCorDapp> = cordappsForAllNodes ?: cordappsInCurrentAndAdditionalPackages(extraCordappPackagesToScan)
|
||||
internal fun DriverParameters.cordappsForAllNodes(): Set<TestCorDapp> = cordappsForAllNodes
|
||||
?: cordappsInCurrentAndAdditionalPackages(extraCordappPackagesToScan)
|
||||
|
||||
fun DriverDSL.startNode(providedName: CordaX500Name, devMode: Boolean, parameters: NodeParameters = NodeParameters()): CordaFuture<NodeHandle> {
|
||||
var customOverrides = emptyMap<String, String>()
|
||||
if (!devMode) {
|
||||
val nodeDir = baseDirectory(providedName)
|
||||
val nodeSslConfig = object : NodeSSLConfiguration {
|
||||
override val baseDirectory = nodeDir
|
||||
override val keyStorePassword = "cordacadevpass"
|
||||
override val trustStorePassword = "trustpass"
|
||||
override val crlCheckSoftFail = true
|
||||
}
|
||||
nodeSslConfig.configureDevKeyAndTrustStores(providedName)
|
||||
val certificatesDirectory = nodeDir / "certificates"
|
||||
val signingCertStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)
|
||||
val p2pSslConfig = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)
|
||||
p2pSslConfig.configureDevKeyAndTrustStores(providedName, signingCertStore, certificatesDirectory)
|
||||
customOverrides = mapOf("devMode" to "false")
|
||||
}
|
||||
return startNode(parameters, providedName = providedName, customOverrides = customOverrides)
|
||||
}
|
||||
}
|
@ -54,6 +54,7 @@ import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.driver.TestCorDapp
|
||||
import net.corda.testing.internal.stubs.CertificateStoreStubs
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
import net.corda.testing.internal.setGlobalSerialization
|
||||
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 {
|
||||
val id = parameters.forcedID ?: nextNodeId++
|
||||
val config = mockNodeConfiguration().also {
|
||||
doReturn(baseDirectory(id).createDirectories()).whenever(it).baseDirectory
|
||||
val baseDirectory = baseDirectory(id)
|
||||
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(makeInternalTestDataSourceProperties("node_$id", "net_$networkId")).whenever(it).dataSourceProperties
|
||||
doReturn(makeTestDatabaseProperties("node_$id")).whenever(it).database
|
||||
@ -562,12 +566,17 @@ abstract class MessagingServiceSpy {
|
||||
abstract fun send(message: Message, target: MessageRecipients, sequenceKey: Any)
|
||||
}
|
||||
|
||||
private fun mockNodeConfiguration(): NodeConfiguration {
|
||||
private fun mockNodeConfiguration(certificatesDirectory: Path): NodeConfiguration {
|
||||
@DoNotImplement
|
||||
abstract class AbstractNodeConfiguration : NodeConfiguration
|
||||
|
||||
val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)
|
||||
val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)
|
||||
|
||||
return rigorousMock<AbstractNodeConfiguration>().also {
|
||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||
doReturn("trustpass").whenever(it).trustStorePassword
|
||||
doReturn(certificatesDirectory.createDirectories()).whenever(it).certificatesDirectory
|
||||
doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
|
||||
doReturn(signingCertificateStore).whenever(it).signingCertificateStore
|
||||
doReturn(emptyList<User>()).whenever(it).rpcUsers
|
||||
doReturn(null).whenever(it).notary
|
||||
doReturn(DatabaseConfig()).whenever(it).database
|
||||
|
@ -10,15 +10,15 @@ import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.node.VersionInfo
|
||||
import net.corda.node.internal.EnterpriseNode
|
||||
import net.corda.node.internal.NodeWithInfo
|
||||
import net.corda.node.internal.EnterpriseNode
|
||||
import net.corda.node.services.config.*
|
||||
import net.corda.nodeapi.internal.config.toConfig
|
||||
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.core.SerializationEnvironmentRule
|
||||
import net.corda.testing.driver.PortAllocation
|
||||
import net.corda.testing.internal.IntegrationTest
|
||||
import net.corda.testing.driver.PortAllocation
|
||||
import net.corda.testing.internal.testThreadFactory
|
||||
import net.corda.testing.node.User
|
||||
import org.apache.logging.log4j.Level
|
||||
@ -86,9 +86,9 @@ abstract class NodeBasedTest(private val cordappPackages: List<String> = emptyLi
|
||||
|
||||
@JvmOverloads
|
||||
fun initNode(legalName: CordaX500Name,
|
||||
platformVersion: Int = 4,
|
||||
rpcUsers: List<User> = emptyList(),
|
||||
configOverrides: Map<String, Any> = emptyMap()): InProcessNode {
|
||||
platformVersion: Int = 4,
|
||||
rpcUsers: List<User> = emptyList(),
|
||||
configOverrides: Map<String, Any> = emptyMap()): InProcessNode {
|
||||
val baseDirectory = baseDirectory(legalName).createDirectories()
|
||||
val p2pAddress = configOverrides["p2pAddress"] ?: portAllocation.nextHostAndPort().toString()
|
||||
val config = ConfigHelper.loadConfig(
|
||||
|
@ -21,8 +21,8 @@ import net.corda.core.utilities.seconds
|
||||
import net.corda.node.internal.security.RPCSecurityManagerImpl
|
||||
import net.corda.node.services.messaging.RPCServer
|
||||
import net.corda.node.services.messaging.RPCServerConfiguration
|
||||
import net.corda.nodeapi.ArtemisTcpTransport
|
||||
import net.corda.nodeapi.RPCApi
|
||||
import net.corda.nodeapi.internal.InternalArtemisTcpTransport
|
||||
import net.corda.serialization.internal.AMQP_RPC_CLIENT_CONTEXT
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.core.MAX_MESSAGE_SIZE
|
||||
@ -220,14 +220,14 @@ data class RPCDriverDSL(
|
||||
bindingsDirectory = "$artemisDir/bindings"
|
||||
journalDirectory = "$artemisDir/journal"
|
||||
largeMessagesDirectory = "$artemisDir/large-messages"
|
||||
acceptorConfigurations = setOf(ArtemisTcpTransport.rpcAcceptorTcpTransport(hostAndPort, null))
|
||||
acceptorConfigurations = setOf(InternalArtemisTcpTransport.rpcAcceptorTcpTransport(hostAndPort, null))
|
||||
configureCommonSettings(maxFileSize, maxBufferedBytesPerClient)
|
||||
}
|
||||
}
|
||||
|
||||
val inVmClientTransportConfiguration = TransportConfiguration(InVMConnectorFactory::class.java.name)
|
||||
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
|
||||
): CordaFuture<I> {
|
||||
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)
|
||||
driverDSL.shutdownManager.registerShutdown {
|
||||
connection.close()
|
||||
|
@ -29,15 +29,11 @@ import java.security.PublicKey
|
||||
import java.time.Instant
|
||||
import javax.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM
|
||||
|
||||
// TODO: Make a shared implementation of CordaRPCOps where every method is unimplemented?
|
||||
|
||||
class CordaRPCProxyClient(private val targetHostAndPort: NetworkHostAndPort) : CordaRPCOps {
|
||||
companion object {
|
||||
val log = contextLogger()
|
||||
}
|
||||
|
||||
override val protocolVersion: Int = 1000
|
||||
|
||||
init {
|
||||
try {
|
||||
AMQPClientSerializationScheme.initialiseSerialization()
|
||||
@ -79,51 +75,51 @@ class CordaRPCProxyClient(private val targetHostAndPort: NetworkHostAndPort) : C
|
||||
}
|
||||
|
||||
override fun stateMachinesSnapshot(): List<StateMachineInfo> {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun stateMachinesFeed(): DataFeed<List<StateMachineInfo>, StateMachineUpdate> {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun <T : ContractState> vaultQueryBy(criteria: QueryCriteria, paging: PageSpecification, sorting: Sort, contractStateType: Class<out T>): Vault.Page<T> {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun <T : ContractState> vaultQueryByCriteria(criteria: QueryCriteria, contractStateType: Class<out T>): Vault.Page<T> {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun <T : ContractState> vaultQueryByWithPagingSpec(contractStateType: Class<out T>, criteria: QueryCriteria, paging: PageSpecification): Vault.Page<T> {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun <T : ContractState> vaultQueryByWithSorting(contractStateType: Class<out T>, criteria: QueryCriteria, sorting: Sort): Vault.Page<T> {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun <T : ContractState> vaultTrackBy(criteria: QueryCriteria, paging: PageSpecification, sorting: Sort, contractStateType: Class<out T>): DataFeed<Vault.Page<T>, Vault.Update<T>> {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun <T : ContractState> vaultTrack(contractStateType: Class<out T>): DataFeed<Vault.Page<T>, Vault.Update<T>> {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun <T : ContractState> vaultTrackByCriteria(contractStateType: Class<out T>, criteria: QueryCriteria): DataFeed<Vault.Page<T>, Vault.Update<T>> {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun <T : ContractState> vaultTrackByWithPagingSpec(contractStateType: Class<out T>, criteria: QueryCriteria, paging: PageSpecification): DataFeed<Vault.Page<T>, Vault.Update<T>> {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun <T : ContractState> vaultTrackByWithSorting(contractStateType: Class<out T>, criteria: QueryCriteria, sorting: Sort): DataFeed<Vault.Page<T>, Vault.Update<T>> {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun internalVerifiedTransactionsSnapshot(): List<SignedTransaction> {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun internalFindVerifiedTransaction(txnId: SecureHash): SignedTransaction? {
|
||||
@ -131,107 +127,107 @@ class CordaRPCProxyClient(private val targetHostAndPort: NetworkHostAndPort) : C
|
||||
}
|
||||
|
||||
override fun internalVerifiedTransactionsFeed(): DataFeed<List<SignedTransaction>, SignedTransaction> {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun stateMachineRecordedTransactionMappingSnapshot(): List<StateMachineTransactionMapping> {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun stateMachineRecordedTransactionMappingFeed(): DataFeed<List<StateMachineTransactionMapping>, StateMachineTransactionMapping> {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun networkMapFeed(): DataFeed<List<NodeInfo>, NetworkMapCache.MapChange> {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun networkParametersFeed(): DataFeed<ParametersUpdateInfo?, ParametersUpdateInfo> {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun acceptNewNetworkParameters(parametersHash: SecureHash) {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun <T> startTrackedFlowDynamic(logicType: Class<out FlowLogic<T>>, vararg args: Any?): FlowProgressHandle<T> {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun addVaultTransactionNote(txnId: SecureHash, txnNote: String) {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun getVaultTransactionNotes(txnId: SecureHash): Iterable<String> {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun attachmentExists(id: SecureHash): Boolean {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun openAttachment(id: SecureHash): InputStream {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun uploadAttachment(jar: InputStream): SecureHash {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun uploadAttachmentWithMetadata(jar: InputStream, uploader: String, filename: String): SecureHash {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun queryAttachments(query: AttachmentQueryCriteria, sorting: AttachmentSort?): List<AttachmentId> {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun currentNodeTime(): Instant {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun waitUntilNetworkReady(): CordaFuture<Void?> {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun wellKnownPartyFromAnonymous(party: AbstractParty): Party? {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun partyFromKey(key: PublicKey): Party? {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun wellKnownPartyFromX500Name(x500Name: CordaX500Name): Party? {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun notaryPartyFromX500Name(x500Name: CordaX500Name): Party? {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun nodeInfoFromParty(party: AbstractParty): NodeInfo? {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun clearNetworkMapCache() {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun setFlowsDrainingModeEnabled(enabled: Boolean) {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun isFlowsDrainingModeEnabled(): Boolean {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun shutdown() {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun killFlow(id: StateMachineRunId): Boolean {
|
||||
TODO("not implemented")
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun refreshNetworkMapCache() {
|
||||
|
@ -4,7 +4,9 @@ import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.createFile
|
||||
import net.corda.core.internal.deleteIfExists
|
||||
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 org.apache.commons.io.FileUtils
|
||||
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 trustStoreFile = trustStore.toTemporaryFile("truststore", directory = directory)
|
||||
|
||||
val sslConfiguration = sslConfiguration(directory)
|
||||
val sslConfiguration = sslConfiguration(keyStoreFile, trustStoreFile)
|
||||
|
||||
return object : AutoClosableSSLConfiguration {
|
||||
override val value = sslConfiguration
|
||||
@ -77,16 +79,16 @@ class KeyStores(val keyStore: UnsafeKeyStore, val trustStore: UnsafeKeyStore) {
|
||||
}
|
||||
}
|
||||
|
||||
data class TestSslOptions(override val certificatesDirectory: Path,
|
||||
override val keyStorePassword: String,
|
||||
override val trustStorePassword: String,
|
||||
override val crlCheckSoftFail: Boolean) : SSLConfiguration
|
||||
private fun sslConfiguration(keyStoreFile: TemporaryFile, trustStoreFile: TemporaryFile): MutualSslConfiguration {
|
||||
|
||||
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 {
|
||||
val value: SSLConfiguration
|
||||
val value: MutualSslConfiguration
|
||||
}
|
||||
|
||||
typealias KeyStoreEntry = Pair<String, UnsafeCertificate>
|
||||
@ -189,7 +191,7 @@ private fun newKeyStore(type: String, password: String): 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)
|
||||
FileUtils.forceDeleteOnExit(serverDir.toFile())
|
||||
|
||||
|
@ -9,15 +9,15 @@ import net.corda.core.identity.Party
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.transactions.WireTransaction
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.node.services.config.configureDevKeyAndTrustStores
|
||||
import net.corda.nodeapi.BrokerRpcSslOptions
|
||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
||||
import net.corda.nodeapi.internal.createDevKeyStores
|
||||
import net.corda.nodeapi.internal.config.MutualSslConfiguration
|
||||
import net.corda.nodeapi.internal.registerDevP2pCertificates
|
||||
import net.corda.nodeapi.internal.createDevNodeCa
|
||||
import net.corda.nodeapi.internal.crypto.*
|
||||
import net.corda.nodeapi.internal.loadDevCaTrustStore
|
||||
import net.corda.serialization.internal.amqp.AMQP_ENABLED
|
||||
import net.corda.testing.internal.stubs.CertificateStoreStubs
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
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")
|
||||
}
|
||||
|
||||
fun configureTestSSL(legalName: CordaX500Name): SSLConfiguration {
|
||||
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
|
||||
fun configureTestSSL(legalName: CordaX500Name): MutualSslConfiguration {
|
||||
|
||||
init {
|
||||
configureDevKeyAndTrustStores(legalName)
|
||||
}
|
||||
val certificatesDirectory = Files.createTempDirectory("certs")
|
||||
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")
|
||||
@ -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.
|
||||
* 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 createNodeSslConfig(path: Path, name: CordaX500Name = CordaX500Name("MegaCorp", "London", "GB")): SSLConfiguration {
|
||||
val sslConfig = object : SSLConfiguration {
|
||||
override val crlCheckSoftFail = true
|
||||
override val certificatesDirectory = path
|
||||
override val keyStorePassword = "serverstorepass"
|
||||
override val trustStorePassword = "trustpass"
|
||||
}
|
||||
fun p2pSslOptions(path: Path, name: CordaX500Name = CordaX500Name("MegaCorp", "London", "GB")): MutualSslConfiguration {
|
||||
val sslConfig = CertificateStoreStubs.P2P.withCertificatesDirectory(path, keyStorePassword = "serverstorepass")
|
||||
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
|
||||
sslConfig.createDevKeyStores(name, rootCa.certificate, intermediateCa)
|
||||
val trustStore = loadOrCreateKeyStore(sslConfig.trustStoreFile, sslConfig.trustStorePassword)
|
||||
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCa.certificate)
|
||||
trustStore.save(sslConfig.trustStoreFile, sslConfig.trustStorePassword)
|
||||
|
||||
sslConfig.keyStore.get(true).registerDevP2pCertificates(name, rootCa.certificate, intermediateCa)
|
||||
val trustStore = sslConfig.trustStore.get(true)
|
||||
trustStore[X509Utilities.CORDA_ROOT_CA] = rootCa.certificate
|
||||
return sslConfig
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -145,7 +145,7 @@ class InteractiveShellIntegrationTest : IntegrationTest() {
|
||||
driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList())) {
|
||||
startNode().getOrThrow().use { node ->
|
||||
val conf = (node as NodeHandleInternal).configuration.toShellConfig()
|
||||
InteractiveShell.startShellInternal(conf)
|
||||
InteractiveShell.startShell(conf)
|
||||
assertThatThrownBy { InteractiveShell.nodeInfo() }.isInstanceOf(ActiveMQSecurityException::class.java)
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ import net.corda.client.jackson.StringToMethodCallParser
|
||||
import net.corda.client.rpc.CordaRPCClientConfiguration
|
||||
import net.corda.client.rpc.CordaRPCConnection
|
||||
import net.corda.client.rpc.PermissionException
|
||||
import net.corda.client.rpc.internal.createCordaRPCClientWithInternalSslAndClassLoader
|
||||
import net.corda.client.rpc.internal.createCordaRPCClientWithSslAndClassLoader
|
||||
import net.corda.core.CordaException
|
||||
import net.corda.core.concurrent.CordaFuture
|
||||
@ -93,24 +92,6 @@ object InteractiveShell {
|
||||
_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) {
|
||||
shellConfiguration = configuration
|
||||
InteractiveShell.classLoader = classLoader
|
||||
|
@ -2,7 +2,6 @@ package net.corda.tools.shell
|
||||
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.messaging.ClientRpcSslOptions
|
||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
||||
import java.nio.file.Path
|
||||
|
||||
data class ShellConfiguration(
|
||||
@ -12,7 +11,6 @@ data class ShellConfiguration(
|
||||
var password: String = "",
|
||||
val hostAndPort: NetworkHostAndPort,
|
||||
val ssl: ClientRpcSslOptions? = null,
|
||||
val nodeSslConfig: SSLConfiguration? = null,
|
||||
val sshdPort: Int? = null,
|
||||
val sshHostKeyDirectory: Path? = null,
|
||||
val noLocalShell: Boolean = false) {
|
||||
|
@ -1,8 +1,8 @@
|
||||
package net.corda.webserver
|
||||
|
||||
import com.typesafe.config.Config
|
||||
import net.corda.core.internal.div
|
||||
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.getValue
|
||||
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.
|
||||
*/
|
||||
class WebServerConfig(override val baseDirectory: Path, val config: Config) : NodeSSLConfiguration {
|
||||
override val keyStorePassword: String by config
|
||||
override val trustStorePassword: String by config
|
||||
override val crlCheckSoftFail: Boolean by config
|
||||
class WebServerConfig(val baseDirectory: Path, val config: Config) {
|
||||
|
||||
val keyStorePath: String by config
|
||||
val keyStorePassword: String by config
|
||||
val trustStorePath: String by config
|
||||
val trustStorePassword: String by config
|
||||
|
||||
val useHTTPS: Boolean by config
|
||||
val myLegalName: String by config
|
||||
val rpcAddress: NetworkHostAndPort by lazy {
|
||||
|
@ -66,10 +66,10 @@ class NodeWebServer(val config: WebServerConfig) {
|
||||
httpsConfiguration.outputBufferSize = 32768
|
||||
httpsConfiguration.addCustomizer(SecureRequestCustomizer())
|
||||
val sslContextFactory = SslContextFactory()
|
||||
sslContextFactory.keyStorePath = config.sslKeystore.toString()
|
||||
sslContextFactory.keyStorePath = config.keyStorePath
|
||||
sslContextFactory.setKeyStorePassword(config.keyStorePassword)
|
||||
sslContextFactory.setKeyManagerPassword(config.keyStorePassword)
|
||||
sslContextFactory.setTrustStorePath(config.trustStoreFile.toString())
|
||||
sslContextFactory.setTrustStorePath(config.trustStorePath)
|
||||
sslContextFactory.setTrustStorePassword(config.trustStorePassword)
|
||||
sslContextFactory.setExcludeProtocols("SSL.*", "TLSv1", "TLSv1.1")
|
||||
sslContextFactory.setIncludeProtocols("TLSv1.2")
|
||||
|
Loading…
x
Reference in New Issue
Block a user