mirror of
https://github.com/corda/corda.git
synced 2025-04-26 13:59:56 +00:00
Infrastructure for confidential identities
* De-anonymise parties in AbstractStateReplacementFlow flows * Convert transaction key negotiation to a subflow instead of utility functions * Add serialization support for CertPath * Restructure cash flows so that a counterparty flow can be added later
This commit is contained in:
parent
851cccbf7e
commit
a8d4dccea4
@ -20,7 +20,6 @@ import net.corda.core.serialization.OpaqueBytes
|
|||||||
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.i2p.crypto.eddsa.EdDSAPublicKey
|
import net.i2p.crypto.eddsa.EdDSAPublicKey
|
||||||
import org.bouncycastle.asn1.ASN1InputStream
|
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
@ -39,28 +38,28 @@ object JacksonSupport {
|
|||||||
interface PartyObjectMapper {
|
interface PartyObjectMapper {
|
||||||
@Deprecated("Use partyFromX500Name instead")
|
@Deprecated("Use partyFromX500Name instead")
|
||||||
fun partyFromName(partyName: String): Party?
|
fun partyFromName(partyName: String): Party?
|
||||||
fun partyFromPrincipal(principal: X500Name): Party?
|
fun partyFromX500Name(name: X500Name): Party?
|
||||||
fun partyFromKey(owningKey: PublicKey): Party?
|
fun partyFromKey(owningKey: PublicKey): Party?
|
||||||
}
|
}
|
||||||
|
|
||||||
class RpcObjectMapper(val rpc: CordaRPCOps, factory: JsonFactory) : PartyObjectMapper, ObjectMapper(factory) {
|
class RpcObjectMapper(val rpc: CordaRPCOps, factory: JsonFactory) : PartyObjectMapper, ObjectMapper(factory) {
|
||||||
@Suppress("OverridingDeprecatedMember", "DEPRECATION")
|
@Suppress("OverridingDeprecatedMember", "DEPRECATION")
|
||||||
override fun partyFromName(partyName: String): Party? = rpc.partyFromName(partyName)
|
override fun partyFromName(partyName: String): Party? = rpc.partyFromName(partyName)
|
||||||
override fun partyFromPrincipal(principal: X500Name): Party? = rpc.partyFromX500Name(principal)
|
override fun partyFromX500Name(name: X500Name): Party? = rpc.partyFromX500Name(name)
|
||||||
override fun partyFromKey(owningKey: PublicKey): Party? = rpc.partyFromKey(owningKey)
|
override fun partyFromKey(owningKey: PublicKey): Party? = rpc.partyFromKey(owningKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
class IdentityObjectMapper(val identityService: IdentityService, factory: JsonFactory) : PartyObjectMapper, ObjectMapper(factory) {
|
class IdentityObjectMapper(val identityService: IdentityService, factory: JsonFactory) : PartyObjectMapper, ObjectMapper(factory) {
|
||||||
@Suppress("OverridingDeprecatedMember", "DEPRECATION")
|
@Suppress("OverridingDeprecatedMember", "DEPRECATION")
|
||||||
override fun partyFromName(partyName: String): Party? = identityService.partyFromName(partyName)
|
override fun partyFromName(partyName: String): Party? = identityService.partyFromName(partyName)
|
||||||
override fun partyFromPrincipal(principal: X500Name): Party? = identityService.partyFromX500Name(principal)
|
override fun partyFromX500Name(name: X500Name): Party? = identityService.partyFromX500Name(name)
|
||||||
override fun partyFromKey(owningKey: PublicKey): Party? = identityService.partyFromKey(owningKey)
|
override fun partyFromKey(owningKey: PublicKey): Party? = identityService.partyFromKey(owningKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
class NoPartyObjectMapper(factory: JsonFactory) : PartyObjectMapper, ObjectMapper(factory) {
|
class NoPartyObjectMapper(factory: JsonFactory) : PartyObjectMapper, ObjectMapper(factory) {
|
||||||
@Suppress("OverridingDeprecatedMember", "DEPRECATION")
|
@Suppress("OverridingDeprecatedMember", "DEPRECATION")
|
||||||
override fun partyFromName(partyName: String): Party? = throw UnsupportedOperationException()
|
override fun partyFromName(partyName: String): Party? = throw UnsupportedOperationException()
|
||||||
override fun partyFromPrincipal(principal: X500Name): Party? = throw UnsupportedOperationException()
|
override fun partyFromX500Name(name: X500Name): Party? = throw UnsupportedOperationException()
|
||||||
override fun partyFromKey(owningKey: PublicKey): Party? = throw UnsupportedOperationException()
|
override fun partyFromKey(owningKey: PublicKey): Party? = throw UnsupportedOperationException()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,7 +169,7 @@ object JacksonSupport {
|
|||||||
// how to parse the content
|
// how to parse the content
|
||||||
return if (parser.text.contains("=")) {
|
return if (parser.text.contains("=")) {
|
||||||
val principal = X500Name(parser.text)
|
val principal = X500Name(parser.text)
|
||||||
mapper.partyFromPrincipal(principal) ?: throw JsonParseException(parser, "Could not find a Party with name ${principal}")
|
mapper.partyFromX500Name(principal) ?: throw JsonParseException(parser, "Could not find a Party with name ${principal}")
|
||||||
} else {
|
} else {
|
||||||
val key = try {
|
val key = try {
|
||||||
parsePublicKeyBase58(parser.text)
|
parsePublicKeyBase58(parser.text)
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package net.corda.core.crypto
|
package net.corda.core.crypto
|
||||||
|
|
||||||
import net.corda.core.crypto.Crypto.generateKeyPair
|
import net.corda.core.crypto.Crypto.generateKeyPair
|
||||||
|
import net.corda.core.identity.Party
|
||||||
|
import net.corda.core.node.ServiceHub
|
||||||
import org.bouncycastle.asn1.ASN1Encodable
|
import org.bouncycastle.asn1.ASN1Encodable
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
import org.bouncycastle.asn1.x500.X500NameBuilder
|
import org.bouncycastle.asn1.x500.X500NameBuilder
|
||||||
@ -24,6 +26,7 @@ import java.time.temporal.ChronoUnit
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
object X509Utilities {
|
object X509Utilities {
|
||||||
|
val DEFAULT_IDENTITY_SIGNATURE_SCHEME = Crypto.EDDSA_ED25519_SHA512
|
||||||
val DEFAULT_TLS_SIGNATURE_SCHEME = Crypto.ECDSA_SECP256R1_SHA256
|
val DEFAULT_TLS_SIGNATURE_SCHEME = Crypto.ECDSA_SECP256R1_SHA256
|
||||||
|
|
||||||
// Aliases for private keys and certificates.
|
// Aliases for private keys and certificates.
|
||||||
|
95
core/src/main/kotlin/net/corda/core/flows/TxKeyFlow.kt
Normal file
95
core/src/main/kotlin/net/corda/core/flows/TxKeyFlow.kt
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
package net.corda.core.flows
|
||||||
|
|
||||||
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
|
import net.corda.core.identity.AnonymousParty
|
||||||
|
import net.corda.core.identity.Party
|
||||||
|
import net.corda.core.utilities.ProgressTracker
|
||||||
|
import net.corda.core.utilities.unwrap
|
||||||
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
|
import java.security.cert.CertPath
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Very basic flow which exchanges transaction key and certificate paths between two parties in a transaction.
|
||||||
|
* This is intended for use as a subflow of another flow.
|
||||||
|
*/
|
||||||
|
object TxKeyFlow {
|
||||||
|
abstract class AbstractIdentityFlow(val otherSide: Party, val revocationEnabled: Boolean): FlowLogic<Map<Party, AnonymousIdentity>>() {
|
||||||
|
fun validateIdentity(untrustedIdentity: Pair<X509Certificate, CertPath>): AnonymousIdentity {
|
||||||
|
val (wellKnownCert, certPath) = untrustedIdentity
|
||||||
|
val theirCert = certPath.certificates.last()
|
||||||
|
// TODO: Don't trust self-signed certificates
|
||||||
|
return if (theirCert is X509Certificate) {
|
||||||
|
val certName = X500Name(theirCert.subjectDN.name)
|
||||||
|
if (certName == otherSide.name) {
|
||||||
|
val anonymousParty = AnonymousParty(theirCert.publicKey)
|
||||||
|
serviceHub.identityService.registerPath(wellKnownCert, anonymousParty, certPath)
|
||||||
|
AnonymousIdentity(certPath, theirCert, anonymousParty)
|
||||||
|
} else
|
||||||
|
throw IllegalStateException("Expected certificate subject to be ${otherSide.name} but found ${certName}")
|
||||||
|
} else
|
||||||
|
throw IllegalStateException("Expected an X.509 certificate but received ${theirCert.javaClass.name}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@StartableByRPC
|
||||||
|
@InitiatingFlow
|
||||||
|
class Requester(otherSide: Party,
|
||||||
|
revocationEnabled: Boolean,
|
||||||
|
override val progressTracker: ProgressTracker) : AbstractIdentityFlow(otherSide, revocationEnabled) {
|
||||||
|
constructor(otherSide: Party,
|
||||||
|
revocationEnabled: Boolean) : this(otherSide, revocationEnabled, tracker())
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
object AWAITING_KEY : ProgressTracker.Step("Awaiting key")
|
||||||
|
|
||||||
|
fun tracker() = ProgressTracker(AWAITING_KEY)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suspendable
|
||||||
|
override fun call(): Map<Party, AnonymousIdentity> {
|
||||||
|
progressTracker.currentStep = AWAITING_KEY
|
||||||
|
val myIdentityFragment = serviceHub.keyManagementService.freshKeyAndCert(serviceHub.myInfo.legalIdentity, revocationEnabled)
|
||||||
|
val theirIdentity = receive<Pair<X509Certificate, CertPath>>(otherSide).unwrap { validateIdentity(it) }
|
||||||
|
send(otherSide, myIdentityFragment)
|
||||||
|
return mapOf(Pair(otherSide, AnonymousIdentity(myIdentityFragment)),
|
||||||
|
Pair(serviceHub.myInfo.legalIdentity, theirIdentity))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flow which waits for a key request from a counterparty, generates a new key and then returns it to the
|
||||||
|
* counterparty and as the result from the flow.
|
||||||
|
*/
|
||||||
|
class Provider(otherSide: Party,
|
||||||
|
revocationEnabled: Boolean,
|
||||||
|
override val progressTracker: ProgressTracker) : AbstractIdentityFlow(otherSide,revocationEnabled) {
|
||||||
|
constructor(otherSide: Party,
|
||||||
|
revocationEnabled: Boolean = false) : this(otherSide, revocationEnabled, tracker())
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
object SENDING_KEY : ProgressTracker.Step("Sending key")
|
||||||
|
|
||||||
|
fun tracker() = ProgressTracker(SENDING_KEY)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suspendable
|
||||||
|
override fun call(): Map<Party, AnonymousIdentity> {
|
||||||
|
progressTracker.currentStep = SENDING_KEY
|
||||||
|
val myIdentityFragment = serviceHub.keyManagementService.freshKeyAndCert(serviceHub.myInfo.legalIdentity, revocationEnabled)
|
||||||
|
send(otherSide, myIdentityFragment)
|
||||||
|
val theirIdentity = receive<Pair<X509Certificate, CertPath>>(otherSide).unwrap { validateIdentity(it) }
|
||||||
|
return mapOf(Pair(otherSide, AnonymousIdentity(myIdentityFragment)),
|
||||||
|
Pair(serviceHub.myInfo.legalIdentity, theirIdentity))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class AnonymousIdentity(
|
||||||
|
val certPath: CertPath,
|
||||||
|
val certificate: X509Certificate,
|
||||||
|
val identity: AnonymousParty) {
|
||||||
|
constructor(myIdentity: Pair<X509Certificate, CertPath>) : this(myIdentity.second,
|
||||||
|
myIdentity.first,
|
||||||
|
AnonymousParty(myIdentity.second.certificates.last().publicKey))
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package net.corda.core.node.services
|
package net.corda.core.node.services
|
||||||
|
|
||||||
import net.corda.core.contracts.PartyAndReference
|
import net.corda.core.contracts.PartyAndReference
|
||||||
|
import net.corda.core.crypto.toStringShort
|
||||||
import net.corda.core.identity.AbstractParty
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.AnonymousParty
|
import net.corda.core.identity.AnonymousParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
@ -55,9 +56,31 @@ interface IdentityService {
|
|||||||
fun partyFromName(name: String): Party?
|
fun partyFromName(name: String): Party?
|
||||||
fun partyFromX500Name(principal: X500Name): Party?
|
fun partyFromX500Name(principal: X500Name): Party?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve the well known identity of a party. If the party passed in is already a well known identity
|
||||||
|
* (i.e. a [Party]) this returns it as-is.
|
||||||
|
*
|
||||||
|
* @return the well known identity, or null if unknown.
|
||||||
|
*/
|
||||||
fun partyFromAnonymous(party: AbstractParty): Party?
|
fun partyFromAnonymous(party: AbstractParty): Party?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve the well known identity of a party. If the party passed in is already a well known identity
|
||||||
|
* (i.e. a [Party]) this returns it as-is.
|
||||||
|
*
|
||||||
|
* @return the well known identity, or null if unknown.
|
||||||
|
*/
|
||||||
fun partyFromAnonymous(partyRef: PartyAndReference) = partyFromAnonymous(partyRef.party)
|
fun partyFromAnonymous(partyRef: PartyAndReference) = partyFromAnonymous(partyRef.party)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve the well known identity of a party. Throws an exception if the party cannot be identified.
|
||||||
|
* If the party passed in is already a well known identity (i.e. a [Party]) this returns it as-is.
|
||||||
|
*
|
||||||
|
* @return the well known identity.
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
*/
|
||||||
|
fun requirePartyFromAnonymous(party: AbstractParty): Party
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the certificate chain showing an anonymous party is owned by the given party.
|
* Get the certificate chain showing an anonymous party is owned by the given party.
|
||||||
*/
|
*/
|
||||||
|
@ -3,10 +3,10 @@ package net.corda.core.node.services
|
|||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import com.google.common.util.concurrent.ListenableFuture
|
import com.google.common.util.concurrent.ListenableFuture
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.DigitalSignature
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.crypto.SecureHash
|
|
||||||
import net.corda.core.flows.FlowException
|
import net.corda.core.flows.FlowException
|
||||||
import net.corda.core.identity.AbstractParty
|
import net.corda.core.identity.AbstractParty
|
||||||
|
import net.corda.core.identity.AnonymousParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.node.services.vault.PageSpecification
|
import net.corda.core.node.services.vault.PageSpecification
|
||||||
import net.corda.core.node.services.vault.QueryCriteria
|
import net.corda.core.node.services.vault.QueryCriteria
|
||||||
@ -20,6 +20,8 @@ import net.corda.core.transactions.WireTransaction
|
|||||||
import rx.Observable
|
import rx.Observable
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
|
import java.security.cert.CertPath
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@ -385,6 +387,17 @@ interface KeyManagementService {
|
|||||||
@Suspendable
|
@Suspendable
|
||||||
fun freshKey(): PublicKey
|
fun freshKey(): PublicKey
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a new random [KeyPair], adds it to the internal key storage, then generates a corresponding
|
||||||
|
* [X509Certificate] and adds it to the identity service.
|
||||||
|
*
|
||||||
|
* @param identity identity to generate a key and certificate for. Must be an identity this node has CA privileges for.
|
||||||
|
* @param revocationEnabled whether to check revocation status of certificates in the certificate path.
|
||||||
|
* @return X.509 certificate and path to the trust root.
|
||||||
|
*/
|
||||||
|
@Suspendable
|
||||||
|
fun freshKeyAndCert(identity: Party, revocationEnabled: Boolean): Pair<X509Certificate, CertPath>
|
||||||
|
|
||||||
/** Using the provided signing [PublicKey] internally looks up the matching [PrivateKey] and signs the data.
|
/** Using the provided signing [PublicKey] internally looks up the matching [PrivateKey] and signs the data.
|
||||||
* @param bytes The data to sign over using the chosen key.
|
* @param bytes The data to sign over using the chosen key.
|
||||||
* @param publicKey The [PublicKey] partner to an internally held [PrivateKey], either derived from the node's primary identity,
|
* @param publicKey The [PublicKey] partner to an internally held [PrivateKey], either derived from the node's primary identity,
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
package net.corda.flows
|
|
||||||
|
|
||||||
import co.paralleluniverse.fibers.Suspendable
|
|
||||||
import net.corda.core.flows.FlowLogic
|
|
||||||
import net.corda.core.identity.Party
|
|
||||||
import net.corda.core.serialization.CordaSerializable
|
|
||||||
import net.corda.core.utilities.unwrap
|
|
||||||
import java.security.PublicKey
|
|
||||||
import java.security.cert.Certificate
|
|
||||||
|
|
||||||
object TxKeyFlowUtilities {
|
|
||||||
/**
|
|
||||||
* Receive a key from a counterparty. This would normally be triggered by a flow as part of a transaction assembly
|
|
||||||
* process.
|
|
||||||
*/
|
|
||||||
@Suspendable
|
|
||||||
fun receiveKey(flow: FlowLogic<*>, otherSide: Party): Pair<PublicKey, Certificate?> {
|
|
||||||
val untrustedKey = flow.receive<ProvidedTransactionKey>(otherSide)
|
|
||||||
return untrustedKey.unwrap {
|
|
||||||
// TODO: Verify the certificate connects the given key to the counterparty, once we have certificates
|
|
||||||
Pair(it.key, it.certificate)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a new key and then returns it to the counterparty and as the result from the function. Note that this
|
|
||||||
* is an expensive operation, and should only be called once the calling flow has confirmed it wants to be part of
|
|
||||||
* a transaction with the counterparty, in order to avoid a DoS risk.
|
|
||||||
*/
|
|
||||||
@Suspendable
|
|
||||||
fun provideKey(flow: FlowLogic<*>, otherSide: Party): PublicKey {
|
|
||||||
val key = flow.serviceHub.keyManagementService.freshKey()
|
|
||||||
// TODO: Generate and sign certificate for the key, once we have signing support for composite keys
|
|
||||||
// (in this case the legal identity key)
|
|
||||||
flow.send(otherSide, ProvidedTransactionKey(key, null))
|
|
||||||
return key
|
|
||||||
}
|
|
||||||
|
|
||||||
@CordaSerializable
|
|
||||||
data class ProvidedTransactionKey(val key: PublicKey, val certificate: Certificate?)
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
package net.corda.core.flows
|
|
||||||
|
|
||||||
import co.paralleluniverse.fibers.Suspendable
|
|
||||||
import net.corda.core.identity.Party
|
|
||||||
import net.corda.core.utilities.ProgressTracker
|
|
||||||
import net.corda.flows.TxKeyFlowUtilities
|
|
||||||
import java.security.PublicKey
|
|
||||||
import java.security.cert.Certificate
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Very basic flow which requests a transaction key from a counterparty, used for testing [TxKeyFlowUtilities].
|
|
||||||
* This MUST not be provided on any real node, as the ability for arbitrary parties to request keys would enable
|
|
||||||
* DoS of the node, as key generation/storage is vastly more expensive than submitting a request.
|
|
||||||
*/
|
|
||||||
object TxKeyFlow {
|
|
||||||
|
|
||||||
@InitiatingFlow
|
|
||||||
class Requester(val otherSide: Party,
|
|
||||||
override val progressTracker: ProgressTracker) : FlowLogic<Pair<PublicKey, Certificate?>>() {
|
|
||||||
constructor(otherSide: Party) : this(otherSide, tracker())
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
object AWAITING_KEY : ProgressTracker.Step("Awaiting key")
|
|
||||||
|
|
||||||
fun tracker() = ProgressTracker(AWAITING_KEY)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suspendable
|
|
||||||
override fun call(): Pair<PublicKey, Certificate?> {
|
|
||||||
progressTracker.currentStep = AWAITING_KEY
|
|
||||||
return TxKeyFlowUtilities.receiveKey(this, otherSide)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flow which waits for a key request from a counterparty, generates a new key and then returns it to the
|
|
||||||
* counterparty and as the result from the flow.
|
|
||||||
*/
|
|
||||||
class Provider(val otherSide: Party,
|
|
||||||
override val progressTracker: ProgressTracker) : FlowLogic<PublicKey>() {
|
|
||||||
constructor(otherSide: Party) : this(otherSide, tracker())
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
object SENDING_KEY : ProgressTracker.Step("Sending key")
|
|
||||||
|
|
||||||
fun tracker() = ProgressTracker(SENDING_KEY)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suspendable
|
|
||||||
override fun call(): PublicKey {
|
|
||||||
progressTracker.currentStep = SENDING_KEY
|
|
||||||
return TxKeyFlowUtilities.provideKey(this, otherSide)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
53
core/src/test/kotlin/net/corda/core/flows/TxKeyFlowTests.kt
Normal file
53
core/src/test/kotlin/net/corda/core/flows/TxKeyFlowTests.kt
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package net.corda.core.flows
|
||||||
|
|
||||||
|
import net.corda.core.getOrThrow
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
|
import net.corda.core.identity.Party
|
||||||
|
import net.corda.core.utilities.ALICE
|
||||||
|
import net.corda.core.utilities.BOB
|
||||||
|
import net.corda.core.utilities.DUMMY_NOTARY
|
||||||
|
import net.corda.testing.node.MockNetwork
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertNotEquals
|
||||||
|
|
||||||
|
class TxKeyFlowTests {
|
||||||
|
lateinit var net: MockNetwork
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun before() {
|
||||||
|
net = MockNetwork(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `issue key`() {
|
||||||
|
// We run this in parallel threads to help catch any race conditions that may exist.
|
||||||
|
net = MockNetwork(false, true)
|
||||||
|
|
||||||
|
// Set up values we'll need
|
||||||
|
val revocationEnabled = false
|
||||||
|
val notaryNode = net.createNotaryNode(null, DUMMY_NOTARY.name)
|
||||||
|
val aliceNode = net.createPartyNode(notaryNode.info.address, ALICE.name)
|
||||||
|
val bobNode = net.createPartyNode(notaryNode.info.address, BOB.name)
|
||||||
|
val alice: Party = aliceNode.services.myInfo.legalIdentity
|
||||||
|
val bob: Party = bobNode.services.myInfo.legalIdentity
|
||||||
|
aliceNode.services.identityService.registerIdentity(bob)
|
||||||
|
aliceNode.services.identityService.registerIdentity(notaryNode.info.legalIdentity)
|
||||||
|
bobNode.services.identityService.registerIdentity(alice)
|
||||||
|
bobNode.services.identityService.registerIdentity(notaryNode.info.legalIdentity)
|
||||||
|
|
||||||
|
// Run the flows
|
||||||
|
bobNode.registerServiceFlow(TxKeyFlow.Requester::class) { TxKeyFlow.Provider(it) }
|
||||||
|
val requesterFlow = aliceNode.services.startFlow(TxKeyFlow.Requester(bob, revocationEnabled))
|
||||||
|
|
||||||
|
// Get the results
|
||||||
|
val actual: Map<Party, TxKeyFlow.AnonymousIdentity> = requesterFlow.resultFuture.getOrThrow()
|
||||||
|
assertEquals(2, actual.size)
|
||||||
|
// Verify that the generated anonymous identities do not match the well known identities
|
||||||
|
val aliceAnonymousIdentity = actual[alice] ?: throw IllegalStateException()
|
||||||
|
val bobAnonymousIdentity = actual[bob] ?: throw IllegalStateException()
|
||||||
|
assertNotEquals<AbstractParty>(alice, aliceAnonymousIdentity.identity)
|
||||||
|
assertNotEquals<AbstractParty>(bob, bobAnonymousIdentity.identity)
|
||||||
|
}
|
||||||
|
}
|
@ -1,40 +0,0 @@
|
|||||||
package net.corda.core.flows
|
|
||||||
|
|
||||||
import net.corda.core.identity.Party
|
|
||||||
import net.corda.core.utilities.ALICE
|
|
||||||
import net.corda.core.utilities.BOB
|
|
||||||
import net.corda.core.utilities.DUMMY_NOTARY
|
|
||||||
import net.corda.testing.node.MockNetwork
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Test
|
|
||||||
import java.security.PublicKey
|
|
||||||
import kotlin.test.assertNotNull
|
|
||||||
|
|
||||||
class TxKeyFlowUtilitiesTests {
|
|
||||||
lateinit var net: MockNetwork
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun before() {
|
|
||||||
net = MockNetwork(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `issue key`() {
|
|
||||||
// We run this in parallel threads to help catch any race conditions that may exist.
|
|
||||||
net = MockNetwork(false, true)
|
|
||||||
|
|
||||||
// Set up values we'll need
|
|
||||||
val notaryNode = net.createNotaryNode(null, DUMMY_NOTARY.name)
|
|
||||||
val aliceNode = net.createPartyNode(notaryNode.info.address, ALICE.name)
|
|
||||||
val bobNode = net.createPartyNode(notaryNode.info.address, BOB.name)
|
|
||||||
val bobKey: Party = bobNode.services.myInfo.legalIdentity
|
|
||||||
|
|
||||||
// Run the flows
|
|
||||||
bobNode.registerServiceFlow(TxKeyFlow.Requester::class) { TxKeyFlow.Provider(it) }
|
|
||||||
val requesterFlow = aliceNode.services.startFlow(TxKeyFlow.Requester(bobKey))
|
|
||||||
|
|
||||||
// Get the results
|
|
||||||
val actual: PublicKey = requesterFlow.resultFuture.get().first
|
|
||||||
assertNotNull(actual)
|
|
||||||
}
|
|
||||||
}
|
|
@ -58,7 +58,7 @@ class CashTests {
|
|||||||
database = dataSourceAndDatabase.second
|
database = dataSourceAndDatabase.second
|
||||||
database.transaction {
|
database.transaction {
|
||||||
services = object : MockServices() {
|
services = object : MockServices() {
|
||||||
override val keyManagementService: MockKeyManagementService = MockKeyManagementService(MINI_CORP_KEY, MEGA_CORP_KEY, OUR_KEY)
|
override val keyManagementService: MockKeyManagementService = MockKeyManagementService(identityService, MINI_CORP_KEY, MEGA_CORP_KEY, OUR_KEY)
|
||||||
override val vaultService: VaultService = makeVaultService(dataSourceProps)
|
override val vaultService: VaultService = makeVaultService(dataSourceProps)
|
||||||
|
|
||||||
override fun recordTransactions(txs: Iterable<SignedTransaction>) {
|
override fun recordTransactions(txs: Iterable<SignedTransaction>) {
|
||||||
|
@ -306,7 +306,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
|||||||
// Place the long term identity key in the KMS. Eventually, this is likely going to be separated again because
|
// Place the long term identity key in the KMS. Eventually, this is likely going to be separated again because
|
||||||
// the KMS is meant for derived temporary keys used in transactions, and we're not supposed to sign things with
|
// the KMS is meant for derived temporary keys used in transactions, and we're not supposed to sign things with
|
||||||
// the identity key. But the infrastructure to make that easy isn't here yet.
|
// the identity key. But the infrastructure to make that easy isn't here yet.
|
||||||
keyManagement = makeKeyManagementService()
|
keyManagement = makeKeyManagementService(identity)
|
||||||
scheduler = NodeSchedulerService(services, database, unfinishedSchedules = busyNodeLatch)
|
scheduler = NodeSchedulerService(services, database, unfinishedSchedules = busyNodeLatch)
|
||||||
|
|
||||||
val tokenizableServices = mutableListOf(storage, net, vault, keyManagement, identity, platformClock, scheduler)
|
val tokenizableServices = mutableListOf(storage, net, vault, keyManagement, identity, platformClock, scheduler)
|
||||||
@ -501,7 +501,9 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
|||||||
"has any other map node been configured.")
|
"has any other map node been configured.")
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun makeKeyManagementService(): KeyManagementService = PersistentKeyManagementService(partyKeys)
|
protected open fun makeKeyManagementService(identityService: IdentityService): KeyManagementService {
|
||||||
|
return PersistentKeyManagementService(identityService, partyKeys)
|
||||||
|
}
|
||||||
|
|
||||||
open protected fun makeNetworkMapService() {
|
open protected fun makeNetworkMapService() {
|
||||||
inNodeNetworkMapService = PersistentNetworkMapService(services, configuration.minimumPlatformVersion)
|
inNodeNetworkMapService = PersistentNetworkMapService(services, configuration.minimumPlatformVersion)
|
||||||
|
@ -54,8 +54,17 @@ class InMemoryIdentityService(identities: Iterable<Party> = emptySet(),
|
|||||||
@Deprecated("Use partyFromX500Name")
|
@Deprecated("Use partyFromX500Name")
|
||||||
override fun partyFromName(name: String): Party? = principalToParties[X500Name(name)]
|
override fun partyFromName(name: String): Party? = principalToParties[X500Name(name)]
|
||||||
override fun partyFromX500Name(principal: X500Name): Party? = principalToParties[principal]
|
override fun partyFromX500Name(principal: X500Name): Party? = principalToParties[principal]
|
||||||
override fun partyFromAnonymous(party: AbstractParty): Party? = partyFromKey(party.owningKey)
|
override fun partyFromAnonymous(party: AbstractParty): Party? {
|
||||||
|
return if (party is Party) {
|
||||||
|
party
|
||||||
|
} else {
|
||||||
|
partyFromKey(party.owningKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
override fun partyFromAnonymous(partyRef: PartyAndReference) = partyFromAnonymous(partyRef.party)
|
override fun partyFromAnonymous(partyRef: PartyAndReference) = partyFromAnonymous(partyRef.party)
|
||||||
|
override fun requirePartyFromAnonymous(party: AbstractParty): Party {
|
||||||
|
return partyFromAnonymous(party) ?: throw IllegalStateException("Could not deanonymise party ${party.owningKey.toStringShort()}")
|
||||||
|
}
|
||||||
|
|
||||||
@Throws(IdentityService.UnknownAnonymousPartyException::class)
|
@Throws(IdentityService.UnknownAnonymousPartyException::class)
|
||||||
override fun assertOwnership(party: Party, anonymousParty: AnonymousParty) {
|
override fun assertOwnership(party: Party, anonymousParty: AnonymousParty) {
|
||||||
|
@ -5,11 +5,15 @@ import net.corda.core.crypto.DigitalSignature
|
|||||||
import net.corda.core.crypto.generateKeyPair
|
import net.corda.core.crypto.generateKeyPair
|
||||||
import net.corda.core.crypto.keys
|
import net.corda.core.crypto.keys
|
||||||
import net.corda.core.crypto.sign
|
import net.corda.core.crypto.sign
|
||||||
|
import net.corda.core.identity.Party
|
||||||
|
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.SingletonSerializeAsToken
|
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.PrivateKey
|
import java.security.PrivateKey
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
|
import java.security.cert.CertPath
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.annotation.concurrent.ThreadSafe
|
import javax.annotation.concurrent.ThreadSafe
|
||||||
|
|
||||||
@ -25,7 +29,8 @@ import javax.annotation.concurrent.ThreadSafe
|
|||||||
* etc.
|
* etc.
|
||||||
*/
|
*/
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
class E2ETestKeyManagementService(initialKeys: Set<KeyPair>) : SingletonSerializeAsToken(), KeyManagementService {
|
class E2ETestKeyManagementService(val identityService: IdentityService,
|
||||||
|
initialKeys: Set<KeyPair>) : SingletonSerializeAsToken(), KeyManagementService {
|
||||||
private class InnerState {
|
private class InnerState {
|
||||||
val keys = HashMap<PublicKey, PrivateKey>()
|
val keys = HashMap<PublicKey, PrivateKey>()
|
||||||
}
|
}
|
||||||
@ -51,6 +56,8 @@ class E2ETestKeyManagementService(initialKeys: Set<KeyPair>) : SingletonSerializ
|
|||||||
return keyPair.public
|
return keyPair.public
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun freshKeyAndCert(identity: Party, revocationEnabled: Boolean): Pair<X509Certificate, CertPath> = freshKeyAndCert(this, identityService, identity, revocationEnabled)
|
||||||
|
|
||||||
private fun getSigningKeyPair(publicKey: PublicKey): KeyPair {
|
private fun getSigningKeyPair(publicKey: PublicKey): KeyPair {
|
||||||
return mutex.locked {
|
return mutex.locked {
|
||||||
val pk = publicKey.keys.first { keys.containsKey(it) }
|
val pk = publicKey.keys.first { keys.containsKey(it) }
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
package net.corda.node.services.keys
|
||||||
|
|
||||||
|
import net.corda.core.crypto.CertificateType
|
||||||
|
import net.corda.core.crypto.Crypto
|
||||||
|
import net.corda.core.crypto.X509Utilities
|
||||||
|
import net.corda.core.identity.AnonymousParty
|
||||||
|
import net.corda.core.identity.Party
|
||||||
|
import net.corda.core.node.services.IdentityService
|
||||||
|
import net.corda.core.node.services.KeyManagementService
|
||||||
|
import java.security.cert.CertPath
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a new random [KeyPair], adds it to the internal key storage, then generates a corresponding
|
||||||
|
* [X509Certificate] and adds it to the identity service.
|
||||||
|
*
|
||||||
|
* @param keyManagementService key service to use when generating the new key.
|
||||||
|
* @param identityService identity service to use when registering the certificate.
|
||||||
|
* @param identity identity to generate a key and certificate for. Must be an identity this node has CA privileges for.
|
||||||
|
* @param revocationEnabled whether to check revocation status of certificates in the certificate path.
|
||||||
|
* @return X.509 certificate and path to the trust root.
|
||||||
|
*/
|
||||||
|
fun freshKeyAndCert(keyManagementService: KeyManagementService,
|
||||||
|
identityService: IdentityService,
|
||||||
|
identity: Party,
|
||||||
|
revocationEnabled: Boolean = false): Pair<X509Certificate, CertPath> {
|
||||||
|
val ourPublicKey = keyManagementService.freshKey()
|
||||||
|
// FIXME: Use the actual certificate for the identity the flow is presenting themselves as
|
||||||
|
val issuerKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_IDENTITY_SIGNATURE_SCHEME)
|
||||||
|
val issuerCertificate = X509Utilities.createSelfSignedCACertificate(identity.name, issuerKey)
|
||||||
|
val ourCertificate = X509Utilities.createCertificate(CertificateType.IDENTITY, issuerCertificate, issuerKey, identity.name, ourPublicKey)
|
||||||
|
val ourCertPath = X509Utilities.createCertificatePath(issuerCertificate, ourCertificate, revocationEnabled = revocationEnabled)
|
||||||
|
identityService.registerPath(issuerCertificate,
|
||||||
|
AnonymousParty(ourPublicKey),
|
||||||
|
ourCertPath)
|
||||||
|
return Pair(issuerCertificate, ourCertPath)
|
||||||
|
}
|
@ -5,6 +5,8 @@ import net.corda.core.crypto.DigitalSignature
|
|||||||
import net.corda.core.crypto.generateKeyPair
|
import net.corda.core.crypto.generateKeyPair
|
||||||
import net.corda.core.crypto.keys
|
import net.corda.core.crypto.keys
|
||||||
import net.corda.core.crypto.sign
|
import net.corda.core.crypto.sign
|
||||||
|
import net.corda.core.identity.Party
|
||||||
|
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.SingletonSerializeAsToken
|
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||||
import net.corda.node.utilities.*
|
import net.corda.node.utilities.*
|
||||||
@ -13,6 +15,8 @@ import org.jetbrains.exposed.sql.statements.InsertStatement
|
|||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.PrivateKey
|
import java.security.PrivateKey
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
|
import java.security.cert.CertPath
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A persistent re-implementation of [E2ETestKeyManagementService] to support node re-start.
|
* A persistent re-implementation of [E2ETestKeyManagementService] to support node re-start.
|
||||||
@ -21,7 +25,8 @@ import java.security.PublicKey
|
|||||||
*
|
*
|
||||||
* This class needs database transactions to be in-flight during method calls and init.
|
* This class needs database transactions to be in-flight during method calls and init.
|
||||||
*/
|
*/
|
||||||
class PersistentKeyManagementService(initialKeys: Set<KeyPair>) : SingletonSerializeAsToken(), KeyManagementService {
|
class PersistentKeyManagementService(val identityService: IdentityService,
|
||||||
|
initialKeys: Set<KeyPair>) : SingletonSerializeAsToken(), KeyManagementService {
|
||||||
|
|
||||||
private object Table : JDBCHashedTable("${NODE_DATABASE_PREFIX}our_key_pairs") {
|
private object Table : JDBCHashedTable("${NODE_DATABASE_PREFIX}our_key_pairs") {
|
||||||
val publicKey = publicKey("public_key")
|
val publicKey = publicKey("public_key")
|
||||||
@ -62,6 +67,8 @@ class PersistentKeyManagementService(initialKeys: Set<KeyPair>) : SingletonSeria
|
|||||||
return keyPair.public
|
return keyPair.public
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun freshKeyAndCert(identity: Party, revocationEnabled: Boolean): Pair<X509Certificate, CertPath> = freshKeyAndCert(this, identityService, identity, revocationEnabled)
|
||||||
|
|
||||||
private fun getSigningKeyPair(publicKey: PublicKey): KeyPair {
|
private fun getSigningKeyPair(publicKey: PublicKey): KeyPair {
|
||||||
return mutex.locked {
|
return mutex.locked {
|
||||||
val pk = publicKey.keys.first { keys.containsKey(it) }
|
val pk = publicKey.keys.first { keys.containsKey(it) }
|
||||||
|
@ -348,7 +348,7 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
|
|||||||
val serviceFlowInfo = serviceHub.getServiceFlowFactory(sessionInit.clientFlowClass)
|
val serviceFlowInfo = serviceHub.getServiceFlowFactory(sessionInit.clientFlowClass)
|
||||||
if (serviceFlowInfo == null) {
|
if (serviceFlowInfo == null) {
|
||||||
logger.warn("${sessionInit.clientFlowClass} has not been registered with a service flow: $sessionInit")
|
logger.warn("${sessionInit.clientFlowClass} has not been registered with a service flow: $sessionInit")
|
||||||
sendSessionReject("Don't know ${sessionInit.clientFlowClass.name}")
|
sendSessionReject("${sessionInit.clientFlowClass.name} has not been registered with a service flow")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ import net.corda.core.serialization.SingletonSerializeAsToken
|
|||||||
import net.corda.core.utilities.ALICE_KEY
|
import net.corda.core.utilities.ALICE_KEY
|
||||||
import net.corda.core.utilities.DUMMY_NOTARY
|
import net.corda.core.utilities.DUMMY_NOTARY
|
||||||
import net.corda.node.services.MockServiceHubInternal
|
import net.corda.node.services.MockServiceHubInternal
|
||||||
|
import net.corda.node.services.identity.InMemoryIdentityService
|
||||||
import net.corda.node.services.persistence.DBCheckpointStorage
|
import net.corda.node.services.persistence.DBCheckpointStorage
|
||||||
import net.corda.node.services.statemachine.FlowLogicRefFactoryImpl
|
import net.corda.node.services.statemachine.FlowLogicRefFactoryImpl
|
||||||
import net.corda.node.services.statemachine.StateMachineManager
|
import net.corda.node.services.statemachine.StateMachineManager
|
||||||
@ -75,9 +76,10 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
|
|||||||
val dataSourceAndDatabase = configureDatabase(dataSourceProps)
|
val dataSourceAndDatabase = configureDatabase(dataSourceProps)
|
||||||
dataSource = dataSourceAndDatabase.first
|
dataSource = dataSourceAndDatabase.first
|
||||||
database = dataSourceAndDatabase.second
|
database = dataSourceAndDatabase.second
|
||||||
|
val identityService = InMemoryIdentityService()
|
||||||
|
val kms = MockKeyManagementService(identityService, ALICE_KEY)
|
||||||
|
|
||||||
database.transaction {
|
database.transaction {
|
||||||
val kms = MockKeyManagementService(ALICE_KEY)
|
|
||||||
val nullIdentity = X500Name("cn=None")
|
val nullIdentity = X500Name("cn=None")
|
||||||
val mockMessagingService = InMemoryMessagingNetwork(false).InMemoryMessaging(
|
val mockMessagingService = InMemoryMessagingNetwork(false).InMemoryMessaging(
|
||||||
false,
|
false,
|
||||||
|
@ -115,4 +115,14 @@ class InMemoryIdentityServiceTests {
|
|||||||
service.assertOwnership(bob, anonymousAlice)
|
service.assertOwnership(bob, anonymousAlice)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure if we feed in a full identity, we get the same identity back.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun `deanonymising a well known identity`() {
|
||||||
|
val expected = ALICE
|
||||||
|
val actual = InMemoryIdentityService().partyFromAnonymous(expected)
|
||||||
|
assertEquals(expected, actual)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,8 +171,8 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
|
|||||||
|
|
||||||
override fun makeVaultService(dataSourceProperties: Properties): VaultService = NodeVaultService(services, dataSourceProperties)
|
override fun makeVaultService(dataSourceProperties: Properties): VaultService = NodeVaultService(services, dataSourceProperties)
|
||||||
|
|
||||||
override fun makeKeyManagementService(): KeyManagementService {
|
override fun makeKeyManagementService(identityService: IdentityService): KeyManagementService {
|
||||||
return E2ETestKeyManagementService(partyKeys + (overrideServices?.values ?: emptySet()))
|
return E2ETestKeyManagementService(identityService, partyKeys + (overrideServices?.values ?: emptySet()))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun startMessagingService(rpcOps: RPCOps) {
|
override fun startMessagingService(rpcOps: RPCOps) {
|
||||||
@ -370,6 +370,11 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
|
|||||||
repeat(numPartyNodes) {
|
repeat(numPartyNodes) {
|
||||||
nodes += createPartyNode(mapNode.info.address)
|
nodes += createPartyNode(mapNode.info.address)
|
||||||
}
|
}
|
||||||
|
nodes.forEach { node ->
|
||||||
|
nodes.map { it.info.legalIdentity }.forEach { identity ->
|
||||||
|
node.services.identityService.registerIdentity(identity)
|
||||||
|
}
|
||||||
|
}
|
||||||
return BasketOfNodes(nodes, notaryNode, mapNode)
|
return BasketOfNodes(nodes, notaryNode, mapNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
package net.corda.testing.node
|
package net.corda.testing.node
|
||||||
|
|
||||||
import net.corda.core.contracts.Attachment
|
import net.corda.core.contracts.Attachment
|
||||||
import net.corda.core.contracts.PartyAndReference
|
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.flows.StateMachineRunId
|
import net.corda.core.flows.StateMachineRunId
|
||||||
import net.corda.core.identity.AbstractParty
|
|
||||||
import net.corda.core.identity.AnonymousParty
|
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.messaging.SingleMessageRecipient
|
import net.corda.core.messaging.SingleMessageRecipient
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
@ -15,6 +12,7 @@ import net.corda.core.serialization.SingletonSerializeAsToken
|
|||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.utilities.DUMMY_NOTARY
|
import net.corda.core.utilities.DUMMY_NOTARY
|
||||||
import net.corda.node.services.identity.InMemoryIdentityService
|
import net.corda.node.services.identity.InMemoryIdentityService
|
||||||
|
import net.corda.node.services.keys.freshKeyAndCert
|
||||||
import net.corda.node.services.persistence.InMemoryStateMachineRecordedTransactionMappingStorage
|
import net.corda.node.services.persistence.InMemoryStateMachineRecordedTransactionMappingStorage
|
||||||
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
|
||||||
@ -23,7 +21,6 @@ import net.corda.node.services.vault.NodeVaultService
|
|||||||
import net.corda.testing.MEGA_CORP
|
import net.corda.testing.MEGA_CORP
|
||||||
import net.corda.testing.MINI_CORP
|
import net.corda.testing.MINI_CORP
|
||||||
import net.corda.testing.MOCK_VERSION_INFO
|
import net.corda.testing.MOCK_VERSION_INFO
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import rx.subjects.PublishSubject
|
import rx.subjects.PublishSubject
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
@ -38,7 +35,6 @@ import java.security.cert.CertPath
|
|||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
import java.time.Clock
|
import java.time.Clock
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
|
||||||
import java.util.jar.JarInputStream
|
import java.util.jar.JarInputStream
|
||||||
import javax.annotation.concurrent.ThreadSafe
|
import javax.annotation.concurrent.ThreadSafe
|
||||||
|
|
||||||
@ -64,8 +60,8 @@ open class MockServices(vararg val keys: KeyPair) : ServiceHub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override val storageService: TxWritableStorageService = MockStorageService()
|
override val storageService: TxWritableStorageService = MockStorageService()
|
||||||
override val identityService: IdentityService = InMemoryIdentityService(listOf(MEGA_CORP, MINI_CORP, DUMMY_NOTARY))
|
override final val identityService: IdentityService = InMemoryIdentityService(listOf(MEGA_CORP, MINI_CORP, DUMMY_NOTARY))
|
||||||
override val keyManagementService: KeyManagementService = MockKeyManagementService(*keys)
|
override val keyManagementService: KeyManagementService = MockKeyManagementService(identityService, *keys)
|
||||||
|
|
||||||
override val vaultService: VaultService get() = throw UnsupportedOperationException()
|
override val vaultService: VaultService get() = throw UnsupportedOperationException()
|
||||||
override val networkMapCache: NetworkMapCache get() = throw UnsupportedOperationException()
|
override val networkMapCache: NetworkMapCache get() = throw UnsupportedOperationException()
|
||||||
@ -81,7 +77,8 @@ open class MockServices(vararg val keys: KeyPair) : ServiceHub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockKeyManagementService(vararg initialKeys: KeyPair) : SingletonSerializeAsToken(), KeyManagementService {
|
class MockKeyManagementService(val identityService: IdentityService,
|
||||||
|
vararg initialKeys: KeyPair) : SingletonSerializeAsToken(), KeyManagementService {
|
||||||
private val keyStore: MutableMap<PublicKey, PrivateKey> = initialKeys.associateByTo(HashMap(), { it.public }, { it.private })
|
private val keyStore: MutableMap<PublicKey, PrivateKey> = initialKeys.associateByTo(HashMap(), { it.public }, { it.private })
|
||||||
|
|
||||||
override val keys: Set<PublicKey> get() = keyStore.keys
|
override val keys: Set<PublicKey> get() = keyStore.keys
|
||||||
@ -94,6 +91,8 @@ class MockKeyManagementService(vararg initialKeys: KeyPair) : SingletonSerialize
|
|||||||
return k.public
|
return k.public
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun freshKeyAndCert(identity: Party, revocationEnabled: Boolean): Pair<X509Certificate, CertPath> = freshKeyAndCert(this, identityService, identity, revocationEnabled)
|
||||||
|
|
||||||
private fun getSigningKeyPair(publicKey: PublicKey): KeyPair {
|
private fun getSigningKeyPair(publicKey: PublicKey): KeyPair {
|
||||||
val pk = publicKey.keys.first { keyStore.containsKey(it) }
|
val pk = publicKey.keys.first { keyStore.containsKey(it) }
|
||||||
return KeyPair(pk, keyStore[pk]!!)
|
return KeyPair(pk, keyStore[pk]!!)
|
||||||
|
@ -6,10 +6,12 @@ import com.google.common.util.concurrent.SettableFuture
|
|||||||
import net.corda.core.crypto.commonName
|
import net.corda.core.crypto.commonName
|
||||||
import net.corda.core.crypto.generateKeyPair
|
import net.corda.core.crypto.generateKeyPair
|
||||||
import net.corda.core.messaging.RPCOps
|
import net.corda.core.messaging.RPCOps
|
||||||
|
import net.corda.core.node.services.IdentityService
|
||||||
import net.corda.core.node.services.KeyManagementService
|
import net.corda.core.node.services.KeyManagementService
|
||||||
import net.corda.node.services.RPCUserServiceImpl
|
import net.corda.node.services.RPCUserServiceImpl
|
||||||
import net.corda.node.services.api.MonitoringService
|
import net.corda.node.services.api.MonitoringService
|
||||||
import net.corda.node.services.config.NodeConfiguration
|
import net.corda.node.services.config.NodeConfiguration
|
||||||
|
import net.corda.node.services.identity.InMemoryIdentityService
|
||||||
import net.corda.node.services.keys.E2ETestKeyManagementService
|
import net.corda.node.services.keys.E2ETestKeyManagementService
|
||||||
import net.corda.node.services.messaging.ArtemisMessagingServer
|
import net.corda.node.services.messaging.ArtemisMessagingServer
|
||||||
import net.corda.node.services.messaging.NodeMessagingClient
|
import net.corda.node.services.messaging.NodeMessagingClient
|
||||||
@ -26,7 +28,7 @@ import kotlin.concurrent.thread
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a bare-bones node which can only send and receive messages. It doesn't register with a network map service or
|
* This is a bare-bones node which can only send and receive messages. It doesn't register with a network map service or
|
||||||
* any other such task that would make it functionable in a network and thus left to the user to do so manually.
|
* any other such task that would make it functional in a network and thus left to the user to do so manually.
|
||||||
*/
|
*/
|
||||||
class SimpleNode(val config: NodeConfiguration, val address: HostAndPort = freeLocalHostAndPort(), rpcAddress: HostAndPort = freeLocalHostAndPort()) : AutoCloseable {
|
class SimpleNode(val config: NodeConfiguration, val address: HostAndPort = freeLocalHostAndPort(), rpcAddress: HostAndPort = freeLocalHostAndPort()) : AutoCloseable {
|
||||||
|
|
||||||
@ -35,7 +37,8 @@ class SimpleNode(val config: NodeConfiguration, val address: HostAndPort = freeL
|
|||||||
val userService = RPCUserServiceImpl(config.rpcUsers)
|
val userService = RPCUserServiceImpl(config.rpcUsers)
|
||||||
val monitoringService = MonitoringService(MetricRegistry())
|
val monitoringService = MonitoringService(MetricRegistry())
|
||||||
val identity: KeyPair = generateKeyPair()
|
val identity: KeyPair = generateKeyPair()
|
||||||
val keyService: KeyManagementService = E2ETestKeyManagementService(setOf(identity))
|
val identityService: IdentityService = InMemoryIdentityService()
|
||||||
|
val keyService: KeyManagementService = E2ETestKeyManagementService(identityService, setOf(identity))
|
||||||
val executor = ServiceAffinityExecutor(config.myLegalName.commonName, 1)
|
val executor = ServiceAffinityExecutor(config.myLegalName.commonName, 1)
|
||||||
val broker = ArtemisMessagingServer(config, address, rpcAddress, InMemoryNetworkMapCache(), userService)
|
val broker = ArtemisMessagingServer(config, address, rpcAddress, InMemoryNetworkMapCache(), userService)
|
||||||
val networkMapRegistrationFuture: SettableFuture<Unit> = SettableFuture.create<Unit>()
|
val networkMapRegistrationFuture: SettableFuture<Unit> = SettableFuture.create<Unit>()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user