mirror of
https://github.com/corda/corda.git
synced 2025-06-17 14:48:16 +00:00
[CORDA-1937]: Refactor NodeConfiguration hierarchy. (#3856)
This commit is contained in:
committed by
GitHub
parent
f6ee263db1
commit
d01dd22419
@ -4,15 +4,14 @@ 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.driver.DriverParameters
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.internal.stubs.CertificateStoreStubs
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.Test
|
||||
import java.nio.file.Path
|
||||
import javax.security.auth.x500.X500Principal
|
||||
|
||||
class NodeKeystoreCheckTest {
|
||||
@ -30,13 +29,11 @@ class NodeKeystoreCheckTest {
|
||||
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(
|
||||
@ -48,7 +45,7 @@ class NodeKeystoreCheckTest {
|
||||
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)
|
||||
|
@ -19,6 +19,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
|
||||
@ -27,7 +28,6 @@ import org.junit.Assert.assertArrayEquals
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import java.security.KeyStore
|
||||
import java.util.*
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@ -168,21 +168,26 @@ 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
|
||||
}
|
||||
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) {
|
||||
@ -194,18 +199,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)
|
||||
}
|
||||
|
@ -12,16 +12,18 @@ import net.corda.core.utilities.NetworkHostAndPort
|
||||
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
|
||||
@ -29,13 +31,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
|
||||
@ -102,34 +104,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)
|
||||
@ -139,7 +137,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
|
||||
@ -388,11 +386,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(true).whenever(it).crlCheckSoftFail
|
||||
@ -400,28 +403,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
|
||||
}
|
||||
@ -434,21 +441,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
|
||||
}
|
||||
@ -460,21 +471,25 @@ class ProtonWrapperTests {
|
||||
}
|
||||
|
||||
private fun createServer(port: Int, name: CordaX500Name = ALICE_NAME, maxMessageSize: Int = MAX_MESSAGE_SIZE): AMQPServer {
|
||||
val baseDirectory = temporaryFolder.root.toPath() / "server"
|
||||
val certificatesDirectory = baseDirectory / "certificates"
|
||||
val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)
|
||||
val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)
|
||||
val serverConfig = rigorousMock<AbstractNodeConfiguration>().also {
|
||||
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(true).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
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package net.corda.node.services.messaging
|
||||
import com.nhaarman.mockito_kotlin.doReturn
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
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.MockServices.Companion.makeTestDataSourceProperties
|
||||
@ -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(FlowTimeoutConfiguration(5.seconds, 3, backoffBase = 1.0)).whenever(it).flowTimeout
|
||||
|
@ -12,7 +12,6 @@ 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
|
||||
@ -21,6 +20,7 @@ import net.corda.testing.core.*
|
||||
import net.corda.testing.driver.NodeHandle
|
||||
import net.corda.testing.driver.PortAllocation
|
||||
import net.corda.testing.driver.internal.NodeHandleInternal
|
||||
import net.corda.testing.internal.stubs.CertificateStoreStubs
|
||||
import net.corda.testing.node.internal.*
|
||||
import net.corda.testing.node.internal.network.NetworkMapServer
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
@ -247,13 +247,10 @@ private fun DriverDSLImpl.startNode(providedName: CordaX500Name, devMode: Boolea
|
||||
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(providedName = providedName, customOverrides = customOverrides)
|
||||
|
@ -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?,
|
||||
|
@ -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,12 +18,12 @@ 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.node.User
|
||||
import net.corda.testing.core.singleIdentity
|
||||
import net.corda.testing.internal.configureTestSSL
|
||||
import net.corda.testing.node.User
|
||||
import net.corda.testing.node.internal.NodeBasedTest
|
||||
import net.corda.testing.node.internal.startFlow
|
||||
import org.apache.activemq.artemis.api.core.ActiveMQNonExistentQueueException
|
||||
@ -94,7 +94,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
|
||||
|
@ -44,6 +44,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
|
||||
@ -248,7 +249,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 {
|
||||
@ -278,7 +279,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 {
|
||||
@ -368,10 +369,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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -696,8 +697,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.")
|
||||
@ -714,9 +715,9 @@ 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." }
|
||||
@ -760,7 +761,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 -> {
|
||||
@ -804,7 +805,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
|
||||
@ -819,25 +820,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
|
||||
}
|
||||
@ -1027,4 +1028,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)
|
||||
}
|
@ -227,12 +227,12 @@ open class Node(configuration: NodeConfiguration,
|
||||
startLocalRpcBroker(securityManager)
|
||||
}
|
||||
|
||||
val bridgeControlListener = BridgeControlListener(configuration, network.serverAddress, networkParameters.maxMessageSize)
|
||||
val bridgeControlListener = BridgeControlListener(configuration.p2pSslOptions, network.serverAddress, networkParameters.maxMessageSize)
|
||||
|
||||
printBasicNodeInfo("Advertised P2P messaging addresses", nodeInfo.addresses.joinToString())
|
||||
val rpcServerConfiguration = RPCServerConfiguration.DEFAULT
|
||||
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())
|
||||
}
|
||||
@ -271,9 +271,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()
|
||||
|
@ -8,11 +8,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
|
||||
|
||||
@ -68,22 +67,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
|
||||
@ -30,7 +32,7 @@ private val DEFAULT_FLOW_MONITOR_PERIOD_MILLIS: Duration = Duration.ofMinutes(1)
|
||||
private val DEFAULT_FLOW_MONITOR_SUSPENSION_LOGGING_THRESHOLD_MILLIS: Duration = Duration.ofMinutes(1)
|
||||
private const val CORDAPPS_DIR_NAME_DEFAULT = "cordapps"
|
||||
|
||||
interface NodeConfiguration : NodeSSLConfiguration {
|
||||
interface NodeConfiguration {
|
||||
val myLegalName: CordaX500Name
|
||||
val emailAddress: String
|
||||
val jmxMonitoringHttpPort: Int?
|
||||
@ -69,9 +71,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 {
|
||||
@ -176,8 +185,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,
|
||||
@ -243,6 +252,17 @@ data class NodeConfigurationImpl(
|
||||
}
|
||||
}
|
||||
|
||||
override val certificatesDirectory = baseDirectory / "certificates"
|
||||
|
||||
private val signingCertificateStorePath = certificatesDirectory / "nodekeystore.jks"
|
||||
override val signingCertificateStore = FileBasedCertificateStoreSupplier(signingCertificateStorePath, keyStorePassword)
|
||||
|
||||
private val p2pKeystorePath: Path get() = certificatesDirectory / "sslkeystore.jks"
|
||||
private val p2pKeyStore = FileBasedCertificateStoreSupplier(p2pKeystorePath, keyStorePassword)
|
||||
private val p2pTrustStoreFilePath: Path get() = certificatesDirectory / "truststore.jks"
|
||||
private val p2pTrustStore = FileBasedCertificateStoreSupplier(p2pTrustStoreFilePath, trustStorePassword)
|
||||
override val p2pSslOptions: MutualSslConfiguration = SslConfiguration.mutual(p2pKeyStore, p2pTrustStore)
|
||||
|
||||
override val rpcOptions: NodeRpcOptions
|
||||
get() {
|
||||
return actualRpcSettings.asOptions()
|
||||
@ -356,8 +376,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
|
||||
@ -162,8 +161,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.
|
||||
|
@ -26,7 +26,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
|
||||
@ -34,6 +33,7 @@ import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.BRIDGE_NOT
|
||||
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.JOURNAL_HEADER_SIZE
|
||||
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.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
|
||||
@ -149,7 +149,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,
|
||||
|
Reference in New Issue
Block a user