mirror of
https://github.com/corda/corda.git
synced 2025-01-23 04:48:09 +00:00
ENT-1125 bootstrap root certificate (#2151)
* ENT-1125 make nodes check that the returned signed certificate from Doorman has the expected root
This commit is contained in:
parent
d5e3f28303
commit
acd2281b20
@ -15,4 +15,5 @@ interface SSLConfiguration {
|
||||
interface NodeSSLConfiguration : SSLConfiguration {
|
||||
val baseDirectory: Path
|
||||
override val certificatesDirectory: Path get() = baseDirectory / "certificates"
|
||||
val rootCaCertFile: Path get() = certificatesDirectory / "rootcacert.cer"
|
||||
}
|
||||
|
@ -9,8 +9,10 @@ import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.utilities.trace
|
||||
import net.corda.nodeapi.internal.crypto.*
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.nio.file.Path
|
||||
import java.security.cert.Certificate
|
||||
|
||||
object ServiceIdentityGenerator {
|
||||
private val log = LoggerFactory.getLogger(javaClass)
|
||||
@ -22,18 +24,25 @@ object ServiceIdentityGenerator {
|
||||
* @param dirs List of node directories to place the generated identity and key pairs in.
|
||||
* @param serviceName The legal name of the distributed service.
|
||||
* @param threshold The threshold for the generated group [CompositeKey].
|
||||
* @param rootCertertificate the certificate to use a Corda root CA. If not specified the one in
|
||||
* net/corda/node/internal/certificates/cordadevcakeys.jks is used.
|
||||
*/
|
||||
fun generateToDisk(dirs: List<Path>,
|
||||
serviceName: CordaX500Name,
|
||||
serviceId: String,
|
||||
threshold: Int = 1): Party {
|
||||
threshold: Int = 1,
|
||||
rootCertertificate: X509CertificateHolder? = null): Party {
|
||||
log.trace { "Generating a group identity \"serviceName\" for nodes: ${dirs.joinToString()}" }
|
||||
val keyPairs = (1..dirs.size).map { generateKeyPair() }
|
||||
val notaryKey = CompositeKey.Builder().addKeys(keyPairs.map { it.public }).build(threshold)
|
||||
|
||||
val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
|
||||
val issuer = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
|
||||
val rootCert = caKeyStore.getCertificate(X509Utilities.CORDA_ROOT_CA)
|
||||
val rootCert: Certificate = if (rootCertertificate != null) {
|
||||
rootCertertificate.cert
|
||||
} else {
|
||||
caKeyStore.getCertificate(X509Utilities.CORDA_ROOT_CA)
|
||||
}
|
||||
|
||||
keyPairs.zip(dirs) { keyPair, dir ->
|
||||
val serviceKeyCert = X509Utilities.createCertificate(CertificateType.CLIENT_CA, issuer.certificate, issuer.keyPair, serviceName, keyPair.public)
|
||||
|
@ -313,6 +313,11 @@ class X509CertificateFactory {
|
||||
fun generateCertificate(input: InputStream): X509Certificate {
|
||||
return delegate.generateCertificate(input) as X509Certificate
|
||||
}
|
||||
|
||||
// TODO migrate calls to [CertificateFactory#generateCertPath] to call this instead.
|
||||
fun generateCertPath(vararg certificates: Certificate): CertPath {
|
||||
return delegate.generateCertPath(certificates.asList())
|
||||
}
|
||||
}
|
||||
|
||||
enum class CertificateType(val keyUsage: KeyUsage, vararg val purposes: KeyPurposeId, val isCA: Boolean) {
|
||||
|
@ -0,0 +1,182 @@
|
||||
package net.corda.node.utilities.registration
|
||||
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.core.utilities.minutes
|
||||
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_CA
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_INTERMEDIATE_CA
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA
|
||||
import net.corda.testing.ALICE_NAME
|
||||
import net.corda.testing.driver.PortAllocation
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.node.network.NetworkMapServer
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
||||
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.InputStream
|
||||
import java.net.URL
|
||||
import java.security.KeyPair
|
||||
import java.security.cert.CertPath
|
||||
import java.security.cert.Certificate
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.TimeoutException
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipOutputStream
|
||||
import javax.ws.rs.*
|
||||
import javax.ws.rs.core.MediaType
|
||||
import javax.ws.rs.core.Response
|
||||
|
||||
private const val REQUEST_ID = "requestId"
|
||||
|
||||
private val x509CertificateFactory = X509CertificateFactory()
|
||||
private val portAllocation = PortAllocation.Incremental(13000)
|
||||
|
||||
/**
|
||||
* Driver based tests for [NetworkRegistrationHelper]
|
||||
*/
|
||||
class NetworkRegistrationHelperDriverTest {
|
||||
val rootCertAndKeyPair = createSelfKeyAndSelfSignedCertificate()
|
||||
val rootCert = rootCertAndKeyPair.certificate
|
||||
val handler = RegistrationHandler(rootCertAndKeyPair)
|
||||
lateinit var server: NetworkMapServer
|
||||
lateinit var host: String
|
||||
var port: Int = 0
|
||||
val compatibilityZoneUrl get() = URL("http", host, port, "")
|
||||
|
||||
@Before
|
||||
fun startServer() {
|
||||
server = NetworkMapServer(1.minutes, portAllocation.nextHostAndPort(), handler)
|
||||
val (host, port) = server.start()
|
||||
this.host = host
|
||||
this.port = port
|
||||
}
|
||||
|
||||
@After
|
||||
fun stopServer() {
|
||||
server.close()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `node registration correct root cert`() {
|
||||
driver(portAllocation = portAllocation,
|
||||
compatibilityZoneURL = compatibilityZoneUrl,
|
||||
startNodesInProcess = true,
|
||||
rootCertificate = rootCert
|
||||
) {
|
||||
startNode(providedName = ALICE_NAME, initialRegistration = true).get()
|
||||
}
|
||||
|
||||
// We're getting:
|
||||
// a request to sign the certificate then
|
||||
// at least one poll request to see if the request has been approved.
|
||||
// all the network map registration and download.
|
||||
assertThat(handler.requests).startsWith("/certificate", "/certificate/" + REQUEST_ID)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `node registration without root cert`() {
|
||||
driver(portAllocation = portAllocation,
|
||||
compatibilityZoneURL = compatibilityZoneUrl,
|
||||
startNodesInProcess = true
|
||||
) {
|
||||
assertThatThrownBy {
|
||||
startNode(providedName = ALICE_NAME, initialRegistration = true).get()
|
||||
}.isInstanceOf(java.nio.file.NoSuchFileException::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `node registration wrong root cert`() {
|
||||
driver(portAllocation = portAllocation,
|
||||
compatibilityZoneURL = compatibilityZoneUrl,
|
||||
startNodesInProcess = true,
|
||||
rootCertificate = createSelfKeyAndSelfSignedCertificate().certificate
|
||||
) {
|
||||
assertThatThrownBy {
|
||||
startNode(providedName = ALICE_NAME, initialRegistration = true).get()
|
||||
}.isInstanceOf(WrongRootCaCertificateException::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simple registration handler which can handle a single request, which will be given request id [REQUEST_ID].
|
||||
*/
|
||||
@Path("certificate")
|
||||
class RegistrationHandler(private val certificateAndKeyPair: CertificateAndKeyPair) {
|
||||
val requests = mutableListOf<String>()
|
||||
lateinit var certificationRequest: JcaPKCS10CertificationRequest
|
||||
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_OCTET_STREAM)
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
fun registration(input: InputStream): Response {
|
||||
requests += "/certificate"
|
||||
certificationRequest = input.use { JcaPKCS10CertificationRequest(it.readBytes()) }
|
||||
return Response.ok(REQUEST_ID).build()
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path(REQUEST_ID)
|
||||
fun reply(): Response {
|
||||
requests += "/certificate/" + REQUEST_ID
|
||||
val certPath = createSignedClientCertificate(certificationRequest,
|
||||
certificateAndKeyPair.keyPair, arrayOf(certificateAndKeyPair.certificate.cert))
|
||||
return buildDoormanReply(certPath.certificates.toTypedArray())
|
||||
}
|
||||
}
|
||||
|
||||
// TODO this logic is shared with doorman itself, refactor this to be somewhere where both doorman and these tests
|
||||
// can depend on
|
||||
private fun createSignedClientCertificate(certificationRequest: PKCS10CertificationRequest,
|
||||
caKeyPair: KeyPair,
|
||||
caCertPath: Array<Certificate>): CertPath {
|
||||
val request = JcaPKCS10CertificationRequest(certificationRequest)
|
||||
val x509CertificateHolder = X509Utilities.createCertificate(CertificateType.CLIENT_CA,
|
||||
caCertPath.first().toX509CertHolder(),
|
||||
caKeyPair,
|
||||
CordaX500Name.parse(request.subject.toString()).copy(commonName = X509Utilities.CORDA_CLIENT_CA_CN),
|
||||
request.publicKey,
|
||||
nameConstraints = null)
|
||||
return x509CertificateFactory.generateCertPath(x509CertificateHolder.cert, *caCertPath)
|
||||
}
|
||||
|
||||
// TODO this logic is shared with doorman itself, refactor this to be somewhere where both doorman and these tests
|
||||
// can depend on
|
||||
private fun buildDoormanReply(certificates: Array<Certificate>): Response {
|
||||
// Write certificate chain to a zip stream and extract the bit array output.
|
||||
val baos = ByteArrayOutputStream()
|
||||
ZipOutputStream(baos).use { zip ->
|
||||
// Client certificate must come first and root certificate should come last.
|
||||
listOf(CORDA_CLIENT_CA, CORDA_INTERMEDIATE_CA, CORDA_ROOT_CA).zip(certificates).forEach {
|
||||
zip.putNextEntry(ZipEntry("${it.first}.cer"))
|
||||
zip.write(it.second.encoded)
|
||||
zip.closeEntry()
|
||||
}
|
||||
}
|
||||
return Response.ok(baos.toByteArray())
|
||||
.type("application/zip")
|
||||
.header("Content-Disposition", "attachment; filename=\"certificates.zip\"").build()
|
||||
}
|
||||
|
||||
private fun createSelfKeyAndSelfSignedCertificate(): CertificateAndKeyPair {
|
||||
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val rootCACert = X509Utilities.createSelfSignedCACertificate(
|
||||
CordaX500Name(commonName = "Integration Test Corda Node Root CA",
|
||||
organisation = "R3 Ltd", locality = "London",
|
||||
country = "GB"), rootCAKey)
|
||||
return CertificateAndKeyPair(rootCACert, rootCAKey)
|
||||
}
|
@ -12,6 +12,7 @@ import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA
|
||||
import org.bouncycastle.openssl.jcajce.JcaPEMWriter
|
||||
import org.bouncycastle.util.io.pem.PemObject
|
||||
import java.io.StringWriter
|
||||
import java.nio.file.Path
|
||||
import java.security.KeyPair
|
||||
import java.security.KeyStore
|
||||
import java.security.cert.Certificate
|
||||
@ -75,10 +76,15 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v
|
||||
caKeyStore.addOrReplaceKey(CORDA_CLIENT_CA, keyPair.private, privateKeyPassword.toCharArray(), certificates)
|
||||
caKeyStore.deleteEntry(SELF_SIGNED_PRIVATE_KEY)
|
||||
caKeyStore.save(config.nodeKeystore, keystorePassword)
|
||||
|
||||
// Check the root certificate.
|
||||
val returnedRootCa = certificates.last()
|
||||
checkReturnedRootCaMatchesExpectedCa(returnedRootCa)
|
||||
|
||||
// Save root certificates to trust store.
|
||||
val trustStore = loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword)
|
||||
// Assumes certificate chain always starts with client certificate and end with root certificate.
|
||||
trustStore.addOrReplaceCertificate(CORDA_ROOT_CA, certificates.last())
|
||||
trustStore.addOrReplaceCertificate(CORDA_ROOT_CA, returnedRootCa)
|
||||
trustStore.save(config.trustStoreFile, config.trustStorePassword)
|
||||
println("Node private key and certificate stored in ${config.nodeKeystore}.")
|
||||
|
||||
@ -98,6 +104,17 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the passed Certificate is the expected root CA.
|
||||
* @throws WrongRootCaCertificateException if the certificates don't match.
|
||||
*/
|
||||
private fun checkReturnedRootCaMatchesExpectedCa(returnedRootCa: Certificate) {
|
||||
val expected = X509Utilities.loadCertificateFromPEMFile(config.rootCaCertFile).cert
|
||||
if (expected != returnedRootCa) {
|
||||
throw WrongRootCaCertificateException(expected, returnedRootCa, config.rootCaCertFile)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Poll Certificate Signing Server for approved certificate,
|
||||
* enter a slow polling loop if server return null.
|
||||
@ -151,3 +168,17 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception thrown when the doorman root certificate doesn't match the expected (out-of-band) root certificate.
|
||||
* This usually means the has been a Man-in-the-middle attack when contacting the doorman.
|
||||
*/
|
||||
class WrongRootCaCertificateException(expected: Certificate,
|
||||
actual: Certificate,
|
||||
expectedFilePath: Path):
|
||||
Exception("""
|
||||
The Root CA returned back from the registration process does not match the expected Root CA
|
||||
expected: $expected
|
||||
actual: $actual
|
||||
the expected certificate is stored in: $expectedFilePath
|
||||
""".trimMargin())
|
||||
|
@ -46,6 +46,9 @@ class NetworkRegistrationHelperTest {
|
||||
baseDirectory = tempFolder.root.toPath(),
|
||||
myLegalName = ALICE.name)
|
||||
|
||||
config.rootCaCertFile.parent.createDirectories()
|
||||
X509Utilities.saveCertificateAsPEMFile(certs.last().toX509CertHolder(), config.rootCaCertFile)
|
||||
|
||||
assertFalse(config.nodeKeystore.exists())
|
||||
assertFalse(config.sslKeystore.exists())
|
||||
assertFalse(config.trustStoreFile.exists())
|
||||
|
@ -5,25 +5,17 @@ import net.corda.core.internal.div
|
||||
import net.corda.core.internal.list
|
||||
import net.corda.core.internal.readLines
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.minutes
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.node.internal.NodeStartup
|
||||
import net.corda.testing.DUMMY_BANK_A
|
||||
import net.corda.testing.DUMMY_NOTARY
|
||||
import net.corda.testing.DUMMY_REGULATOR
|
||||
import net.corda.testing.common.internal.ProjectStructure.projectRootDir
|
||||
import net.corda.testing.node.NotarySpec
|
||||
import net.corda.testing.node.network.NetworkMapServer
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.Test
|
||||
import java.net.URL
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.ScheduledExecutorService
|
||||
import javax.ws.rs.GET
|
||||
import javax.ws.rs.POST
|
||||
import javax.ws.rs.Path
|
||||
import javax.ws.rs.core.Response
|
||||
import javax.ws.rs.core.Response.ok
|
||||
|
||||
|
||||
class DriverTests {
|
||||
companion object {
|
||||
@ -61,22 +53,6 @@ class DriverTests {
|
||||
nodeMustBeDown(nodeHandle)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `node registration`() {
|
||||
val handler = RegistrationHandler()
|
||||
NetworkMapServer(1.seconds, portAllocation.nextHostAndPort(), handler).use {
|
||||
val (host, port) = it.start()
|
||||
driver(portAllocation = portAllocation, compatibilityZoneURL = URL("http://$host:$port")) {
|
||||
// Wait for the node to have started.
|
||||
startNode(initialRegistration = true).get()
|
||||
}
|
||||
}
|
||||
// We're getting:
|
||||
// a request to sign the certificate then
|
||||
// at least one poll request to see if the request has been approved.
|
||||
// all the network map registration and download.
|
||||
assertThat(handler.requests).startsWith("/certificate", "/certificate/reply")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `debug mode enables debug logging level`() {
|
||||
@ -106,20 +82,3 @@ class DriverTests {
|
||||
assertThat(baseDirectory / "process-id").doesNotExist()
|
||||
}
|
||||
}
|
||||
|
||||
@Path("certificate")
|
||||
class RegistrationHandler {
|
||||
val requests = mutableListOf<String>()
|
||||
@POST
|
||||
fun registration(): Response {
|
||||
requests += "/certificate"
|
||||
return ok("reply").build()
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("reply")
|
||||
fun reply(): Response {
|
||||
requests += "/certificate/reply"
|
||||
return ok().build()
|
||||
}
|
||||
}
|
||||
|
@ -78,6 +78,7 @@ fun testNodeConfiguration(
|
||||
doCallRealMethod().whenever(it).trustStoreFile
|
||||
doCallRealMethod().whenever(it).sslKeystore
|
||||
doCallRealMethod().whenever(it).nodeKeystore
|
||||
doCallRealMethod().whenever(it).rootCaCertFile
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,7 @@ import net.corda.nodeapi.config.parseAs
|
||||
import net.corda.nodeapi.config.toConfig
|
||||
import net.corda.nodeapi.internal.NotaryInfo
|
||||
import net.corda.nodeapi.internal.addShutdownHook
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
import net.corda.testing.*
|
||||
import net.corda.nodeapi.internal.NetworkParametersCopier
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
@ -51,6 +52,7 @@ import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO
|
||||
import net.corda.testing.node.NotarySpec
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import org.slf4j.Logger
|
||||
import rx.Observable
|
||||
import rx.observables.ConnectableObservable
|
||||
@ -343,9 +345,11 @@ data class NodeParameters(
|
||||
* available from [DriverDSLExposedInterface.notaryHandles]. Defaults to a simple validating notary.
|
||||
* @param compatibilityZoneURL if not null each node is started once in registration mode (which makes the node register and quit),
|
||||
* and then re-starts the node with the given parameters.
|
||||
* @param rootCertificate if not null every time a node is started for registration that certificate is written on disk
|
||||
* @param dsl The dsl itself.
|
||||
* @return The value returned in the [dsl] closure.
|
||||
*/
|
||||
// TODO: make the registration testing parameters internal.
|
||||
fun <A> driver(
|
||||
defaultParameters: DriverParameters = DriverParameters(),
|
||||
isDebug: Boolean = defaultParameters.isDebug,
|
||||
@ -360,6 +364,7 @@ fun <A> driver(
|
||||
notarySpecs: List<NotarySpec> = defaultParameters.notarySpecs,
|
||||
extraCordappPackagesToScan: List<String> = defaultParameters.extraCordappPackagesToScan,
|
||||
compatibilityZoneURL: URL? = defaultParameters.compatibilityZoneURL,
|
||||
rootCertificate: X509CertificateHolder? = defaultParameters.rootCertificate,
|
||||
dsl: DriverDSLExposedInterface.() -> A
|
||||
): A {
|
||||
return genericDriver(
|
||||
@ -374,7 +379,8 @@ fun <A> driver(
|
||||
waitForNodesToFinish = waitForAllNodesToFinish,
|
||||
notarySpecs = notarySpecs,
|
||||
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
||||
compatibilityZoneURL = compatibilityZoneURL
|
||||
compatibilityZoneURL = compatibilityZoneURL,
|
||||
rootCertificate = rootCertificate
|
||||
),
|
||||
coerce = { it },
|
||||
dsl = dsl,
|
||||
@ -410,7 +416,8 @@ data class DriverParameters(
|
||||
val waitForNodesToFinish: Boolean = false,
|
||||
val notarySpecs: List<NotarySpec> = listOf(NotarySpec(DUMMY_NOTARY.name)),
|
||||
val extraCordappPackagesToScan: List<String> = emptyList(),
|
||||
val compatibilityZoneURL: URL? = null
|
||||
val compatibilityZoneURL: URL? = null,
|
||||
val rootCertificate: X509CertificateHolder? = null
|
||||
) {
|
||||
fun setIsDebug(isDebug: Boolean) = copy(isDebug = isDebug)
|
||||
fun setDriverDirectory(driverDirectory: Path) = copy(driverDirectory = driverDirectory)
|
||||
@ -421,8 +428,10 @@ data class DriverParameters(
|
||||
fun setInitialiseSerialization(initialiseSerialization: Boolean) = copy(initialiseSerialization = initialiseSerialization)
|
||||
fun setStartNodesInProcess(startNodesInProcess: Boolean) = copy(startNodesInProcess = startNodesInProcess)
|
||||
fun setTerminateNodesOnShutdown(terminateNodesOnShutdown: Boolean) = copy(waitForNodesToFinish = terminateNodesOnShutdown)
|
||||
fun setExtraCordappPackagesToScan(extraCordappPackagesToScan: List<String>) = copy(extraCordappPackagesToScan = extraCordappPackagesToScan)
|
||||
fun setNotarySpecs(notarySpecs: List<NotarySpec>) = copy(notarySpecs = notarySpecs)
|
||||
fun setExtraCordappPackagesToScan(extraCordappPackagesToScan: List<String>) = copy(extraCordappPackagesToScan = extraCordappPackagesToScan)
|
||||
fun setCompatibilityZoneURL(compatibilityZoneURL: URL?) = copy(compatibilityZoneURL = compatibilityZoneURL)
|
||||
fun setRootCertificate(rootCertificate: X509CertificateHolder?) = copy(rootCertificate = rootCertificate)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -476,6 +485,7 @@ fun <DI : DriverDSLExposedInterface, D : DriverDSLInternalInterface, A> genericD
|
||||
notarySpecs: List<NotarySpec>,
|
||||
extraCordappPackagesToScan: List<String> = defaultParameters.extraCordappPackagesToScan,
|
||||
compatibilityZoneURL: URL? = defaultParameters.compatibilityZoneURL,
|
||||
rootCertificate: X509CertificateHolder? = defaultParameters.rootCertificate,
|
||||
driverDslWrapper: (DriverDSL) -> D,
|
||||
coerce: (D) -> DI, dsl: DI.() -> A
|
||||
): A {
|
||||
@ -492,7 +502,8 @@ fun <DI : DriverDSLExposedInterface, D : DriverDSLInternalInterface, A> genericD
|
||||
waitForNodesToFinish = waitForNodesToFinish,
|
||||
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
||||
notarySpecs = notarySpecs,
|
||||
compatibilityZoneURL = compatibilityZoneURL
|
||||
compatibilityZoneURL = compatibilityZoneURL,
|
||||
rootCertificate = rootCertificate
|
||||
)
|
||||
)
|
||||
val shutdownHook = addShutdownHook(driverDsl::shutdown)
|
||||
@ -599,7 +610,8 @@ class DriverDSL(
|
||||
val waitForNodesToFinish: Boolean,
|
||||
extraCordappPackagesToScan: List<String>,
|
||||
val notarySpecs: List<NotarySpec>,
|
||||
val compatibilityZoneURL: URL?
|
||||
val compatibilityZoneURL: URL?,
|
||||
val rootCertificate: X509CertificateHolder?
|
||||
) : DriverDSLInternalInterface {
|
||||
private var _executorService: ScheduledExecutorService? = null
|
||||
val executorService get() = _executorService!!
|
||||
@ -711,6 +723,11 @@ class DriverDSL(
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeRootCaCertificateForNode(path: Path, caRootCertificate: X509CertificateHolder) {
|
||||
path.parent.createDirectories()
|
||||
X509Utilities.saveCertificateAsPEMFile(caRootCertificate, path)
|
||||
}
|
||||
|
||||
private fun registerNode(providedName: CordaX500Name, compatibilityZoneURL: URL): CordaFuture<Unit> {
|
||||
val config = ConfigHelper.loadConfig(
|
||||
baseDirectory = baseDirectory(providedName),
|
||||
@ -720,10 +737,12 @@ class DriverDSL(
|
||||
"compatibilityZoneURL" to compatibilityZoneURL.toString(),
|
||||
"myLegalName" to providedName.toString())
|
||||
)
|
||||
val configuration = config.parseAsNodeConfiguration()
|
||||
// If a rootCertificate is specified, put that in the node expected path.
|
||||
rootCertificate?.let { writeRootCaCertificateForNode(configuration.rootCaCertFile, it) }
|
||||
if (startNodesInProcess) {
|
||||
// This is a bit cheating, we're not starting a full node, we're just calling the code nodes call
|
||||
// when registering.
|
||||
val configuration = config.parseAsNodeConfiguration()
|
||||
NetworkRegistrationHelper(configuration, HTTPNetworkRegistrationService(compatibilityZoneURL))
|
||||
.buildKeystore()
|
||||
return doneFuture(Unit)
|
||||
@ -857,12 +876,14 @@ class DriverDSL(
|
||||
ServiceIdentityGenerator.generateToDisk(
|
||||
dirs = listOf(baseDirectory(spec.name)),
|
||||
serviceName = spec.name,
|
||||
rootCertertificate = rootCertificate,
|
||||
serviceId = "identity"
|
||||
)
|
||||
} else {
|
||||
ServiceIdentityGenerator.generateToDisk(
|
||||
dirs = generateNodeNames(spec).map { baseDirectory(it) },
|
||||
serviceName = spec.name,
|
||||
rootCertertificate = rootCertificate,
|
||||
serviceId = NotaryService.constructId(
|
||||
validating = spec.validating,
|
||||
raft = spec.cluster is ClusterSpec.Raft
|
||||
|
@ -47,6 +47,7 @@ import org.apache.activemq.artemis.core.settings.impl.AddressFullMessagePolicy
|
||||
import org.apache.activemq.artemis.core.settings.impl.AddressSettings
|
||||
import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection
|
||||
import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager3
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import java.lang.reflect.Method
|
||||
import java.net.URL
|
||||
import java.nio.file.Path
|
||||
@ -237,6 +238,7 @@ fun <A> rpcDriver(
|
||||
notarySpecs: List<NotarySpec> = emptyList(),
|
||||
externalTrace: Trace? = null,
|
||||
compatibilityZoneURL: URL? = null,
|
||||
rootCertificate: X509CertificateHolder? = null,
|
||||
dsl: RPCDriverExposedDSLInterface.() -> A
|
||||
) = genericDriver(
|
||||
driverDsl = RPCDriverDSL(
|
||||
@ -251,7 +253,8 @@ fun <A> rpcDriver(
|
||||
waitForNodesToFinish = waitForNodesToFinish,
|
||||
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
||||
notarySpecs = notarySpecs,
|
||||
compatibilityZoneURL = compatibilityZoneURL
|
||||
compatibilityZoneURL = compatibilityZoneURL,
|
||||
rootCertificate = rootCertificate
|
||||
), externalTrace
|
||||
),
|
||||
coerce = { it },
|
||||
|
@ -37,6 +37,7 @@ import org.apache.activemq.artemis.core.security.CheckType
|
||||
import org.apache.activemq.artemis.core.security.Role
|
||||
import org.apache.activemq.artemis.core.server.impl.ActiveMQServerImpl
|
||||
import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import java.net.URL
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
@ -87,6 +88,7 @@ fun <A> verifierDriver(
|
||||
extraCordappPackagesToScan: List<String> = emptyList(),
|
||||
notarySpecs: List<NotarySpec> = emptyList(),
|
||||
compatibilityZoneURL: URL? = null,
|
||||
rootCertificate: X509CertificateHolder? = null,
|
||||
dsl: VerifierExposedDSLInterface.() -> A
|
||||
) = genericDriver(
|
||||
driverDsl = VerifierDriverDSL(
|
||||
@ -101,7 +103,8 @@ fun <A> verifierDriver(
|
||||
waitForNodesToFinish = waitForNodesToFinish,
|
||||
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
||||
notarySpecs = notarySpecs,
|
||||
compatibilityZoneURL = compatibilityZoneURL
|
||||
compatibilityZoneURL = compatibilityZoneURL,
|
||||
rootCertificate = rootCertificate
|
||||
)
|
||||
),
|
||||
coerce = { it },
|
||||
|
Loading…
Reference in New Issue
Block a user