CORDA-3979: Support for multiple trust roots (#6772)

This commit is contained in:
Denis Rekalov 2020-10-20 12:18:00 +03:00 committed by GitHub
parent 401d8b8856
commit 4193adf6fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 486 additions and 131 deletions

View File

@ -42,8 +42,11 @@ class PartyAndCertificate(val certPath: CertPath) {
override fun toString(): String = party.toString() override fun toString(): String = party.toString()
/** Verify the certificate path is valid. */ /** Verify the certificate path is valid. */
fun verify(trustAnchor: TrustAnchor): PKIXCertPathValidatorResult { fun verify(trustAnchor: TrustAnchor): PKIXCertPathValidatorResult = verify(setOf(trustAnchor))
val result = certPath.validate(trustAnchor)
/** Verify the certificate path is valid against one of the specified trust anchors. */
fun verify(trustAnchors: Set<TrustAnchor>): PKIXCertPathValidatorResult {
val result = certPath.validate(trustAnchors)
// Apply Corda-specific validity rules to the chain. This only applies to chains with any roles present, so // Apply Corda-specific validity rules to the chain. This only applies to chains with any roles present, so
// an all-null chain is in theory valid. // an all-null chain is in theory valid.
var parentRole: CertRole? = CertRole.extract(result.trustAnchor.trustedCert) var parentRole: CertRole? = CertRole.extract(result.trustAnchor.trustedCert)

View File

@ -506,8 +506,8 @@ fun ExecutorService.join() {
} }
// TODO: Currently the certificate revocation status is not handled here. Nowhere in the code the second parameter is used. Consider adding the support in the future. // TODO: Currently the certificate revocation status is not handled here. Nowhere in the code the second parameter is used. Consider adding the support in the future.
fun CertPath.validate(trustAnchor: TrustAnchor, checkRevocation: Boolean = false): PKIXCertPathValidatorResult { fun CertPath.validate(trustAnchors: Set<TrustAnchor>, checkRevocation: Boolean = false): PKIXCertPathValidatorResult {
val parameters = PKIXParameters(setOf(trustAnchor)).apply { isRevocationEnabled = checkRevocation } val parameters = PKIXParameters(trustAnchors).apply { isRevocationEnabled = checkRevocation }
try { try {
return CertPathValidator.getInstance("PKIX").validate(this, parameters) as PKIXCertPathValidatorResult return CertPathValidator.getInstance("PKIX").validate(this, parameters) as PKIXCertPathValidatorResult
} catch (e: CertPathValidatorException) { } catch (e: CertPathValidatorException) {
@ -517,8 +517,8 @@ Reason: ${e.reason}
Offending cert index: ${e.index} Offending cert index: ${e.index}
Cert path: $this Cert path: $this
Trust anchor: Trust anchors:
$trustAnchor""", e, this, e.index) $trustAnchors""", e, this, e.index)
} }
} }

View File

@ -40,5 +40,5 @@ interface NetworkParametersStorage : NetworkParametersService {
/** /**
* Set information that given parameters are current parameters for the network. * Set information that given parameters are current parameters for the network.
*/ */
fun setCurrentParameters(currentSignedParameters: SignedDataWithCert<NetworkParameters>, trustRoot: X509Certificate) fun setCurrentParameters(currentSignedParameters: SignedDataWithCert<NetworkParameters>, trustRoots: Set<X509Certificate>)
} }

View File

@ -358,7 +358,7 @@ class X509UtilitiesTest {
val peerChain = clientSocket.session.peerCertificates.x509 val peerChain = clientSocket.session.peerCertificates.x509
val peerX500Principal = peerChain[0].subjectX500Principal val peerX500Principal = peerChain[0].subjectX500Principal
assertEquals(MEGA_CORP.name.x500Principal, peerX500Principal) assertEquals(MEGA_CORP.name.x500Principal, peerX500Principal)
X509Utilities.validateCertificateChain(rootCa.certificate, peerChain) X509Utilities.validateCertificateChain(setOf(rootCa.certificate), peerChain)
val output = DataOutputStream(clientSocket.outputStream) val output = DataOutputStream(clientSocket.outputStream)
output.writeUTF("Hello World") output.writeUTF("Hello World")
var timeout = 0 var timeout = 0
@ -432,7 +432,7 @@ class X509UtilitiesTest {
val peerChain = client.engine!!.session.peerCertificates.x509 val peerChain = client.engine!!.session.peerCertificates.x509
val peerX500Principal = peerChain[0].subjectX500Principal val peerX500Principal = peerChain[0].subjectX500Principal
assertEquals(MEGA_CORP.name.x500Principal, peerX500Principal) assertEquals(MEGA_CORP.name.x500Principal, peerX500Principal)
X509Utilities.validateCertificateChain(rootCa.certificate, peerChain) X509Utilities.validateCertificateChain(setOf(rootCa.certificate), peerChain)
} }
} }
} }

View File

@ -372,7 +372,7 @@ class NetworkBootstrapperTest {
private val Path.networkParameters: NetworkParameters private val Path.networkParameters: NetworkParameters
get() { get() {
return (this / NETWORK_PARAMS_FILE_NAME).readObject<SignedNetworkParameters>() return (this / NETWORK_PARAMS_FILE_NAME).readObject<SignedNetworkParameters>()
.verifiedNetworkParametersCert(DEV_ROOT_CA.certificate) .verifiedNetworkParametersCert(setOf(DEV_ROOT_CA.certificate))
} }
private val Path.nodeInfoFile: Path private val Path.nodeInfoFile: Path

View File

@ -105,6 +105,17 @@ fun createDevNodeCa(intermediateCa: CertificateAndKeyPair,
return CertificateAndKeyPair(cert, nodeKeyPair) return CertificateAndKeyPair(cert, nodeKeyPair)
} }
fun createDevNodeIdentity(nodeCa: CertificateAndKeyPair, legalName: CordaX500Name): CertificateAndKeyPair {
val keyPair = generateKeyPair()
val cert = X509Utilities.createCertificate(
CertificateType.LEGAL_IDENTITY,
nodeCa.certificate,
nodeCa.keyPair,
legalName.x500Principal,
keyPair.public)
return CertificateAndKeyPair(cert, keyPair)
}
val DEV_INTERMEDIATE_CA: CertificateAndKeyPair get() = DevCaHelper.loadDevCa(X509Utilities.CORDA_INTERMEDIATE_CA) val DEV_INTERMEDIATE_CA: CertificateAndKeyPair get() = DevCaHelper.loadDevCa(X509Utilities.CORDA_INTERMEDIATE_CA)
val DEV_ROOT_CA: CertificateAndKeyPair get() = DevCaHelper.loadDevCa(X509Utilities.CORDA_ROOT_CA) val DEV_ROOT_CA: CertificateAndKeyPair get() = DevCaHelper.loadDevCa(X509Utilities.CORDA_ROOT_CA)
const val DEV_CA_PRIVATE_KEY_PASS: String = "cordacadevkeypass" const val DEV_CA_PRIVATE_KEY_PASS: String = "cordacadevkeypass"

View File

@ -124,17 +124,17 @@ object X509Utilities {
return createCertificate(CertificateType.ROOT_CA, subject, keyPair, subject, keyPair.public, window) return createCertificate(CertificateType.ROOT_CA, subject, keyPair, subject, keyPair.public, window)
} }
fun validateCertificateChain(trustedRoot: X509Certificate, vararg certificates: X509Certificate) { fun validateCertificateChain(trustedRoots: Set<X509Certificate>, vararg certificates: X509Certificate) {
validateCertificateChain(trustedRoot, certificates.asList()) validateCertificateChain(trustedRoots, certificates.asList())
} }
fun validateCertificateChain(trustedRoot: X509Certificate, certificates: List<X509Certificate>) { fun validateCertificateChain(trustedRoots: Set<X509Certificate>, certificates: List<X509Certificate>) {
require(certificates.isNotEmpty()) { "Certificate path must contain at least one certificate" } require(certificates.isNotEmpty()) { "Certificate path must contain at least one certificate" }
validateCertPath(trustedRoot, buildCertPath(certificates)) validateCertPath(trustedRoots, buildCertPath(certificates))
} }
fun validateCertPath(trustedRoot: X509Certificate, certPath: CertPath) { fun validateCertPath(trustedRoots: Set<X509Certificate>, certPath: CertPath) {
certPath.validate(TrustAnchor(trustedRoot, null)) certPath.validate(trustedRoots.map { TrustAnchor(it, null) }.toSet())
} }
/** /**

View File

@ -399,7 +399,7 @@ constructor(private val initSerEnv: Boolean,
when (netParamsFilesGrouped.size) { when (netParamsFilesGrouped.size) {
0 -> return null 0 -> return null
1 -> return netParamsFilesGrouped.keys.first().deserialize().verifiedNetworkParametersCert(DEV_ROOT_CA.certificate) 1 -> return netParamsFilesGrouped.keys.first().deserialize().verifiedNetworkParametersCert(setOf(DEV_ROOT_CA.certificate))
} }
val msg = StringBuilder("Differing sets of network parameters were found. Make sure all the nodes have the same " + val msg = StringBuilder("Differing sets of network parameters were found. Make sure all the nodes have the same " +
@ -409,7 +409,7 @@ constructor(private val initSerEnv: Boolean,
netParamsFiles.map { it.parent.fileName }.joinTo(msg, ", ") netParamsFiles.map { it.parent.fileName }.joinTo(msg, ", ")
msg.append(":\n") msg.append(":\n")
val netParamsString = try { val netParamsString = try {
bytes.deserialize().verifiedNetworkParametersCert(DEV_ROOT_CA.certificate).toString() bytes.deserialize().verifiedNetworkParametersCert(setOf(DEV_ROOT_CA.certificate)).toString()
} catch (e: Exception) { } catch (e: Exception) {
"Invalid network parameters file: $e" "Invalid network parameters file: $e"
} }

View File

@ -55,25 +55,26 @@ data class ParametersUpdate(
) )
/** Verify that a certificate path and its [CertRole] is correct. */ /** Verify that a certificate path and its [CertRole] is correct. */
fun <T : Any> SignedDataWithCert<T>.verifiedCertWithRole(rootCert: X509Certificate, vararg certRoles: CertRole): T { fun <T : Any> SignedDataWithCert<T>.verifiedCertWithRole(rootCerts: Set<X509Certificate>, vararg certRoles: CertRole): T {
require(CertRole.extract(sig.by) in certRoles) { "Incorrect cert role: ${CertRole.extract(sig.by)}" } require(CertRole.extract(sig.by) in certRoles) { "Incorrect cert role: ${CertRole.extract(sig.by)}" }
val path = if (sig.parentCertsChain.isEmpty()) { val path = if (sig.parentCertsChain.isEmpty()) {
listOf(sig.by, rootCert) val rootCandidate = rootCerts.firstOrNull { it.subjectX500Principal == sig.by.issuerX500Principal }
listOf(sig.by, rootCandidate ?: rootCerts.first())
} else { } else {
sig.fullCertChain sig.fullCertChain
} }
X509Utilities.validateCertificateChain(rootCert, path) X509Utilities.validateCertificateChain(rootCerts, path)
return verified() return verified()
} }
/** Verify that a Network Map certificate path and its [CertRole] is correct. */ /** Verify that a Network Map certificate path and its [CertRole] is correct. */
fun <T : Any> SignedDataWithCert<T>.verifiedNetworkMapCert(rootCert: X509Certificate): T { fun <T : Any> SignedDataWithCert<T>.verifiedNetworkMapCert(rootCerts: Set<X509Certificate>): T {
return verifiedCertWithRole(rootCert, CertRole.NETWORK_MAP) return verifiedCertWithRole(rootCerts, CertRole.NETWORK_MAP)
} }
/** Verify that a Network Parameters certificate path and its [CertRole] is correct. */ /** Verify that a Network Parameters certificate path and its [CertRole] is correct. */
fun <T : Any> SignedDataWithCert<T>.verifiedNetworkParametersCert(rootCert: X509Certificate): T { fun <T : Any> SignedDataWithCert<T>.verifiedNetworkParametersCert(rootCerts: Set<X509Certificate>): T {
// for backwards compatibility we allow network parameters to be signed with // for backwards compatibility we allow network parameters to be signed with
// the networkmap cert, but going forwards we also accept the specific netparams cert as well // the networkmap cert, but going forwards we also accept the specific netparams cert as well
return verifiedCertWithRole(rootCert, CertRole.NETWORK_PARAMETERS, CertRole.NETWORK_MAP) return verifiedCertWithRole(rootCerts, CertRole.NETWORK_PARAMETERS, CertRole.NETWORK_MAP)
} }

