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()
/** Verify the certificate path is valid. */
fun verify(trustAnchor: TrustAnchor): PKIXCertPathValidatorResult {
val result = certPath.validate(trustAnchor)
fun verify(trustAnchor: TrustAnchor): PKIXCertPathValidatorResult = verify(setOf(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
// an all-null chain is in theory valid.
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.
fun CertPath.validate(trustAnchor: TrustAnchor, checkRevocation: Boolean = false): PKIXCertPathValidatorResult {
val parameters = PKIXParameters(setOf(trustAnchor)).apply { isRevocationEnabled = checkRevocation }
fun CertPath.validate(trustAnchors: Set<TrustAnchor>, checkRevocation: Boolean = false): PKIXCertPathValidatorResult {
val parameters = PKIXParameters(trustAnchors).apply { isRevocationEnabled = checkRevocation }
try {
return CertPathValidator.getInstance("PKIX").validate(this, parameters) as PKIXCertPathValidatorResult
} catch (e: CertPathValidatorException) {
@ -517,8 +517,8 @@ Reason: ${e.reason}
Offending cert index: ${e.index}
Cert path: $this
Trust anchor:
$trustAnchor""", e, this, e.index)
Trust anchors:
$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.
*/
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 peerX500Principal = peerChain[0].subjectX500Principal
assertEquals(MEGA_CORP.name.x500Principal, peerX500Principal)
X509Utilities.validateCertificateChain(rootCa.certificate, peerChain)
X509Utilities.validateCertificateChain(setOf(rootCa.certificate), peerChain)
val output = DataOutputStream(clientSocket.outputStream)
output.writeUTF("Hello World")
var timeout = 0
@ -432,7 +432,7 @@ class X509UtilitiesTest {
val peerChain = client.engine!!.session.peerCertificates.x509
val peerX500Principal = peerChain[0].subjectX500Principal
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
get() {
return (this / NETWORK_PARAMS_FILE_NAME).readObject<SignedNetworkParameters>()
.verifiedNetworkParametersCert(DEV_ROOT_CA.certificate)
.verifiedNetworkParametersCert(setOf(DEV_ROOT_CA.certificate))
}
private val Path.nodeInfoFile: Path

View File

@ -105,6 +105,17 @@ fun createDevNodeCa(intermediateCa: CertificateAndKeyPair,
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_ROOT_CA: CertificateAndKeyPair get() = DevCaHelper.loadDevCa(X509Utilities.CORDA_ROOT_CA)
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)
}
fun validateCertificateChain(trustedRoot: X509Certificate, vararg certificates: X509Certificate) {
validateCertificateChain(trustedRoot, certificates.asList())
fun validateCertificateChain(trustedRoots: Set<X509Certificate>, vararg certificates: X509Certificate) {
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" }
validateCertPath(trustedRoot, buildCertPath(certificates))
validateCertPath(trustedRoots, buildCertPath(certificates))
}
fun validateCertPath(trustedRoot: X509Certificate, certPath: CertPath) {
certPath.validate(TrustAnchor(trustedRoot, null))
fun validateCertPath(trustedRoots: Set<X509Certificate>, certPath: CertPath) {
certPath.validate(trustedRoots.map { TrustAnchor(it, null) }.toSet())
}
/**

View File

@ -399,7 +399,7 @@ constructor(private val initSerEnv: Boolean,
when (netParamsFilesGrouped.size) {
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 " +
@ -409,7 +409,7 @@ constructor(private val initSerEnv: Boolean,
netParamsFiles.map { it.parent.fileName }.joinTo(msg, ", ")
msg.append(":\n")
val netParamsString = try {
bytes.deserialize().verifiedNetworkParametersCert(DEV_ROOT_CA.certificate).toString()
bytes.deserialize().verifiedNetworkParametersCert(setOf(DEV_ROOT_CA.certificate)).toString()
} catch (e: Exception) {
"Invalid network parameters file: $e"
}

View File

@ -55,25 +55,26 @@ data class ParametersUpdate(
)
/** 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)}" }
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 {
sig.fullCertChain
}
X509Utilities.validateCertificateChain(rootCert, path)
X509Utilities.validateCertificateChain(rootCerts, path)
return verified()
}
/** Verify that a Network Map certificate path and its [CertRole] is correct. */
fun <T : Any> SignedDataWithCert<T>.verifiedNetworkMapCert(rootCert: X509Certificate): T {
return verifiedCertWithRole(rootCert, CertRole.NETWORK_MAP)
fun <T : Any> SignedDataWithCert<T>.verifiedNetworkMapCert(rootCerts: Set<X509Certificate>): T {
return verifiedCertWithRole(rootCerts, CertRole.NETWORK_MAP)
}
/** 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
// 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)
// when
certPath.validate(newTrustAnchor)
certPath.validate(setOf(newTrustAnchor))
// 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 peerX500Principal = peerChain[0].subjectX500Principal
assertEquals(serverCa.certificate.subjectX500Principal, peerX500Principal)
X509Utilities.validateCertificateChain(rootCa, peerChain)
X509Utilities.validateCertificateChain(setOf(rootCa), peerChain)
with(DataOutputStream(clientSocket.outputStream)) {
writeUTF(testPhrase)
}

View File

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

View File

@ -279,7 +279,7 @@ class CordappConstraintsTests {
printVault(alice, states)
// 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(
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.identity.CordaX500Name
import net.corda.core.internal.createDirectories
import net.corda.core.internal.div
import net.corda.core.internal.exists
import net.corda.core.internal.toX500Name
import net.corda.coretesting.internal.configureTestSSL
import net.corda.nodeapi.RPCApi
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_P2P_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.coretesting.internal.stubs.CertificateStoreStubs
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.testing.core.BOB_NAME
import net.corda.testing.core.CHARLIE_NAME
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.core.ActiveMQClusterSecurityException
import org.apache.activemq.artemis.api.core.ActiveMQNotConnectedException
@ -29,6 +36,7 @@ import org.bouncycastle.asn1.x509.NameConstraints
import org.junit.Test
import java.nio.file.Files
import javax.jms.JMSSecurityException
import javax.security.auth.x500.X500Principal
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`() {
assertProducerQueueCreationAttackFails(ArtemisMessagingComponent.NOTIFICATIONS_ADDRESS)
}

View File

@ -437,9 +437,9 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
open fun generateAndSaveNodeInfo(): NodeInfo {
check(started == null) { "Node has already been started" }
log.info("Generating nodeInfo ...")
val trustRoot = initKeyStores()
val trustRoots = initKeyStores()
startDatabase()
identityService.start(trustRoot, keyStoreHandler.nodeIdentity, pkToIdCache = pkToIdCache)
identityService.start(trustRoots, keyStoreHandler.nodeIdentity, pkToIdCache = pkToIdCache)
return database.use {
it.transaction {
val nodeInfoAndSigned = updateNodeInfo(publish = false)
@ -486,9 +486,9 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
logVendorString(database, log)
if (allowHibernateToManageAppSchema) {
Node.printBasicNodeInfo("Initialising CorDapps to get schemas created by hibernate")
val trustRoot = initKeyStores()
networkMapClient?.start(trustRoot)
val (netParams, signedNetParams) = NetworkParametersReader(trustRoot, networkMapClient, configuration.networkParametersPath).read()
val trustRoots = initKeyStores()
networkMapClient?.start(trustRoots)
val (netParams, signedNetParams) = NetworkParametersReader(trustRoots, networkMapClient, configuration.networkParametersPath).read()
log.info("Loaded network parameters: $netParams")
check(netParams.minimumPlatformVersion <= versionInfo.platformVersion) {
"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)
database.transaction {
networkParametersStorage.setCurrentParameters(signedNetParams, trustRoot)
networkParametersStorage.setCurrentParameters(signedNetParams, trustRoots)
cordappProvider.start()
}
}
@ -536,7 +536,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
nodeLifecycleEventsDistributor.distributeEvent(NodeLifecycleEvent.BeforeNodeStart(nodeServicesContext))
log.info("Node starting up ...")
val trustRoot = initKeyStores()
val trustRoots = initKeyStores()
initialiseJolokia()
schemaService.mappedSchemasWarnings().forEach {
@ -549,9 +549,9 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
registerCordappFlows()
services.rpcFlows += cordappLoader.cordapps.flatMap { it.rpcFlows }
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()
log.info("Loaded network parameters: $netParams")
check(netParams.minimumPlatformVersion <= versionInfo.platformVersion) {
@ -565,7 +565,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
schedulerService.closeOnStop()
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 {
updateNodeInfo(publish = true)
@ -577,7 +577,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
val networkParametersHotloader = if (networkMapClient == null) {
null
} else {
NetworkParametersHotloader(networkMapClient, trustRoot, netParams, networkParametersReader, networkParametersStorage).also {
NetworkParametersHotloader(networkMapClient, trustRoots, netParams, networkParametersReader, networkParametersStorage).also {
it.addNotaryUpdateListener(networkMapCache)
it.addNotaryUpdateListener(identityService)
it.addNetworkParametersChangedListeners(services)
@ -586,7 +586,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
}
networkMapUpdater.start(
trustRoot,
trustRoots,
signedNetParams.raw.hash,
signedNodeInfo,
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.
val (resultingNodeInfo, readyFuture) = database.transaction(recoverableFailureTolerance = 0) {
networkParametersStorage.setCurrentParameters(signedNetParams, trustRoot)
networkParametersStorage.setCurrentParameters(signedNetParams, trustRoots)
identityService.loadIdentities(nodeInfo.legalIdentitiesAndCerts)
attachments.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.
private val networkMapClient: NetworkMapClient?
) : NetworkParametersStorage, SingletonSerializeAsToken() {
private lateinit var trustRoot: X509Certificate
private lateinit var trustRoots: Set<X509Certificate>
companion object {
private val log = contextLogger()
@ -58,8 +58,8 @@ class DBNetworkParametersStorage(
}
}
override fun setCurrentParameters(currentSignedParameters: SignedDataWithCert<NetworkParameters>, trustRoot: X509Certificate) {
this.trustRoot = trustRoot
override fun setCurrentParameters(currentSignedParameters: SignedDataWithCert<NetworkParameters>, trustRoots: Set<X509Certificate>) {
this.trustRoots = trustRoots
saveParameters(currentSignedParameters)
_currentHash = currentSignedParameters.raw.hash
}
@ -85,7 +85,7 @@ class DBNetworkParametersStorage(
override fun saveParameters(signedNetworkParameters: SignedNetworkParameters) {
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
log.trace { "Parameters to save $networkParameters with hash $hash" }
database.transaction {
@ -97,7 +97,7 @@ class DBNetworkParametersStorage(
return if (networkMapClient != null) {
try {
val signedParams = networkMapClient.getNetworkParameters(parametersHash)
val networkParameters = signedParams.verifiedNetworkParametersCert(trustRoot)
val networkParameters = signedParams.verifiedNetworkParametersCert(trustRoots)
saveParameters(signedParams)
networkParameters
} catch (e: Exception) {

View File

@ -23,6 +23,7 @@ import java.nio.file.NoSuchFileException
import java.security.GeneralSecurityException
import java.security.PublicKey
import java.security.cert.X509Certificate
import java.util.Collections.unmodifiableSet
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()
val signingKeys: Set<KeyAndAlias> get() = _signingKeys.toSet()
private lateinit var trustRoot: X509Certificate
private lateinit var trustRoots: Set<X509Certificate>
private lateinit var nodeKeyStore: CertificateStore
/**
* Initialize key-stores and load identities.
* @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) {
configuration.configureWithDevSSLCertificate(cryptoService, devModeKeyEntropy)
// 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()
trustRoot = validateKeyStores(certStores)
trustRoots = validateKeyStores(certStores)
nodeKeyStore = certStores.nodeKeyStore
loadIdentities()
return trustRoot
return unmodifiableSet(trustRoots)
}
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.
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."
}
val trustRoot = certStores.trustStore[X509Utilities.CORDA_ROOT_CA]
certStores.sslKeyStore.let {
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.
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) {
"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."
}
@ -169,7 +173,7 @@ class KeyStoreHandler(private val configuration: NodeConfiguration, private val
}
val identity = PartyAndCertificate(X509Utilities.buildCertPath(certificates))
X509Utilities.validateCertPath(trustRoot, identity.certPath)
X509Utilities.validateCertPath(trustRoots, identity.certPath)
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 identity = PartyAndCertificate(X509Utilities.buildCertPath(certificates))
X509Utilities.validateCertPath(trustRoot, identity.certPath)
X509Utilities.validateCertPath(trustRoots, identity.certPath)
return identity
}
}

View File

@ -18,7 +18,7 @@ import java.nio.file.Path
import java.nio.file.StandardCopyOption
import java.security.cert.X509Certificate
class NetworkParametersReader(private val trustRoot: X509Certificate,
class NetworkParametersReader(private val trustRoots: Set<X509Certificate>,
private val networkMapClient: NetworkMapClient?,
private val networkParamsPath: Path) {
companion object {
@ -68,7 +68,7 @@ class NetworkParametersReader(private val trustRoot: X509Certificate,
signedParametersFromFile ?: throw Error.ParamsNotConfigured()
}
return NetworkParametersAndSigned(signedParameters, trustRoot)
return NetworkParametersAndSigned(signedParameters, trustRoots)
}
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.")
networkMapClient ?: throw Error.NetworkMapNotConfigured()
val signedParams = networkMapClient.getNetworkParameters(parametersHash)
signedParams.verifiedNetworkParametersCert(trustRoot)
signedParams.verifiedNetworkParametersCert(trustRoots)
networkParamsFile.parent.toFile().mkdirs()
signedParams.serialize().open().copyTo(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
// 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,
// 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 component2() = signed
}

View File

@ -41,12 +41,13 @@ sealed class CertificateChainCheckPolicy {
object RootMustMatch : CertificateChainCheckPolicy() {
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 {
@Suppress("DEPRECATION") // should use java.security.cert.X509Certificate
override fun checkCertificateChain(theirChain: Array<javax.security.cert.X509Certificate>) {
val theirRoot = theirChain.last().publicKey
if (rootPublicKey != theirRoot) {
if (theirRoot !in rootPublicKeys) {
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.cert.CertificateExpiredException
import java.security.cert.CertificateNotYetValidException
import java.security.cert.TrustAnchor
interface IdentityServiceInternal : IdentityService {
val trustAnchors: Set<TrustAnchor>
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
fun verifyAndRegisterNewRandomIdentity(identity: PartyAndCertificate)

View File

@ -40,6 +40,7 @@ class InMemoryIdentityService(
*/
override val caCertStore: CertStore = CertStore.getInstance("Collection", CollectionCertStoreParameters(setOf(trustRoot)))
override val trustAnchor: TrustAnchor = TrustAnchor(trustRoot, null)
override val trustAnchors = setOf(trustAnchor)
private val keyToExternalId = ConcurrentHashMap<String, UUID>()
private val keyToPartyAndCerts = ConcurrentHashMap<PublicKey, PartyAndCertificate>()
private val nameToKey = ConcurrentHashMap<CordaX500Name, PublicKey>()
@ -55,22 +56,23 @@ class InMemoryIdentityService(
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
override fun verifyAndRegisterIdentity(identity: PartyAndCertificate): PartyAndCertificate? {
return verifyAndRegisterIdentity(trustAnchor, identity)
return verifyAndRegisterIdentity(trustAnchors, identity)
}
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
override fun verifyAndRegisterNewRandomIdentity(identity: PartyAndCertificate) {
verifyAndRegisterIdentity(trustAnchor, identity)
verifyAndRegisterIdentity(trustAnchors, identity)
}
@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
val identityCertChain = identity.certPath.x509Certificates
try {
identity.verify(trustAnchor)
} 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 :")
identityCertChain.reversed().forEachIndexed { index, certificate ->
val space = (0 until index).joinToString("") { " " }
@ -83,7 +85,7 @@ class InMemoryIdentityService(
if (wellKnownCert != identity.certificate) {
val idx = identityCertChain.lastIndexOf(wellKnownCert)
val firstPath = X509Utilities.buildCertPath(identityCertChain.slice(idx until identityCertChain.size))
verifyAndRegisterIdentity(trustAnchor, PartyAndCertificate(firstPath))
verifyAndRegisterIdentity(trustAnchors, PartyAndCertificate(firstPath))
}
return registerIdentity(identity, false)
}

View File

@ -185,6 +185,9 @@ class PersistentIdentityService(cacheFactory: NamedCacheFactory) : SingletonSeri
private lateinit var _trustAnchor: 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
/** 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)
fun start(
trustRoot: X509Certificate,
trustRoots: Set<X509Certificate>,
ourIdentity: PartyAndCertificate,
notaryIdentities: List<Party> = emptyList(),
pkToIdCache: WritablePublicKeyToOwningIdentityCache
) {
_trustRoot = trustRoot
_trustRoot = ourIdentity.certPath.certificates.last() as X509Certificate
_trustAnchor = TrustAnchor(trustRoot, null)
_trustAnchors = trustRoots.map { TrustAnchor(it, null) }.toSet()
// Extract Node CA certificate from node identity certificate path
val certificates = setOf(ourIdentity.certificate, ourIdentity.certPath.certificates[1], trustRoot)
_caCertStore = CertStore.getInstance("Collection", CollectionCertStoreParameters(certificates))
@ -229,23 +233,24 @@ class PersistentIdentityService(cacheFactory: NamedCacheFactory) : SingletonSeri
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
override fun verifyAndRegisterIdentity(identity: PartyAndCertificate): PartyAndCertificate? {
return verifyAndRegisterIdentity(trustAnchor, identity)
return verifyAndRegisterIdentity(trustAnchors, identity)
}
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
override fun verifyAndRegisterNewRandomIdentity(identity: PartyAndCertificate) {
verifyAndRegisterIdentity(trustAnchor, identity, true)
verifyAndRegisterIdentity(trustAnchors, identity, true)
}
@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? {
// Validate the chain first, before we do anything clever with it
val identityCertChain = identity.certPath.x509Certificates
try {
identity.verify(trustAnchor)
identity.verify(trustAnchors)
} 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 :")
identityCertChain.reversed().forEachIndexed { index, certificate ->
val space = (0 until index).joinToString("") { " " }
@ -258,7 +263,7 @@ class PersistentIdentityService(cacheFactory: NamedCacheFactory) : SingletonSeri
if (wellKnownCert != identity.certificate && !isNewRandomIdentity) {
val idx = identityCertChain.lastIndexOf(wellKnownCert)
val firstPath = X509Utilities.buildCertPath(identityCertChain.slice(idx until identityCertChain.size))
verifyAndRegisterIdentity(trustAnchor, PartyAndCertificate(firstPath))
verifyAndRegisterIdentity(trustAnchors, PartyAndCertificate(firstPath))
}
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 lateinit var trustRoot: X509Certificate
private lateinit var trustRoots: Set<X509Certificate>
fun start(trustRoot: X509Certificate) {
this.trustRoot = trustRoot
fun start(trustRoots: Set<X509Certificate>) {
this.trustRoots = trustRoots
}
fun publish(signedNodeInfo: SignedNodeInfo) {
@ -61,7 +61,7 @@ class NetworkMapClient(compatibilityZoneURL: URL, private val versionInfo: Versi
logger.trace { "Fetching network map update from $url." }
val connection = url.openHttpConnection()
val signedNetworkMap = connection.responseAs<SignedNetworkMap>()
val networkMap = signedNetworkMap.verifiedNetworkMapCert(trustRoot)
val networkMap = signedNetworkMap.verifiedNetworkMapCert(trustRoots)
val timeout = connection.cacheControl.maxAgeSeconds().seconds
val version = connection.cordaServerVersion
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." }
val verifiedNodeInfo = url.openHttpConnection().responseAs<Pair<SignedNetworkMap, List<SignedNodeInfo>>>()
.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() }
require(
verifiedNodeInfoHashes.containsAll(nodeInfoHashes) &&

View File

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

View File

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

View File

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

View File

@ -63,7 +63,7 @@ open class NetworkRegistrationHelper(
private val certificateStore = config.certificateStore
private val requestIdStore = certificatesDirectory / "certificate-request-id.txt"
protected val rootTrustStore: X509KeyStore
protected val rootCert: X509Certificate
protected val rootCerts: Set<X509Certificate>
private val notaryServiceConfig: NotaryServiceConfig? = config.notaryServiceConfig
init {
@ -72,7 +72,8 @@ open class NetworkRegistrationHelper(
"Please contact your CZ operator."
}
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")
val nodeIdentityCertificateChain: List<X509Certificate> = listOf(nodeIdentityCert) + nodeCaCertChain
X509Utilities.validateCertificateChain(rootCert, nodeIdentityCertificateChain)
X509Utilities.validateCertificateChain(rootCerts, nodeIdentityCertificateChain)
certStore.setCertPathOnly(nodeIdentityAlias, nodeIdentityCertificateChain)
}
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.
X509Utilities.validateCertificateChain(rootCert, certificates)
X509Utilities.validateCertificateChain(rootCerts, certificates)
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?) {
createSSLKeystore(publicKey, contentSigner, certificates, tlsCrlCertificateIssuer)
createTruststore(certificates.last())
createTruststore()
}
private fun createSSLKeystore(nodeCaPublicKey: PublicKey, nodeCaContentSigner: ContentSigner, nodeCaCertificateChain: List<X509Certificate>, tlsCertCrlIssuer: X500Name?) {
@ -449,23 +450,21 @@ class NodeRegistrationHelper(
logger.info("Generated TLS certificate: $sslCert")
val sslCertificateChain: List<X509Certificate> = listOf(sslCert) + nodeCaCertificateChain
X509Utilities.validateCertificateChain(rootCert, sslCertificateChain)
X509Utilities.validateCertificateChain(rootCerts, sslCertificateChain)
setPrivateKey(CORDA_CLIENT_TLS, sslKeyPair.private, sslCertificateChain, keyStore.entryPassword)
}
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.
config.p2pSslOptions.trustStore.get(createNew = true).update {
if (this.aliases().hasNext()) {
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.")
// Assumes certificate chain always starts with client certificate and end with root certificate.
setCertificate(CORDA_ROOT_CA, rootCertificate)
// Copy remaining certificates from the network-trust-store
rootTrustStore.aliases().asSequence().filter { it != CORDA_ROOT_CA }.forEach {
// Copy all certificates from the network-trust-store.
rootTrustStore.aliases().forEach {
val certificate = rootTrustStore.getCertificate(it)
logger.info("Copying trusted certificate to the node's trust store: Alias: $it, Certificate: $certificate")
setCertificate(it, certificate)
@ -477,8 +476,10 @@ class NodeRegistrationHelper(
override fun validateAndGetTlsCrlIssuerCert(): X509Certificate? {
val tlsCertCrlIssuer = config.tlsCertCrlIssuer
tlsCertCrlIssuer ?: return null
if (principalMatchesCertificatePrincipal(tlsCertCrlIssuer, rootCert)) {
return rootCert
for (rootCert in rootCerts) {
if (principalMatchesCertificatePrincipal(tlsCertCrlIssuer, rootCert)) {
return rootCert
}
}
return findMatchingCertificate(tlsCertCrlIssuer, rootTrustStore)
}

View File

@ -171,11 +171,20 @@ class KeyStoreHandlerTest {
@Test(timeout = 300_000)
fun `valid trust root is returned`() {
val expectedRoot = config.p2pSslOptions.trustStore.get()[CORDA_ROOT_CA]
val actualRoot = keyStoreHandler.init()
val actualRoot = keyStoreHandler.init().first()
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)
fun `keystore creation in dev mode`() {
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.node.services.network.PersistentNetworkMapCache
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.X509Utilities
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.testing.core.ALICE_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.SerializationEnvironmentRule
import net.corda.testing.core.TestIdentity
import net.corda.testing.core.getTestPartyAndCertificate
import net.corda.testing.internal.TestingNamedCacheFactory
import net.corda.testing.internal.configureDatabase
import net.corda.testing.internal.createDevIntermediateCaCertPath
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
import org.assertj.core.api.Assertions.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
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.assertFailsWith
import kotlin.test.assertNull
@ -70,7 +77,7 @@ class PersistentIdentityServiceTests {
)
identityService.database = database
identityService.start(
DEV_ROOT_CA.certificate,
setOf(DEV_ROOT_CA.certificate),
alice.identity,
listOf(notary.party),
PublicKeyToOwningIdentityCacheImpl(database, cacheFactory)
@ -221,7 +228,11 @@ class PersistentIdentityServiceTests {
// Create new identity service mounted onto same DB
val newPersistentIdentityService = PersistentIdentityService(TestingNamedCacheFactory()).also {
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())
@ -350,4 +361,39 @@ class PersistentIdentityServiceTests {
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()
networkParametersService = DBNetworkParametersStorage(TestingNamedCacheFactory(), database, networkMapClient).apply {
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)
val address = server.start()
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

View File

@ -113,7 +113,7 @@ class NetworkMapUpdaterTest {
server = NetworkMapServer(cacheExpiryMs.millis)
val address = server.start()
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
@ -132,7 +132,7 @@ class NetworkMapUpdaterTest {
autoAcceptNetworkParameters: Boolean = true,
excludedAutoAcceptNetworkParameters: Set<String> = emptySet()) {
updater!!.start(DEV_ROOT_CA.certificate,
updater!!.start(setOf(DEV_ROOT_CA.certificate),
server.networkParameters.serialize().hash,
ourNodeInfo,
networkParameters,
@ -363,7 +363,7 @@ class NetworkMapUpdaterTest {
updater!!.acceptNewNetworkParameters(newHash) { it.serialize().sign(ourKeyPair) }
verify(networkParametersStorage, times(1)).saveParameters(any())
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(newHash, server.latestParametersAccepted(ourKeyPair.public))
}
@ -381,7 +381,7 @@ class NetworkMapUpdaterTest {
val newHash = newParameters.serialize().hash
val updateFile = baseDir / NETWORK_PARAMS_UPDATE_FILE_NAME
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(newHash, server.latestParametersAccepted(ourKeyPair.public))
}

View File

@ -26,7 +26,7 @@ class NetworkParametersHotloaderTest {
@JvmField
val testSerialization = SerializationEnvironmentRule(true)
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 notary: Party = TestIdentity.fresh("test notary").party
@ -118,8 +118,9 @@ class NetworkParametersHotloaderTest {
Mockito.`when`(networkMapClient.getNetworkParameters(newNetworkParameters.serialize().hash)).thenReturn(signedNetworkParameters)
val networkParametersReader = Mockito.mock(NetworkParametersReader::class.java)
Mockito.`when`(networkParametersReader.read())
.thenReturn(NetworkParametersReader.NetworkParametersAndSigned(signedNetworkParameters, trustRoot))
return NetworkParametersHotloader(networkMapClient, trustRoot, originalNetworkParameters, networkParametersReader, networkParametersStorage)
.thenReturn(NetworkParametersReader.NetworkParametersAndSigned(signedNetworkParameters, trustRoots))
return NetworkParametersHotloader(networkMapClient, trustRoots, originalNetworkParameters, networkParametersReader,
networkParametersStorage)
}
}

View File

@ -54,7 +54,7 @@ class NetworkParametersReaderTest {
server = NetworkMapServer(cacheTimeout)
val address = server.start()
networkMapClient = NetworkMapClient(URL("http://$address"), VersionInfo(1, "TEST", "TEST", "TEST"))
networkMapClient.start(DEV_ROOT_CA.certificate)
networkMapClient.start(setOf(DEV_ROOT_CA.certificate))
}
@After
@ -69,13 +69,13 @@ class NetworkParametersReaderTest {
val oldParameters = testNetworkParameters(epoch = 1)
NetworkParametersCopier(oldParameters).install(networkParamsPath)
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())
assertEquals(server.networkParameters, parameters)
// Parameters from update should be moved to `network-parameters` file.
val parametersFromFile = (networkParamsPath / NETWORK_PARAMS_FILE_NAME)
.readObject<SignedNetworkParameters>()
.verifiedNetworkParametersCert(DEV_ROOT_CA.certificate)
.verifiedNetworkParametersCert(setOf(DEV_ROOT_CA.certificate))
assertEquals(server.networkParameters, parametersFromFile)
}
@ -85,13 +85,13 @@ class NetworkParametersReaderTest {
val oldParameters = testNetworkParameters(epoch = 1)
NetworkParametersCopier(oldParameters).install(networkParamsPath)
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())
assertEquals(server.networkParameters, parameters)
// Parameters from update should be moved to `network-parameters` file.
val parametersFromFile = (networkParamsPath / NETWORK_PARAMS_FILE_NAME)
.readObject<SignedNetworkParameters>()
.verifiedNetworkParametersCert(DEV_ROOT_CA.certificate)
.verifiedNetworkParametersCert(setOf(DEV_ROOT_CA.certificate))
assertEquals(server.networkParameters, parametersFromFile)
}
@ -101,7 +101,7 @@ class NetworkParametersReaderTest {
val networkParamsPath = fs.getPath("/node").createDirectories()
val fileParameters = testNetworkParameters(epoch = 1)
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)
}
@ -120,17 +120,17 @@ class NetworkParametersReaderTest {
val netParameters = testNetworkParameters(epoch = 1)
val certKeyPairNetworkParameters: CertificateAndKeyPair = createDevNetworkParametersCa()
val netParamsForNetworkParameters= certKeyPairNetworkParameters.sign(netParameters)
netParamsForNetworkParameters.verifiedNetworkParametersCert(DEV_ROOT_CA.certificate)
netParamsForNetworkParameters.verifiedNetworkParametersCert(setOf(DEV_ROOT_CA.certificate))
val certKeyPairNetworkMap: CertificateAndKeyPair = createDevNetworkMapCa()
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 x = createDevNodeCa(DEV_INTERMEDIATE_CA, megaCorp.name)
val netParamsForNode = x.sign(netParameters)
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.
identityService.apply {
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) }
}
val networkMapCache = PersistentNetworkMapCache(cacheFactory, persistence, identityService)

View File

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