Use NetworkMap and SignedNetworkMap in NetworkMapClient, and enable signature verification. (#2054)

* new network map object for network map, and verify signature and root in Signed network map and node info

* fixup after rebase

* * added certificate and key to network map server
* move DigitalSignature.WithCert back to NetworkMap.kt, as its breaking API test, will raise another PR to move it back.
* Make DigitalSignature.WithCert not extend WithKey, as per PR discussion.
* various fixes after rebase.

* move Network map back to core/node, as its breaking API test

* revert unintended changes

* move network map objects to node-api
This commit is contained in:
Patrick Kuo
2017-11-29 15:55:13 +00:00
committed by GitHub
parent cc1fba641e
commit 572c4af40c
20 changed files with 245 additions and 147 deletions

View File

@ -1678,27 +1678,6 @@ public @interface net.corda.core.messaging.RPCReturnsObservables
@org.jetbrains.annotations.NotNull public abstract net.corda.core.messaging.FlowHandle startFlow(net.corda.core.flows.FlowLogic) @org.jetbrains.annotations.NotNull public abstract net.corda.core.messaging.FlowHandle startFlow(net.corda.core.flows.FlowLogic)
@org.jetbrains.annotations.NotNull public abstract net.corda.core.messaging.FlowProgressHandle startTrackedFlow(net.corda.core.flows.FlowLogic) @org.jetbrains.annotations.NotNull public abstract net.corda.core.messaging.FlowProgressHandle startTrackedFlow(net.corda.core.flows.FlowLogic)
## ##
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.node.NetworkParameters extends java.lang.Object
public <init>(int, List, java.time.Duration, int, int, java.time.Instant, int)
public final int component1()
@org.jetbrains.annotations.NotNull public final List component2()
@org.jetbrains.annotations.NotNull public final java.time.Duration component3()
public final int component4()
public final int component5()
@org.jetbrains.annotations.NotNull public final java.time.Instant component6()
public final int component7()
@org.jetbrains.annotations.NotNull public final net.corda.core.node.NetworkParameters copy(int, List, java.time.Duration, int, int, java.time.Instant, int)
public boolean equals(Object)
public final int getEpoch()
@org.jetbrains.annotations.NotNull public final java.time.Duration getEventHorizon()
public final int getMaxMessageSize()
public final int getMaxTransactionSize()
public final int getMinimumPlatformVersion()
@org.jetbrains.annotations.NotNull public final java.time.Instant getModifiedTime()
@org.jetbrains.annotations.NotNull public final List getNotaries()
public int hashCode()
public String toString()
##
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.node.NodeInfo extends java.lang.Object @net.corda.core.serialization.CordaSerializable public final class net.corda.core.node.NodeInfo extends java.lang.Object
public <init>(List, List, int, long) public <init>(List, List, int, long)
@org.jetbrains.annotations.NotNull public final List component1() @org.jetbrains.annotations.NotNull public final List component1()
@ -1717,17 +1696,6 @@ public @interface net.corda.core.messaging.RPCReturnsObservables
public final boolean isLegalIdentity(net.corda.core.identity.Party) public final boolean isLegalIdentity(net.corda.core.identity.Party)
public String toString() public String toString()
## ##
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.node.NotaryInfo extends java.lang.Object
public <init>(net.corda.core.identity.Party, boolean)
@org.jetbrains.annotations.NotNull public final net.corda.core.identity.Party component1()
public final boolean component2()
@org.jetbrains.annotations.NotNull public final net.corda.core.node.NotaryInfo copy(net.corda.core.identity.Party, boolean)
public boolean equals(Object)
@org.jetbrains.annotations.NotNull public final net.corda.core.identity.Party getIdentity()
public final boolean getValidating()
public int hashCode()
public String toString()
##
@net.corda.core.DoNotImplement public interface net.corda.core.node.ServiceHub extends net.corda.core.node.ServicesForResolution @net.corda.core.DoNotImplement public interface net.corda.core.node.ServiceHub extends net.corda.core.node.ServicesForResolution
@org.jetbrains.annotations.NotNull public abstract net.corda.core.transactions.SignedTransaction addSignature(net.corda.core.transactions.SignedTransaction) @org.jetbrains.annotations.NotNull public abstract net.corda.core.transactions.SignedTransaction addSignature(net.corda.core.transactions.SignedTransaction)
@org.jetbrains.annotations.NotNull public abstract net.corda.core.transactions.SignedTransaction addSignature(net.corda.core.transactions.SignedTransaction, java.security.PublicKey) @org.jetbrains.annotations.NotNull public abstract net.corda.core.transactions.SignedTransaction addSignature(net.corda.core.transactions.SignedTransaction, java.security.PublicKey)

View File

@ -1,40 +0,0 @@
package net.corda.core.node
import net.corda.core.identity.Party
import net.corda.core.serialization.CordaSerializable
import java.time.Duration
import java.time.Instant
/**
* @property minimumPlatformVersion
* @property notaries
* @property eventHorizon
* @property maxMessageSize Maximum P2P message sent over the wire in bytes.
* @property maxTransactionSize Maximum permitted transaction size in bytes.
* @property modifiedTime
* @property epoch Version number of the network parameters. Starting from 1, this will always increment on each new set
* of parameters.
*/
// TODO Wire up the parameters
@CordaSerializable
data class NetworkParameters(
val minimumPlatformVersion: Int,
val notaries: List<NotaryInfo>,
val eventHorizon: Duration,
val maxMessageSize: Int,
val maxTransactionSize: Int,
val modifiedTime: Instant,
val epoch: Int
) {
init {
require(minimumPlatformVersion > 0) { "minimumPlatformVersion must be at least 1" }
require(notaries.distinctBy { it.identity } == notaries) { "Duplicate notary identities" }
require(epoch > 0) { "epoch must be at least 1" }
}
}
/**
*
*/
@CordaSerializable
data class NotaryInfo(val identity: Party, val validating: Boolean)

View File

@ -0,0 +1,75 @@
package net.corda.nodeapi.internal
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.verify
import net.corda.core.identity.Party
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.deserialize
import java.security.SignatureException
import java.security.cert.CertPathValidatorException
import java.security.cert.X509Certificate
import java.time.Duration
import java.time.Instant
// TODO: Need more discussion on rather we should move this class out of internal.
/**
* Data class containing hash of [NetworkParameters] and network participant's [NodeInfo] hashes.
*/
@CordaSerializable
data class NetworkMap(val nodeInfoHashes: List<SecureHash>, val networkParameterHash: SecureHash)
/**
* @property minimumPlatformVersion
* @property notaries
* @property eventHorizon
* @property maxMessageSize Maximum P2P message sent over the wire in bytes.
* @property maxTransactionSize Maximum permitted transaction size in bytes.
* @property modifiedTime
* @property epoch Version number of the network parameters. Starting from 1, this will always increment on each new set
* of parameters.
*/
// TODO Wire up the parameters
@CordaSerializable
data class NetworkParameters(
val minimumPlatformVersion: Int,
val notaries: List<NotaryInfo>,
val eventHorizon: Duration,
val maxMessageSize: Int,
val maxTransactionSize: Int,
val modifiedTime: Instant,
val epoch: Int
) {
init {
require(minimumPlatformVersion > 0) { "minimumPlatformVersion must be at least 1" }
require(notaries.distinctBy { it.identity } == notaries) { "Duplicate notary identities" }
require(epoch > 0) { "epoch must be at least 1" }
}
}
@CordaSerializable
data class NotaryInfo(val identity: Party, val validating: Boolean)
/**
* A serialized [NetworkMap] and its signature and certificate. Enforces signature validity in order to deserialize the data
* contained within.
*/
@CordaSerializable
class SignedNetworkMap(val raw: SerializedBytes<NetworkMap>, val sig: DigitalSignatureWithCert) {
/**
* Return the deserialized NetworkMap if the signature and certificate can be verified.
*
* @throws CertPathValidatorException if the certificate path is invalid.
* @throws SignatureException if the signature is invalid.
*/
@Throws(SignatureException::class)
fun verified(): NetworkMap {
sig.by.publicKey.verify(raw.bytes, sig)
return raw.deserialize()
}
}
// TODO: This class should reside in the [DigitalSignature] class.
/** A digital signature that identifies who the public key is owned by, and the certificate which provides prove of the identity */
class DigitalSignatureWithCert(val by: X509Certificate, val signatureBytes: ByteArray) : DigitalSignature(signatureBytes)

View File

@ -6,6 +6,7 @@ import net.corda.core.utilities.NetworkHostAndPort
import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.api.core.SimpleString
import rx.Notification import rx.Notification
import rx.exceptions.OnErrorNotImplementedException import rx.exceptions.OnErrorNotImplementedException
import sun.security.x509.X509CertImpl
import java.util.* import java.util.*
/** /**
@ -58,6 +59,9 @@ object DefaultWhitelist : SerializationWhitelist {
java.util.LinkedHashMap::class.java, java.util.LinkedHashMap::class.java,
BitSet::class.java, BitSet::class.java,
OnErrorNotImplementedException::class.java, OnErrorNotImplementedException::class.java,
StackTraceElement::class.java StackTraceElement::class.java,
)
// Implementation of X509Certificate.
X509CertImpl::class.java
)
} }

View File

@ -44,6 +44,7 @@ import org.objenesis.strategy.StdInstantiatorStrategy
import org.slf4j.Logger import org.slf4j.Logger
import sun.security.ec.ECPublicKeyImpl import sun.security.ec.ECPublicKeyImpl
import sun.security.provider.certpath.X509CertPath import sun.security.provider.certpath.X509CertPath
import sun.security.x509.X509CertImpl
import java.io.BufferedInputStream import java.io.BufferedInputStream
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.FileInputStream import java.io.FileInputStream
@ -75,6 +76,7 @@ object DefaultKryoCustomizer {
addDefaultSerializer(InputStream::class.java, InputStreamSerializer) addDefaultSerializer(InputStream::class.java, InputStreamSerializer)
addDefaultSerializer(SerializeAsToken::class.java, SerializeAsTokenSerializer<SerializeAsToken>()) addDefaultSerializer(SerializeAsToken::class.java, SerializeAsTokenSerializer<SerializeAsToken>())
addDefaultSerializer(Logger::class.java, LoggerSerializer) addDefaultSerializer(Logger::class.java, LoggerSerializer)
addDefaultSerializer(X509Certificate::class.java, X509CertificateSerializer)
// WARNING: reordering the registrations here will cause a change in the serialized form, since classes // WARNING: reordering the registrations here will cause a change in the serialized form, since classes
// with custom serializers get written as registration ids. This will break backwards-compatibility. // with custom serializers get written as registration ids. This will break backwards-compatibility.
@ -108,7 +110,6 @@ object DefaultKryoCustomizer {
register(FileInputStream::class.java, InputStreamSerializer) register(FileInputStream::class.java, InputStreamSerializer)
register(CertPath::class.java, CertPathSerializer) register(CertPath::class.java, CertPathSerializer)
register(X509CertPath::class.java, CertPathSerializer) register(X509CertPath::class.java, CertPathSerializer)
register(X509Certificate::class.java, X509CertificateSerializer)
register(BCECPrivateKey::class.java, PrivateKeySerializer) register(BCECPrivateKey::class.java, PrivateKeySerializer)
register(BCECPublicKey::class.java, publicKeySerializer) register(BCECPublicKey::class.java, publicKeySerializer)
register(BCRSAPrivateCrtKey::class.java, PrivateKeySerializer) register(BCRSAPrivateCrtKey::class.java, PrivateKeySerializer)

View File

@ -13,7 +13,6 @@ import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.deleteIfExists import net.corda.core.internal.deleteIfExists
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.node.NotaryInfo
import net.corda.core.node.services.NotaryService import net.corda.core.node.services.NotaryService
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
@ -26,6 +25,7 @@ import net.corda.node.services.config.NotaryConfig
import net.corda.node.services.transactions.minClusterSize import net.corda.node.services.transactions.minClusterSize
import net.corda.node.services.transactions.minCorrectReplicas import net.corda.node.services.transactions.minCorrectReplicas
import net.corda.node.utilities.ServiceIdentityGenerator import net.corda.node.utilities.ServiceIdentityGenerator
import net.corda.nodeapi.internal.NotaryInfo
import net.corda.testing.chooseIdentity import net.corda.testing.chooseIdentity
import net.corda.testing.common.internal.NetworkParametersCopier import net.corda.testing.common.internal.NetworkParametersCopier
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters

View File

@ -1,7 +1,6 @@
package net.corda.node.services.network package net.corda.node.services.network
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.utilities.minutes
import net.corda.core.utilities.seconds import net.corda.core.utilities.seconds
import net.corda.testing.ALICE import net.corda.testing.ALICE
import net.corda.testing.BOB import net.corda.testing.BOB
@ -18,7 +17,7 @@ class NetworkMapClientTest {
@Test @Test
fun `nodes can see each other using the http network map`() { fun `nodes can see each other using the http network map`() {
NetworkMapServer(1.minutes, portAllocation.nextHostAndPort()).use { NetworkMapServer(1.seconds, portAllocation.nextHostAndPort()).use {
val (host, port) = it.start() val (host, port) = it.start()
driver(portAllocation = portAllocation, compatibilityZoneURL = URL("http://$host:$port")) { driver(portAllocation = portAllocation, compatibilityZoneURL = URL("http://$host:$port")) {
val alice = startNode(providedName = ALICE.name) val alice = startNode(providedName = ALICE.name)

View File

@ -44,15 +44,8 @@ import net.corda.node.services.events.ScheduledActivityObserver
import net.corda.node.services.identity.PersistentIdentityService import net.corda.node.services.identity.PersistentIdentityService
import net.corda.node.services.keys.PersistentKeyManagementService import net.corda.node.services.keys.PersistentKeyManagementService
import net.corda.node.services.messaging.MessagingService import net.corda.node.services.messaging.MessagingService
import net.corda.node.services.network.NetworkMapCacheImpl
import net.corda.node.services.network.NodeInfoWatcher
import net.corda.node.services.network.PersistentNetworkMapCache
import net.corda.node.services.persistence.*
import net.corda.node.services.network.* import net.corda.node.services.network.*
import net.corda.node.services.persistence.DBCheckpointStorage import net.corda.node.services.persistence.*
import net.corda.node.services.persistence.DBTransactionMappingStorage
import net.corda.node.services.persistence.DBTransactionStorage
import net.corda.node.services.persistence.NodeAttachmentService
import net.corda.node.services.schema.HibernateObserver import net.corda.node.services.schema.HibernateObserver
import net.corda.node.services.schema.NodeSchemaService import net.corda.node.services.schema.NodeSchemaService
import net.corda.node.services.statemachine.* import net.corda.node.services.statemachine.*
@ -64,6 +57,7 @@ import net.corda.node.shell.InteractiveShell
import net.corda.node.utilities.AffinityExecutor import net.corda.node.utilities.AffinityExecutor
import net.corda.node.utilities.CordaPersistence import net.corda.node.utilities.CordaPersistence
import net.corda.node.utilities.configureDatabase import net.corda.node.utilities.configureDatabase
import net.corda.nodeapi.internal.NetworkParameters
import net.corda.nodeapi.internal.crypto.* import net.corda.nodeapi.internal.crypto.*
import org.apache.activemq.artemis.utils.ReusableLatch import org.apache.activemq.artemis.utils.ReusableLatch
import org.slf4j.Logger import org.slf4j.Logger
@ -137,7 +131,11 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
protected lateinit var network: MessagingService protected lateinit var network: MessagingService
protected val runOnStop = ArrayList<() -> Any?>() protected val runOnStop = ArrayList<() -> Any?>()
protected val _nodeReadyFuture = openFuture<Unit>() protected val _nodeReadyFuture = openFuture<Unit>()
protected val networkMapClient: NetworkMapClient? by lazy { configuration.compatibilityZoneURL?.let(::NetworkMapClient) } protected val networkMapClient: NetworkMapClient? by lazy {
configuration.compatibilityZoneURL?.let {
NetworkMapClient(it, services.identityService.trustRoot)
}
}
lateinit var userService: RPCUserService get lateinit var userService: RPCUserService get

View File

@ -1,6 +1,5 @@
package net.corda.node.services.network package net.corda.node.services.network
import com.fasterxml.jackson.databind.ObjectMapper
import com.google.common.util.concurrent.MoreExecutors import com.google.common.util.concurrent.MoreExecutors
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.SignedData import net.corda.core.crypto.SignedData
@ -13,6 +12,10 @@ import net.corda.core.utilities.minutes
import net.corda.core.utilities.seconds import net.corda.core.utilities.seconds
import net.corda.node.services.api.NetworkMapCacheInternal import net.corda.node.services.api.NetworkMapCacheInternal
import net.corda.node.utilities.NamedThreadFactory import net.corda.node.utilities.NamedThreadFactory
import net.corda.nodeapi.internal.NetworkMap
import net.corda.nodeapi.internal.NetworkParameters
import net.corda.nodeapi.internal.SignedNetworkMap
import net.corda.nodeapi.internal.crypto.X509Utilities
import okhttp3.CacheControl import okhttp3.CacheControl
import okhttp3.Headers import okhttp3.Headers
import rx.Subscription import rx.Subscription
@ -20,11 +23,12 @@ import java.io.BufferedReader
import java.io.Closeable import java.io.Closeable
import java.net.HttpURLConnection import java.net.HttpURLConnection
import java.net.URL import java.net.URL
import java.security.cert.X509Certificate
import java.time.Duration import java.time.Duration
import java.util.concurrent.Executors import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
class NetworkMapClient(compatibilityZoneURL: URL) { class NetworkMapClient(compatibilityZoneURL: URL, private val trustedRoot: X509Certificate) {
private val networkMapUrl = URL("$compatibilityZoneURL/network-map") private val networkMapUrl = URL("$compatibilityZoneURL/network-map")
fun publish(signedNodeInfo: SignedData<NodeInfo>) { fun publish(signedNodeInfo: SignedData<NodeInfo>) {
@ -42,14 +46,30 @@ class NetworkMapClient(compatibilityZoneURL: URL) {
fun getNetworkMap(): NetworkMapResponse { fun getNetworkMap(): NetworkMapResponse {
val conn = networkMapUrl.openHttpConnection() val conn = networkMapUrl.openHttpConnection()
val response = conn.inputStream.bufferedReader().use(BufferedReader::readLine) val signedNetworkMap = conn.inputStream.use { it.readBytes() }.deserialize<SignedNetworkMap>()
val networkMap = ObjectMapper().readValue(response, List::class.java).map { SecureHash.parse(it.toString()) } val networkMap = signedNetworkMap.verified()
// Assume network map cert is issued by the root.
X509Utilities.validateCertificateChain(trustedRoot, signedNetworkMap.sig.by, trustedRoot)
val timeout = CacheControl.parse(Headers.of(conn.headerFields.filterKeys { it != null }.mapValues { it.value.first() })).maxAgeSeconds().seconds val timeout = CacheControl.parse(Headers.of(conn.headerFields.filterKeys { it != null }.mapValues { it.value.first() })).maxAgeSeconds().seconds
return NetworkMapResponse(networkMap, timeout) return NetworkMapResponse(networkMap, timeout)
} }
fun getNodeInfo(nodeInfoHash: SecureHash): NodeInfo? { fun getNodeInfo(nodeInfoHash: SecureHash): NodeInfo? {
val conn = URL("$networkMapUrl/$nodeInfoHash").openHttpConnection() val conn = URL("$networkMapUrl/node-info/$nodeInfoHash").openHttpConnection()
return if (conn.responseCode == HttpURLConnection.HTTP_NOT_FOUND) {
null
} else {
val signedNodeInfo = conn.inputStream.use { it.readBytes() }.deserialize<SignedData<NodeInfo>>()
val nodeInfo = signedNodeInfo.verified()
// Verify node info is signed by node identity
// TODO : Validate multiple signatures when NodeInfo supports multiple identities.
require(nodeInfo.legalIdentities.any { it.owningKey == signedNodeInfo.sig.by }) { "NodeInfo must be signed by the node owning key." }
nodeInfo
}
}
fun getNetworkParameter(networkParameterHash: SecureHash): NetworkParameters? {
val conn = URL("$networkMapUrl/network-parameter/$networkParameterHash").openHttpConnection()
return if (conn.responseCode == HttpURLConnection.HTTP_NOT_FOUND) { return if (conn.responseCode == HttpURLConnection.HTTP_NOT_FOUND) {
null null
} else { } else {
@ -63,7 +83,7 @@ class NetworkMapClient(compatibilityZoneURL: URL) {
} }
} }
data class NetworkMapResponse(val networkMap: List<SecureHash>, val cacheMaxAge: Duration) data class NetworkMapResponse(val networkMap: NetworkMap, val cacheMaxAge: Duration)
class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal, class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal,
private val fileWatcher: NodeInfoWatcher, private val fileWatcher: NodeInfoWatcher,
@ -107,21 +127,28 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal,
val nextScheduleDelay = try { val nextScheduleDelay = try {
val (networkMap, cacheTimeout) = networkMapClient.getNetworkMap() val (networkMap, cacheTimeout) = networkMapClient.getNetworkMap()
val currentNodeHashes = networkMapCache.allNodeHashes val currentNodeHashes = networkMapCache.allNodeHashes
(networkMap - currentNodeHashes).mapNotNull { val hashesFromNetworkMap = networkMap.nodeInfoHashes
(hashesFromNetworkMap - currentNodeHashes).mapNotNull {
// Download new node info from network map // Download new node info from network map
networkMapClient.getNodeInfo(it) try {
networkMapClient.getNodeInfo(it)
} catch (t: Throwable) {
// Failure to retrieve one node info shouldn't stop the whole update, log and return null instead.
logger.warn("Error encountered when downloading node info '$it', skipping...", t)
null
}
}.forEach { }.forEach {
// Add new node info to the network map cache, these could be new node info or modification of node info for existing nodes. // Add new node info to the network map cache, these could be new node info or modification of node info for existing nodes.
networkMapCache.addNode(it) networkMapCache.addNode(it)
} }
// Remove node info from network map. // Remove node info from network map.
(currentNodeHashes - networkMap - fileWatcher.processedNodeInfoHashes) (currentNodeHashes - hashesFromNetworkMap - fileWatcher.processedNodeInfoHashes)
.mapNotNull(networkMapCache::getNodeByHash) .mapNotNull(networkMapCache::getNodeByHash)
.forEach(networkMapCache::removeNode) .forEach(networkMapCache::removeNode)
// TODO: Check NetworkParameter.
cacheTimeout cacheTimeout
} catch (t: Throwable) { } catch (t: Throwable) {
logger.warn("Error encountered while updating network map, will retry in $retryInterval", t) logger.warn("Error encountered while updating network map, will retry in ${retryInterval.seconds} seconds", t)
retryInterval retryInterval
} }
// Schedule the next update. // Schedule the next update.
@ -137,7 +164,7 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal,
try { try {
networkMapClient.publish(signedNodeInfo) networkMapClient.publish(signedNodeInfo)
} catch (t: Throwable) { } catch (t: Throwable) {
logger.warn("Error encountered while publishing node info, will retry in $retryInterval.", t) logger.warn("Error encountered while publishing node info, will retry in ${retryInterval.seconds} seconds.", t)
// TODO: Exponential backoff? // TODO: Exponential backoff?
executor.schedule(this, retryInterval.toMillis(), TimeUnit.MILLISECONDS) executor.schedule(this, retryInterval.toMillis(), TimeUnit.MILLISECONDS)
} }

View File

@ -12,7 +12,6 @@ import net.corda.core.internal.concurrent.openFuture
import net.corda.core.internal.schemas.NodeInfoSchemaV1 import net.corda.core.internal.schemas.NodeInfoSchemaV1
import net.corda.core.messaging.DataFeed import net.corda.core.messaging.DataFeed
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.node.NotaryInfo
import net.corda.core.node.services.IdentityService import net.corda.core.node.services.IdentityService
import net.corda.core.node.services.NetworkMapCache.MapChange import net.corda.core.node.services.NetworkMapCache.MapChange
import net.corda.core.node.services.PartyInfo import net.corda.core.node.services.PartyInfo
@ -25,6 +24,7 @@ import net.corda.node.services.api.NetworkMapCacheInternal
import net.corda.node.utilities.CordaPersistence import net.corda.node.utilities.CordaPersistence
import net.corda.node.utilities.bufferUntilDatabaseCommit import net.corda.node.utilities.bufferUntilDatabaseCommit
import net.corda.node.utilities.wrapWithDatabaseTransaction import net.corda.node.utilities.wrapWithDatabaseTransaction
import net.corda.nodeapi.internal.NotaryInfo
import org.hibernate.Session import org.hibernate.Session
import rx.Observable import rx.Observable
import rx.subjects.PublishSubject import rx.subjects.PublishSubject

View File

@ -1,11 +1,14 @@
package net.corda.node.services.network package net.corda.node.services.network
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.sha256 import net.corda.core.crypto.sha256
import net.corda.core.internal.cert
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.utilities.seconds import net.corda.core.utilities.seconds
import net.corda.node.services.network.TestNodeInfoFactory.createNodeInfo import net.corda.node.services.network.TestNodeInfoFactory.createNodeInfo
import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.testing.DEV_CA
import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.testing.DEV_TRUST_ROOT
import net.corda.testing.ROOT_CA
import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.SerializationEnvironmentRule
import net.corda.testing.driver.PortAllocation import net.corda.testing.driver.PortAllocation
import net.corda.testing.node.network.NetworkMapServer import net.corda.testing.node.network.NetworkMapServer
@ -16,6 +19,7 @@ import org.junit.Rule
import org.junit.Test import org.junit.Test
import java.net.URL import java.net.URL
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertNotNull
class NetworkMapClientTest { class NetworkMapClientTest {
@Rule @Rule
@ -32,7 +36,7 @@ class NetworkMapClientTest {
fun setUp() { fun setUp() {
server = NetworkMapServer(cacheTimeout, PortAllocation.Incremental(10000).nextHostAndPort()) server = NetworkMapServer(cacheTimeout, PortAllocation.Incremental(10000).nextHostAndPort())
val hostAndPort = server.start() val hostAndPort = server.start()
networkMapClient = NetworkMapClient(URL("http://${hostAndPort.host}:${hostAndPort.port}")) networkMapClient = NetworkMapClient(URL("http://${hostAndPort.host}:${hostAndPort.port}"), DEV_TRUST_ROOT.cert)
} }
@After @After
@ -50,7 +54,7 @@ class NetworkMapClientTest {
val nodeInfoHash = nodeInfo.serialize().sha256() val nodeInfoHash = nodeInfo.serialize().sha256()
assertThat(networkMapClient.getNetworkMap().networkMap).containsExactly(nodeInfoHash) assertThat(networkMapClient.getNetworkMap().networkMap.nodeInfoHashes).containsExactly(nodeInfoHash)
assertEquals(nodeInfo, networkMapClient.getNodeInfo(nodeInfoHash)) assertEquals(nodeInfo, networkMapClient.getNodeInfo(nodeInfoHash))
val signedNodeInfo2 = createNodeInfo("Test2") val signedNodeInfo2 = createNodeInfo("Test2")
@ -58,13 +62,22 @@ class NetworkMapClientTest {
networkMapClient.publish(signedNodeInfo2) networkMapClient.publish(signedNodeInfo2)
val nodeInfoHash2 = nodeInfo2.serialize().sha256() val nodeInfoHash2 = nodeInfo2.serialize().sha256()
assertThat(networkMapClient.getNetworkMap().networkMap).containsExactly(nodeInfoHash, nodeInfoHash2) assertThat(networkMapClient.getNetworkMap().networkMap.nodeInfoHashes).containsExactly(nodeInfoHash, nodeInfoHash2)
assertEquals(cacheTimeout, networkMapClient.getNetworkMap().cacheMaxAge) assertEquals(cacheTimeout, networkMapClient.getNetworkMap().cacheMaxAge)
assertEquals(nodeInfo2, networkMapClient.getNodeInfo(nodeInfoHash2)) assertEquals(nodeInfo2, networkMapClient.getNodeInfo(nodeInfoHash2))
} }
@Test
fun `download NetworkParameter correctly`() {
// The test server returns same network parameter for any hash.
val networkParameter = networkMapClient.getNetworkParameter(SecureHash.randomSHA256())
assertNotNull(networkParameter)
assertEquals(NetworkMapServer.stubNetworkParameter, networkParameter)
}
@Test @Test
fun `get hostname string from http response correctly`() { fun `get hostname string from http response correctly`() {
assertEquals("test.host.name", networkMapClient.myPublicHostname()) assertEquals("test.host.name", networkMapClient.myPublicHostname())
} }
} }

View File

@ -13,6 +13,7 @@ import net.corda.core.crypto.SignedData
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.internal.uncheckedCast import net.corda.core.internal.uncheckedCast
import net.corda.nodeapi.internal.NetworkMap
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
@ -95,7 +96,7 @@ class NetworkMapUpdaterTest {
val signedNodeInfo: SignedData<NodeInfo> = uncheckedCast(it.arguments.first()) val signedNodeInfo: SignedData<NodeInfo> = uncheckedCast(it.arguments.first())
nodeInfoMap.put(signedNodeInfo.verified().serialize().hash, signedNodeInfo) nodeInfoMap.put(signedNodeInfo.verified().serialize().hash, signedNodeInfo)
} }
on { getNetworkMap() }.then { NetworkMapResponse(nodeInfoMap.keys.toList(), 100.millis) } on { getNetworkMap() }.then { NetworkMapResponse(NetworkMap(nodeInfoMap.keys.toList(), SecureHash.randomSHA256()), 100.millis) }
on { getNodeInfo(any()) }.then { nodeInfoMap[it.arguments.first()]?.verified() } on { getNodeInfo(any()) }.then { nodeInfoMap[it.arguments.first()]?.verified() }
} }
@ -149,7 +150,7 @@ class NetworkMapUpdaterTest {
val signedNodeInfo: SignedData<NodeInfo> = uncheckedCast(it.arguments.first()) val signedNodeInfo: SignedData<NodeInfo> = uncheckedCast(it.arguments.first())
nodeInfoMap.put(signedNodeInfo.verified().serialize().hash, signedNodeInfo) nodeInfoMap.put(signedNodeInfo.verified().serialize().hash, signedNodeInfo)
} }
on { getNetworkMap() }.then { NetworkMapResponse(nodeInfoMap.keys.toList(), 100.millis) } on { getNetworkMap() }.then { NetworkMapResponse(NetworkMap(nodeInfoMap.keys.toList(), SecureHash.randomSHA256()), 100.millis) }
on { getNodeInfo(any()) }.then { nodeInfoMap[it.arguments.first()]?.verified() } on { getNodeInfo(any()) }.then { nodeInfoMap[it.arguments.first()]?.verified() }
} }

View File

@ -6,6 +6,7 @@ import net.corda.core.internal.list
import net.corda.core.internal.readLines import net.corda.core.internal.readLines
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.minutes import net.corda.core.utilities.minutes
import net.corda.core.utilities.seconds
import net.corda.node.internal.NodeStartup import net.corda.node.internal.NodeStartup
import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_BANK_A
import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_NOTARY
@ -63,7 +64,7 @@ class DriverTests {
@Test @Test
fun `node registration`() { fun `node registration`() {
val handler = RegistrationHandler() val handler = RegistrationHandler()
NetworkMapServer(1.minutes, portAllocation.nextHostAndPort(), handler).use { NetworkMapServer(1.seconds, portAllocation.nextHostAndPort(), handler).use {
val (host, port) = it.start() val (host, port) = it.start()
driver(portAllocation = portAllocation, compatibilityZoneURL = URL("http://$host:$port")) { driver(portAllocation = portAllocation, compatibilityZoneURL = URL("http://$host:$port")) {
// Wait for the node to have started. // Wait for the node to have started.

View File

@ -17,7 +17,6 @@ import net.corda.core.internal.*
import net.corda.core.internal.concurrent.* import net.corda.core.internal.concurrent.*
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.node.NotaryInfo
import net.corda.core.node.services.NetworkMapCache import net.corda.core.node.services.NetworkMapCache
import net.corda.core.node.services.NotaryService import net.corda.core.node.services.NotaryService
import net.corda.core.toFuture import net.corda.core.toFuture
@ -34,6 +33,7 @@ import net.corda.node.utilities.registration.NetworkRegistrationHelper
import net.corda.nodeapi.NodeInfoFilesCopier import net.corda.nodeapi.NodeInfoFilesCopier
import net.corda.nodeapi.User import net.corda.nodeapi.User
import net.corda.nodeapi.config.toConfig import net.corda.nodeapi.config.toConfig
import net.corda.nodeapi.internal.NotaryInfo
import net.corda.nodeapi.internal.addShutdownHook import net.corda.nodeapi.internal.addShutdownHook
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.common.internal.NetworkParametersCopier import net.corda.testing.common.internal.NetworkParametersCopier

View File

@ -17,7 +17,6 @@ import net.corda.core.messaging.MessageRecipients
import net.corda.core.messaging.RPCOps import net.corda.core.messaging.RPCOps
import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.node.NotaryInfo
import net.corda.core.node.services.IdentityService import net.corda.core.node.services.IdentityService
import net.corda.core.node.services.KeyManagementService import net.corda.core.node.services.KeyManagementService
import net.corda.core.serialization.SerializationWhitelist import net.corda.core.serialization.SerializationWhitelist
@ -39,12 +38,13 @@ import net.corda.node.utilities.AffinityExecutor
import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor
import net.corda.node.utilities.CordaPersistence import net.corda.node.utilities.CordaPersistence
import net.corda.node.utilities.ServiceIdentityGenerator import net.corda.node.utilities.ServiceIdentityGenerator
import net.corda.nodeapi.internal.NotaryInfo
import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.common.internal.NetworkParametersCopier import net.corda.testing.common.internal.NetworkParametersCopier
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.setGlobalSerialization
import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
import net.corda.testing.setGlobalSerialization
import net.corda.testing.testNodeConfiguration import net.corda.testing.testNodeConfiguration
import org.apache.activemq.artemis.utils.ReusableLatch import org.apache.activemq.artemis.utils.ReusableLatch
import org.apache.sshd.common.util.security.SecurityUtils import org.apache.sshd.common.util.security.SecurityUtils
@ -159,29 +159,32 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete
* Returns the single notary node on the network. Throws if there are none or more than one. * Returns the single notary node on the network. Throws if there are none or more than one.
* @see notaryNodes * @see notaryNodes
*/ */
val defaultNotaryNode: StartedNode<MockNode> get() { val defaultNotaryNode: StartedNode<MockNode>
return when (notaryNodes.size) { get() {
0 -> throw IllegalStateException("There are no notaries defined on the network") return when (notaryNodes.size) {
1 -> notaryNodes[0] 0 -> throw IllegalStateException("There are no notaries defined on the network")
else -> throw IllegalStateException("There is more than one notary defined on the network") 1 -> notaryNodes[0]
else -> throw IllegalStateException("There is more than one notary defined on the network")
}
} }
}
/** /**
* Return the identity of the default notary node. * Return the identity of the default notary node.
* @see defaultNotaryNode * @see defaultNotaryNode
*/ */
val defaultNotaryIdentity: Party get() { val defaultNotaryIdentity: Party
return defaultNotaryNode.info.legalIdentities.singleOrNull() ?: throw IllegalStateException("Default notary has multiple identities") get() {
} return defaultNotaryNode.info.legalIdentities.singleOrNull() ?: throw IllegalStateException("Default notary has multiple identities")
}
/** /**
* Return the identity of the default notary node. * Return the identity of the default notary node.
* @see defaultNotaryNode * @see defaultNotaryNode
*/ */
val defaultNotaryIdentityAndCert: PartyAndCertificate get() { val defaultNotaryIdentityAndCert: PartyAndCertificate
return defaultNotaryNode.info.legalIdentitiesAndCerts.singleOrNull() ?: throw IllegalStateException("Default notary has multiple identities") get() {
} return defaultNotaryNode.info.legalIdentitiesAndCerts.singleOrNull() ?: throw IllegalStateException("Default notary has multiple identities")
}
/** /**
* Because this executor is shared, we need to be careful about nodes shutting it down. * Because this executor is shared, we need to be careful about nodes shutting it down.

View File

@ -1,13 +1,25 @@
package net.corda.testing.node.network package net.corda.testing.node.network
import com.fasterxml.jackson.databind.ObjectMapper import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.SignedData import net.corda.core.crypto.SignedData
import net.corda.core.crypto.sha256 import net.corda.core.crypto.sha256
import net.corda.core.internal.cert
import net.corda.core.internal.toX509CertHolder
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.serialization.deserialize import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.hours
import net.corda.nodeapi.internal.DigitalSignatureWithCert
import net.corda.nodeapi.internal.NetworkMap
import net.corda.nodeapi.internal.NetworkParameters
import net.corda.nodeapi.internal.SignedNetworkMap
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
import net.corda.nodeapi.internal.crypto.CertificateType
import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.testing.ROOT_CA
import org.bouncycastle.asn1.x500.X500Name
import org.eclipse.jetty.server.Server import org.eclipse.jetty.server.Server
import org.eclipse.jetty.server.ServerConnector import org.eclipse.jetty.server.ServerConnector
import org.eclipse.jetty.server.handler.HandlerCollection import org.eclipse.jetty.server.handler.HandlerCollection
@ -19,15 +31,34 @@ import java.io.Closeable
import java.io.InputStream import java.io.InputStream
import java.net.InetSocketAddress import java.net.InetSocketAddress
import java.time.Duration import java.time.Duration
import java.time.Instant
import javax.ws.rs.* import javax.ws.rs.*
import javax.ws.rs.core.MediaType import javax.ws.rs.core.MediaType
import javax.ws.rs.core.Response import javax.ws.rs.core.Response
import javax.ws.rs.core.Response.ok import javax.ws.rs.core.Response.ok
class NetworkMapServer(cacheTimeout: Duration, hostAndPort: NetworkHostAndPort, vararg additionalServices: Any) : Closeable { class NetworkMapServer(cacheTimeout: Duration,
private val server: Server hostAndPort: NetworkHostAndPort,
vararg additionalServices: Any) : Closeable {
companion object {
val stubNetworkParameter = NetworkParameters(1, emptyList(), 1.hours, 10, 10, Instant.now(), 10)
private val service = InMemoryNetworkMapService(cacheTimeout) private fun networkMapKeyAndCert(rootCAKeyAndCert: CertificateAndKeyPair): CertificateAndKeyPair {
val networkMapKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val networkMapCert = X509Utilities.createCertificate(
CertificateType.IDENTITY,
rootCAKeyAndCert.certificate,
rootCAKeyAndCert.keyPair,
X500Name("CN=Corda Network Map,L=London"),
networkMapKey.public).cert
return CertificateAndKeyPair(networkMapCert.toX509CertHolder(), networkMapKey)
}
}
private val server: Server
// Default to ROOT_CA for testing.
// TODO: make this configurable?
private val service = InMemoryNetworkMapService(cacheTimeout, networkMapKeyAndCert(ROOT_CA))
init { init {
server = Server(InetSocketAddress(hostAndPort.host, hostAndPort.port)).apply { server = Server(InetSocketAddress(hostAndPort.host, hostAndPort.port)).apply {
@ -68,8 +99,9 @@ class NetworkMapServer(cacheTimeout: Duration, hostAndPort: NetworkHostAndPort,
} }
@Path("network-map") @Path("network-map")
class InMemoryNetworkMapService(private val cacheTimeout: Duration) { class InMemoryNetworkMapService(private val cacheTimeout: Duration, private val networkMapKeyAndCert: CertificateAndKeyPair) {
private val nodeInfoMap = mutableMapOf<SecureHash, NodeInfo>() private val nodeInfoMap = mutableMapOf<SecureHash, SignedData<NodeInfo>>()
@POST @POST
@Path("publish") @Path("publish")
@Consumes(MediaType.APPLICATION_OCTET_STREAM) @Consumes(MediaType.APPLICATION_OCTET_STREAM)
@ -77,39 +109,48 @@ class NetworkMapServer(cacheTimeout: Duration, hostAndPort: NetworkHostAndPort,
val registrationData = input.readBytes().deserialize<SignedData<NodeInfo>>() val registrationData = input.readBytes().deserialize<SignedData<NodeInfo>>()
val nodeInfo = registrationData.verified() val nodeInfo = registrationData.verified()
val nodeInfoHash = nodeInfo.serialize().sha256() val nodeInfoHash = nodeInfo.serialize().sha256()
nodeInfoMap.put(nodeInfoHash, nodeInfo) nodeInfoMap.put(nodeInfoHash, registrationData)
return ok().build() return ok().build()
} }
@GET @GET
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_OCTET_STREAM)
fun getNetworkMap(): Response { fun getNetworkMap(): Response {
return Response.ok(ObjectMapper().writeValueAsString(nodeInfoMap.keys.map { it.toString() })) val networkMap = NetworkMap(nodeInfoMap.keys.map { it }, SecureHash.randomSHA256())
.header("Cache-Control", "max-age=${cacheTimeout.seconds}") val serializedNetworkMap = networkMap.serialize()
.build() val signature = Crypto.doSign(networkMapKeyAndCert.keyPair.private, serializedNetworkMap.bytes)
val signedNetworkMap = SignedNetworkMap(networkMap.serialize(), DigitalSignatureWithCert(networkMapKeyAndCert.certificate.cert, signature))
return Response.ok(signedNetworkMap.serialize().bytes).header("Cache-Control", "max-age=${cacheTimeout.seconds}").build()
}
// Remove nodeInfo for testing.
fun removeNodeInfo(nodeInfo: NodeInfo) {
nodeInfoMap.remove(nodeInfo.serialize().hash)
} }
@GET @GET
@Path("{var}") @Path("node-info/{var}")
@Produces(MediaType.APPLICATION_OCTET_STREAM) @Produces(MediaType.APPLICATION_OCTET_STREAM)
fun getNodeInfo(@PathParam("var") nodeInfoHash: String): Response { fun getNodeInfo(@PathParam("var") nodeInfoHash: String): Response {
val nodeInfo = nodeInfoMap[SecureHash.parse(nodeInfoHash)] val signedNodeInfo = nodeInfoMap[SecureHash.parse(nodeInfoHash)]
return if (nodeInfo != null) { return if (signedNodeInfo != null) {
Response.ok(nodeInfo.serialize().bytes) Response.ok(signedNodeInfo.serialize().bytes)
} else { } else {
Response.status(Response.Status.NOT_FOUND) Response.status(Response.Status.NOT_FOUND)
}.build() }.build()
} }
@GET
@Path("network-parameter/{var}")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
fun getNetworkParameter(@PathParam("var") networkParameterHash: String): Response {
return Response.ok(stubNetworkParameter.serialize().bytes).build()
}
@GET @GET
@Path("my-hostname") @Path("my-hostname")
fun getHostName(): Response { fun getHostName(): Response {
return Response.ok("test.host.name").build() return Response.ok("test.host.name").build()
} }
// Remove nodeInfo for testing.
fun removeNodeInfo(nodeInfo: NodeInfo) {
nodeInfoMap.remove(nodeInfo.serialize().hash)
}
} }
} }

View File

@ -3,6 +3,7 @@ apply plugin: 'com.jfrog.artifactory'
dependencies { dependencies {
compile project(':core') compile project(':core')
compile project(':node-api')
} }
jar { jar {

View File

@ -5,8 +5,8 @@ import net.corda.core.crypto.entropyToKeyPair
import net.corda.core.crypto.sign import net.corda.core.crypto.sign
import net.corda.core.internal.copyTo import net.corda.core.internal.copyTo
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.node.NetworkParameters
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.nodeapi.internal.NetworkParameters
import java.math.BigInteger import java.math.BigInteger
import java.nio.file.FileAlreadyExistsException import java.nio.file.FileAlreadyExistsException
import java.nio.file.Path import java.nio.file.Path

View File

@ -1,8 +1,8 @@
package net.corda.testing.common.internal package net.corda.testing.common.internal
import net.corda.core.node.NetworkParameters
import net.corda.core.node.NotaryInfo
import net.corda.core.utilities.days import net.corda.core.utilities.days
import net.corda.nodeapi.internal.NetworkParameters
import net.corda.nodeapi.internal.NotaryInfo
import java.time.Instant import java.time.Instant
fun testNetworkParameters(notaries: List<NotaryInfo>): NetworkParameters { fun testNetworkParameters(notaries: List<NotaryInfo>): NetworkParameters {

View File

@ -70,6 +70,12 @@ val DEV_CA: CertificateAndKeyPair by lazy {
val caKeyStore = loadKeyStore(ClassLoader.getSystemResourceAsStream("net/corda/node/internal/certificates/cordadevcakeys.jks"), "cordacadevpass") val caKeyStore = loadKeyStore(ClassLoader.getSystemResourceAsStream("net/corda/node/internal/certificates/cordadevcakeys.jks"), "cordacadevpass")
caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass") caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
} }
val ROOT_CA: CertificateAndKeyPair by lazy {
// TODO: Should be identity scheme
val caKeyStore = loadKeyStore(ClassLoader.getSystemResourceAsStream("net/corda/node/internal/certificates/cordadevcakeys.jks"), "cordacadevpass")
caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_ROOT_CA, "cordacadevkeypass")
}
val DEV_TRUST_ROOT: X509CertificateHolder by lazy { val DEV_TRUST_ROOT: X509CertificateHolder by lazy {
// TODO: Should be identity scheme // TODO: Should be identity scheme
val caKeyStore = loadKeyStore(ClassLoader.getSystemResourceAsStream("net/corda/node/internal/certificates/cordadevcakeys.jks"), "cordacadevpass") val caKeyStore = loadKeyStore(ClassLoader.getSystemResourceAsStream("net/corda/node/internal/certificates/cordadevcakeys.jks"), "cordacadevpass")