View File

@ -33,7 +33,7 @@ class DevCertificatesTest {
val certPath = X509Utilities.buildCertPath(*oldX509Certificates) val certPath = X509Utilities.buildCertPath(*oldX509Certificates)
// when // when
certPath.validate(newTrustAnchor) certPath.validate(setOf(newTrustAnchor))
// then no exception is thrown // then no exception is thrown
} }

View File

@ -127,7 +127,7 @@ class TlsDiffAlgorithmsTest(private val serverAlgo: String, private val clientAl
val peerChain = peerChainTry.getOrThrow() val peerChain = peerChainTry.getOrThrow()
val peerX500Principal = peerChain[0].subjectX500Principal val peerX500Principal = peerChain[0].subjectX500Principal
assertEquals(serverCa.certificate.subjectX500Principal, peerX500Principal) assertEquals(serverCa.certificate.subjectX500Principal, peerX500Principal)
X509Utilities.validateCertificateChain(rootCa, peerChain) X509Utilities.validateCertificateChain(setOf(rootCa), peerChain)
with(DataOutputStream(clientSocket.outputStream)) { with(DataOutputStream(clientSocket.outputStream)) {
writeUTF(testPhrase) writeUTF(testPhrase)
} }

View File

@ -175,7 +175,7 @@ class TlsDiffProtocolsTest(private val serverAlgo: String, private val clientAlg
val peerChain = peerChainTry.getOrThrow() val peerChain = peerChainTry.getOrThrow()
val peerX500Principal = peerChain[0].subjectX500Principal val peerX500Principal = peerChain[0].subjectX500Principal
assertEquals(serverCa.certificate.subjectX500Principal, peerX500Principal) assertEquals(serverCa.certificate.subjectX500Principal, peerX500Principal)
X509Utilities.validateCertificateChain(rootCa, peerChain) X509Utilities.validateCertificateChain(setOf(rootCa), peerChain)
with(DataOutputStream(clientSocket.outputStream)) { with(DataOutputStream(clientSocket.outputStream)) {
writeUTF(testPhrase) writeUTF(testPhrase)
} }

View File

@ -279,7 +279,7 @@ class CordappConstraintsTests {
printVault(alice, states) printVault(alice, states)
// Claim the package, publish the new network parameters , and restart all nodes. // Claim the package, publish the new network parameters , and restart all nodes.
val parameters = NetworkParametersReader(DEV_ROOT_CA.certificate, null, notary.baseDirectory).read().networkParameters val parameters = NetworkParametersReader(setOf(DEV_ROOT_CA.certificate), null, notary.baseDirectory).read().networkParameters
val newParams = parameters.copy( val newParams = parameters.copy(
packageOwnership = mapOf("net.corda.finance.contracts.asset" to packageOwnerKey) packageOwnership = mapOf("net.corda.finance.contracts.asset" to packageOwnerKey)

View File

@ -0,0 +1,215 @@
package net.corda.node.services.identity
import com.nhaarman.mockito_kotlin.doReturn
import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.internal.div
import net.corda.core.utilities.OpaqueBytes
import net.corda.coretesting.internal.stubs.CertificateStoreStubs
import net.corda.finance.DOLLARS
import net.corda.finance.GBP
import net.corda.finance.POUNDS
import net.corda.finance.USD
import net.corda.finance.flows.CashIssueAndPaymentFlow
import net.corda.finance.workflows.getCashBalance
import net.corda.node.services.config.NotaryConfig
import net.corda.nodeapi.internal.createDevNetworkMapCa
import net.corda.nodeapi.internal.createDevNodeCa
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.nodeapi.internal.installDevNodeCaCertPath
import net.corda.nodeapi.internal.network.NetworkParametersCopier
import net.corda.nodeapi.internal.registerDevP2pCertificates
import net.corda.nodeapi.internal.storeLegalIdentity
import net.corda.testing.common.internal.addNotary
import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.internal.createDevIntermediateCaCertPath
import net.corda.testing.node.internal.FINANCE_CORDAPPS
import net.corda.testing.node.internal.InternalMockNetwork
import net.corda.testing.node.internal.InternalMockNodeParameters
import net.corda.testing.node.internal.TestStartedNode
import net.corda.testing.node.internal.startFlow
import org.junit.After
import org.junit.Test
import javax.security.auth.x500.X500Principal
import kotlin.test.assertEquals
class TrustRootTest {
private val ref = OpaqueBytes.of(0x01)
private val TestStartedNode.party get() = info.legalIdentities.first()
private lateinit var mockNet: InternalMockNetwork
@After
fun tearDown() {
mockNet.stopNodes()
}
@Test(timeout = 300_000)
fun `can start flow between nodes with different roots`() {
mockNet = InternalMockNetwork(cordappsForAllNodes = FINANCE_CORDAPPS, notarySpecs = listOf())
val (rootCa1, intermediateCa1) = createDevIntermediateCaCertPath(X500Principal("CN=Root1"))
val (rootCa2, intermediateCa2) = createDevIntermediateCaCertPath(X500Principal("CN=Root2"))
// Initiator and acceptor have different roots, both are present on all truststores.
createKeyStores(0, DUMMY_NOTARY_NAME, intermediateCa1, rootCa1, rootCa2)
createKeyStores(1, ALICE_NAME, intermediateCa1, rootCa1, rootCa2)
createKeyStores(2, BOB_NAME, intermediateCa2, rootCa2, rootCa1)
createNetworkParameters(rootCa1, 0, 1, 2)
val notary = mockNet.createNode(InternalMockNodeParameters(
forcedID = 0,
legalName = DUMMY_NOTARY_NAME,
configOverrides = { doReturn(NotaryConfig(false)).whenever(it).notary }
))
val alice = mockNet.createNode(InternalMockNodeParameters(forcedID = 1, legalName = ALICE_NAME))
val bob = mockNet.createNode(InternalMockNodeParameters(forcedID = 2, legalName = BOB_NAME))
assertEquals(rootCa1.certificate, notary.services.identityService.trustRoot)
assertEquals(rootCa1.certificate, alice.services.identityService.trustRoot)
assertEquals(rootCa2.certificate, bob.services.identityService.trustRoot)
alice.services.startFlow(CashIssueAndPaymentFlow(1000.DOLLARS, ref, bob.party, false, notary.party))
mockNet.runNetwork()
bob.services.startFlow(CashIssueAndPaymentFlow(1000.POUNDS, ref, alice.party, false, notary.party))
mockNet.runNetwork()
// Ledger state was changed
assertEquals(1000.POUNDS, alice.services.getCashBalance(GBP))
assertEquals(1000.DOLLARS, bob.services.getCashBalance(USD))
}
@Test(timeout = 300_000)
fun `fail to start flow when missing acceptor's root on the initiator side`() {
mockNet = InternalMockNetwork(cordappsForAllNodes = FINANCE_CORDAPPS, notarySpecs = listOf())
val (rootCa1, intermediateCa1) = createDevIntermediateCaCertPath(X500Principal("CN=Root1"))
val (rootCa2, intermediateCa2) = createDevIntermediateCaCertPath(X500Principal("CN=Root2"))
// Acceptor's root is missing on the initiator side.
createKeyStores(0, DUMMY_NOTARY_NAME, intermediateCa1, rootCa1, rootCa2)
createKeyStores(1, ALICE_NAME, intermediateCa1, rootCa1)
createKeyStores(2, BOB_NAME, intermediateCa2, rootCa2, rootCa1)
createNetworkParameters(rootCa1, 0, 1, 2)
val notary = mockNet.createNode(InternalMockNodeParameters(
forcedID = 0,
legalName = DUMMY_NOTARY_NAME,
configOverrides = { doReturn(NotaryConfig(false)).whenever(it).notary }
))
val alice = mockNet.createNode(InternalMockNodeParameters(forcedID = 1, legalName = ALICE_NAME))
val bob = mockNet.createNode(InternalMockNodeParameters(forcedID = 2, legalName = BOB_NAME))
assertEquals(rootCa1.certificate, notary.services.identityService.trustRoot)
assertEquals(rootCa1.certificate, alice.services.identityService.trustRoot)
assertEquals(rootCa2.certificate, bob.services.identityService.trustRoot)
alice.services.startFlow(CashIssueAndPaymentFlow(1000.DOLLARS, ref, bob.party, false, notary.party))
mockNet.runNetwork()
// Ledger state remains unchanged.
assertEquals(1000.DOLLARS, alice.services.getCashBalance(USD))
assertEquals(0.DOLLARS, bob.services.getCashBalance(USD))
}
@Test(timeout = 300_000)
fun `fail to notarise when missing initiator's root on the notary side`() {
mockNet = InternalMockNetwork(cordappsForAllNodes = FINANCE_CORDAPPS, notarySpecs = listOf())
val (rootCa1, intermediateCa1) = createDevIntermediateCaCertPath(X500Principal("CN=Root1"))
val (rootCa2, intermediateCa2) = createDevIntermediateCaCertPath(X500Principal("CN=Root2"))
// Initiator's root is missing on the notary side.
createKeyStores(0, DUMMY_NOTARY_NAME, intermediateCa1, rootCa1)
createKeyStores(1, ALICE_NAME, intermediateCa2, rootCa2, rootCa1)
createKeyStores(2, BOB_NAME, intermediateCa1, rootCa1, rootCa2)
createNetworkParameters(rootCa1, 0, 1, 2)
val notary = mockNet.createNode(InternalMockNodeParameters(
forcedID = 0,
legalName = DUMMY_NOTARY_NAME,
configOverrides = { doReturn(NotaryConfig(false)).whenever(it).notary }
))
val alice = mockNet.createNode(InternalMockNodeParameters(forcedID = 1, legalName = ALICE_NAME))
val bob = mockNet.createNode(InternalMockNodeParameters(forcedID = 2, legalName = BOB_NAME))
assertEquals(rootCa1.certificate, notary.services.identityService.trustRoot)
assertEquals(rootCa2.certificate, alice.services.identityService.trustRoot)
assertEquals(rootCa1.certificate, bob.services.identityService.trustRoot)
alice.services.startFlow(CashIssueAndPaymentFlow(1000.DOLLARS, ref, bob.party, false, notary.party))
mockNet.runNetwork()
// Ledger state remains unchanged
assertEquals(1000.DOLLARS, alice.services.getCashBalance(USD))
assertEquals(0.DOLLARS, bob.services.getCashBalance(USD))
}
@Test(timeout = 300_000)
fun `can notarise when missing acceptor's root on the notary side`() {
mockNet = InternalMockNetwork(cordappsForAllNodes = FINANCE_CORDAPPS, notarySpecs = listOf())
val (rootCa1, intermediateCa1) = createDevIntermediateCaCertPath(X500Principal("CN=Root1"))
val (rootCa2, intermediateCa2) = createDevIntermediateCaCertPath(X500Principal("CN=Root2"))
// Acceptor's root is missing on the notary side.
createKeyStores(0, DUMMY_NOTARY_NAME, intermediateCa1, rootCa1)
createKeyStores(1, ALICE_NAME, intermediateCa1, rootCa1, rootCa2)
createKeyStores(2, BOB_NAME, intermediateCa2, rootCa2, rootCa1)
createNetworkParameters(rootCa1, 0, 1, 2)
val notary = mockNet.createNode(InternalMockNodeParameters(
forcedID = 0,
legalName = DUMMY_NOTARY_NAME,
configOverrides = { doReturn(NotaryConfig(false)).whenever(it).notary }
))
val alice = mockNet.createNode(InternalMockNodeParameters(forcedID = 1, legalName = ALICE_NAME))
val bob = mockNet.createNode(InternalMockNodeParameters(forcedID = 2, legalName = BOB_NAME))
assertEquals(rootCa1.certificate, notary.services.identityService.trustRoot)
assertEquals(rootCa1.certificate, alice.services.identityService.trustRoot)
assertEquals(rootCa2.certificate, bob.services.identityService.trustRoot)
alice.services.startFlow(CashIssueAndPaymentFlow(1000.DOLLARS, ref, bob.party, false, notary.party))
mockNet.runNetwork()
// Ledger state was changed
assertEquals(0.DOLLARS, alice.services.getCashBalance(USD))
assertEquals(1000.DOLLARS, bob.services.getCashBalance(USD))
}
private fun createKeyStores(nodeId: Int,
legalName: CordaX500Name,
intermediateCa: CertificateAndKeyPair,
vararg rootCa: CertificateAndKeyPair) {
val certDir = mockNet.baseDirectory(nodeId) / "certificates"
val keyStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certDir)
val sslConfig = CertificateStoreStubs.P2P.withCertificatesDirectory(certDir)
val nodeCa = createDevNodeCa(intermediateCa, legalName)
keyStore.get(true).apply {
installDevNodeCaCertPath(legalName, rootCa.first().certificate, intermediateCa, nodeCa)
storeLegalIdentity(X509Utilities.NODE_IDENTITY_KEY_ALIAS)
}
sslConfig.keyStore.get(true).apply {
registerDevP2pCertificates(legalName, rootCa.first().certificate, intermediateCa, nodeCa)
}
sslConfig.trustStore.get(true).apply {
rootCa.forEachIndexed { i, root ->
this["${X509Utilities.CORDA_ROOT_CA}-$i"] = root.certificate
}
}
}
private fun createNetworkParameters(rootCa: CertificateAndKeyPair, vararg nodeIds: Int) {
val notaryCertDir = mockNet.baseDirectory(nodeIds.first()) / "certificates"
val notaryKeyStore = CertificateStoreStubs.Signing.withCertificatesDirectory(notaryCertDir)
val notary = Party(notaryKeyStore.get()[X509Utilities.NODE_IDENTITY_KEY_ALIAS])
val networkParameters = testNetworkParameters(epoch = 2).addNotary(notary, false)
NetworkParametersCopier(networkParameters, createDevNetworkMapCa(rootCa), overwriteFile = true).apply {
nodeIds.forEach { install(mockNet.baseDirectory(it)) }
}
}
}

View File

@ -4,8 +4,10 @@ import net.corda.core.crypto.Crypto
import net.corda.core.crypto.toStringShort import net.corda.core.crypto.toStringShort
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.createDirectories import net.corda.core.internal.createDirectories
import net.corda.core.internal.div
import net.corda.core.internal.exists import net.corda.core.internal.exists
import net.corda.core.internal.toX500Name import net.corda.core.internal.toX500Name
import net.corda.coretesting.internal.configureTestSSL
import net.corda.nodeapi.RPCApi import net.corda.nodeapi.RPCApi
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_P2P_USER import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_P2P_USER
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEER_USER import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEER_USER
@ -16,8 +18,13 @@ import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.nodeapi.internal.loadDevCaTrustStore import net.corda.nodeapi.internal.loadDevCaTrustStore
import net.corda.coretesting.internal.stubs.CertificateStoreStubs import net.corda.coretesting.internal.stubs.CertificateStoreStubs
import net.corda.nodeapi.internal.ArtemisMessagingComponent import net.corda.nodeapi.internal.ArtemisMessagingComponent
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA
import net.corda.nodeapi.internal.registerDevP2pCertificates
import net.corda.services.messaging.SimpleAMQPClient.Companion.sendAndVerify import net.corda.services.messaging.SimpleAMQPClient.Companion.sendAndVerify
import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.CHARLIE_NAME
import net.corda.testing.core.singleIdentity import net.corda.testing.core.singleIdentity
import net.corda.testing.internal.createDevIntermediateCaCertPath
import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration
import org.apache.activemq.artemis.api.core.ActiveMQClusterSecurityException import org.apache.activemq.artemis.api.core.ActiveMQClusterSecurityException
import org.apache.activemq.artemis.api.core.ActiveMQNotConnectedException import org.apache.activemq.artemis.api.core.ActiveMQNotConnectedException
@ -29,6 +36,7 @@ import org.bouncycastle.asn1.x509.NameConstraints
import org.junit.Test import org.junit.Test
import java.nio.file.Files import java.nio.file.Files
import javax.jms.JMSSecurityException import javax.jms.JMSSecurityException
import javax.security.auth.x500.X500Principal
import kotlin.test.assertEquals import kotlin.test.assertEquals
/** /**
@ -124,6 +132,51 @@ class MQSecurityAsNodeTest : P2PMQSecurityTest() {
} }
} }
@Test(timeout = 300_000)
fun `login with invalid root`() {
val legalName = CordaX500Name("MegaCorp", "London", "GB")
val sslConfig = configureTestSSL(legalName)
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath(X500Principal("CN=Root2"))
sslConfig.trustStore.get()[CORDA_ROOT_CA] = rootCa.certificate
sslConfig.keyStore.get().registerDevP2pCertificates(legalName, rootCa.certificate, intermediateCa)
val attacker = clientTo(alice.node.configuration.p2pAddress, sslConfig)
assertThatExceptionOfType(ActiveMQNotConnectedException::class.java).isThrownBy {
attacker.start(PEER_USER, PEER_USER)
}
}
@Test(timeout = 300_000)
fun `login with different roots`() {
val (rootCa2, intermediateCa2) = createDevIntermediateCaCertPath(X500Principal("CN=Root2"))
val (rootCa3, intermediateCa3) = createDevIntermediateCaCertPath(X500Principal("CN=Root3"))
val certificatesDirectory = baseDirectory(BOB_NAME).createDirectories() / "certificates"
CertificateStoreStubs.P2P.TrustStore.withCertificatesDirectory(certificatesDirectory).get(true).let {
it[CORDA_ROOT_CA] = DEV_ROOT_CA.certificate
it["$CORDA_ROOT_CA-2"] = rootCa2.certificate
}
val bob = startNode(BOB_NAME)
// Login with different trusted root.
configureTestSSL(CHARLIE_NAME).apply {
trustStore.get()[CORDA_ROOT_CA] = rootCa2.certificate
trustStore.get()["$CORDA_ROOT_CA-2"] = DEV_ROOT_CA.certificate
keyStore.get().registerDevP2pCertificates(CHARLIE_NAME, rootCa2.certificate, intermediateCa2)
clientTo(bob.node.configuration.p2pAddress, this).start(PEER_USER, PEER_USER)
}
// Login with different non-trusted root.
configureTestSSL(CHARLIE_NAME).apply {
trustStore.get()[CORDA_ROOT_CA] = rootCa3.certificate
trustStore.get()["$CORDA_ROOT_CA-2"] = DEV_ROOT_CA.certificate
keyStore.get().registerDevP2pCertificates(CHARLIE_NAME, rootCa3.certificate, intermediateCa3)
assertThatExceptionOfType(ActiveMQNotConnectedException::class.java).isThrownBy {
clientTo(bob.node.configuration.p2pAddress, this).start(PEER_USER, PEER_USER)
}
}
}
override fun `send message to notifications address`() { override fun `send message to notifications address`() {
assertProducerQueueCreationAttackFails(ArtemisMessagingComponent.NOTIFICATIONS_ADDRESS) assertProducerQueueCreationAttackFails(ArtemisMessagingComponent.NOTIFICATIONS_ADDRESS)
} }

View File

@ -437,9 +437,9 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
open fun generateAndSaveNodeInfo(): NodeInfo { open fun generateAndSaveNodeInfo(): NodeInfo {
check(started == null) { "Node has already been started" } check(started == null) { "Node has already been started" }
log.info("Generating nodeInfo ...") log.info("Generating nodeInfo ...")
val trustRoot = initKeyStores() val trustRoots = initKeyStores()
startDatabase() startDatabase()
identityService.start(trustRoot, keyStoreHandler.nodeIdentity, pkToIdCache = pkToIdCache) identityService.start(trustRoots, keyStoreHandler.nodeIdentity, pkToIdCache = pkToIdCache)
return database.use { return database.use {
it.transaction { it.transaction {
val nodeInfoAndSigned = updateNodeInfo(publish = false) val nodeInfoAndSigned = updateNodeInfo(publish = false)
@ -486,9 +486,9 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
logVendorString(database, log) logVendorString(database, log)
if (allowHibernateToManageAppSchema) { if (allowHibernateToManageAppSchema) {
Node.printBasicNodeInfo("Initialising CorDapps to get schemas created by hibernate") Node.printBasicNodeInfo("Initialising CorDapps to get schemas created by hibernate")
val trustRoot = initKeyStores() val trustRoots = initKeyStores()
networkMapClient?.start(trustRoot) networkMapClient?.start(trustRoots)
val (netParams, signedNetParams) = NetworkParametersReader(trustRoot, networkMapClient, configuration.networkParametersPath).read() val (netParams, signedNetParams) = NetworkParametersReader(trustRoots, networkMapClient, configuration.networkParametersPath).read()
log.info("Loaded network parameters: $netParams") log.info("Loaded network parameters: $netParams")
check(netParams.minimumPlatformVersion <= versionInfo.platformVersion) { check(netParams.minimumPlatformVersion <= versionInfo.platformVersion) {
"Node's platform version is lower than network's required minimumPlatformVersion" "Node's platform version is lower than network's required minimumPlatformVersion"
@ -496,7 +496,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
networkMapCache.start(netParams.notaries) networkMapCache.start(netParams.notaries)
database.transaction { database.transaction {
networkParametersStorage.setCurrentParameters(signedNetParams, trustRoot) networkParametersStorage.setCurrentParameters(signedNetParams, trustRoots)
cordappProvider.start() cordappProvider.start()
} }
} }
@ -536,7 +536,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
nodeLifecycleEventsDistributor.distributeEvent(NodeLifecycleEvent.BeforeNodeStart(nodeServicesContext)) nodeLifecycleEventsDistributor.distributeEvent(NodeLifecycleEvent.BeforeNodeStart(nodeServicesContext))
log.info("Node starting up ...") log.info("Node starting up ...")
val trustRoot = initKeyStores() val trustRoots = initKeyStores()
initialiseJolokia() initialiseJolokia()
schemaService.mappedSchemasWarnings().forEach { schemaService.mappedSchemasWarnings().forEach {
@ -549,9 +549,9 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
registerCordappFlows() registerCordappFlows()
services.rpcFlows += cordappLoader.cordapps.flatMap { it.rpcFlows } services.rpcFlows += cordappLoader.cordapps.flatMap { it.rpcFlows }
startShell() startShell()
networkMapClient?.start(trustRoot) networkMapClient?.start(trustRoots)
val networkParametersReader = NetworkParametersReader(trustRoot, networkMapClient, configuration.networkParametersPath) val networkParametersReader = NetworkParametersReader(trustRoots, networkMapClient, configuration.networkParametersPath)
val (netParams, signedNetParams) = networkParametersReader.read() val (netParams, signedNetParams) = networkParametersReader.read()
log.info("Loaded network parameters: $netParams") log.info("Loaded network parameters: $netParams")
check(netParams.minimumPlatformVersion <= versionInfo.platformVersion) { check(netParams.minimumPlatformVersion <= versionInfo.platformVersion) {
@ -565,7 +565,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
schedulerService.closeOnStop() schedulerService.closeOnStop()
val rpcOps = makeRPCOps(cordappLoader) val rpcOps = makeRPCOps(cordappLoader)
identityService.start(trustRoot, keyStoreHandler.nodeIdentity, netParams.notaries.map { it.identity }, pkToIdCache) identityService.start(trustRoots, keyStoreHandler.nodeIdentity, netParams.notaries.map { it.identity }, pkToIdCache)
val nodeInfoAndSigned = database.transaction { val nodeInfoAndSigned = database.transaction {
updateNodeInfo(publish = true) updateNodeInfo(publish = true)
@ -577,7 +577,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
val networkParametersHotloader = if (networkMapClient == null) { val networkParametersHotloader = if (networkMapClient == null) {
null null
} else { } else {
NetworkParametersHotloader(networkMapClient, trustRoot, netParams, networkParametersReader, networkParametersStorage).also { NetworkParametersHotloader(networkMapClient, trustRoots, netParams, networkParametersReader, networkParametersStorage).also {
it.addNotaryUpdateListener(networkMapCache) it.addNotaryUpdateListener(networkMapCache)
it.addNotaryUpdateListener(identityService) it.addNotaryUpdateListener(identityService)
it.addNetworkParametersChangedListeners(services) it.addNetworkParametersChangedListeners(services)
@ -586,7 +586,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
} }
networkMapUpdater.start( networkMapUpdater.start(
trustRoot, trustRoots,
signedNetParams.raw.hash, signedNetParams.raw.hash,
signedNodeInfo, signedNodeInfo,
netParams, netParams,
@ -608,7 +608,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
// Do all of this in a database transaction so anything that might need a connection has one. // Do all of this in a database transaction so anything that might need a connection has one.
val (resultingNodeInfo, readyFuture) = database.transaction(recoverableFailureTolerance = 0) { val (resultingNodeInfo, readyFuture) = database.transaction(recoverableFailureTolerance = 0) {
networkParametersStorage.setCurrentParameters(signedNetParams, trustRoot) networkParametersStorage.setCurrentParameters(signedNetParams, trustRoots)
identityService.loadIdentities(nodeInfo.legalIdentitiesAndCerts) identityService.loadIdentities(nodeInfo.legalIdentitiesAndCerts)
attachments.start() attachments.start()
cordappProvider.start() cordappProvider.start()

View File

@ -33,7 +33,7 @@ class DBNetworkParametersStorage(
// We could have historic parameters endpoint or always add parameters as an attachment to the transaction. // We could have historic parameters endpoint or always add parameters as an attachment to the transaction.
private val networkMapClient: NetworkMapClient? private val networkMapClient: NetworkMapClient?
) : NetworkParametersStorage, SingletonSerializeAsToken() { ) : NetworkParametersStorage, SingletonSerializeAsToken() {
private lateinit var trustRoot: X509Certificate private lateinit var trustRoots: Set<X509Certificate>
companion object { companion object {
private val log = contextLogger() private val log = contextLogger()
@ -58,8 +58,8 @@ class DBNetworkParametersStorage(
} }
} }
override fun setCurrentParameters(currentSignedParameters: SignedDataWithCert<NetworkParameters>, trustRoot: X509Certificate) { override fun setCurrentParameters(currentSignedParameters: SignedDataWithCert<NetworkParameters>, trustRoots: Set<X509Certificate>) {
this.trustRoot = trustRoot this.trustRoots = trustRoots
saveParameters(currentSignedParameters) saveParameters(currentSignedParameters)
_currentHash = currentSignedParameters.raw.hash _currentHash = currentSignedParameters.raw.hash
} }
@ -85,7 +85,7 @@ class DBNetworkParametersStorage(
override fun saveParameters(signedNetworkParameters: SignedNetworkParameters) { override fun saveParameters(signedNetworkParameters: SignedNetworkParameters) {
log.trace { "Saving new network parameters to network parameters storage." } log.trace { "Saving new network parameters to network parameters storage." }
val networkParameters = signedNetworkParameters.verifiedNetworkParametersCert(trustRoot) val networkParameters = signedNetworkParameters.verifiedNetworkParametersCert(trustRoots)
val hash = signedNetworkParameters.raw.hash val hash = signedNetworkParameters.raw.hash
log.trace { "Parameters to save $networkParameters with hash $hash" } log.trace { "Parameters to save $networkParameters with hash $hash" }
database.transaction { database.transaction {
@ -97,7 +97,7 @@ class DBNetworkParametersStorage(
return if (networkMapClient != null) { return if (networkMapClient != null) {
try { try {
val signedParams = networkMapClient.getNetworkParameters(parametersHash) val signedParams = networkMapClient.getNetworkParameters(parametersHash)
val networkParameters = signedParams.verifiedNetworkParametersCert(trustRoot) val networkParameters = signedParams.verifiedNetworkParametersCert(trustRoots)
saveParameters(signedParams) saveParameters(signedParams)
networkParameters networkParameters
} catch (e: Exception) { } catch (e: Exception) {

View File

@ -23,6 +23,7 @@ import java.nio.file.NoSuchFileException
import java.security.GeneralSecurityException import java.security.GeneralSecurityException
import java.security.PublicKey import java.security.PublicKey
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import java.util.Collections.unmodifiableSet
data class KeyAndAlias(val key: PublicKey, val alias: String) data class KeyAndAlias(val key: PublicKey, val alias: String)
@ -40,16 +41,16 @@ class KeyStoreHandler(private val configuration: NodeConfiguration, private val
private val _signingKeys: MutableSet<KeyAndAlias> = mutableSetOf() private val _signingKeys: MutableSet<KeyAndAlias> = mutableSetOf()
val signingKeys: Set<KeyAndAlias> get() = _signingKeys.toSet() val signingKeys: Set<KeyAndAlias> get() = _signingKeys.toSet()
private lateinit var trustRoot: X509Certificate private lateinit var trustRoots: Set<X509Certificate>
private lateinit var nodeKeyStore: CertificateStore private lateinit var nodeKeyStore: CertificateStore
/** /**
* Initialize key-stores and load identities. * Initialize key-stores and load identities.
* @param devModeKeyEntropy entropy for legal identity key derivation * @param devModeKeyEntropy entropy for legal identity key derivation
* @return trust root certificate * @return trust root certificates
*/ */
fun init(devModeKeyEntropy: BigInteger? = null): X509Certificate { fun init(devModeKeyEntropy: BigInteger? = null): Set<X509Certificate> {
if (configuration.devMode) { if (configuration.devMode) {
configuration.configureWithDevSSLCertificate(cryptoService, devModeKeyEntropy) configuration.configureWithDevSSLCertificate(cryptoService, devModeKeyEntropy)
// configureWithDevSSLCertificate is a devMode process that writes directly to keystore files, so // configureWithDevSSLCertificate is a devMode process that writes directly to keystore files, so
@ -59,10 +60,10 @@ class KeyStoreHandler(private val configuration: NodeConfiguration, private val
} }
} }
val certStores = getCertificateStores() val certStores = getCertificateStores()
trustRoot = validateKeyStores(certStores) trustRoots = validateKeyStores(certStores)
nodeKeyStore = certStores.nodeKeyStore nodeKeyStore = certStores.nodeKeyStore
loadIdentities() loadIdentities()
return trustRoot return unmodifiableSet(trustRoots)
} }
private data class AllCertificateStores(val trustStore: CertificateStore, private data class AllCertificateStores(val trustStore: CertificateStore,
@ -88,12 +89,15 @@ class KeyStoreHandler(private val configuration: NodeConfiguration, private val
} }
} }
private fun validateKeyStores(certStores: AllCertificateStores): X509Certificate { private fun validateKeyStores(certStores: AllCertificateStores): Set<X509Certificate> {
// Check that trustStore contains the correct key-alias entry. // Check that trustStore contains the correct key-alias entry.
require(X509Utilities.CORDA_ROOT_CA in certStores.trustStore) { val trustRoots = certStores.trustStore.filter { it.first.startsWith(X509Utilities.CORDA_ROOT_CA) }.map {
log.info("Loaded trusted root certificate: ${it.second.publicKey.toStringShort()}, alias: ${it.first}")
it.second
}.toSet()
require(trustRoots.isNotEmpty()) {
"Alias for trustRoot key not found. Please ensure you have an updated trustStore file." "Alias for trustRoot key not found. Please ensure you have an updated trustStore file."
} }
val trustRoot = certStores.trustStore[X509Utilities.CORDA_ROOT_CA]
certStores.sslKeyStore.let { certStores.sslKeyStore.let {
val tlsKeyAlias = CORDA_CLIENT_TLS val tlsKeyAlias = CORDA_CLIENT_TLS
@ -112,10 +116,10 @@ class KeyStoreHandler(private val configuration: NodeConfiguration, private val
// Check TLS cert path chains to the trusted root. // Check TLS cert path chains to the trusted root.
val sslCertChainRoot = it.query { getCertificateChain(tlsKeyAlias) }.last() val sslCertChainRoot = it.query { getCertificateChain(tlsKeyAlias) }.last()
require(sslCertChainRoot == trustRoot) { "TLS certificate must chain to the trusted root." } require(sslCertChainRoot in trustRoots) { "TLS certificate must chain to the trusted root." }
} }
return trustRoot return trustRoots
} }
/** /**
@ -159,7 +163,7 @@ class KeyStoreHandler(private val configuration: NodeConfiguration, private val
check(certificates.first() == certificate) { check(certificates.first() == certificate) {
"Certificates from key store do not line up!" "Certificates from key store do not line up!"
} }
check(certificates.last() == trustRoot) { check(certificates.last() in trustRoots) {
"Certificate for node identity must chain to the trusted root." "Certificate for node identity must chain to the trusted root."
} }
@ -169,7 +173,7 @@ class KeyStoreHandler(private val configuration: NodeConfiguration, private val
} }
val identity = PartyAndCertificate(X509Utilities.buildCertPath(certificates)) val identity = PartyAndCertificate(X509Utilities.buildCertPath(certificates))
X509Utilities.validateCertPath(trustRoot, identity.certPath) X509Utilities.validateCertPath(trustRoots, identity.certPath)
return identity return identity
} }
@ -224,7 +228,7 @@ class KeyStoreHandler(private val configuration: NodeConfiguration, private val
val certificates: List<X509Certificate> = uncheckedCast(listOf(certificate) + baseIdentity.certPath.certificates.drop(1)) val certificates: List<X509Certificate> = uncheckedCast(listOf(certificate) + baseIdentity.certPath.certificates.drop(1))
val identity = PartyAndCertificate(X509Utilities.buildCertPath(certificates)) val identity = PartyAndCertificate(X509Utilities.buildCertPath(certificates))
X509Utilities.validateCertPath(trustRoot, identity.certPath) X509Utilities.validateCertPath(trustRoots, identity.certPath)
return identity return identity
} }
} }

View File

@ -18,7 +18,7 @@ import java.nio.file.Path
import java.nio.file.StandardCopyOption import java.nio.file.StandardCopyOption
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
class NetworkParametersReader(private val trustRoot: X509Certificate, class NetworkParametersReader(private val trustRoots: Set<X509Certificate>,
private val networkMapClient: NetworkMapClient?, private val networkMapClient: NetworkMapClient?,
private val networkParamsPath: Path) { private val networkParamsPath: Path) {
companion object { companion object {
@ -68,7 +68,7 @@ class NetworkParametersReader(private val trustRoot: X509Certificate,
signedParametersFromFile ?: throw Error.ParamsNotConfigured() signedParametersFromFile ?: throw Error.ParamsNotConfigured()
} }
return NetworkParametersAndSigned(signedParameters, trustRoot) return NetworkParametersAndSigned(signedParameters, trustRoots)
} }
private fun readParametersUpdate(advertisedParametersHash: SecureHash, previousParametersHash: SecureHash): SignedNetworkParameters { private fun readParametersUpdate(advertisedParametersHash: SecureHash, previousParametersHash: SecureHash): SignedNetworkParameters {
@ -90,7 +90,7 @@ class NetworkParametersReader(private val trustRoot: X509Certificate,
logger.info("No network-parameters file found. Expecting network parameters to be available from the network map.") logger.info("No network-parameters file found. Expecting network parameters to be available from the network map.")
networkMapClient ?: throw Error.NetworkMapNotConfigured() networkMapClient ?: throw Error.NetworkMapNotConfigured()
val signedParams = networkMapClient.getNetworkParameters(parametersHash) val signedParams = networkMapClient.getNetworkParameters(parametersHash)
signedParams.verifiedNetworkParametersCert(trustRoot) signedParams.verifiedNetworkParametersCert(trustRoots)
networkParamsFile.parent.toFile().mkdirs() networkParamsFile.parent.toFile().mkdirs()
signedParams.serialize().open().copyTo(networkParamsFile) signedParams.serialize().open().copyTo(networkParamsFile)
logger.info("Saved network parameters into: $networkParamsFile") logger.info("Saved network parameters into: $networkParamsFile")
@ -99,10 +99,10 @@ class NetworkParametersReader(private val trustRoot: X509Certificate,
// By passing in just the SignedNetworkParameters object, this class guarantees that the networkParameters property // By passing in just the SignedNetworkParameters object, this class guarantees that the networkParameters property
// could have only been derived from it. // could have only been derived from it.
class NetworkParametersAndSigned(val signed: SignedNetworkParameters, trustRoot: X509Certificate) { class NetworkParametersAndSigned(val signed: SignedNetworkParameters, trustRoots: Set<X509Certificate>) {
// for backwards compatibility we allow netparams to be signed with the networkmap cert, // for backwards compatibility we allow netparams to be signed with the networkmap cert,
// but going forwards we also accept the distinct netparams cert as well // but going forwards we also accept the distinct netparams cert as well
val networkParameters: NetworkParameters = signed.verifiedNetworkParametersCert(trustRoot) val networkParameters: NetworkParameters = signed.verifiedNetworkParametersCert(trustRoots)
operator fun component1() = networkParameters operator fun component1() = networkParameters
operator fun component2() = signed operator fun component2() = signed
} }

View File

@ -41,12 +41,13 @@ sealed class CertificateChainCheckPolicy {
object RootMustMatch : CertificateChainCheckPolicy() { object RootMustMatch : CertificateChainCheckPolicy() {
override fun createCheck(keyStore: KeyStore, trustStore: KeyStore): Check { override fun createCheck(keyStore: KeyStore, trustStore: KeyStore): Check {
val rootPublicKey = trustStore.getCertificate(X509Utilities.CORDA_ROOT_CA).publicKey val rootAliases = trustStore.aliases().asSequence().filter { it.startsWith(X509Utilities.CORDA_ROOT_CA) }
val rootPublicKeys = rootAliases.map { trustStore.getCertificate(it).publicKey }.toSet()
return object : Check { return object : Check {
@Suppress("DEPRECATION") // should use java.security.cert.X509Certificate @Suppress("DEPRECATION") // should use java.security.cert.X509Certificate
override fun checkCertificateChain(theirChain: Array<javax.security.cert.X509Certificate>) { override fun checkCertificateChain(theirChain: Array<javax.security.cert.X509Certificate>) {
val theirRoot = theirChain.last().publicKey val theirRoot = theirChain.last().publicKey
if (rootPublicKey != theirRoot) { if (theirRoot !in rootPublicKeys) {
throw CertificateException("Root certificate mismatch, their root = $theirRoot") throw CertificateException("Root certificate mismatch, their root = $theirRoot")
} }
} }

View File

@ -6,8 +6,11 @@ import net.corda.core.node.services.IdentityService
import java.security.InvalidAlgorithmParameterException import java.security.InvalidAlgorithmParameterException
import java.security.cert.CertificateExpiredException import java.security.cert.CertificateExpiredException
import java.security.cert.CertificateNotYetValidException import java.security.cert.CertificateNotYetValidException
import java.security.cert.TrustAnchor
interface IdentityServiceInternal : IdentityService { interface IdentityServiceInternal : IdentityService {
val trustAnchors: Set<TrustAnchor>
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class) @Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
fun verifyAndRegisterNewRandomIdentity(identity: PartyAndCertificate) fun verifyAndRegisterNewRandomIdentity(identity: PartyAndCertificate)

View File

@ -40,6 +40,7 @@ class InMemoryIdentityService(
*/ */
override val caCertStore: CertStore = CertStore.getInstance("Collection", CollectionCertStoreParameters(setOf(trustRoot))) override val caCertStore: CertStore = CertStore.getInstance("Collection", CollectionCertStoreParameters(setOf(trustRoot)))
override val trustAnchor: TrustAnchor = TrustAnchor(trustRoot, null) override val trustAnchor: TrustAnchor = TrustAnchor(trustRoot, null)
override val trustAnchors = setOf(trustAnchor)
private val keyToExternalId = ConcurrentHashMap<String, UUID>() private val keyToExternalId = ConcurrentHashMap<String, UUID>()
private val keyToPartyAndCerts = ConcurrentHashMap<PublicKey, PartyAndCertificate>() private val keyToPartyAndCerts = ConcurrentHashMap<PublicKey, PartyAndCertificate>()
private val nameToKey = ConcurrentHashMap<CordaX500Name, PublicKey>() private val nameToKey = ConcurrentHashMap<CordaX500Name, PublicKey>()
@ -55,22 +56,23 @@ class InMemoryIdentityService(
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class) @Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
override fun verifyAndRegisterIdentity(identity: PartyAndCertificate): PartyAndCertificate? { override fun verifyAndRegisterIdentity(identity: PartyAndCertificate): PartyAndCertificate? {
return verifyAndRegisterIdentity(trustAnchor, identity) return verifyAndRegisterIdentity(trustAnchors, identity)
} }
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class) @Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
override fun verifyAndRegisterNewRandomIdentity(identity: PartyAndCertificate) { override fun verifyAndRegisterNewRandomIdentity(identity: PartyAndCertificate) {
verifyAndRegisterIdentity(trustAnchor, identity) verifyAndRegisterIdentity(trustAnchors, identity)
} }
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class) @Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
private fun verifyAndRegisterIdentity(trustAnchor: TrustAnchor, identity: PartyAndCertificate): PartyAndCertificate? { private fun verifyAndRegisterIdentity(trustAnchors: Set<TrustAnchor>, identity: PartyAndCertificate): PartyAndCertificate? {
// Validate the chain first, before we do anything clever with it // Validate the chain first, before we do anything clever with it
val identityCertChain = identity.certPath.x509Certificates val identityCertChain = identity.certPath.x509Certificates
try { try {
identity.verify(trustAnchor) identity.verify(trustAnchor)
} catch (e: CertPathValidatorException) { } catch (e: CertPathValidatorException) {
log.warn("Certificate validation failed for ${identity.name} against trusted root ${trustAnchor.trustedCert.subjectX500Principal}.") val roots = trustAnchors.map { it.trustedCert.subjectX500Principal }
log.warn("Certificate validation failed for ${identity.name} against trusted roots $roots.")
log.warn("Certificate path :") log.warn("Certificate path :")
identityCertChain.reversed().forEachIndexed { index, certificate -> identityCertChain.reversed().forEachIndexed { index, certificate ->
val space = (0 until index).joinToString("") { " " } val space = (0 until index).joinToString("") { " " }
@ -83,7 +85,7 @@ class InMemoryIdentityService(
if (wellKnownCert != identity.certificate) { if (wellKnownCert != identity.certificate) {
val idx = identityCertChain.lastIndexOf(wellKnownCert) val idx = identityCertChain.lastIndexOf(wellKnownCert)
val firstPath = X509Utilities.buildCertPath(identityCertChain.slice(idx until identityCertChain.size)) val firstPath = X509Utilities.buildCertPath(identityCertChain.slice(idx until identityCertChain.size))
verifyAndRegisterIdentity(trustAnchor, PartyAndCertificate(firstPath)) verifyAndRegisterIdentity(trustAnchors, PartyAndCertificate(firstPath))
} }
return registerIdentity(identity, false) return registerIdentity(identity, false)
} }

View File

@ -185,6 +185,9 @@ class PersistentIdentityService(cacheFactory: NamedCacheFactory) : SingletonSeri
private lateinit var _trustAnchor: TrustAnchor private lateinit var _trustAnchor: TrustAnchor
override val trustAnchor: TrustAnchor get() = _trustAnchor override val trustAnchor: TrustAnchor get() = _trustAnchor
private lateinit var _trustAnchors: Set<TrustAnchor>
override val trustAnchors: Set<TrustAnchor> get() = _trustAnchors
private lateinit var ourParty: Party private lateinit var ourParty: Party
/** Stores notary identities obtained from the network parameters, for which we don't need to perform a database lookup. */ /** Stores notary identities obtained from the network parameters, for which we don't need to perform a database lookup. */
@ -202,13 +205,14 @@ class PersistentIdentityService(cacheFactory: NamedCacheFactory) : SingletonSeri
private val hashToKey = createHashToKeyMap(cacheFactory) private val hashToKey = createHashToKeyMap(cacheFactory)
fun start( fun start(
trustRoot: X509Certificate, trustRoots: Set<X509Certificate>,
ourIdentity: PartyAndCertificate, ourIdentity: PartyAndCertificate,
notaryIdentities: List<Party> = emptyList(), notaryIdentities: List<Party> = emptyList(),
pkToIdCache: WritablePublicKeyToOwningIdentityCache pkToIdCache: WritablePublicKeyToOwningIdentityCache
) { ) {
_trustRoot = trustRoot _trustRoot = ourIdentity.certPath.certificates.last() as X509Certificate
_trustAnchor = TrustAnchor(trustRoot, null) _trustAnchor = TrustAnchor(trustRoot, null)
_trustAnchors = trustRoots.map { TrustAnchor(it, null) }.toSet()
// Extract Node CA certificate from node identity certificate path // Extract Node CA certificate from node identity certificate path
val certificates = setOf(ourIdentity.certificate, ourIdentity.certPath.certificates[1], trustRoot) val certificates = setOf(ourIdentity.certificate, ourIdentity.certPath.certificates[1], trustRoot)
_caCertStore = CertStore.getInstance("Collection", CollectionCertStoreParameters(certificates)) _caCertStore = CertStore.getInstance("Collection", CollectionCertStoreParameters(certificates))
@ -229,23 +233,24 @@ class PersistentIdentityService(cacheFactory: NamedCacheFactory) : SingletonSeri
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class) @Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
override fun verifyAndRegisterIdentity(identity: PartyAndCertificate): PartyAndCertificate? { override fun verifyAndRegisterIdentity(identity: PartyAndCertificate): PartyAndCertificate? {
return verifyAndRegisterIdentity(trustAnchor, identity) return verifyAndRegisterIdentity(trustAnchors, identity)
} }
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class) @Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
override fun verifyAndRegisterNewRandomIdentity(identity: PartyAndCertificate) { override fun verifyAndRegisterNewRandomIdentity(identity: PartyAndCertificate) {
verifyAndRegisterIdentity(trustAnchor, identity, true) verifyAndRegisterIdentity(trustAnchors, identity, true)
} }
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class) @Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
private fun verifyAndRegisterIdentity(trustAnchor: TrustAnchor, identity: PartyAndCertificate, isNewRandomIdentity: Boolean = false): private fun verifyAndRegisterIdentity(trustAnchors: Set<TrustAnchor>, identity: PartyAndCertificate, isNewRandomIdentity: Boolean = false):
PartyAndCertificate? { PartyAndCertificate? {
// Validate the chain first, before we do anything clever with it // Validate the chain first, before we do anything clever with it
val identityCertChain = identity.certPath.x509Certificates val identityCertChain = identity.certPath.x509Certificates
try { try {
identity.verify(trustAnchor) identity.verify(trustAnchors)
} catch (e: CertPathValidatorException) { } catch (e: CertPathValidatorException) {
log.warn("Certificate validation failed for ${identity.name} against trusted root ${trustAnchor.trustedCert.subjectX500Principal}.") val roots = trustAnchors.map { it.trustedCert.subjectX500Principal }
log.warn("Certificate validation failed for ${identity.name} against trusted roots $roots.")
log.warn("Certificate path :") log.warn("Certificate path :")
identityCertChain.reversed().forEachIndexed { index, certificate -> identityCertChain.reversed().forEachIndexed { index, certificate ->
val space = (0 until index).joinToString("") { " " } val space = (0 until index).joinToString("") { " " }
@ -258,7 +263,7 @@ class PersistentIdentityService(cacheFactory: NamedCacheFactory) : SingletonSeri
if (wellKnownCert != identity.certificate && !isNewRandomIdentity) { if (wellKnownCert != identity.certificate && !isNewRandomIdentity) {
val idx = identityCertChain.lastIndexOf(wellKnownCert) val idx = identityCertChain.lastIndexOf(wellKnownCert)
val firstPath = X509Utilities.buildCertPath(identityCertChain.slice(idx until identityCertChain.size)) val firstPath = X509Utilities.buildCertPath(identityCertChain.slice(idx until identityCertChain.size))
verifyAndRegisterIdentity(trustAnchor, PartyAndCertificate(firstPath)) verifyAndRegisterIdentity(trustAnchors, PartyAndCertificate(firstPath))
} }
return registerIdentity(identity, isNewRandomIdentity) return registerIdentity(identity, isNewRandomIdentity)
} }

View File

@ -32,10 +32,10 @@ class NetworkMapClient(compatibilityZoneURL: URL, private val versionInfo: Versi
} }
private val networkMapUrl = URL("$compatibilityZoneURL/network-map") private val networkMapUrl = URL("$compatibilityZoneURL/network-map")
private lateinit var trustRoot: X509Certificate private lateinit var trustRoots: Set<X509Certificate>
fun start(trustRoot: X509Certificate) { fun start(trustRoots: Set<X509Certificate>) {
this.trustRoot = trustRoot this.trustRoots = trustRoots
} }
fun publish(signedNodeInfo: SignedNodeInfo) { fun publish(signedNodeInfo: SignedNodeInfo) {
@ -61,7 +61,7 @@ class NetworkMapClient(compatibilityZoneURL: URL, private val versionInfo: Versi
logger.trace { "Fetching network map update from $url." } logger.trace { "Fetching network map update from $url." }
val connection = url.openHttpConnection() val connection = url.openHttpConnection()
val signedNetworkMap = connection.responseAs<SignedNetworkMap>() val signedNetworkMap = connection.responseAs<SignedNetworkMap>()
val networkMap = signedNetworkMap.verifiedNetworkMapCert(trustRoot) val networkMap = signedNetworkMap.verifiedNetworkMapCert(trustRoots)
val timeout = connection.cacheControl.maxAgeSeconds().seconds val timeout = connection.cacheControl.maxAgeSeconds().seconds
val version = connection.cordaServerVersion val version = connection.cordaServerVersion
logger.trace { "Fetched network map update from $url successfully: $networkMap" } logger.trace { "Fetched network map update from $url successfully: $networkMap" }
@ -89,7 +89,7 @@ class NetworkMapClient(compatibilityZoneURL: URL, private val versionInfo: Versi
logger.trace { "Fetching node infos from $url." } logger.trace { "Fetching node infos from $url." }
val verifiedNodeInfo = url.openHttpConnection().responseAs<Pair<SignedNetworkMap, List<SignedNodeInfo>>>() val verifiedNodeInfo = url.openHttpConnection().responseAs<Pair<SignedNetworkMap, List<SignedNodeInfo>>>()
.also { .also {
val verifiedNodeInfoHashes = it.first.verifiedNetworkMapCert(trustRoot).nodeInfoHashes val verifiedNodeInfoHashes = it.first.verifiedNetworkMapCert(trustRoots).nodeInfoHashes
val nodeInfoHashes = it.second.map { signedNodeInfo -> signedNodeInfo.verified().serialize().sha256() } val nodeInfoHashes = it.second.map { signedNodeInfo -> signedNodeInfo.verified().serialize().sha256() }
require( require(
verifiedNodeInfoHashes.containsAll(nodeInfoHashes) && verifiedNodeInfoHashes.containsAll(nodeInfoHashes) &&

View File

@ -80,7 +80,7 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal,
private var newNetworkParameters: Pair<ParametersUpdate, SignedNetworkParameters>? = null private var newNetworkParameters: Pair<ParametersUpdate, SignedNetworkParameters>? = null
private val fileWatcherSubscription = AtomicReference<Subscription?>() private val fileWatcherSubscription = AtomicReference<Subscription?>()
private var autoAcceptNetworkParameters: Boolean = true private var autoAcceptNetworkParameters: Boolean = true
private lateinit var trustRoot: X509Certificate private lateinit var trustRoots: Set<X509Certificate>
@Volatile @Volatile
private lateinit var currentParametersHash: SecureHash private lateinit var currentParametersHash: SecureHash
private lateinit var ourNodeInfo: SignedNodeInfo private lateinit var ourNodeInfo: SignedNodeInfo
@ -103,7 +103,7 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal,
MoreExecutors.shutdownAndAwaitTermination(networkMapPoller, 50, TimeUnit.SECONDS) MoreExecutors.shutdownAndAwaitTermination(networkMapPoller, 50, TimeUnit.SECONDS)
} }
@Suppress("LongParameterList") @Suppress("LongParameterList")
fun start(trustRoot: X509Certificate, fun start(trustRoots: Set<X509Certificate>,
currentParametersHash: SecureHash, currentParametersHash: SecureHash,
ourNodeInfo: SignedNodeInfo, ourNodeInfo: SignedNodeInfo,
networkParameters: NetworkParameters, networkParameters: NetworkParameters,
@ -113,7 +113,7 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal,
) { ) {
fileWatcherSubscription.updateAndGet { subscription -> fileWatcherSubscription.updateAndGet { subscription ->
require(subscription == null) { "Should not call this method twice" } require(subscription == null) { "Should not call this method twice" }
this.trustRoot = trustRoot this.trustRoots = trustRoots
this.currentParametersHash = currentParametersHash this.currentParametersHash = currentParametersHash
this.ourNodeInfo = ourNodeInfo this.ourNodeInfo = ourNodeInfo
this.ourNodeInfoHash = ourNodeInfo.raw.hash this.ourNodeInfoHash = ourNodeInfo.raw.hash
@ -342,7 +342,7 @@ The node will shutdown now.""")
return return
} }
val newSignedNetParams = networkMapClient.getNetworkParameters(update.newParametersHash) val newSignedNetParams = networkMapClient.getNetworkParameters(update.newParametersHash)
val newNetParams = newSignedNetParams.verifiedNetworkParametersCert(trustRoot) val newNetParams = newSignedNetParams.verifiedNetworkParametersCert(trustRoots)
networkParametersStorage.saveParameters(newSignedNetParams) networkParametersStorage.saveParameters(newSignedNetParams)
logger.info("Downloaded new network parameters: $newNetParams from the update: $update") logger.info("Downloaded new network parameters: $newNetParams from the update: $update")
newNetworkParameters = Pair(update, newSignedNetParams) newNetworkParameters = Pair(update, newSignedNetParams)
@ -369,7 +369,7 @@ The node will shutdown now.""")
// Add persisting of newest parameters from update. // Add persisting of newest parameters from update.
val (update, signedNewNetParams) = requireNotNull(newNetworkParameters) { "Couldn't find parameters update for the hash: $parametersHash" } val (update, signedNewNetParams) = requireNotNull(newNetworkParameters) { "Couldn't find parameters update for the hash: $parametersHash" }
// We should check that we sign the right data structure hash. // We should check that we sign the right data structure hash.
val newNetParams = signedNewNetParams.verifiedNetworkParametersCert(trustRoot) val newNetParams = signedNewNetParams.verifiedNetworkParametersCert(trustRoots)
val newParametersHash = signedNewNetParams.raw.hash val newParametersHash = signedNewNetParams.raw.hash
if (parametersHash == newParametersHash) { if (parametersHash == newParametersHash) {
// The latest parameters have priority. // The latest parameters have priority.

View File

@ -16,7 +16,7 @@ import kotlin.reflect.jvm.javaGetter
* Currently only hotloading notary changes are supported. * Currently only hotloading notary changes are supported.
*/ */
class NetworkParametersHotloader(private val networkMapClient: NetworkMapClient, class NetworkParametersHotloader(private val networkMapClient: NetworkMapClient,
private val trustRoot: X509Certificate, private val trustRoots: Set<X509Certificate>,
@Volatile private var networkParameters: NetworkParameters, @Volatile private var networkParameters: NetworkParameters,
private val networkParametersReader: NetworkParametersReader, private val networkParametersReader: NetworkParametersReader,
private val networkParametersStorage: NetworkParametersStorage) { private val networkParametersStorage: NetworkParametersStorage) {
@ -42,7 +42,7 @@ class NetworkParametersHotloader(private val networkMapClient: NetworkMapClient,
fun attemptHotload(newNetworkParameterHash: SecureHash): Boolean { fun attemptHotload(newNetworkParameterHash: SecureHash): Boolean {
val newSignedNetParams = networkMapClient.getNetworkParameters(newNetworkParameterHash) val newSignedNetParams = networkMapClient.getNetworkParameters(newNetworkParameterHash)
val newNetParams = newSignedNetParams.verifiedNetworkParametersCert(trustRoot) val newNetParams = newSignedNetParams.verifiedNetworkParametersCert(trustRoots)
if (canHotload(newNetParams)) { if (canHotload(newNetParams)) {
logger.info("All changed parameters are hotloadable") logger.info("All changed parameters are hotloadable")
@ -81,7 +81,7 @@ class NetworkParametersHotloader(private val networkMapClient: NetworkMapClient,
networkParameters = newNetworkParameters networkParameters = newNetworkParameters
val networkParametersAndSigned = networkParametersReader.read() val networkParametersAndSigned = networkParametersReader.read()
networkParametersStorage.setCurrentParameters(networkParametersAndSigned.signed, trustRoot) networkParametersStorage.setCurrentParameters(networkParametersAndSigned.signed, trustRoots)
notifyListenersFor(newNetworkParameters) notifyListenersFor(newNetworkParameters)
notifyListenersFor(newNetworkParameters.notaries) notifyListenersFor(newNetworkParameters.notaries)
} }

View File

@ -268,7 +268,7 @@ open class PersistentNetworkMapCache(cacheFactory: NamedCacheFactory,
private fun verifyIdentities(node: NodeInfo): Boolean { private fun verifyIdentities(node: NodeInfo): Boolean {
for (identity in node.legalIdentitiesAndCerts) { for (identity in node.legalIdentitiesAndCerts) {
try { try {
identity.verify(identityService.trustAnchor) identity.verify(identityService.trustAnchors)
} catch (e: CertPathValidatorException) { } catch (e: CertPathValidatorException) {
logger.warn("$node has invalid identity:\nError:$e\nIdentity:${identity.certPath}") logger.warn("$node has invalid identity:\nError:$e\nIdentity:${identity.certPath}")
return false return false

View File

@ -63,7 +63,7 @@ open class NetworkRegistrationHelper(
private val certificateStore = config.certificateStore private val certificateStore = config.certificateStore
private val requestIdStore = certificatesDirectory / "certificate-request-id.txt" private val requestIdStore = certificatesDirectory / "certificate-request-id.txt"
protected val rootTrustStore: X509KeyStore protected val rootTrustStore: X509KeyStore
protected val rootCert: X509Certificate protected val rootCerts: Set<X509Certificate>
private val notaryServiceConfig: NotaryServiceConfig? = config.notaryServiceConfig private val notaryServiceConfig: NotaryServiceConfig? = config.notaryServiceConfig
init { init {
@ -72,7 +72,8 @@ open class NetworkRegistrationHelper(
"Please contact your CZ operator." "Please contact your CZ operator."
} }
rootTrustStore = X509KeyStore.fromFile(networkRootTrustStorePath, networkRootTrustStorePassword) rootTrustStore = X509KeyStore.fromFile(networkRootTrustStorePath, networkRootTrustStorePassword)
rootCert = rootTrustStore.getCertificate(CORDA_ROOT_CA) val rootAliases = rootTrustStore.aliases().asSequence().filter { it.startsWith(CORDA_ROOT_CA) }
rootCerts = rootAliases.map { rootTrustStore.getCertificate(it) }.toSet()
} }
/** /**
@ -194,7 +195,7 @@ open class NetworkRegistrationHelper(
logger.info("Generated Node Identity certificate: $nodeIdentityCert") logger.info("Generated Node Identity certificate: $nodeIdentityCert")
val nodeIdentityCertificateChain: List<X509Certificate> = listOf(nodeIdentityCert) + nodeCaCertChain val nodeIdentityCertificateChain: List<X509Certificate> = listOf(nodeIdentityCert) + nodeCaCertChain
X509Utilities.validateCertificateChain(rootCert, nodeIdentityCertificateChain) X509Utilities.validateCertificateChain(rootCerts, nodeIdentityCertificateChain)
certStore.setCertPathOnly(nodeIdentityAlias, nodeIdentityCertificateChain) certStore.setCertPathOnly(nodeIdentityAlias, nodeIdentityCertificateChain)
} }
logProgress("Node identity private key and certificate chain stored in $nodeIdentityAlias.") logProgress("Node identity private key and certificate chain stored in $nodeIdentityAlias.")
@ -252,7 +253,7 @@ open class NetworkRegistrationHelper(
} }
// Validate certificate chain returned from the doorman with the root cert obtained via out-of-band process, to prevent MITM attack on doorman server. // Validate certificate chain returned from the doorman with the root cert obtained via out-of-band process, to prevent MITM attack on doorman server.
X509Utilities.validateCertificateChain(rootCert, certificates) X509Utilities.validateCertificateChain(rootCerts, certificates)
logProgress("Certificate signing request approved, storing private key with the certificate chain.") logProgress("Certificate signing request approved, storing private key with the certificate chain.")
} }
@ -423,7 +424,7 @@ class NodeRegistrationHelper(
override fun onSuccess(publicKey: PublicKey, contentSigner: ContentSigner, certificates: List<X509Certificate>, tlsCrlCertificateIssuer: X500Name?) { override fun onSuccess(publicKey: PublicKey, contentSigner: ContentSigner, certificates: List<X509Certificate>, tlsCrlCertificateIssuer: X500Name?) {
createSSLKeystore(publicKey, contentSigner, certificates, tlsCrlCertificateIssuer) createSSLKeystore(publicKey, contentSigner, certificates, tlsCrlCertificateIssuer)
createTruststore(certificates.last()) createTruststore()
} }
private fun createSSLKeystore(nodeCaPublicKey: PublicKey, nodeCaContentSigner: ContentSigner, nodeCaCertificateChain: List<X509Certificate>, tlsCertCrlIssuer: X500Name?) { private fun createSSLKeystore(nodeCaPublicKey: PublicKey, nodeCaContentSigner: ContentSigner, nodeCaCertificateChain: List<X509Certificate>, tlsCertCrlIssuer: X500Name?) {
@ -449,23 +450,21 @@ class NodeRegistrationHelper(
logger.info("Generated TLS certificate: $sslCert") logger.info("Generated TLS certificate: $sslCert")
val sslCertificateChain: List<X509Certificate> = listOf(sslCert) + nodeCaCertificateChain val sslCertificateChain: List<X509Certificate> = listOf(sslCert) + nodeCaCertificateChain
X509Utilities.validateCertificateChain(rootCert, sslCertificateChain) X509Utilities.validateCertificateChain(rootCerts, sslCertificateChain)
setPrivateKey(CORDA_CLIENT_TLS, sslKeyPair.private, sslCertificateChain, keyStore.entryPassword) setPrivateKey(CORDA_CLIENT_TLS, sslKeyPair.private, sslCertificateChain, keyStore.entryPassword)
} }
logProgress("SSL private key and certificate chain stored in ${keyStore.path}.") logProgress("SSL private key and certificate chain stored in ${keyStore.path}.")
} }
private fun createTruststore(rootCertificate: X509Certificate) { private fun createTruststore() {
// Save root certificates to trust store. // Save root certificates to trust store.
config.p2pSslOptions.trustStore.get(createNew = true).update { config.p2pSslOptions.trustStore.get(createNew = true).update {
if (this.aliases().hasNext()) { if (this.aliases().hasNext()) {
logger.warn("The node's trust store already exists. The following certificates will be overridden: ${this.aliases().asSequence()}") logger.warn("The node's trust store already exists. The following certificates will be overridden: ${this.aliases().asSequence()}")
} }
logProgress("Generating trust store for corda node.") logProgress("Generating trust store for corda node.")
// Assumes certificate chain always starts with client certificate and end with root certificate. // Copy all certificates from the network-trust-store.
setCertificate(CORDA_ROOT_CA, rootCertificate) rootTrustStore.aliases().forEach {
// Copy remaining certificates from the network-trust-store
rootTrustStore.aliases().asSequence().filter { it != CORDA_ROOT_CA }.forEach {
val certificate = rootTrustStore.getCertificate(it) val certificate = rootTrustStore.getCertificate(it)
logger.info("Copying trusted certificate to the node's trust store: Alias: $it, Certificate: $certificate") logger.info("Copying trusted certificate to the node's trust store: Alias: $it, Certificate: $certificate")
setCertificate(it, certificate) setCertificate(it, certificate)
@ -477,8 +476,10 @@ class NodeRegistrationHelper(
override fun validateAndGetTlsCrlIssuerCert(): X509Certificate? { override fun validateAndGetTlsCrlIssuerCert(): X509Certificate? {
val tlsCertCrlIssuer = config.tlsCertCrlIssuer val tlsCertCrlIssuer = config.tlsCertCrlIssuer
tlsCertCrlIssuer ?: return null tlsCertCrlIssuer ?: return null
if (principalMatchesCertificatePrincipal(tlsCertCrlIssuer, rootCert)) { for (rootCert in rootCerts) {
return rootCert if (principalMatchesCertificatePrincipal(tlsCertCrlIssuer, rootCert)) {
return rootCert
}
} }
return findMatchingCertificate(tlsCertCrlIssuer, rootTrustStore) return findMatchingCertificate(tlsCertCrlIssuer, rootTrustStore)
} }

View File

@ -171,11 +171,20 @@ class KeyStoreHandlerTest {
@Test(timeout = 300_000) @Test(timeout = 300_000)
fun `valid trust root is returned`() { fun `valid trust root is returned`() {
val expectedRoot = config.p2pSslOptions.trustStore.get()[CORDA_ROOT_CA] val expectedRoot = config.p2pSslOptions.trustStore.get()[CORDA_ROOT_CA]
val actualRoot = keyStoreHandler.init() val actualRoot = keyStoreHandler.init().first()
assertThat(actualRoot).isEqualTo(expectedRoot) assertThat(actualRoot).isEqualTo(expectedRoot)
} }
@Test(timeout = 300_000)
fun `valid multiple trust roots are returned`() {
val trustStore = config.p2pSslOptions.trustStore.get()
trustStore["$CORDA_ROOT_CA-2"] = X509Utilities.createSelfSignedCACertificate(ALICE_NAME.x500Principal, Crypto.generateKeyPair())
trustStore["non-root"] = X509Utilities.createSelfSignedCACertificate(BOB_NAME.x500Principal, Crypto.generateKeyPair())
assertThat(keyStoreHandler.init()).containsExactlyInAnyOrder(trustStore[CORDA_ROOT_CA], trustStore["$CORDA_ROOT_CA-2"])
}
@Test(timeout = 300_000) @Test(timeout = 300_000)
fun `keystore creation in dev mode`() { fun `keystore creation in dev mode`() {
val devCertificateDir = tempFolder.root.toPath() / "certificates-dev" val devCertificateDir = tempFolder.root.toPath() / "certificates-dev"

View File

@ -14,6 +14,7 @@ import net.corda.coretesting.internal.DEV_INTERMEDIATE_CA
import net.corda.coretesting.internal.DEV_ROOT_CA import net.corda.coretesting.internal.DEV_ROOT_CA
import net.corda.node.services.network.PersistentNetworkMapCache import net.corda.node.services.network.PersistentNetworkMapCache
import net.corda.node.services.persistence.PublicKeyToOwningIdentityCacheImpl import net.corda.node.services.persistence.PublicKeyToOwningIdentityCacheImpl
import net.corda.nodeapi.internal.createDevNodeIdentity
import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.CertificateType
import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.nodeapi.internal.crypto.x509Certificates import net.corda.nodeapi.internal.crypto.x509Certificates
@ -21,18 +22,24 @@ import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.nodeapi.internal.persistence.DatabaseConfig
import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.CHARLIE_NAME
import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.core.SerializationEnvironmentRule
import net.corda.testing.core.TestIdentity import net.corda.testing.core.TestIdentity
import net.corda.testing.core.getTestPartyAndCertificate import net.corda.testing.core.getTestPartyAndCertificate
import net.corda.testing.internal.TestingNamedCacheFactory import net.corda.testing.internal.TestingNamedCacheFactory
import net.corda.testing.internal.configureDatabase import net.corda.testing.internal.configureDatabase
import net.corda.testing.internal.createDevIntermediateCaCertPath
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
import org.assertj.core.api.Assertions.assertThat
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.jupiter.api.assertDoesNotThrow import org.junit.jupiter.api.assertDoesNotThrow
import java.security.cert.CertPathValidatorException
import java.security.cert.X509CertSelector
import javax.security.auth.x500.X500Principal
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
import kotlin.test.assertNull import kotlin.test.assertNull
@ -70,7 +77,7 @@ class PersistentIdentityServiceTests {
) )
identityService.database = database identityService.database = database
identityService.start( identityService.start(
DEV_ROOT_CA.certificate, setOf(DEV_ROOT_CA.certificate),
alice.identity, alice.identity,
listOf(notary.party), listOf(notary.party),
PublicKeyToOwningIdentityCacheImpl(database, cacheFactory) PublicKeyToOwningIdentityCacheImpl(database, cacheFactory)
@ -221,7 +228,11 @@ class PersistentIdentityServiceTests {
// Create new identity service mounted onto same DB // Create new identity service mounted onto same DB
val newPersistentIdentityService = PersistentIdentityService(TestingNamedCacheFactory()).also { val newPersistentIdentityService = PersistentIdentityService(TestingNamedCacheFactory()).also {
it.database = database it.database = database
it.start(DEV_ROOT_CA.certificate, Companion.alice.identity, pkToIdCache = PublicKeyToOwningIdentityCacheImpl(database, cacheFactory)) it.start(
setOf(DEV_ROOT_CA.certificate),
Companion.alice.identity,
pkToIdCache = PublicKeyToOwningIdentityCacheImpl(database, cacheFactory)
)
} }
newPersistentIdentityService.assertOwnership(alice.party, anonymousAlice.party.anonymise()) newPersistentIdentityService.assertOwnership(alice.party, anonymousAlice.party.anonymise())
@ -350,4 +361,39 @@ class PersistentIdentityServiceTests {
assertEquals(setOf(alice2.party), identityService.partiesFromName("Alice Corp", true)) assertEquals(setOf(alice2.party), identityService.partiesFromName("Alice Corp", true))
} }
@Test(timeout = 300_000)
fun `multiple trust roots`() {
val (root2, doorman2) = createDevIntermediateCaCertPath(X500Principal("CN=Root2"))
val node2 = createDevNodeIdentity(doorman2, BOB_NAME)
val bob2 = PartyAndCertificate(X509Utilities.buildCertPath(node2.certificate, doorman2.certificate, root2.certificate))
val newIdentityService = PersistentIdentityService(cacheFactory)
newIdentityService.database = database
newIdentityService.start(
setOf(DEV_ROOT_CA.certificate, root2.certificate),
bob2,
listOf(),
PublicKeyToOwningIdentityCacheImpl(database, cacheFactory))
// Trust root should be taken from bobWithRoot2 identity.
assertEquals(root2.certificate, newIdentityService.trustRoot)
assertEquals(root2.certificate, newIdentityService.trustAnchor.trustedCert)
assertEquals(listOf(DEV_ROOT_CA.certificate, root2.certificate), newIdentityService.trustAnchors.map { it.trustedCert })
assertThat(newIdentityService.caCertStore.getCertificates(X509CertSelector())).contains(bob2.certificate, root2.certificate)
// Verify identities with trusted root.
newIdentityService.verifyAndRegisterIdentity(bob2)
newIdentityService.verifyAndRegisterIdentity(ALICE_IDENTITY)
val (root3, doorman3) = createDevIntermediateCaCertPath(X500Principal("CN=Root3"))
val node3 = createDevNodeIdentity(doorman3, CHARLIE_NAME)
val charlie3 = PartyAndCertificate(X509Utilities.buildCertPath(node3.certificate, doorman3.certificate, root3.certificate))
// Fail to verify identities with untrusted root.
assertFailsWith<CertPathValidatorException> {
newIdentityService.verifyAndRegisterIdentity(charlie3)
}
}
} }

View File

@ -61,7 +61,7 @@ class DBNetworkParametersStorageTest {
networkMapClient = createMockNetworkMapClient() networkMapClient = createMockNetworkMapClient()
networkParametersService = DBNetworkParametersStorage(TestingNamedCacheFactory(), database, networkMapClient).apply { networkParametersService = DBNetworkParametersStorage(TestingNamedCacheFactory(), database, networkMapClient).apply {
database.transaction { database.transaction {
setCurrentParameters(netParams1, DEV_ROOT_CA.certificate) setCurrentParameters(netParams1, setOf(DEV_ROOT_CA.certificate))
} }
} }
} }

View File

@ -43,7 +43,7 @@ class NetworkMapClientTest {
server = NetworkMapServer(cacheTimeout) server = NetworkMapServer(cacheTimeout)
val address = server.start() val address = server.start()
networkMapClient = NetworkMapClient(URL("http://$address"), networkMapClient = NetworkMapClient(URL("http://$address"),
VersionInfo(1, "TEST", "TEST", "TEST")).apply { start(DEV_ROOT_CA.certificate) } VersionInfo(1, "TEST", "TEST", "TEST")).apply { start(setOf(DEV_ROOT_CA.certificate)) }
} }
@After @After

View File

@ -113,7 +113,7 @@ class NetworkMapUpdaterTest {
server = NetworkMapServer(cacheExpiryMs.millis) server = NetworkMapServer(cacheExpiryMs.millis)
val address = server.start() val address = server.start()
networkMapClient = NetworkMapClient(URL("http://$address"), networkMapClient = NetworkMapClient(URL("http://$address"),
VersionInfo(1, "TEST", "TEST", "TEST")).apply { start(DEV_ROOT_CA.certificate) } VersionInfo(1, "TEST", "TEST", "TEST")).apply { start(setOf(DEV_ROOT_CA.certificate)) }
} }
@After @After
@ -132,7 +132,7 @@ class NetworkMapUpdaterTest {
autoAcceptNetworkParameters: Boolean = true, autoAcceptNetworkParameters: Boolean = true,
excludedAutoAcceptNetworkParameters: Set<String> = emptySet()) { excludedAutoAcceptNetworkParameters: Set<String> = emptySet()) {
updater!!.start(DEV_ROOT_CA.certificate, updater!!.start(setOf(DEV_ROOT_CA.certificate),
server.networkParameters.serialize().hash, server.networkParameters.serialize().hash,
ourNodeInfo, ourNodeInfo,
networkParameters, networkParameters,
@ -363,7 +363,7 @@ class NetworkMapUpdaterTest {
updater!!.acceptNewNetworkParameters(newHash) { it.serialize().sign(ourKeyPair) } updater!!.acceptNewNetworkParameters(newHash) { it.serialize().sign(ourKeyPair) }
verify(networkParametersStorage, times(1)).saveParameters(any()) verify(networkParametersStorage, times(1)).saveParameters(any())
val signedNetworkParams = updateFile.readObject<SignedNetworkParameters>() val signedNetworkParams = updateFile.readObject<SignedNetworkParameters>()
val paramsFromFile = signedNetworkParams.verifiedNetworkParametersCert(DEV_ROOT_CA.certificate) val paramsFromFile = signedNetworkParams.verifiedNetworkParametersCert(setOf(DEV_ROOT_CA.certificate))
assertEquals(newParameters, paramsFromFile) assertEquals(newParameters, paramsFromFile)
assertEquals(newHash, server.latestParametersAccepted(ourKeyPair.public)) assertEquals(newHash, server.latestParametersAccepted(ourKeyPair.public))
} }
@ -381,7 +381,7 @@ class NetworkMapUpdaterTest {
val newHash = newParameters.serialize().hash val newHash = newParameters.serialize().hash
val updateFile = baseDir / NETWORK_PARAMS_UPDATE_FILE_NAME val updateFile = baseDir / NETWORK_PARAMS_UPDATE_FILE_NAME
val signedNetworkParams = updateFile.readObject<SignedNetworkParameters>() val signedNetworkParams = updateFile.readObject<SignedNetworkParameters>()
val paramsFromFile = signedNetworkParams.verifiedNetworkParametersCert(DEV_ROOT_CA.certificate) val paramsFromFile = signedNetworkParams.verifiedNetworkParametersCert(setOf(DEV_ROOT_CA.certificate))
assertEquals(newParameters, paramsFromFile) assertEquals(newParameters, paramsFromFile)
assertEquals(newHash, server.latestParametersAccepted(ourKeyPair.public)) assertEquals(newHash, server.latestParametersAccepted(ourKeyPair.public))
} }

View File

@ -26,7 +26,7 @@ class NetworkParametersHotloaderTest {
@JvmField @JvmField
val testSerialization = SerializationEnvironmentRule(true) val testSerialization = SerializationEnvironmentRule(true)
private val networkMapCertAndKeyPair: CertificateAndKeyPair = createDevNetworkMapCa() private val networkMapCertAndKeyPair: CertificateAndKeyPair = createDevNetworkMapCa()
private val trustRoot = DEV_ROOT_CA.certificate private val trustRoots = setOf(DEV_ROOT_CA.certificate)
private val originalNetworkParameters = testNetworkParameters() private val originalNetworkParameters = testNetworkParameters()
private val notary: Party = TestIdentity.fresh("test notary").party private val notary: Party = TestIdentity.fresh("test notary").party
@ -118,8 +118,9 @@ class NetworkParametersHotloaderTest {
Mockito.`when`(networkMapClient.getNetworkParameters(newNetworkParameters.serialize().hash)).thenReturn(signedNetworkParameters) Mockito.`when`(networkMapClient.getNetworkParameters(newNetworkParameters.serialize().hash)).thenReturn(signedNetworkParameters)
val networkParametersReader = Mockito.mock(NetworkParametersReader::class.java) val networkParametersReader = Mockito.mock(NetworkParametersReader::class.java)
Mockito.`when`(networkParametersReader.read()) Mockito.`when`(networkParametersReader.read())
.thenReturn(NetworkParametersReader.NetworkParametersAndSigned(signedNetworkParameters, trustRoot)) .thenReturn(NetworkParametersReader.NetworkParametersAndSigned(signedNetworkParameters, trustRoots))
return NetworkParametersHotloader(networkMapClient, trustRoot, originalNetworkParameters, networkParametersReader, networkParametersStorage) return NetworkParametersHotloader(networkMapClient, trustRoots, originalNetworkParameters, networkParametersReader,
networkParametersStorage)
} }
} }

View File

@ -54,7 +54,7 @@ class NetworkParametersReaderTest {
server = NetworkMapServer(cacheTimeout) server = NetworkMapServer(cacheTimeout)
val address = server.start() val address = server.start()
networkMapClient = NetworkMapClient(URL("http://$address"), VersionInfo(1, "TEST", "TEST", "TEST")) networkMapClient = NetworkMapClient(URL("http://$address"), VersionInfo(1, "TEST", "TEST", "TEST"))
networkMapClient.start(DEV_ROOT_CA.certificate) networkMapClient.start(setOf(DEV_ROOT_CA.certificate))
} }
@After @After
@ -69,13 +69,13 @@ class NetworkParametersReaderTest {
val oldParameters = testNetworkParameters(epoch = 1) val oldParameters = testNetworkParameters(epoch = 1)
NetworkParametersCopier(oldParameters).install(networkParamsPath) NetworkParametersCopier(oldParameters).install(networkParamsPath)
NetworkParametersCopier(server.networkParameters, update = true).install(networkParamsPath) // Parameters update file. NetworkParametersCopier(server.networkParameters, update = true).install(networkParamsPath) // Parameters update file.
val parameters = NetworkParametersReader(DEV_ROOT_CA.certificate, networkMapClient, networkParamsPath).read().networkParameters val parameters = NetworkParametersReader(setOf(DEV_ROOT_CA.certificate), networkMapClient, networkParamsPath).read().networkParameters
assertFalse((networkParamsPath / NETWORK_PARAMS_UPDATE_FILE_NAME).exists()) assertFalse((networkParamsPath / NETWORK_PARAMS_UPDATE_FILE_NAME).exists())
assertEquals(server.networkParameters, parameters) assertEquals(server.networkParameters, parameters)
// Parameters from update should be moved to `network-parameters` file. // Parameters from update should be moved to `network-parameters` file.
val parametersFromFile = (networkParamsPath / NETWORK_PARAMS_FILE_NAME) val parametersFromFile = (networkParamsPath / NETWORK_PARAMS_FILE_NAME)
.readObject<SignedNetworkParameters>() .readObject<SignedNetworkParameters>()
.verifiedNetworkParametersCert(DEV_ROOT_CA.certificate) .verifiedNetworkParametersCert(setOf(DEV_ROOT_CA.certificate))
assertEquals(server.networkParameters, parametersFromFile) assertEquals(server.networkParameters, parametersFromFile)
} }
@ -85,13 +85,13 @@ class NetworkParametersReaderTest {
val oldParameters = testNetworkParameters(epoch = 1) val oldParameters = testNetworkParameters(epoch = 1)
NetworkParametersCopier(oldParameters).install(networkParamsPath) NetworkParametersCopier(oldParameters).install(networkParamsPath)
NetworkParametersCopier(server.networkParameters, update = true).install(networkParamsPath) // Parameters update file. NetworkParametersCopier(server.networkParameters, update = true).install(networkParamsPath) // Parameters update file.
val parameters = NetworkParametersReader(DEV_ROOT_CA.certificate, networkMapClient, networkParamsPath).read().networkParameters val parameters = NetworkParametersReader(setOf(DEV_ROOT_CA.certificate), networkMapClient, networkParamsPath).read().networkParameters
assertFalse((networkParamsPath / NETWORK_PARAMS_UPDATE_FILE_NAME).exists()) assertFalse((networkParamsPath / NETWORK_PARAMS_UPDATE_FILE_NAME).exists())
assertEquals(server.networkParameters, parameters) assertEquals(server.networkParameters, parameters)
// Parameters from update should be moved to `network-parameters` file. // Parameters from update should be moved to `network-parameters` file.
val parametersFromFile = (networkParamsPath / NETWORK_PARAMS_FILE_NAME) val parametersFromFile = (networkParamsPath / NETWORK_PARAMS_FILE_NAME)
.readObject<SignedNetworkParameters>() .readObject<SignedNetworkParameters>()
.verifiedNetworkParametersCert(DEV_ROOT_CA.certificate) .verifiedNetworkParametersCert(setOf(DEV_ROOT_CA.certificate))
assertEquals(server.networkParameters, parametersFromFile) assertEquals(server.networkParameters, parametersFromFile)
} }
@ -101,7 +101,7 @@ class NetworkParametersReaderTest {
val networkParamsPath = fs.getPath("/node").createDirectories() val networkParamsPath = fs.getPath("/node").createDirectories()
val fileParameters = testNetworkParameters(epoch = 1) val fileParameters = testNetworkParameters(epoch = 1)
NetworkParametersCopier(fileParameters).install(networkParamsPath) NetworkParametersCopier(fileParameters).install(networkParamsPath)
val parameters = NetworkParametersReader(DEV_ROOT_CA.certificate, networkMapClient, networkParamsPath).read().networkParameters val parameters = NetworkParametersReader(setOf(DEV_ROOT_CA.certificate), networkMapClient, networkParamsPath).read().networkParameters
assertThat(parameters).isEqualTo(fileParameters) assertThat(parameters).isEqualTo(fileParameters)
} }
@ -120,17 +120,17 @@ class NetworkParametersReaderTest {
val netParameters = testNetworkParameters(epoch = 1) val netParameters = testNetworkParameters(epoch = 1)
val certKeyPairNetworkParameters: CertificateAndKeyPair = createDevNetworkParametersCa() val certKeyPairNetworkParameters: CertificateAndKeyPair = createDevNetworkParametersCa()
val netParamsForNetworkParameters= certKeyPairNetworkParameters.sign(netParameters) val netParamsForNetworkParameters= certKeyPairNetworkParameters.sign(netParameters)
netParamsForNetworkParameters.verifiedNetworkParametersCert(DEV_ROOT_CA.certificate) netParamsForNetworkParameters.verifiedNetworkParametersCert(setOf(DEV_ROOT_CA.certificate))
val certKeyPairNetworkMap: CertificateAndKeyPair = createDevNetworkMapCa() val certKeyPairNetworkMap: CertificateAndKeyPair = createDevNetworkMapCa()
val netParamsForNetworkMap = certKeyPairNetworkMap.sign(netParameters) val netParamsForNetworkMap = certKeyPairNetworkMap.sign(netParameters)
netParamsForNetworkMap.verifiedNetworkParametersCert(DEV_ROOT_CA.certificate) netParamsForNetworkMap.verifiedNetworkParametersCert(setOf(DEV_ROOT_CA.certificate))
val megaCorp = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")) val megaCorp = TestIdentity(CordaX500Name("MegaCorp", "London", "GB"))
val x = createDevNodeCa(DEV_INTERMEDIATE_CA, megaCorp.name) val x = createDevNodeCa(DEV_INTERMEDIATE_CA, megaCorp.name)
val netParamsForNode = x.sign(netParameters) val netParamsForNode = x.sign(netParameters)
assertFailsWith(IllegalArgumentException::class, "Incorrect cert role: NODE_CA") { assertFailsWith(IllegalArgumentException::class, "Incorrect cert role: NODE_CA") {
netParamsForNode.verifiedNetworkParametersCert(DEV_ROOT_CA.certificate) netParamsForNode.verifiedNetworkParametersCert(setOf(DEV_ROOT_CA.certificate))
} }
} }
} }

View File

@ -188,7 +188,7 @@ open class MockServices private constructor(
// Create a persistent identity service and add all the supplied identities. // Create a persistent identity service and add all the supplied identities.
identityService.apply { identityService.apply {
database = persistence database = persistence
start(DEV_ROOT_CA.certificate, initialIdentity.identity, pkToIdCache = pkToIdCache) start(setOf(DEV_ROOT_CA.certificate), initialIdentity.identity, pkToIdCache = pkToIdCache)
persistence.transaction { identityService.loadIdentities(moreIdentities + initialIdentity.identity) } persistence.transaction { identityService.loadIdentities(moreIdentities + initialIdentity.identity) }
} }
val networkMapCache = PersistentNetworkMapCache(cacheFactory, persistence, identityService) val networkMapCache = PersistentNetworkMapCache(cacheFactory, persistence, identityService)

View File

@ -29,8 +29,8 @@ class MockNetworkParametersStorage(private var currentParameters: NetworkParamet
storeCurrentParameters() storeCurrentParameters()
} }
override fun setCurrentParameters(currentSignedParameters: SignedDataWithCert<NetworkParameters>, trustRoot: X509Certificate) { override fun setCurrentParameters(currentSignedParameters: SignedDataWithCert<NetworkParameters>, trustRoots: Set<X509Certificate>) {
setCurrentParametersUnverified(currentSignedParameters.verifiedNetworkParametersCert(trustRoot)) setCurrentParametersUnverified(currentSignedParameters.verifiedNetworkParametersCert(trustRoots))
} }
override fun lookupSigned(hash: SecureHash): SignedDataWithCert<NetworkParameters>? { override fun lookupSigned(hash: SecureHash): SignedDataWithCert<NetworkParameters>? {