Store certificate and path with well known identity (#726)

* Construct standard flows using PartyAndCertificate, and add support for launching
flows that are constructed with PartyAndCertificate or just Party.
* Store PartyAndCertificate in network map service
* Expand identity service to store certificates along with all identities.
This commit is contained in:
Ross Nicoll 2017-05-31 14:45:58 +01:00 committed by GitHub
parent 08c91bd611
commit 34eb5a3b70
66 changed files with 379 additions and 249 deletions

View File

@ -3,45 +3,34 @@ 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.identity.PartyAndCertificate
import net.corda.core.serialization.CordaSerializable
import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.unwrap
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.cert.X509CertificateHolder
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<out T>(val otherSide: Party): FlowLogic<T>() {
fun validateIdentity(untrustedIdentity: Pair<X509CertificateHolder, 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, X509CertificateHolder(theirCert.encoded), 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}")
}
abstract class AbstractIdentityFlow<out T>(val otherSide: PartyAndCertificate, val revocationEnabled: Boolean): FlowLogic<T>() {
fun validateIdentity(untrustedIdentity: AnonymousIdentity): AnonymousIdentity {
val (certPath, theirCert, txIdentity) = untrustedIdentity
if (theirCert.subject == otherSide.name) {
serviceHub.identityService.registerAnonymousIdentity(txIdentity, otherSide, certPath)
return AnonymousIdentity(certPath, theirCert, txIdentity)
} else
throw IllegalStateException("Expected certificate subject to be ${otherSide.name} but found ${theirCert.subject}")
}
}
@StartableByRPC
@InitiatingFlow
class Requester(otherSide: Party,
val revocationEnabled: Boolean,
override val progressTracker: ProgressTracker) : AbstractIdentityFlow<Map<Party, AnonymousIdentity>>(otherSide) {
constructor(otherSide: Party, revocationEnabled: Boolean) : this(otherSide, revocationEnabled, tracker())
class Requester(otherSide: PartyAndCertificate,
override val progressTracker: ProgressTracker) : AbstractIdentityFlow<Map<Party, AnonymousIdentity>>(otherSide, false) {
constructor(otherSide: PartyAndCertificate) : this(otherSide, tracker())
companion object {
object AWAITING_KEY : ProgressTracker.Step("Awaiting key")
@ -52,9 +41,10 @@ object TxKeyFlow {
override fun call(): Map<Party, AnonymousIdentity> {
progressTracker.currentStep = AWAITING_KEY
val myIdentityFragment = serviceHub.keyManagementService.freshKeyAndCert(serviceHub.myInfo.legalIdentity, revocationEnabled)
val theirIdentity = receive<Pair<X509CertificateHolder, CertPath>>(otherSide).unwrap { validateIdentity(it) }
send(otherSide, myIdentityFragment)
return mapOf(Pair(otherSide, AnonymousIdentity(myIdentityFragment)),
val myIdentity = AnonymousIdentity(myIdentityFragment)
val theirIdentity = receive<AnonymousIdentity>(otherSide).unwrap { validateIdentity(it) }
send(otherSide, myIdentity)
return mapOf(Pair(otherSide, myIdentity),
Pair(serviceHub.myInfo.legalIdentity, theirIdentity))
}
}
@ -64,7 +54,7 @@ object TxKeyFlow {
* counterparty and as the result from the flow.
*/
@InitiatedBy(Requester::class)
class Provider(otherSide: Party) : AbstractIdentityFlow<Unit>(otherSide) {
class Provider(otherSide: PartyAndCertificate) : AbstractIdentityFlow<Map<Party, AnonymousIdentity>>(otherSide, false) {
companion object {
object SENDING_KEY : ProgressTracker.Step("Sending key")
}
@ -72,15 +62,19 @@ object TxKeyFlow {
override val progressTracker: ProgressTracker = ProgressTracker(SENDING_KEY)
@Suspendable
override fun call() {
override fun call(): Map<Party, AnonymousIdentity> {
val revocationEnabled = false
progressTracker.currentStep = SENDING_KEY
val myIdentityFragment = serviceHub.keyManagementService.freshKeyAndCert(serviceHub.myInfo.legalIdentity, revocationEnabled)
send(otherSide, myIdentityFragment)
receive<Pair<X509CertificateHolder, CertPath>>(otherSide).unwrap { validateIdentity(it) }
val myIdentity = AnonymousIdentity(myIdentityFragment)
send(otherSide, myIdentity)
val theirIdentity = receive<AnonymousIdentity>(otherSide).unwrap { validateIdentity(it) }
return mapOf(Pair(otherSide, myIdentity),
Pair(serviceHub.myInfo.legalIdentity, theirIdentity))
}
}
@CordaSerializable
data class AnonymousIdentity(
val certPath: CertPath,
val certificate: X509CertificateHolder,

View File

@ -1,12 +1,9 @@
package net.corda.core.identity
import net.corda.core.contracts.PartyAndReference
import net.corda.core.serialization.OpaqueBytes
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.cert.X509CertificateHolder
import java.security.PublicKey
import java.security.cert.CertPath
import java.security.cert.X509Certificate
/**
* A full party plus the X.509 certificate and path linking the party back to a trust root.

View File

@ -11,6 +11,7 @@ import net.corda.core.flows.FlowInitiator
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.StateMachineRunId
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.NodeInfo
import net.corda.core.node.services.NetworkMapCache
import net.corda.core.node.services.StateMachineTransactionMapping
@ -231,18 +232,18 @@ interface CordaRPCOps : RPCOps {
/**
* Returns the [Party] corresponding to the given key, if found.
*/
fun partyFromKey(key: PublicKey): Party?
fun partyFromKey(key: PublicKey): PartyAndCertificate?
/**
* Returns the [Party] with the given name as it's [Party.name]
*/
@Deprecated("Use partyFromX500Name instead")
fun partyFromName(name: String): Party?
fun partyFromName(name: String): PartyAndCertificate?
/**
* Returns the [Party] with the X.500 principal as it's [Party.name]
*/
fun partyFromX500Name(x500Name: X500Name): Party?
fun partyFromX500Name(x500Name: X500Name): PartyAndCertificate?
/** Enumerates the class names of the flows that this node knows about. */
fun registeredFlows(): List<String>

View File

@ -1,24 +1,28 @@
package net.corda.core.node
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.node.services.ServiceInfo
import net.corda.core.node.services.ServiceType
import net.corda.core.serialization.CordaSerializable
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter
import java.security.cert.TrustAnchor
import java.security.cert.X509Certificate
/**
* Information for an advertised service including the service specific identity information.
* The identity can be used in flows and is distinct from the Node's legalIdentity
*/
@CordaSerializable
data class ServiceEntry(val info: ServiceInfo, val identity: Party)
data class ServiceEntry(val info: ServiceInfo, val identity: PartyAndCertificate)
/**
* Info about a network node that acts on behalf of some form of contract party.
*/
@CordaSerializable
data class NodeInfo(val address: SingleMessageRecipient,
val legalIdentity: Party,
val legalIdentity: PartyAndCertificate,
val platformVersion: Int,
var advertisedServices: List<ServiceEntry> = emptyList(),
val physicalLocation: PhysicalLocation? = null) {
@ -26,6 +30,6 @@ data class NodeInfo(val address: SingleMessageRecipient,
require(advertisedServices.none { it.identity == legalIdentity }) { "Service identities must be different from node legal identity" }
}
val notaryIdentity: Party get() = advertisedServices.single { it.info.type.isNotary() }.identity
val notaryIdentity: PartyAndCertificate get() = advertisedServices.single { it.info.type.isNotary() }.identity
fun serviceIdentities(type: ServiceType): List<Party> = advertisedServices.filter { it.info.type.isSubTypeOf(type) }.map { it.identity }
}

View File

@ -2,6 +2,7 @@ package net.corda.core.node
import net.corda.core.flows.FlowLogic
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
/**
* A service hub to be used by the [CordaPluginRegistry]
@ -9,5 +10,5 @@ import net.corda.core.identity.Party
interface PluginServiceHub : ServiceHub {
@Deprecated("This is no longer used. Instead annotate the flows produced by your factory with @InitiatedBy and have " +
"them point to the initiating flow class.", level = DeprecationLevel.ERROR)
fun registerFlowInitiator(initiatingFlowClass: Class<out FlowLogic<*>>, serviceFlowFactory: (Party) -> FlowLogic<*>) = Unit
fun registerFlowInitiator(initiatingFlowClass: Class<out FlowLogic<*>>, serviceFlowFactory: (PartyAndCertificate) -> FlowLogic<*>) = Unit
}

View File

@ -1,15 +1,16 @@
package net.corda.core.node.services
import net.corda.core.contracts.PartyAndReference
import net.corda.core.crypto.toStringShort
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.cert.X509CertificateHolder
import java.security.InvalidAlgorithmParameterException
import java.security.PublicKey
import java.security.cert.CertPath
import java.security.cert.X509Certificate
import java.security.cert.CertificateExpiredException
import java.security.cert.CertificateNotYetValidException
/**
* An identity service maintains an bidirectional map of [Party]s to their associated public keys and thus supports
@ -17,21 +18,26 @@ import java.security.cert.X509Certificate
* service would provide.
*/
interface IdentityService {
fun registerIdentity(party: Party)
/**
* Verify and then store a well known identity.
*
* @param party a party representing a legal entity.
* @throws IllegalArgumentException if the certificate path is invalid, or if there is already an existing
* certificate chain for the anonymous party.
*/
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
fun registerIdentity(party: PartyAndCertificate)
/**
* Verify and then store the certificates proving that an anonymous party's key is owned by the given full
* party.
* Verify and then store an identity.
*
* @param trustedRoot trusted root certificate, typically the R3 master signing certificate.
* @param anonymousParty an anonymised party belonging to the legal entity.
* @param path certificate path from the trusted root to the anonymised party.
* @throws IllegalArgumentException if the chain does not link the two parties, or if there is already an existing
* certificate chain for the anonymous party. Anonymous parties must always resolve to a single owning party.
* @param anonymousParty a party representing a legal entity in a transaction.
* @param path certificate path from the trusted root to the party.
* @throws IllegalArgumentException if the certificate path is invalid, or if there is already an existing
* certificate chain for the anonymous party.
*/
// TODO: Move this into internal identity service once available
@Throws(IllegalArgumentException::class)
fun registerPath(trustedRoot: X509CertificateHolder, anonymousParty: AnonymousParty, path: CertPath)
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
fun registerAnonymousIdentity(anonymousParty: AnonymousParty, fullParty: PartyAndCertificate, path: CertPath)
/**
* Asserts that an anonymous party maps to the given full party, by looking up the certificate chain associated with
@ -52,10 +58,10 @@ interface IdentityService {
// indefinitely. It may be that in the long term we need to drop or archive very old Party information for space,
// but for now this is not supported.
fun partyFromKey(key: PublicKey): Party?
fun partyFromKey(key: PublicKey): PartyAndCertificate?
@Deprecated("Use partyFromX500Name")
fun partyFromName(name: String): Party?
fun partyFromX500Name(principal: X500Name): Party?
fun partyFromName(name: String): PartyAndCertificate?
fun partyFromX500Name(principal: X500Name): PartyAndCertificate?
/**
* Resolve the well known identity of a party. If the party passed in is already a well known identity
@ -63,7 +69,7 @@ interface IdentityService {
*
* @return the well known identity, or null if unknown.
*/
fun partyFromAnonymous(party: AbstractParty): Party?
fun partyFromAnonymous(party: AbstractParty): PartyAndCertificate?
/**
* Resolve the well known identity of a party. If the party passed in is already a well known identity

View File

@ -10,6 +10,7 @@ import net.corda.core.crypto.keys
import net.corda.core.flows.FlowException
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.services.vault.PageSpecification
import net.corda.core.node.services.vault.QueryCriteria
import net.corda.core.node.services.vault.Sort
@ -20,6 +21,7 @@ import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.transactions.WireTransaction
import org.bouncycastle.cert.X509CertificateHolder
import org.bouncycastle.operator.ContentSigner
import rx.Observable
import java.io.InputStream
import java.security.PublicKey
@ -399,7 +401,7 @@ interface KeyManagementService {
* @return X.509 certificate and path to the trust root.
*/
@Suspendable
fun freshKeyAndCert(identity: Party, revocationEnabled: Boolean): Pair<X509CertificateHolder, CertPath>
fun freshKeyAndCert(identity: PartyAndCertificate, revocationEnabled: Boolean): Pair<X509CertificateHolder, CertPath>
/** 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.

View File

@ -10,6 +10,7 @@ import net.corda.core.flows.FlowException
import net.corda.core.flows.FlowLogic
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction
@ -119,7 +120,7 @@ abstract class AbstractStateReplacementFlow {
// Type parameter should ideally be Unit but that prevents Java code from subclassing it (https://youtrack.jetbrains.com/issue/KT-15964).
// We use Void? instead of Unit? as that's what you'd use in Java.
abstract class Acceptor<in T>(val otherSide: Party,
abstract class Acceptor<in T>(val otherSide: PartyAndCertificate,
override val progressTracker: ProgressTracker = tracker()) : FlowLogic<Void?>() {
companion object {
object VERIFYING : ProgressTracker.Step("Verifying state replacement proposal")

View File

@ -7,6 +7,7 @@ import net.corda.core.crypto.toBase58String
import net.corda.core.flows.FlowException
import net.corda.core.flows.FlowLogic
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.ServiceHub
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction
@ -173,7 +174,7 @@ class CollectSignaturesFlow(val partiallySignedTx: SignedTransaction,
*
* @param otherParty The counter-party which is providing you a transaction to sign.
*/
abstract class SignTransactionFlow(val otherParty: Party,
abstract class SignTransactionFlow(val otherParty: PartyAndCertificate,
override val progressTracker: ProgressTracker = tracker()) : FlowLogic<SignedTransaction>() {
companion object {

View File

@ -5,7 +5,7 @@ import net.corda.core.contracts.Attachment
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.sha256
import net.corda.core.flows.InitiatingFlow
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.serialization.SerializationToken
import net.corda.core.serialization.SerializeAsToken
import net.corda.core.serialization.SerializeAsTokenContext
@ -16,7 +16,7 @@ import net.corda.core.serialization.SerializeAsTokenContext
*/
@InitiatingFlow
class FetchAttachmentsFlow(requests: Set<SecureHash>,
otherSide: Party) : FetchDataFlow<Attachment, ByteArray>(requests, otherSide) {
otherSide: PartyAndCertificate) : FetchDataFlow<Attachment, ByteArray>(requests, otherSide) {
override fun load(txid: SecureHash): Attachment? = serviceHub.storageService.attachments.openAttachment(txid)

View File

@ -6,6 +6,7 @@ import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowException
import net.corda.core.flows.FlowLogic
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.serialization.CordaSerializable
import net.corda.core.utilities.UntrustworthyData
import net.corda.core.utilities.unwrap
@ -31,7 +32,7 @@ import java.util.*
*/
abstract class FetchDataFlow<T : NamedByHash, in W : Any>(
protected val requests: Set<SecureHash>,
protected val otherSide: Party) : FlowLogic<FetchDataFlow.Result<T>>() {
protected val otherSide: PartyAndCertificate) : FlowLogic<FetchDataFlow.Result<T>>() {
@CordaSerializable
class DownloadedVsRequestedDataMismatch(val requested: SecureHash, val got: SecureHash) : IllegalArgumentException()

View File

@ -2,7 +2,7 @@ package net.corda.flows
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.InitiatingFlow
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.transactions.SignedTransaction
/**
@ -14,7 +14,7 @@ import net.corda.core.transactions.SignedTransaction
* the database, because it's up to the caller to actually verify the transactions are valid.
*/
@InitiatingFlow
class FetchTransactionsFlow(requests: Set<SecureHash>, otherSide: Party) :
class FetchTransactionsFlow(requests: Set<SecureHash>, otherSide: PartyAndCertificate) :
FetchDataFlow<SignedTransaction, SignedTransaction>(requests, otherSide) {
override fun load(txid: SecureHash): SignedTransaction? {

View File

@ -4,6 +4,7 @@ import net.corda.core.contracts.*
import net.corda.core.flows.InitiatingFlow
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.ProgressTracker
@ -21,7 +22,7 @@ import java.security.PublicKey
@InitiatingFlow
class NotaryChangeFlow<out T : ContractState>(
originalState: StateAndRef<T>,
newNotary: Party,
newNotary: PartyAndCertificate,
progressTracker: ProgressTracker = tracker())
: AbstractStateReplacementFlow.Instigator<T, T, Party>(originalState, newNotary, progressTracker) {

View File

@ -11,6 +11,7 @@ import net.corda.core.flows.FlowException
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatingFlow
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.services.TimeWindowChecker
import net.corda.core.node.services.UniquenessException
import net.corda.core.node.services.UniquenessProvider
@ -96,7 +97,7 @@ object NotaryFlow {
* Additional transaction validation logic can be added when implementing [receiveAndVerifyTx].
*/
// See AbstractStateReplacementFlow.Acceptor for why it's Void?
abstract class Service(val otherSide: Party,
abstract class Service(val otherSide: PartyAndCertificate,
val timeWindowChecker: TimeWindowChecker,
val uniquenessProvider: UniquenessProvider) : FlowLogic<Void?>() {
@Suspendable

View File

@ -5,7 +5,7 @@ import net.corda.core.checkedAdd
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowLogic
import net.corda.core.getOrThrow
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.SignedTransaction
@ -30,7 +30,7 @@ import java.util.*
* The flow returns a list of verified [LedgerTransaction] objects, in a depth-first order.
*/
class ResolveTransactionsFlow(private val txHashes: Set<SecureHash>,
private val otherSide: Party) : FlowLogic<List<LedgerTransaction>>() {
private val otherSide: PartyAndCertificate) : FlowLogic<List<LedgerTransaction>>() {
companion object {
private fun dependencyIDs(wtx: WireTransaction) = wtx.inputs.map { it.txhash }.toSet()
@ -82,14 +82,14 @@ class ResolveTransactionsFlow(private val txHashes: Set<SecureHash>,
/**
* Resolve the full history of a transaction and verify it with its dependencies.
*/
constructor(stx: SignedTransaction, otherSide: Party) : this(stx.tx, otherSide) {
constructor(stx: SignedTransaction, otherSide: PartyAndCertificate) : this(stx.tx, otherSide) {
this.stx = stx
}
/**
* Resolve the full history of a transaction and verify it with its dependencies.
*/
constructor(wtx: WireTransaction, otherSide: Party) : this(dependencyIDs(wtx), otherSide) {
constructor(wtx: WireTransaction, otherSide: PartyAndCertificate) : this(dependencyIDs(wtx), otherSide) {
this.wtx = wtx
}

View File

@ -7,6 +7,7 @@ import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowLogic
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.NodeInfo
import net.corda.core.node.services.ServiceType
import net.corda.core.seconds
@ -45,7 +46,7 @@ object TwoPartyDealFlow {
abstract val payload: Any
abstract val notaryNode: NodeInfo
abstract val otherParty: Party
abstract val otherParty: PartyAndCertificate
abstract val myKey: PublicKey
@Suspendable override fun call(): SignedTransaction {
@ -149,7 +150,7 @@ object TwoPartyDealFlow {
/**
* One side of the flow for inserting a pre-agreed deal.
*/
open class Instigator(override val otherParty: Party,
open class Instigator(override val otherParty: PartyAndCertificate,
override val payload: AutoOffer,
override val myKey: PublicKey,
override val progressTracker: ProgressTracker = Primary.tracker()) : Primary() {

View File

@ -7,6 +7,7 @@ import net.corda.core.contracts.TransactionType
import net.corda.core.contracts.requireThat
import net.corda.core.getOrThrow
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.unwrap
import net.corda.flows.CollectSignaturesFlow
@ -54,7 +55,7 @@ class CollectSignaturesFlowTests {
// "collectSignaturesFlow" and "SignTransactionFlow" can be used in practise.
object TestFlow {
@InitiatingFlow
class Initiator(val state: DummyContract.MultiOwnerState, val otherParty: Party) : FlowLogic<SignedTransaction>() {
class Initiator(val state: DummyContract.MultiOwnerState, val otherParty: PartyAndCertificate) : FlowLogic<SignedTransaction>() {
@Suspendable
override fun call(): SignedTransaction {
send(otherParty, state)
@ -114,7 +115,7 @@ class CollectSignaturesFlowTests {
}
@InitiatedBy(TestFlowTwo.Initiator::class)
class Responder(val otherParty: Party) : FlowLogic<SignedTransaction>() {
class Responder(val otherParty: PartyAndCertificate) : FlowLogic<SignedTransaction>() {
@Suspendable override fun call(): SignedTransaction {
val flow = object : SignTransactionFlow(otherParty) {
@Suspendable override fun checkTransaction(stx: SignedTransaction) = requireThat {

View File

@ -3,6 +3,7 @@ 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.identity.PartyAndCertificate
import net.corda.core.utilities.ALICE
import net.corda.core.utilities.BOB
import net.corda.core.utilities.DUMMY_NOTARY
@ -26,12 +27,11 @@ class TxKeyFlowTests {
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
val alice: PartyAndCertificate = aliceNode.services.myInfo.legalIdentity
val bob: PartyAndCertificate = bobNode.services.myInfo.legalIdentity
aliceNode.services.identityService.registerIdentity(bob)
aliceNode.services.identityService.registerIdentity(notaryNode.info.legalIdentity)
bobNode.services.identityService.registerIdentity(alice)
@ -39,7 +39,7 @@ class TxKeyFlowTests {
// Run the flows
bobNode.registerInitiatedFlow(TxKeyFlow.Provider::class.java)
val requesterFlow = aliceNode.services.startFlow(TxKeyFlow.Requester(bob, revocationEnabled))
val requesterFlow = aliceNode.services.startFlow(TxKeyFlow.Requester(bob))
// Get the results
val actual: Map<Party, TxKeyFlow.AnonymousIdentity> = requesterFlow.resultFuture.getOrThrow()

View File

@ -7,6 +7,7 @@ import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatingFlow
import net.corda.core.getOrThrow
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.messaging.RPCOps
import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.node.services.ServiceInfo
@ -139,7 +140,7 @@ class AttachmentSerializationTest {
private fun launchFlow(clientLogic: ClientLogic, rounds: Int) {
server.registerFlowFactory(ClientLogic::class.java, object : InitiatedFlowFactory<ServerLogic> {
override fun createFlow(platformVersion: Int, otherParty: Party, sessionInit: SessionInit): ServerLogic {
override fun createFlow(platformVersion: Int, otherParty: PartyAndCertificate, sessionInit: SessionInit): ServerLogic {
return ServerLogic(otherParty)
}
}, ServerLogic::class.java, track = false)

View File

@ -43,9 +43,18 @@ UNRELEASED
* There is a new ``AbstractParty`` superclass to ``Party``, which contains just the public key. This now replaces
use of ``Party`` and ``PublicKey`` in state objects, and allows use of full or anonymised parties depending on
use-case.
* A new ``PartyAndCertificate`` class has been added which contains an X.509 certificate and certificate path back
to a network trust root. This is widely used in place of ``Party`` inside Corda, with the exception of a few cases
where proof of identity is not required.
* Names of parties are now stored as a ``X500Name`` rather than a ``String``, to correctly enforce basic structure of the
name. As a result all node legal names must now be structured as X.500 distinguished names.
* Flows can now accept ``PartyAndCertificate`` instead of ``Party`` in their constructor, which should be the default for
new flows. The sample flows have been updated to use ``PartyAndCertificate`` to illustrate this.
* The identity management service takes an optional network trust root which it will validate certificate paths to, if
provided. A later release will make this a required parameter.
* There are major changes to transaction signing in flows:
* You should use the new ``CollectSignaturesFlow`` and corresponding ``SignTransactionFlow`` which handle most

View File

@ -12,6 +12,8 @@ import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatedBy
import net.corda.core.flows.InitiatingFlow
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.PluginServiceHub
import net.corda.core.node.ServiceHub
import net.corda.core.node.services.unconsumedStates
import net.corda.core.serialization.CordaSerializable
@ -24,8 +26,8 @@ import java.util.*
@CordaSerializable
private data class FxRequest(val tradeId: String,
val amount: Amount<Issued<Currency>>,
val owner: Party,
val counterparty: Party,
val owner: PartyAndCertificate,
val counterparty: PartyAndCertificate,
val notary: Party? = null)
@CordaSerializable
@ -101,8 +103,8 @@ private fun prepareOurInputsAndOutputs(serviceHub: ServiceHub, request: FxReques
class ForeignExchangeFlow(val tradeId: String,
val baseCurrencyAmount: Amount<Issued<Currency>>,
val quoteCurrencyAmount: Amount<Issued<Currency>>,
val baseCurrencyBuyer: Party,
val baseCurrencySeller: Party) : FlowLogic<SecureHash>() {
val baseCurrencyBuyer: PartyAndCertificate,
val baseCurrencySeller: PartyAndCertificate) : FlowLogic<SecureHash>() {
@Suspendable
override fun call(): SecureHash {
// Select correct sides of the Fx exchange to query for.
@ -206,7 +208,7 @@ class ForeignExchangeFlow(val tradeId: String,
}
@InitiatedBy(ForeignExchangeFlow::class)
class ForeignExchangeRemoteFlow(val source: Party) : FlowLogic<Unit>() {
class ForeignExchangeRemoteFlow(val source: PartyAndCertificate) : FlowLogic<Unit>() {
@Suspendable
override fun call() {
// Initial receive from remote party

View File

@ -7,7 +7,7 @@ import net.corda.core.crypto.DigitalSignature
import net.corda.core.flows.FlowException
import net.corda.core.flows.FlowLogic
import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.NodeInfo
import net.corda.core.seconds
import net.corda.core.serialization.CordaSerializable
@ -61,7 +61,7 @@ object TwoPartyTradeFlow {
data class SignaturesFromSeller(val sellerSig: DigitalSignature.WithKey,
val notarySig: DigitalSignature.WithKey)
open class Seller(val otherParty: Party,
open class Seller(val otherParty: PartyAndCertificate,
val notaryNode: NodeInfo,
val assetToSell: StateAndRef<OwnableState>,
val price: Amount<Currency>,
@ -141,8 +141,8 @@ object TwoPartyTradeFlow {
}
}
open class Buyer(val otherParty: Party,
val notary: Party,
open class Buyer(val otherParty: PartyAndCertificate,
val notary: PartyAndCertificate,
val acceptablePrice: Amount<Currency>,
val typeToBuy: Class<out OwnableState>) : FlowLogic<SignedTransaction>() {
// DOCSTART 2

View File

@ -1,6 +1,7 @@
package net.corda.flows;
import net.corda.core.identity.Party;
import net.corda.core.identity.PartyAndCertificate;
import net.corda.core.utilities.*;
import org.jetbrains.annotations.*;
@ -9,7 +10,7 @@ public class AbstractStateReplacementFlowTest {
// Acceptor used to have a type parameter of Unit which prevented Java code from subclassing it (https://youtrack.jetbrains.com/issue/KT-15964).
private static class TestAcceptorCanBeInheritedInJava extends AbstractStateReplacementFlow.Acceptor {
public TestAcceptorCanBeInheritedInJava(@NotNull Party otherSide, @NotNull ProgressTracker progressTracker) {
public TestAcceptorCanBeInheritedInJava(@NotNull PartyAndCertificate otherSide, @NotNull ProgressTracker progressTracker) {
super(otherSide, progressTracker);
}

View File

@ -24,6 +24,7 @@ import net.corda.testing.node.SimpleNode
import org.assertj.core.api.Assertions.assertThatExceptionOfType
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.cert.X509CertificateHolder
import org.junit.Test
import java.time.Instant
import java.util.concurrent.TimeoutException
@ -56,17 +57,19 @@ class P2PSecurityTest : NodeBasedTest() {
}
}
private fun startSimpleNode(legalName: X500Name): SimpleNode {
private fun startSimpleNode(legalName: X500Name,
trustRoot: X509CertificateHolder? = null): SimpleNode {
val config = TestNodeConfiguration(
baseDirectory = baseDirectory(legalName),
myLegalName = legalName,
networkMapService = NetworkMapInfo(networkMapNode.configuration.p2pAddress, networkMapNode.info.legalIdentity.name))
config.configureWithDevSSLCertificate() // This creates the node's TLS cert with the CN as the legal name
return SimpleNode(config).apply { start() }
return SimpleNode(config, trustRoot = trustRoot).apply { start() }
}
private fun SimpleNode.registerWithNetworkMap(registrationName: X500Name): ListenableFuture<NetworkMapService.RegistrationResponse> {
val nodeInfo = NodeInfo(net.myAddress, getTestPartyAndCertificate(registrationName, identity.public), MOCK_VERSION_INFO.platformVersion)
val legalIdentity = getTestPartyAndCertificate(registrationName, identity.public)
val nodeInfo = NodeInfo(net.myAddress, legalIdentity, MOCK_VERSION_INFO.platformVersion)
val registration = NodeRegistration(nodeInfo, System.currentTimeMillis(), AddOrRemove.ADD, Instant.MAX)
val request = RegistrationRequest(registration.toWire(keyService, identity.public), net.myAddress)
return net.sendRequest<NetworkMapService.RegistrationResponse>(NetworkMapService.REGISTER_TOPIC, request, networkMapNode.net.myAddress)

View File

@ -14,6 +14,9 @@ import net.corda.core.crypto.X509Utilities
import net.corda.core.crypto.appendToCommonName
import net.corda.core.crypto.commonName
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.internal.ShutdownHook
import net.corda.core.internal.addShutdownHook
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.node.NodeInfo
import net.corda.core.node.services.ServiceInfo
@ -28,8 +31,6 @@ import net.corda.nodeapi.ArtemisMessagingComponent
import net.corda.nodeapi.User
import net.corda.nodeapi.config.SSLConfiguration
import net.corda.nodeapi.config.parseAs
import net.corda.core.internal.ShutdownHook
import net.corda.core.internal.addShutdownHook
import okhttp3.OkHttpClient
import okhttp3.Request
import org.bouncycastle.asn1.x500.X500Name
@ -96,7 +97,7 @@ interface DriverDSLExposedInterface : CordformContext {
clusterSize: Int = 3,
type: ServiceType = RaftValidatingNotaryService.type,
verifierType: VerifierType = VerifierType.InMemory,
rpcUsers: List<User> = emptyList()): Future<Pair<Party, List<NodeHandle>>>
rpcUsers: List<User> = emptyList()): Future<Pair<PartyAndCertificate, List<NodeHandle>>>
/**
* Starts a web server for a node
@ -553,7 +554,7 @@ class DriverDSL(
type: ServiceType,
verifierType: VerifierType,
rpcUsers: List<User>
): ListenableFuture<Pair<Party, List<NodeHandle>>> {
): ListenableFuture<Pair<PartyAndCertificate, List<NodeHandle>>> {
val nodeNames = (0 until clusterSize).map { DUMMY_NOTARY.name.appendToCommonName(" $it") }
val paths = nodeNames.map { baseDirectory(it) }
ServiceIdentityGenerator.generateToDisk(paths, DUMMY_CA, type.id, notaryName)

View File

@ -71,6 +71,7 @@ import java.nio.file.Path
import java.nio.file.Paths
import java.security.KeyPair
import java.security.KeyStoreException
import java.security.cert.X509Certificate
import java.time.Clock
import java.util.*
import java.util.concurrent.ConcurrentHashMap
@ -346,7 +347,13 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
}
private fun <F : FlowLogic<*>> registerInitiatedFlowInternal(initiatedFlow: Class<F>, track: Boolean): Observable<F> {
val ctor = initiatedFlow.getDeclaredConstructor(Party::class.java).apply { isAccessible = true }
val ctor = try {
initiatedFlow.getDeclaredConstructor(PartyAndCertificate::class.java).apply { isAccessible = true }
} catch(ex: NoSuchMethodException) {
// Fall back to a constructor that takes in a Party
// TODO: Consider removing for 1.0 release, as flows should generally take the more detailed class
initiatedFlow.getDeclaredConstructor(Party::class.java).apply { isAccessible = true }
}
val initiatingFlow = initiatedFlow.requireAnnotation<InitiatedBy>().value.java
val (version, classWithAnnotation) = initiatingFlow.flowVersionAndInitiatingClass
require(classWithAnnotation == initiatingFlow) {
@ -394,7 +401,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
* @suppress
*/
@VisibleForTesting
fun installCoreFlow(clientFlowClass: KClass<out FlowLogic<*>>, flowFactory: (Party, Int) -> FlowLogic<*>) {
fun installCoreFlow(clientFlowClass: KClass<out FlowLogic<*>>, flowFactory: (PartyAndCertificate, Int) -> FlowLogic<*>) {
require(clientFlowClass.java.flowVersionAndInitiatingClass.first == 1) {
"${InitiatingFlow::class.java.name}.version not applicable for core flows; their version is the node's platform version"
}
@ -656,7 +663,9 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
protected abstract fun makeUniquenessProvider(type: ServiceType): UniquenessProvider
protected open fun makeIdentityService(): IdentityService {
val service = InMemoryIdentityService()
val keyStore = KeyStoreUtilities.loadKeyStore(configuration.trustStoreFile, configuration.trustStorePassword)
val trustRoot = keyStore.getCertificate(X509Utilities.CORDA_ROOT_CA) as? X509Certificate
val service = InMemoryIdentityService(trustRoot = trustRoot)
service.registerIdentity(info.legalIdentity)
services.networkMapCache.partyNodes.forEach { service.registerIdentity(it.legalIdentity) }
netMapCache.changed.subscribe { mapChange ->

View File

@ -2,19 +2,20 @@ package net.corda.node.internal
import net.corda.core.flows.FlowLogic
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.node.services.statemachine.SessionInit
interface InitiatedFlowFactory<out F : FlowLogic<*>> {
fun createFlow(platformVersion: Int, otherParty: Party, sessionInit: SessionInit): F
fun createFlow(platformVersion: Int, otherParty: PartyAndCertificate, sessionInit: SessionInit): F
data class Core<out F : FlowLogic<*>>(val factory: (Party, Int) -> F) : InitiatedFlowFactory<F> {
override fun createFlow(platformVersion: Int, otherParty: Party, sessionInit: SessionInit): F {
data class Core<out F : FlowLogic<*>>(val factory: (PartyAndCertificate, Int) -> F) : InitiatedFlowFactory<F> {
override fun createFlow(platformVersion: Int, otherParty: PartyAndCertificate, sessionInit: SessionInit): F {
return factory(otherParty, platformVersion)
}
}
data class CorDapp<out F : FlowLogic<*>>(val version: Int, val factory: (Party) -> F) : InitiatedFlowFactory<F> {
override fun createFlow(platformVersion: Int, otherParty: Party, sessionInit: SessionInit): F {
override fun createFlow(platformVersion: Int, otherParty: PartyAndCertificate, sessionInit: SessionInit): F {
// TODO Add support for multiple versions of the same flow when CorDapps are loaded in separate class loaders
if (sessionInit.flowVerison == version) return factory(otherParty)
throw SessionRejectException(

View File

@ -5,10 +5,11 @@ import net.corda.core.contracts.ContractState
import net.corda.core.contracts.TransactionType
import net.corda.core.contracts.UpgradedContract
import net.corda.core.contracts.requireThat
import net.corda.core.identity.Party
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowException
import net.corda.core.flows.FlowLogic
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.unwrap
import net.corda.flows.*
@ -25,20 +26,20 @@ import net.corda.flows.*
*
* Additionally, because nodes do not store invalid transactions, requesting such a transaction will always yield null.
*/
class FetchTransactionsHandler(otherParty: Party) : FetchDataHandler<SignedTransaction>(otherParty) {
class FetchTransactionsHandler(otherParty: PartyAndCertificate) : FetchDataHandler<SignedTransaction>(otherParty) {
override fun getData(id: SecureHash): SignedTransaction? {
return serviceHub.storageService.validatedTransactions.getTransaction(id)
}
}
// TODO: Use Artemis message streaming support here, called "large messages". This avoids the need to buffer.
class FetchAttachmentsHandler(otherParty: Party) : FetchDataHandler<ByteArray>(otherParty) {
class FetchAttachmentsHandler(otherParty: PartyAndCertificate) : FetchDataHandler<ByteArray>(otherParty) {
override fun getData(id: SecureHash): ByteArray? {
return serviceHub.storageService.attachments.openAttachment(id)?.open()?.readBytes()
}
}
abstract class FetchDataHandler<out T>(val otherParty: Party) : FlowLogic<Unit>() {
abstract class FetchDataHandler<out T>(val otherParty: PartyAndCertificate) : FlowLogic<Unit>() {
@Suspendable
@Throws(FetchDataFlow.HashNotFound::class)
override fun call() {
@ -59,7 +60,7 @@ abstract class FetchDataHandler<out T>(val otherParty: Party) : FlowLogic<Unit>(
// includes us in any outside that list. Potentially just if it includes any outside that list at all.
// TODO: Do we want to be able to reject specific transactions on more complex rules, for example reject incoming
// cash without from unknown parties?
class NotifyTransactionHandler(val otherParty: Party) : FlowLogic<Unit>() {
class NotifyTransactionHandler(val otherParty: PartyAndCertificate) : FlowLogic<Unit>() {
@Suspendable
override fun call() {
val request = receive<BroadcastTransactionFlow.NotifyTxRequest>(otherParty).unwrap { it }
@ -68,7 +69,7 @@ class NotifyTransactionHandler(val otherParty: Party) : FlowLogic<Unit>() {
}
}
class NotaryChangeHandler(otherSide: Party) : AbstractStateReplacementFlow.Acceptor<Party>(otherSide) {
class NotaryChangeHandler(otherSide: PartyAndCertificate) : AbstractStateReplacementFlow.Acceptor<PartyAndCertificate>(otherSide) {
/**
* Check the notary change proposal.
*
@ -76,7 +77,7 @@ class NotaryChangeHandler(otherSide: Party) : AbstractStateReplacementFlow.Accep
* and is also in a geographically convenient location we can just automatically approve the change.
* TODO: In more difficult cases this should call for human attention to manually verify and approve the proposal
*/
override fun verifyProposal(proposal: AbstractStateReplacementFlow.Proposal<Party>): Unit {
override fun verifyProposal(proposal: AbstractStateReplacementFlow.Proposal<PartyAndCertificate>): Unit {
val state = proposal.stateRef
val proposedTx = proposal.stx.tx
@ -101,7 +102,7 @@ class NotaryChangeHandler(otherSide: Party) : AbstractStateReplacementFlow.Accep
}
}
class ContractUpgradeHandler(otherSide: Party) : AbstractStateReplacementFlow.Acceptor<Class<out UpgradedContract<ContractState, *>>>(otherSide) {
class ContractUpgradeHandler(otherSide: PartyAndCertificate) : AbstractStateReplacementFlow.Acceptor<Class<out UpgradedContract<ContractState, *>>>(otherSide) {
@Suspendable
@Throws(StateReplacementException::class)
override fun verifyProposal(proposal: AbstractStateReplacementFlow.Proposal<Class<out UpgradedContract<ContractState, *>>>) {

View File

@ -2,10 +2,12 @@ package net.corda.node.services.identity
import net.corda.core.contracts.PartyAndReference
import net.corda.core.contracts.requireThat
import net.corda.core.crypto.subject
import net.corda.core.crypto.toStringShort
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.services.IdentityService
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.utilities.loggerFor
@ -27,15 +29,20 @@ import javax.annotation.concurrent.ThreadSafe
* @param certPaths initial set of certificate paths for the service, typically only used for unit tests.
*/
@ThreadSafe
class InMemoryIdentityService(identities: Iterable<Party> = emptySet(),
certPaths: Map<AnonymousParty, CertPath> = emptyMap()) : SingletonSerializeAsToken(), IdentityService {
class InMemoryIdentityService(identities: Iterable<PartyAndCertificate> = emptySet(),
certPaths: Map<AnonymousParty, CertPath> = emptyMap(),
val trustRoot: X509Certificate?) : SingletonSerializeAsToken(), IdentityService {
constructor(identities: Iterable<PartyAndCertificate> = emptySet(),
certPaths: Map<AnonymousParty, CertPath> = emptyMap(),
trustRoot: X509CertificateHolder?) : this(identities, certPaths, trustRoot?.let { JcaX509CertificateConverter().getCertificate(it) })
companion object {
private val log = loggerFor<InMemoryIdentityService>()
}
private val keyToParties = ConcurrentHashMap<PublicKey, Party>()
private val principalToParties = ConcurrentHashMap<X500Name, Party>()
private val partyToPath = ConcurrentHashMap<AnonymousParty, CertPath>()
private val trustAnchor: TrustAnchor? = trustRoot?.let { cert -> TrustAnchor(cert, null) }
private val keyToParties = ConcurrentHashMap<PublicKey, PartyAndCertificate>()
private val principalToParties = ConcurrentHashMap<X500Name, PartyAndCertificate>()
private val partyToPath = ConcurrentHashMap<AbstractParty, CertPath>()
init {
keyToParties.putAll(identities.associateBy { it.owningKey } )
@ -43,8 +50,29 @@ class InMemoryIdentityService(identities: Iterable<Party> = emptySet(),
partyToPath.putAll(certPaths)
}
override fun registerIdentity(party: Party) {
// TODO: Check the validation logic
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
override fun registerIdentity(party: PartyAndCertificate) {
require(party.certPath.certificates.isNotEmpty()) { "Certificate path must contain at least one certificate" }
// Validate the chain first, before we do anything clever with it
val validatorParameters = if (trustAnchor != null) {
PKIXParameters(setOf(trustAnchor))
} else {
// TODO: We should always require a full chain back to a trust anchor, but until we have a network
// trust anchor everywhere, this will have to do.
val converter = JcaX509CertificateConverter()
PKIXParameters(setOf(TrustAnchor(converter.getCertificate(party.certificate), null)))
}
val validator = CertPathValidator.getInstance("PKIX")
validatorParameters.isRevocationEnabled = false
// TODO: val result = validator.validate(party.certPath, validatorParameters) as PKIXCertPathValidatorResult
// require(trustAnchor == null || result.trustAnchor == trustAnchor)
// require(result.publicKey == party.owningKey) { "Certificate path validation must end at transaction key ${anonymousParty.owningKey.toStringShort()}, found ${result.publicKey.toStringShort()}" }
log.trace { "Registering identity $party" }
require(Arrays.equals(party.certificate.subjectPublicKeyInfo.encoded, party.owningKey.encoded)) { "Party certificate must end with party's public key" }
partyToPath[party] = party.certPath
keyToParties[party.owningKey] = party
principalToParties[party.name] = party
}
@ -52,12 +80,12 @@ class InMemoryIdentityService(identities: Iterable<Party> = emptySet(),
// We give the caller a copy of the data set to avoid any locking problems
override fun getAllIdentities(): Iterable<Party> = ArrayList(keyToParties.values)
override fun partyFromKey(key: PublicKey): Party? = keyToParties[key]
override fun partyFromKey(key: PublicKey): PartyAndCertificate? = keyToParties[key]
@Deprecated("Use partyFromX500Name")
override fun partyFromName(name: String): Party? = principalToParties[X500Name(name)]
override fun partyFromX500Name(principal: X500Name): Party? = principalToParties[principal]
override fun partyFromAnonymous(party: AbstractParty): Party? {
return if (party is Party) {
override fun partyFromName(name: String): PartyAndCertificate? = principalToParties[X500Name(name)]
override fun partyFromX500Name(principal: X500Name): PartyAndCertificate? = principalToParties[principal]
override fun partyFromAnonymous(party: AbstractParty): PartyAndCertificate? {
return if (party is PartyAndCertificate) {
party
} else {
partyFromKey(party.owningKey)
@ -84,20 +112,29 @@ class InMemoryIdentityService(identities: Iterable<Party> = emptySet(),
override fun pathForAnonymous(anonymousParty: AnonymousParty): CertPath? = partyToPath[anonymousParty]
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
override fun registerPath(trustedRoot: X509CertificateHolder, anonymousParty: AnonymousParty, path: CertPath) {
val converter = JcaX509CertificateConverter()
val expectedTrustAnchor = TrustAnchor(converter.getCertificate(trustedRoot), null)
override fun registerAnonymousIdentity(anonymousParty: AnonymousParty, fullParty: PartyAndCertificate, path: CertPath) {
require(path.certificates.isNotEmpty()) { "Certificate path must contain at least one certificate" }
val target = path.certificates.last() as X509Certificate
require(target.publicKey == anonymousParty.owningKey) { "Certificate path must end with anonymous party's public key" }
// Validate the chain first, before we do anything clever with it
val validator = CertPathValidator.getInstance("PKIX")
val validatorParameters = PKIXParameters(setOf(expectedTrustAnchor)).apply {
isRevocationEnabled = false
val validatorParameters = if (trustAnchor != null) {
PKIXParameters(setOf(trustAnchor))
} else {
// TODO: We should always require a full chain back to a trust anchor, but until we have a network
// trust anchor everywhere, this will have to do.
val converter = JcaX509CertificateConverter()
PKIXParameters(setOf(TrustAnchor(converter.getCertificate(fullParty.certificate), null)))
}
validatorParameters.isRevocationEnabled = false
val result = validator.validate(path, validatorParameters) as PKIXCertPathValidatorResult
require(result.trustAnchor == expectedTrustAnchor)
require(result.publicKey == anonymousParty.owningKey)
val subjectCertificate = path.certificates.first()
require(trustAnchor == null || result.trustAnchor == trustAnchor)
require(result.publicKey == anonymousParty.owningKey) { "Certificate path validation must end at transaction key ${anonymousParty.owningKey.toStringShort()}, found ${result.publicKey.toStringShort()}" }
require(subjectCertificate is X509Certificate && subjectCertificate.subject == fullParty.name) { "Subject of the transaction certificate must match the well known identity" }
log.trace { "Registering identity $fullParty" }
partyToPath[anonymousParty] = path
keyToParties[anonymousParty.owningKey] = fullParty
principalToParties[fullParty.name] = fullParty
}
}

View File

@ -6,6 +6,7 @@ import net.corda.core.crypto.generateKeyPair
import net.corda.core.crypto.keys
import net.corda.core.crypto.sign
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.services.IdentityService
import net.corda.core.node.services.KeyManagementService
import net.corda.core.serialization.SingletonSerializeAsToken
@ -57,7 +58,11 @@ class E2ETestKeyManagementService(val identityService: IdentityService,
return keyPair.public
}
override fun freshKeyAndCert(identity: Party, revocationEnabled: Boolean): Pair<X509CertificateHolder, CertPath> = freshKeyAndCert(this, identityService, identity, revocationEnabled)
override fun freshKeyAndCert(identity: PartyAndCertificate, revocationEnabled: Boolean): Pair<X509CertificateHolder, CertPath> {
return freshCertificate(identityService, freshKey(), identity, getSigner(identity.owningKey), revocationEnabled)
}
private fun getSigner(publicKey: PublicKey): ContentSigner = getSigner(getSigningKeyPair(publicKey))
private fun getSigningKeyPair(publicKey: PublicKey): KeyPair {
return mutex.locked {

View File

@ -5,38 +5,49 @@ import net.corda.core.crypto.ContentSignerBuilder
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.identity.PartyAndCertificate
import net.corda.core.node.services.IdentityService
import net.corda.core.node.services.KeyManagementService
import org.bouncycastle.cert.X509CertificateHolder
import org.bouncycastle.operator.ContentSigner
import java.security.KeyPair
import java.security.PublicKey
import java.security.Security
import java.security.cert.CertPath
import java.security.cert.X509Certificate
import java.time.Duration
import java.util.*
/**
* 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 subjectPublicKey public key of new identity.
* @param issuerSigner a content signer for the issuer.
* @param identityService issuer service to use when registering the certificate.
* @param issuer issuer to generate a key and certificate for. Must be an identity this node has the private key 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<X509CertificateHolder, 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)
fun freshCertificate(identityService: IdentityService,
subjectPublicKey: PublicKey,
issuer: PartyAndCertificate,
issuerSigner: ContentSigner,
revocationEnabled: Boolean = false): Pair<X509CertificateHolder, CertPath> {
val issuerCertificate = issuer.certificate
val window = X509Utilities.getCertificateValidityWindow(Duration.ZERO, Duration.ofDays(10 * 365), issuerCertificate)
val ourCertificate = Crypto.createCertificate(CertificateType.IDENTITY, issuerCertificate.subject, issuerSigner, issuer.name, subjectPublicKey, window)
val actualPublicKey = Crypto.toSupportedPublicKey(ourCertificate.subjectPublicKeyInfo)
require(subjectPublicKey == actualPublicKey)
val ourCertPath = X509Utilities.createCertificatePath(issuerCertificate, ourCertificate, revocationEnabled = revocationEnabled)
identityService.registerPath(issuerCertificate,
AnonymousParty(ourPublicKey),
require(Arrays.equals(ourCertificate.subjectPublicKeyInfo.encoded, subjectPublicKey.encoded))
identityService.registerAnonymousIdentity(AnonymousParty(subjectPublicKey),
issuer,
ourCertPath)
return Pair(issuerCertificate, ourCertPath)
}
fun getSigner(issuerKeyPair: KeyPair): ContentSigner {
val signatureScheme = Crypto.findSignatureScheme(issuerKeyPair.private)
val provider = Security.getProvider(signatureScheme.providerName)
return ContentSignerBuilder.build(signatureScheme, issuerKeyPair.private, provider)
}

View File

@ -5,7 +5,7 @@ import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.generateKeyPair
import net.corda.core.crypto.keys
import net.corda.core.crypto.sign
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.services.IdentityService
import net.corda.core.node.services.KeyManagementService
import net.corda.core.serialization.SingletonSerializeAsToken
@ -67,7 +67,12 @@ class PersistentKeyManagementService(val identityService: IdentityService,
}
return keyPair.public
}
override fun freshKeyAndCert(identity: Party, revocationEnabled: Boolean): Pair<X509CertificateHolder, CertPath> = freshKeyAndCert(this, identityService, identity, revocationEnabled)
override fun freshKeyAndCert(identity: PartyAndCertificate, revocationEnabled: Boolean): Pair<X509CertificateHolder, CertPath> {
return freshCertificate(identityService, freshKey(), identity, getSigner(identity.owningKey), revocationEnabled)
}
private fun getSigner(publicKey: PublicKey): ContentSigner = getSigner(getSigningKeyPair(publicKey))
private fun getSigningKeyPair(publicKey: PublicKey): KeyPair {
return mutex.locked {

View File

@ -6,6 +6,7 @@ import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SignedData
import net.corda.core.crypto.isFulfilledBy
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.messaging.MessageRecipients
import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.node.NodeInfo
@ -116,7 +117,7 @@ interface NetworkMapService {
class InMemoryNetworkMapService(services: ServiceHubInternal, minimumPlatformVersion: Int)
: AbstractNetworkMapService(services, minimumPlatformVersion) {
override val nodeRegistrations: MutableMap<Party, NodeRegistrationInfo> = ConcurrentHashMap()
override val nodeRegistrations: MutableMap<PartyAndCertificate, NodeRegistrationInfo> = ConcurrentHashMap()
override val subscribers = ThreadBox(mutableMapOf<SingleMessageRecipient, LastAcknowledgeInfo>())
init {
@ -142,7 +143,7 @@ abstract class AbstractNetworkMapService(services: ServiceHubInternal,
private val logger = loggerFor<AbstractNetworkMapService>()
}
protected abstract val nodeRegistrations: MutableMap<Party, NodeRegistrationInfo>
protected abstract val nodeRegistrations: MutableMap<PartyAndCertificate, NodeRegistrationInfo>
// Map from subscriber address, to most recently acknowledged update map version.
protected abstract val subscribers: ThreadBox<MutableMap<SingleMessageRecipient, LastAcknowledgeInfo>>

View File

@ -1,7 +1,7 @@
package net.corda.node.services.network
import net.corda.core.ThreadBox
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.messaging.SingleMessageRecipient
import net.corda.node.services.api.ServiceHubInternal
import net.corda.node.utilities.*
@ -22,22 +22,25 @@ class PersistentNetworkMapService(services: ServiceHubInternal, minimumPlatformV
: AbstractNetworkMapService(services, minimumPlatformVersion) {
private object Table : JDBCHashedTable("${NODE_DATABASE_PREFIX}network_map_nodes") {
val nodeParty = party("node_party_name", "node_party_key")
val nodeParty = partyAndCertificate("node_party_name", "node_party_key", "node_party_certificate", "node_party_path")
val registrationInfo = blob("node_registration_info")
}
override val nodeRegistrations: MutableMap<Party, NodeRegistrationInfo> = synchronizedMap(object : AbstractJDBCHashMap<Party, NodeRegistrationInfo, Table>(Table, loadOnInit = true) {
override val nodeRegistrations: MutableMap<PartyAndCertificate, NodeRegistrationInfo> = synchronizedMap(object : AbstractJDBCHashMap<PartyAndCertificate, NodeRegistrationInfo, Table>(Table, loadOnInit = true) {
// TODO: We should understand an X500Name database field type, rather than manually doing the conversion ourselves
override fun keyFromRow(row: ResultRow): Party = Party(X500Name(row[table.nodeParty.name]), row[table.nodeParty.owningKey])
override fun keyFromRow(row: ResultRow): PartyAndCertificate = PartyAndCertificate(X500Name(row[table.nodeParty.name]), row[table.nodeParty.owningKey],
row[table.nodeParty.certificate], row[table.nodeParty.certPath])
override fun valueFromRow(row: ResultRow): NodeRegistrationInfo = deserializeFromBlob(row[table.registrationInfo])
override fun addKeyToInsert(insert: InsertStatement, entry: Map.Entry<Party, NodeRegistrationInfo>, finalizables: MutableList<() -> Unit>) {
override fun addKeyToInsert(insert: InsertStatement, entry: Map.Entry<PartyAndCertificate, NodeRegistrationInfo>, finalizables: MutableList<() -> Unit>) {
insert[table.nodeParty.name] = entry.key.name.toString()
insert[table.nodeParty.owningKey] = entry.key.owningKey
insert[table.nodeParty.certPath] = entry.key.certPath
insert[table.nodeParty.certificate] = entry.key.certificate
}
override fun addValueToInsert(insert: InsertStatement, entry: Map.Entry<Party, NodeRegistrationInfo>, finalizables: MutableList<() -> Unit>) {
override fun addValueToInsert(insert: InsertStatement, entry: Map.Entry<PartyAndCertificate, NodeRegistrationInfo>, finalizables: MutableList<() -> Unit>) {
insert[table.registrationInfo] = serializeToBlob(entry.value, finalizables)
}
})

View File

@ -18,6 +18,7 @@ import net.corda.core.*
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.*
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.serialization.*
import net.corda.core.utilities.debug
import net.corda.core.utilities.loggerFor
@ -339,7 +340,7 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
waitingForResponse is WaitForLedgerCommit && message is ErrorSessionEnd
}
private fun onSessionInit(sessionInit: SessionInit, receivedMessage: ReceivedMessage, sender: Party) {
private fun onSessionInit(sessionInit: SessionInit, receivedMessage: ReceivedMessage, sender: PartyAndCertificate) {
logger.trace { "Received $sessionInit from $sender" }
val otherPartySessionId = sessionInit.initiatorSessionId

View File

@ -3,7 +3,7 @@ package net.corda.node.services.transactions
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.crypto.DigitalSignature
import net.corda.core.flows.FlowLogic
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.services.TimeWindowChecker
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
@ -42,11 +42,11 @@ class BFTNonValidatingNotaryService(config: BFTSMaRtConfig,
private val log = loggerFor<BFTNonValidatingNotaryService>()
}
override val serviceFlowFactory: (Party, Int) -> FlowLogic<Void?> = { otherParty, _ ->
override val serviceFlowFactory: (PartyAndCertificate, Int) -> FlowLogic<Void?> = { otherParty, _ ->
ServiceFlow(otherParty, client)
}
private class ServiceFlow(val otherSide: Party, val client: BFTSMaRt.Client) : FlowLogic<Void?>() {
private class ServiceFlow(val otherSide: PartyAndCertificate, val client: BFTSMaRt.Client) : FlowLogic<Void?>() {
@Suspendable
override fun call(): Void? {
val stx = receive<FilteredTransaction>(otherSide).unwrap { it }
@ -81,7 +81,7 @@ class BFTNonValidatingNotaryService(config: BFTSMaRtConfig,
return response.serialize().bytes
}
fun verifyAndCommitTx(ftx: FilteredTransaction, callerIdentity: Party): BFTSMaRt.ReplicaResponse {
fun verifyAndCommitTx(ftx: FilteredTransaction, callerIdentity: PartyAndCertificate): BFTSMaRt.ReplicaResponse {
return try {
val id = ftx.rootHash
val inputs = ftx.filteredLeaves.inputs

View File

@ -13,7 +13,7 @@ import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.SignedData
import net.corda.core.crypto.sign
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.services.TimeWindowChecker
import net.corda.core.node.services.UniquenessProvider
import net.corda.core.serialization.CordaSerializable
@ -51,7 +51,7 @@ import java.util.*
object BFTSMaRt {
/** Sent from [Client] to [Server]. */
@CordaSerializable
data class CommitRequest(val tx: Any, val callerIdentity: Party)
data class CommitRequest(val tx: Any, val callerIdentity: PartyAndCertificate)
/** Sent from [Server] to [Client]. */
@CordaSerializable
@ -83,7 +83,7 @@ object BFTSMaRt {
* Sends a transaction commit request to the BFT cluster. The [proxy] will deliver the request to every
* replica, and block until a sufficient number of replies are received.
*/
fun commitTransaction(transaction: Any, otherSide: Party): ClusterResponse {
fun commitTransaction(transaction: Any, otherSide: PartyAndCertificate): ClusterResponse {
require(transaction is FilteredTransaction || transaction is SignedTransaction) { "Unsupported transaction type: ${transaction.javaClass.name}" }
val request = CommitRequest(transaction, otherSide)
val responseBytes = proxy.invokeOrdered(request.serialize().bytes)
@ -178,7 +178,7 @@ object BFTSMaRt {
*/
abstract fun executeCommand(command: ByteArray): ByteArray?
protected fun commitInputStates(states: List<StateRef>, txId: SecureHash, callerIdentity: Party) {
protected fun commitInputStates(states: List<StateRef>, txId: SecureHash, callerIdentity: PartyAndCertificate) {
log.debug { "Attempting to commit inputs for transaction: $txId" }
val conflicts = mutableMapOf<StateRef, UniquenessProvider.ConsumingTx>()
db.transaction {

View File

@ -1,7 +1,7 @@
package net.corda.node.services.transactions
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.services.TimeWindowChecker
import net.corda.core.node.services.UniquenessProvider
import net.corda.core.transactions.FilteredTransaction
@ -9,7 +9,7 @@ import net.corda.core.utilities.unwrap
import net.corda.flows.NotaryFlow
import net.corda.flows.TransactionParts
class NonValidatingNotaryFlow(otherSide: Party,
class NonValidatingNotaryFlow(otherSide: PartyAndCertificate,
timeWindowChecker: TimeWindowChecker,
uniquenessProvider: UniquenessProvider) : NotaryFlow.Service(otherSide, timeWindowChecker, uniquenessProvider) {
/**

View File

@ -2,13 +2,15 @@ package net.corda.node.services.transactions
import net.corda.core.flows.FlowLogic
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
interface NotaryService {
/**
* Factory for producing notary service flows which have the corresponding sends and receives as NotaryFlow.Client.
* The first parameter is the client [Party] making the request and the second is the platform version of the client's
* node. Use this version parameter to provide backwards compatibility if the notary flow protocol changes.
* The first parameter is the client [PartyAndCertificate] making the request and the second is the platform version
* of the client's node. Use this version parameter to provide backwards compatibility if the notary flow protocol
* changes.
*/
val serviceFlowFactory: (Party, Int) -> FlowLogic<Void?>
val serviceFlowFactory: (PartyAndCertificate, Int) -> FlowLogic<Void?>
}

View File

@ -1,7 +1,7 @@
package net.corda.node.services.transactions
import net.corda.core.flows.FlowLogic
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.services.TimeWindowChecker
/** A non-validating notary service operated by a group of mutually trusting parties, uses the Raft algorithm to achieve consensus. */
@ -11,7 +11,7 @@ class RaftNonValidatingNotaryService(val timeWindowChecker: TimeWindowChecker,
val type = SimpleNotaryService.type.getSubType("raft")
}
override val serviceFlowFactory: (Party, Int) -> FlowLogic<Void?> = { otherParty, _ ->
override val serviceFlowFactory: (PartyAndCertificate, Int) -> FlowLogic<Void?> = { otherParty, _ ->
NonValidatingNotaryFlow(otherParty, timeWindowChecker, uniquenessProvider)
}
}

View File

@ -1,7 +1,7 @@
package net.corda.node.services.transactions
import net.corda.core.flows.FlowLogic
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.services.TimeWindowChecker
/** A validating notary service operated by a group of mutually trusting parties, uses the Raft algorithm to achieve consensus. */
@ -11,7 +11,7 @@ class RaftValidatingNotaryService(val timeWindowChecker: TimeWindowChecker,
val type = ValidatingNotaryService.type.getSubType("raft")
}
override val serviceFlowFactory: (Party, Int) -> FlowLogic<Void?> = { otherParty, _ ->
override val serviceFlowFactory: (PartyAndCertificate, Int) -> FlowLogic<Void?> = { otherParty, _ ->
ValidatingNotaryFlow(otherParty, timeWindowChecker, uniquenessProvider)
}
}

View File

@ -1,7 +1,7 @@
package net.corda.node.services.transactions
import net.corda.core.flows.FlowLogic
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.services.ServiceType
import net.corda.core.node.services.TimeWindowChecker
import net.corda.core.node.services.UniquenessProvider
@ -13,7 +13,7 @@ class SimpleNotaryService(val timeWindowChecker: TimeWindowChecker,
val type = ServiceType.notary.getSubType("simple")
}
override val serviceFlowFactory: (Party, Int) -> FlowLogic<Void?> = { otherParty, _ ->
override val serviceFlowFactory: (PartyAndCertificate, Int) -> FlowLogic<Void?> = { otherParty, _ ->
NonValidatingNotaryFlow(otherParty, timeWindowChecker, uniquenessProvider)
}
}

View File

@ -2,7 +2,7 @@ package net.corda.node.services.transactions
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.TransactionVerificationException
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.services.TimeWindowChecker
import net.corda.core.node.services.UniquenessProvider
import net.corda.core.transactions.SignedTransaction
@ -17,7 +17,7 @@ import java.security.SignatureException
* has its input states "blocked" by a transaction from another party, and needs to establish whether that transaction was
* indeed valid.
*/
class ValidatingNotaryFlow(otherSide: Party,
class ValidatingNotaryFlow(otherSide: PartyAndCertificate,
timeWindowChecker: TimeWindowChecker,
uniquenessProvider: UniquenessProvider) :
NotaryFlow.Service(otherSide, timeWindowChecker, uniquenessProvider) {

View File

@ -1,7 +1,7 @@
package net.corda.node.services.transactions
import net.corda.core.flows.FlowLogic
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.services.ServiceType
import net.corda.core.node.services.TimeWindowChecker
import net.corda.core.node.services.UniquenessProvider
@ -13,7 +13,7 @@ class ValidatingNotaryService(val timeWindowChecker: TimeWindowChecker,
val type = ServiceType.notary.getSubType("validating")
}
override val serviceFlowFactory: (Party, Int) -> FlowLogic<Void?> = { otherParty, _ ->
override val serviceFlowFactory: (PartyAndCertificate, Int) -> FlowLogic<Void?> = { otherParty, _ ->
ValidatingNotaryFlow(otherParty, timeWindowChecker, uniquenessProvider)
}
}

View File

@ -11,7 +11,7 @@ import net.corda.core.flows.StateMachineRunId
import net.corda.core.identity.Party
import net.corda.core.node.ServiceHub
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.DUMMY_PUBKEY_1
import net.corda.core.utilities.DUMMY_CA
import net.corda.core.utilities.UntrustworthyData
import net.corda.jackson.JacksonSupport
import net.corda.node.services.identity.InMemoryIdentityService
@ -33,8 +33,7 @@ class InteractiveShellTest {
override fun call() = a
}
private val someCorpLegalName = MEGA_CORP.name
private val ids = InMemoryIdentityService().apply { registerIdentity(Party(someCorpLegalName, DUMMY_PUBKEY_1)) }
private val ids = InMemoryIdentityService(listOf(MEGA_CORP), trustRoot = DUMMY_CA.certificate)
private val om = JacksonSupport.createInMemoryMapper(ids, YAMLFactory())
private fun check(input: String, expected: String) {
@ -67,7 +66,7 @@ class InteractiveShellTest {
fun flowTooManyParams() = check("b: 12, c: Yo, d: Bar", "")
@Test
fun party() = check("party: \"$someCorpLegalName\"", someCorpLegalName.toString())
fun party() = check("party: \"${MEGA_CORP.name}\"", MEGA_CORP.name.toString())
class DummyFSM(val logic: FlowA) : FlowStateMachine<Any?> {
override fun <T : Any> sendAndReceive(receiveType: Class<T>, otherParty: Party, payload: Any, sessionFlow: FlowLogic<*>, retrySend: Boolean): UntrustworthyData<T> {

View File

@ -13,6 +13,8 @@ import net.corda.core.flows.*
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.map
import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.node.NodeInfo
import net.corda.core.node.services.*
@ -493,7 +495,7 @@ class TwoPartyTradeFlowTests {
}
@InitiatingFlow
class SellerInitiator(val buyer: Party,
class SellerInitiator(val buyer: PartyAndCertificate,
val notary: NodeInfo,
val assetToSell: StateAndRef<OwnableState>,
val price: Amount<Currency>) : FlowLogic<SignedTransaction>() {
@ -510,10 +512,10 @@ class TwoPartyTradeFlowTests {
}
@InitiatedBy(SellerInitiator::class)
class BuyerAcceptor(val seller: Party) : FlowLogic<SignedTransaction>() {
class BuyerAcceptor(val seller: PartyAndCertificate) : FlowLogic<SignedTransaction>() {
@Suspendable
override fun call(): SignedTransaction {
val (notary, price) = receive<Pair<Party, Amount<Currency>>>(seller).unwrap {
val (notary, price) = receive<Pair<PartyAndCertificate, Amount<Currency>>>(seller).unwrap {
require(serviceHub.networkMapCache.isNotary(it.first)) { "${it.first} is not a notary" }
it
}

View File

@ -3,6 +3,7 @@ package net.corda.node.services
import com.codahale.metrics.MetricRegistry
import net.corda.core.flows.FlowInitiator
import net.corda.core.flows.FlowLogic
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.NodeInfo
import net.corda.core.node.services.*
import net.corda.core.serialization.SerializeAsToken

View File

@ -8,6 +8,7 @@ import net.corda.core.node.services.ServiceInfo
import net.corda.core.seconds
import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.core.utilities.getTestPartyAndCertificate
import net.corda.flows.NotaryChangeFlow
import net.corda.flows.StateReplacementException
import net.corda.node.internal.AbstractNode
@ -75,7 +76,7 @@ class NotaryChangeTests {
@Test
fun `should throw when a participant refuses to change Notary`() {
val state = issueMultiPartyState(clientNodeA, clientNodeB, oldNotaryNode)
val newEvilNotary = Party(X500Name("CN=Evil Notary,O=Evil R3,OU=corda,L=London,C=UK"), generateKeyPair().public)
val newEvilNotary = getTestPartyAndCertificate(X500Name("CN=Evil Notary,O=Evil R3,OU=corda,L=London,C=UK"), generateKeyPair().public)
val flow = NotaryChangeFlow(state, newEvilNotary)
val future = clientNodeA.services.startFlow(flow)

View File

@ -10,6 +10,7 @@ import net.corda.core.node.ServiceHub
import net.corda.core.node.services.VaultService
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.utilities.ALICE_KEY
import net.corda.core.utilities.DUMMY_CA
import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.node.services.MockServiceHubInternal
import net.corda.node.services.identity.InMemoryIdentityService
@ -76,7 +77,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
val dataSourceAndDatabase = configureDatabase(dataSourceProps)
dataSource = dataSourceAndDatabase.first
database = dataSourceAndDatabase.second
val identityService = InMemoryIdentityService()
val identityService = InMemoryIdentityService(trustRoot = DUMMY_CA.certificate)
val kms = MockKeyManagementService(identityService, ALICE_KEY)
database.transaction {

View File

@ -3,14 +3,15 @@ package net.corda.node.services.network
import net.corda.core.crypto.*
import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.services.IdentityService
import net.corda.core.utilities.ALICE
import net.corda.core.utilities.BOB
import net.corda.core.utilities.*
import net.corda.node.services.identity.InMemoryIdentityService
import net.corda.testing.ALICE_PUBKEY
import net.corda.testing.BOB_PUBKEY
import org.bouncycastle.asn1.x500.X500Name
import org.junit.Test
import java.security.cert.X509Certificate
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertNull
@ -21,7 +22,7 @@ import kotlin.test.assertNull
class InMemoryIdentityServiceTests {
@Test
fun `get all identities`() {
val service = InMemoryIdentityService()
val service = InMemoryIdentityService(trustRoot = DUMMY_CA.certificate)
assertNull(service.getAllIdentities().firstOrNull())
service.registerIdentity(ALICE)
var expected = setOf<Party>(ALICE)
@ -37,7 +38,7 @@ class InMemoryIdentityServiceTests {
@Test
fun `get identity by key`() {
val service = InMemoryIdentityService()
val service = InMemoryIdentityService(trustRoot = DUMMY_CA.certificate)
assertNull(service.partyFromKey(ALICE_PUBKEY))
service.registerIdentity(ALICE)
assertEquals(ALICE, service.partyFromKey(ALICE_PUBKEY))
@ -46,15 +47,15 @@ class InMemoryIdentityServiceTests {
@Test
fun `get identity by name with no registered identities`() {
val service = InMemoryIdentityService()
val service = InMemoryIdentityService(trustRoot = DUMMY_CA.certificate)
assertNull(service.partyFromX500Name(ALICE.name))
}
@Test
fun `get identity by name`() {
val service = InMemoryIdentityService()
val service = InMemoryIdentityService(trustRoot = DUMMY_CA.certificate)
val identities = listOf("Node A", "Node B", "Node C")
.map { Party(X500Name("CN=$it,O=R3,OU=corda,L=London,C=UK"), generateKeyPair().public) }
.map { getTestPartyAndCertificate(X500Name("CN=$it,O=R3,OU=corda,L=London,C=UK"), generateKeyPair().public) }
assertNull(service.partyFromX500Name(identities.first().name))
identities.forEach { service.registerIdentity(it) }
identities.forEach { assertEquals(it, service.partyFromX500Name(it.name)) }
@ -68,7 +69,7 @@ class InMemoryIdentityServiceTests {
val rootKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootKey)
val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val service = InMemoryIdentityService()
val service = InMemoryIdentityService(trustRoot = DUMMY_CA.certificate)
// TODO: Generate certificate with an EdDSA key rather than ECDSA
val identity = Party(CertificateAndKeyPair(rootCert, rootKey))
val txIdentity = AnonymousParty(txKey.public)
@ -84,26 +85,26 @@ class InMemoryIdentityServiceTests {
*/
@Test
fun `assert ownership`() {
val service = InMemoryIdentityService(trustRoot = null as X509Certificate?)
val aliceRootKey = Crypto.generateKeyPair()
val aliceRootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name, aliceRootKey)
val aliceTxKey = Crypto.generateKeyPair()
val aliceTxCert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, aliceRootCert, aliceRootKey, ALICE.name, aliceTxKey.public)
val aliceTxCert = X509Utilities.createCertificate(CertificateType.IDENTITY, aliceRootCert, aliceRootKey, ALICE.name, aliceTxKey.public)
val aliceCertPath = X509Utilities.createCertificatePath(aliceRootCert, aliceTxCert, revocationEnabled = false)
val alice = PartyAndCertificate(ALICE.name, aliceRootKey.public, aliceRootCert, aliceCertPath)
val anonymousAlice = AnonymousParty(aliceTxKey.public)
service.registerAnonymousIdentity(anonymousAlice, alice, aliceCertPath)
val bobRootKey = Crypto.generateKeyPair()
val bobRootCert = X509Utilities.createSelfSignedCACertificate(BOB.name, bobRootKey)
val bobTxKey = Crypto.generateKeyPair()
val bobTxCert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, bobRootCert, bobRootKey, BOB.name, bobTxKey.public)
val bobTxCert = X509Utilities.createCertificate(CertificateType.IDENTITY, bobRootCert, bobRootKey, BOB.name, bobTxKey.public)
val bobCertPath = X509Utilities.createCertificatePath(bobRootCert, bobTxCert, revocationEnabled = false)
val bob = PartyAndCertificate(BOB.name, bobRootKey.public, bobRootCert, bobCertPath)
val service = InMemoryIdentityService()
val alice = Party(CertificateAndKeyPair(aliceRootCert, aliceRootKey))
val anonymousAlice = AnonymousParty(aliceTxKey.public)
val bob = Party(CertificateAndKeyPair(bobRootCert, bobRootKey))
val anonymousBob = AnonymousParty(bobTxKey.public)
service.registerPath(aliceRootCert, anonymousAlice, aliceCertPath)
service.registerPath(bobRootCert, anonymousBob, bobCertPath)
service.registerAnonymousIdentity(anonymousBob, bob, bobCertPath)
// Verify that paths are verified
service.assertOwnership(alice, anonymousAlice)
@ -122,7 +123,7 @@ class InMemoryIdentityServiceTests {
@Test
fun `deanonymising a well known identity`() {
val expected = ALICE
val actual = InMemoryIdentityService().partyFromAnonymous(expected)
val actual = InMemoryIdentityService(trustRoot = null as X509Certificate?).partyFromAnonymous(expected)
assertEquals(expected, actual)
}
}

View File

@ -9,7 +9,7 @@ import net.corda.core.contracts.USD
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatedBy
import net.corda.core.flows.InitiatingFlow
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.services.unconsumedStates
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.DUMMY_NOTARY
@ -93,13 +93,13 @@ class DataVendingServiceTests {
}
@InitiatingFlow
private class NotifyTxFlow(val otherParty: Party, val stx: SignedTransaction) : FlowLogic<Unit>() {
private class NotifyTxFlow(val otherParty: PartyAndCertificate, val stx: SignedTransaction) : FlowLogic<Unit>() {
@Suspendable
override fun call() = send(otherParty, NotifyTxRequest(stx))
}
@InitiatedBy(NotifyTxFlow::class)
private class InitiateNotifyTxFlow(val otherParty: Party) : FlowLogic<Unit>() {
private class InitiateNotifyTxFlow(val otherParty: PartyAndCertificate) : FlowLogic<Unit>() {
@Suspendable
override fun call() = subFlow(NotifyTransactionHandler(otherParty))
}

View File

@ -14,6 +14,7 @@ import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowSessionException
import net.corda.core.flows.InitiatingFlow
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.messaging.MessageRecipients
import net.corda.core.node.services.PartyInfo
import net.corda.core.node.services.ServiceInfo
@ -673,10 +674,10 @@ class FlowFrameworkTests {
private inline fun <reified P : FlowLogic<*>> MockNode.registerFlowFactory(
initiatingFlowClass: KClass<out FlowLogic<*>>,
noinline flowFactory: (Party) -> P): ListenableFuture<P>
noinline flowFactory: (PartyAndCertificate) -> P): ListenableFuture<P>
{
val observable = registerFlowFactory(initiatingFlowClass.java, object : InitiatedFlowFactory<P> {
override fun createFlow(platformVersion: Int, otherParty: Party, sessionInit: SessionInit): P {
override fun createFlow(platformVersion: Int, otherParty: PartyAndCertificate, sessionInit: SessionInit): P {
return flowFactory(otherParty)
}
}, P::class.java, track = true)

View File

@ -7,6 +7,7 @@ import net.corda.core.crypto.*
import net.corda.core.exists
import net.corda.core.mapToArray
import net.corda.core.utilities.ALICE
import net.corda.core.utilities.getTestPartyAndCertificate
import net.corda.testing.TestNodeConfiguration
import net.corda.testing.getTestX509Name
import org.bouncycastle.cert.X509CertificateHolder
@ -14,6 +15,7 @@ import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import java.security.cert.Certificate
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue

View File

@ -9,6 +9,7 @@ import net.corda.core.flows.InitiatedBy
import net.corda.core.flows.InitiatingFlow
import net.corda.core.flows.SchedulableFlow
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.NodeInfo
import net.corda.core.node.services.ServiceType
import net.corda.core.seconds
@ -30,8 +31,7 @@ object FixingFlow {
* who does what in the flow.
*/
@InitiatedBy(FixingRoleDecider::class)
class Fixer(override val otherParty: Party) : TwoPartyDealFlow.Secondary<FixingSession>() {
override val progressTracker: ProgressTracker = TwoPartyDealFlow.Secondary.tracker()
class Fixer(override val otherParty: PartyAndCertificate) : TwoPartyDealFlow.Secondary<FixingSession>() {
private lateinit var txState: TransactionState<*>
private lateinit var deal: FixableDealState
@ -97,7 +97,7 @@ object FixingFlow {
* is just the "side" of the flow run by the party with the floating leg as a way of deciding who
* does what in the flow.
*/
class Floater(override val otherParty: Party,
class Floater(override val otherParty: PartyAndCertificate,
override val payload: FixingSession,
override val progressTracker: ProgressTracker = TwoPartyDealFlow.Primary.tracker()) : TwoPartyDealFlow.Primary() {

View File

@ -15,8 +15,10 @@ import net.corda.core.flows.FlowStateMachine
import net.corda.core.flows.InitiatedBy
import net.corda.core.flows.InitiatingFlow
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.services.linearHeadsOfType
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.DUMMY_CA
import net.corda.flows.TwoPartyDealFlow.Acceptor
import net.corda.flows.TwoPartyDealFlow.AutoOffer
import net.corda.flows.TwoPartyDealFlow.Instigator
@ -46,7 +48,7 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten
override fun startMainSimulation(): ListenableFuture<Unit> {
val future = SettableFuture.create<Unit>()
om = JacksonSupport.createInMemoryMapper(InMemoryIdentityService((banks + regulators + networkMap).map { it.info.legalIdentity }))
om = JacksonSupport.createInMemoryMapper(InMemoryIdentityService((banks + regulators + networkMap).map { it.info.legalIdentity }, trustRoot = DUMMY_CA.certificate))
startIRSDealBetween(0, 1).success {
// Next iteration is a pause.
@ -129,7 +131,7 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten
node2.registerInitiatedFlow(FixingFlow.Fixer::class.java)
@InitiatingFlow
class StartDealFlow(val otherParty: Party,
class StartDealFlow(val otherParty: PartyAndCertificate,
val payload: AutoOffer,
val myKey: PublicKey) : FlowLogic<SignedTransaction>() {
@Suspendable

View File

@ -9,6 +9,7 @@ import net.corda.core.crypto.*
import net.corda.core.getOrThrow
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.startFlow
import net.corda.core.utilities.DUMMY_MAP
@ -35,7 +36,7 @@ import javax.ws.rs.core.Response
@Path("simmvaluationdemo")
class PortfolioApi(val rpc: CordaRPCOps) {
private val ownParty: Party get() = rpc.nodeIdentity().legalIdentity
private val ownParty: PartyAndCertificate get() = rpc.nodeIdentity().legalIdentity
private val portfolioUtils = PortfolioApiUtils(ownParty)
private inline fun <reified T : DealState> dealsWith(party: AbstractParty): List<StateAndRef<T>> {
@ -48,7 +49,7 @@ class PortfolioApi(val rpc: CordaRPCOps) {
* DSL to get a party and then executing the passed function with the party as a parameter.
* Used as such: withParty(name) { doSomethingWith(it) }
*/
private fun withParty(partyName: String, func: (Party) -> Response): Response {
private fun withParty(partyName: String, func: (PartyAndCertificate) -> Response): Response {
val otherParty = rpc.partyFromKey(parsePublicKeyBase58(partyName))
return if (otherParty != null) {
func(otherParty)

View File

@ -6,6 +6,7 @@ import net.corda.core.flows.InitiatedBy
import net.corda.core.flows.InitiatingFlow
import net.corda.core.flows.StartableByRPC
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.unwrap
@ -20,7 +21,7 @@ object IRSTradeFlow {
@InitiatingFlow
@StartableByRPC
class Requester(val swap: SwapData, val otherParty: Party) : FlowLogic<SignedTransaction>() {
class Requester(val swap: SwapData, val otherParty: PartyAndCertificate) : FlowLogic<SignedTransaction>() {
@Suspendable
override fun call(): SignedTransaction {
require(serviceHub.networkMapCache.notaryNodes.isNotEmpty()) { "No notary nodes registered" }

View File

@ -15,6 +15,7 @@ import net.corda.core.flows.InitiatedBy
import net.corda.core.flows.InitiatingFlow
import net.corda.core.flows.StartableByRPC
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.PluginServiceHub
import net.corda.core.node.services.dealsWith
import net.corda.core.serialization.CordaSerializable
@ -185,7 +186,7 @@ object SimmFlow {
* Receives and validates a portfolio and comes to consensus over the portfolio initial margin using SIMM.
*/
@InitiatedBy(Requester::class)
class Receiver(val replyToParty: Party) : FlowLogic<Unit>() {
class Receiver(val replyToParty: PartyAndCertificate) : FlowLogic<Unit>() {
lateinit var ownParty: Party
lateinit var offer: OfferMessage

View File

@ -3,6 +3,7 @@ package net.corda.vega.flows
import net.corda.core.contracts.StateAndRef
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.seconds
import net.corda.core.transactions.SignedTransaction
import net.corda.flows.AbstractStateReplacementFlow
@ -26,7 +27,7 @@ object StateRevisionFlow {
}
}
open class Receiver<in T>(otherParty: Party) : AbstractStateReplacementFlow.Acceptor<T>(otherParty) {
open class Receiver<in T>(otherParty: PartyAndCertificate) : AbstractStateReplacementFlow.Acceptor<T>(otherParty) {
override fun verifyProposal(proposal: AbstractStateReplacementFlow.Proposal<T>) {
val proposedTx = proposal.stx.tx
val state = proposal.stateRef

View File

@ -7,7 +7,7 @@ import net.corda.core.contracts.TransactionGraphSearch
import net.corda.core.div
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatedBy
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.NodeInfo
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.Emoji
@ -17,7 +17,7 @@ import net.corda.flows.TwoPartyTradeFlow
import java.util.*
@InitiatedBy(SellerFlow::class)
class BuyerFlow(val otherParty: Party) : FlowLogic<Unit>() {
class BuyerFlow(val otherParty: PartyAndCertificate) : FlowLogic<Unit>() {
object STARTING_BUY : ProgressTracker.Step("Seller connected, purchasing commercial paper asset")

View File

@ -12,6 +12,7 @@ import net.corda.core.flows.InitiatingFlow
import net.corda.core.flows.StartableByRPC
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.NodeInfo
import net.corda.core.seconds
import net.corda.core.transactions.SignedTransaction
@ -24,10 +25,10 @@ import java.util.*
@InitiatingFlow
@StartableByRPC
class SellerFlow(val otherParty: Party,
class SellerFlow(val otherParty: PartyAndCertificate,
val amount: Amount<Currency>,
override val progressTracker: ProgressTracker) : FlowLogic<SignedTransaction>() {
constructor(otherParty: Party, amount: Amount<Currency>) : this(otherParty, amount, tracker())
constructor(otherParty: PartyAndCertificate, amount: Amount<Currency>) : this(otherParty, amount, tracker())
companion object {
val PROSPECTUS_HASH = SecureHash.parse("decd098666b9657314870e192ced0c3519c2c9d395507a238338f8d003929de9")

View File

@ -9,7 +9,7 @@ import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.X509Utilities
import net.corda.core.crypto.commonName
import net.corda.core.crypto.generateKeyPair
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.ServiceHub
import net.corda.core.node.VersionInfo
import net.corda.core.node.services.IdentityService
@ -66,22 +66,22 @@ val ALICE_PUBKEY: PublicKey get() = ALICE_KEY.public
val BOB_PUBKEY: PublicKey get() = BOB_KEY.public
val CHARLIE_PUBKEY: PublicKey get() = CHARLIE_KEY.public
val MEGA_CORP: Party get() = Party(X509Utilities.getDevX509Name("MegaCorp"), MEGA_CORP_PUBKEY)
val MINI_CORP: Party get() = Party(X509Utilities.getDevX509Name("MiniCorp"), MINI_CORP_PUBKEY)
val MEGA_CORP: PartyAndCertificate get() = getTestPartyAndCertificate(X509Utilities.getDevX509Name("MegaCorp"), MEGA_CORP_PUBKEY)
val MINI_CORP: PartyAndCertificate get() = getTestPartyAndCertificate(X509Utilities.getDevX509Name("MiniCorp"), MINI_CORP_PUBKEY)
val BOC_KEY: KeyPair by lazy { generateKeyPair() }
val BOC_PUBKEY: PublicKey get() = BOC_KEY.public
val BOC: Party get() = Party(getTestX509Name("BankOfCorda"), BOC_PUBKEY)
val BOC: PartyAndCertificate get() = getTestPartyAndCertificate(getTestX509Name("BankOfCorda"), BOC_PUBKEY)
val BOC_PARTY_REF = BOC.ref(OpaqueBytes.of(1)).reference
val BIG_CORP_KEY: KeyPair by lazy { generateKeyPair() }
val BIG_CORP_PUBKEY: PublicKey get() = BIG_CORP_KEY.public
val BIG_CORP: Party get() = Party(X509Utilities.getDevX509Name("BigCorporation"), BIG_CORP_PUBKEY)
val BIG_CORP: PartyAndCertificate get() = getTestPartyAndCertificate(X509Utilities.getDevX509Name("BigCorporation"), BIG_CORP_PUBKEY)
val BIG_CORP_PARTY_REF = BIG_CORP.ref(OpaqueBytes.of(1)).reference
val ALL_TEST_KEYS: List<KeyPair> get() = listOf(MEGA_CORP_KEY, MINI_CORP_KEY, ALICE_KEY, BOB_KEY, DUMMY_NOTARY_KEY)
val MOCK_IDENTITY_SERVICE: IdentityService get() = InMemoryIdentityService(listOf(MEGA_CORP, MINI_CORP, DUMMY_NOTARY))
val MOCK_IDENTITY_SERVICE: IdentityService get() = InMemoryIdentityService(listOf(MEGA_CORP, MINI_CORP, DUMMY_NOTARY), emptyMap(), DUMMY_CA.certificate)
val MOCK_VERSION_INFO = VersionInfo(1, "Mock release", "Mock revision", "Mock Vendor")

View File

@ -7,6 +7,7 @@ import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.*
import net.corda.core.crypto.entropyToKeyPair
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.messaging.RPCOps
import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.node.CordaPluginRegistry
@ -14,6 +15,7 @@ import net.corda.core.node.PhysicalLocation
import net.corda.core.node.ServiceEntry
import net.corda.core.node.services.*
import net.corda.core.utilities.DUMMY_NOTARY_KEY
import net.corda.core.utilities.getTestPartyAndCertificate
import net.corda.core.utilities.loggerFor
import net.corda.node.internal.AbstractNode
import net.corda.node.services.config.NodeConfiguration
@ -38,6 +40,7 @@ import org.slf4j.Logger
import java.math.BigInteger
import java.nio.file.FileSystem
import java.security.KeyPair
import java.security.cert.X509Certificate
import java.util.*
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicInteger
@ -68,7 +71,7 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
// A unique identifier for this network to segregate databases with the same nodeID but different networks.
private val networkId = random63BitValue()
val identities = ArrayList<Party>()
val identities = ArrayList<PartyAndCertificate>()
private val _nodes = ArrayList<MockNode>()
/** A read only view of the current set of executing nodes. */
@ -162,7 +165,8 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
.getOrThrow()
}
override fun makeIdentityService() = InMemoryIdentityService(mockNet.identities)
// TODO: Specify a CA to validate registration against
override fun makeIdentityService() = InMemoryIdentityService(mockNet.identities, trustRoot = null as X509Certificate?)
override fun makeVaultService(dataSourceProperties: Properties): VaultService = NodeVaultService(services, dataSourceProperties)
@ -187,7 +191,7 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
val override = overrideServices[it.info]
if (override != null) {
// TODO: Store the key
ServiceEntry(it.info, Party(it.identity.name, override.public))
ServiceEntry(it.info, getTestPartyAndCertificate(it.identity.name, override.public))
} else {
it
}

View File

@ -3,7 +3,7 @@ package net.corda.testing.node
import net.corda.core.contracts.Attachment
import net.corda.core.crypto.*
import net.corda.core.flows.StateMachineRunId
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.node.NodeInfo
import net.corda.core.node.ServiceHub
@ -11,10 +11,12 @@ import net.corda.core.node.services.*
import net.corda.core.serialization.SerializeAsToken
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.DUMMY_CA
import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.core.utilities.getTestPartyAndCertificate
import net.corda.node.services.identity.InMemoryIdentityService
import net.corda.node.services.keys.freshKeyAndCert
import net.corda.node.services.keys.freshCertificate
import net.corda.node.services.keys.getSigner
import net.corda.node.services.persistence.InMemoryStateMachineRecordedTransactionMappingStorage
import net.corda.node.services.schema.HibernateObserver
import net.corda.node.services.schema.NodeSchemaService
@ -24,6 +26,7 @@ import net.corda.testing.MEGA_CORP
import net.corda.testing.MINI_CORP
import net.corda.testing.MOCK_VERSION_INFO
import org.bouncycastle.cert.X509CertificateHolder
import org.bouncycastle.operator.ContentSigner
import rx.Observable
import rx.subjects.PublishSubject
import java.io.ByteArrayInputStream
@ -63,7 +66,8 @@ open class MockServices(vararg val keys: KeyPair) : ServiceHub {
}
override val storageService: TxWritableStorageService = MockStorageService()
override final val identityService: IdentityService = InMemoryIdentityService(listOf(MEGA_CORP, MINI_CORP, DUMMY_NOTARY))
override final val identityService: IdentityService = InMemoryIdentityService(listOf(MEGA_CORP, MINI_CORP, DUMMY_NOTARY),
trustRoot = DUMMY_CA.certificate)
override val keyManagementService: KeyManagementService = MockKeyManagementService(identityService, *keys)
override val vaultService: VaultService get() = throw UnsupportedOperationException()
@ -96,10 +100,12 @@ class MockKeyManagementService(val identityService: IdentityService,
return k.public
}
override fun freshKeyAndCert(identity: Party, revocationEnabled: Boolean): Pair<X509CertificateHolder, CertPath> {
return freshKeyAndCert(this, identityService, identity, revocationEnabled)
override fun freshKeyAndCert(identity: PartyAndCertificate, revocationEnabled: Boolean): Pair<X509CertificateHolder, CertPath> {
return freshCertificate(identityService, freshKey(), identity, getSigner(identity.owningKey), revocationEnabled)
}
private fun getSigner(publicKey: PublicKey): ContentSigner = getSigner(getSigningKeyPair(publicKey))
private fun getSigningKeyPair(publicKey: PublicKey): KeyPair {
val pk = publicKey.keys.first { keyStore.containsKey(it) }
return KeyPair(pk, keyStore[pk]!!)

View File

@ -3,7 +3,6 @@ package net.corda.testing.node
import com.codahale.metrics.MetricRegistry
import com.google.common.net.HostAndPort
import com.google.common.util.concurrent.SettableFuture
import net.corda.core.crypto.CertificateAndKeyPair
import net.corda.core.crypto.commonName
import net.corda.core.crypto.generateKeyPair
import net.corda.core.messaging.RPCOps
@ -22,6 +21,7 @@ import net.corda.node.utilities.configureDatabase
import net.corda.node.utilities.transaction
import net.corda.testing.MOCK_VERSION_INFO
import net.corda.testing.freeLocalHostAndPort
import org.bouncycastle.cert.X509CertificateHolder
import org.jetbrains.exposed.sql.Database
import java.io.Closeable
import java.security.KeyPair
@ -33,14 +33,14 @@ import kotlin.concurrent.thread
*/
class SimpleNode(val config: NodeConfiguration, val address: HostAndPort = freeLocalHostAndPort(),
rpcAddress: HostAndPort = freeLocalHostAndPort(),
networkRoot: CertificateAndKeyPair? = null) : AutoCloseable {
trustRoot: X509CertificateHolder? = null) : AutoCloseable {
private val databaseWithCloseable: Pair<Closeable, Database> = configureDatabase(config.dataSourceProperties)
val database: Database get() = databaseWithCloseable.second
val userService = RPCUserServiceImpl(config.rpcUsers)
val monitoringService = MonitoringService(MetricRegistry())
val identity: KeyPair = generateKeyPair()
val identityService: IdentityService = InMemoryIdentityService()
val identityService: IdentityService = InMemoryIdentityService(trustRoot = trustRoot)
val keyService: KeyManagementService = E2ETestKeyManagementService(identityService, setOf(identity))
val executor = ServiceAffinityExecutor(config.myLegalName.commonName, 1)
val broker = ArtemisMessagingServer(config, address, rpcAddress, InMemoryNetworkMapCache(), userService)

View File

@ -183,7 +183,7 @@ class NewTransaction : Fragment() {
// Issuer
issuerLabel.visibleProperty().bind(transactionTypeCB.valueProperty().isNotNull)
issuerChoiceBox.apply {
items = issuers.map { it.legalIdentity }.unique().sorted()
items = issuers.map { it.legalIdentity as Party }.unique().sorted()
converter = stringConverter { PartyNameFormatter.short.format(it.name) }
visibleProperty().bind(transactionTypeCB.valueProperty().map { it == CashTransaction.Pay })
}