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

@ -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