mirror of
https://github.com/corda/corda.git
synced 2025-05-04 09:43:05 +00:00
CORDA-3979: Support for multiple trust roots (#6772)
This commit is contained in:
parent
401d8b8856
commit
4193adf6fd
@ -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)
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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>)
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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"
|
||||||
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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"
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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) &&
|
||||||
|
@ -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.
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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,9 +476,11 @@ class NodeRegistrationHelper(
|
|||||||
override fun validateAndGetTlsCrlIssuerCert(): X509Certificate? {
|
override fun validateAndGetTlsCrlIssuerCert(): X509Certificate? {
|
||||||
val tlsCertCrlIssuer = config.tlsCertCrlIssuer
|
val tlsCertCrlIssuer = config.tlsCertCrlIssuer
|
||||||
tlsCertCrlIssuer ?: return null
|
tlsCertCrlIssuer ?: return null
|
||||||
|
for (rootCert in rootCerts) {
|
||||||
if (principalMatchesCertificatePrincipal(tlsCertCrlIssuer, rootCert)) {
|
if (principalMatchesCertificatePrincipal(tlsCertCrlIssuer, rootCert)) {
|
||||||
return rootCert
|
return rootCert
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return findMatchingCertificate(tlsCertCrlIssuer, rootTrustStore)
|
return findMatchingCertificate(tlsCertCrlIssuer, rootTrustStore)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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"
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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>? {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user