mirror of
https://github.com/corda/corda.git
synced 2024-12-18 20:47:57 +00:00
Remove duplicated code between PersistentIdentityService and InMemoryIdentityService (#3428)
This commit is contained in:
parent
54161a630a
commit
7b4ace16e1
@ -3,6 +3,7 @@ package net.corda.core.node.services
|
||||
import net.corda.core.CordaException
|
||||
import net.corda.core.DoNotImplement
|
||||
import net.corda.core.contracts.PartyAndReference
|
||||
import net.corda.core.crypto.toStringShort
|
||||
import net.corda.core.identity.*
|
||||
import java.security.InvalidAlgorithmParameterException
|
||||
import java.security.PublicKey
|
||||
@ -37,10 +38,17 @@ interface IdentityService {
|
||||
* Asserts that an anonymous party maps to the given full party, by looking up the certificate chain associated with
|
||||
* the anonymous party and resolving it back to the given full party.
|
||||
*
|
||||
* @throws IllegalStateException if the anonymous party is not owned by the full party.
|
||||
* @throws UnknownAnonymousPartyException if the anonymous party is not owned by the full party.
|
||||
*/
|
||||
@Throws(IllegalStateException::class)
|
||||
fun assertOwnership(party: Party, anonymousParty: AnonymousParty)
|
||||
@Throws(UnknownAnonymousPartyException::class)
|
||||
fun assertOwnership(party: Party, anonymousParty: AnonymousParty) {
|
||||
val anonymousIdentity = certificateFromKey(anonymousParty.owningKey)
|
||||
?: throw UnknownAnonymousPartyException("Unknown $anonymousParty")
|
||||
val issuingCert = anonymousIdentity.certPath.certificates[1]
|
||||
require(issuingCert.publicKey == party.owningKey) {
|
||||
"Issuing certificate's public key must match the party key ${party.owningKey.toStringShort()}."
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all identities known to the service. This is expensive, and [partyFromKey] or [partyFromX500Name] should be
|
||||
@ -63,7 +71,7 @@ interface IdentityService {
|
||||
* @param key The owning [PublicKey] of the [Party].
|
||||
* @return Returns a [Party] with a matching owningKey if known, else returns null.
|
||||
*/
|
||||
fun partyFromKey(key: PublicKey): Party?
|
||||
fun partyFromKey(key: PublicKey): Party? = certificateFromKey(key)?.party
|
||||
|
||||
/**
|
||||
* Resolves a party name to the well known identity [Party] instance for this name. Where possible well known identity
|
||||
@ -82,7 +90,21 @@ interface IdentityService {
|
||||
* @param party identity to determine well known identity for.
|
||||
* @return well known identity, if found.
|
||||
*/
|
||||
fun wellKnownPartyFromAnonymous(party: AbstractParty): Party?
|
||||
fun wellKnownPartyFromAnonymous(party: AbstractParty): Party? {
|
||||
// The original version of this would return the party as-is if it was a Party (rather than AnonymousParty),
|
||||
// however that means that we don't verify that we know who owns the key. As such as now enforce turning the key
|
||||
// into a party, and from there figure out the well known party.
|
||||
val candidate = partyFromKey(party.owningKey)
|
||||
// TODO: This should be done via the network map cache, which is the authoritative source of well known identities
|
||||
return if (candidate != null) {
|
||||
require(party.nameOrNull() == null || party.nameOrNull() == candidate.name) {
|
||||
"Candidate party $candidate does not match expected $party"
|
||||
}
|
||||
wellKnownPartyFromX500Name(candidate.name)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a (optionally) confidential identity to the corresponding well known identity [Party].
|
||||
@ -93,7 +115,7 @@ interface IdentityService {
|
||||
* @param partyRef identity (and reference, which is unused) to determine well known identity for.
|
||||
* @return the well known identity, or null if unknown.
|
||||
*/
|
||||
fun wellKnownPartyFromAnonymous(partyRef: PartyAndReference) = wellKnownPartyFromAnonymous(partyRef.party)
|
||||
fun wellKnownPartyFromAnonymous(partyRef: PartyAndReference): Party? = wellKnownPartyFromAnonymous(partyRef.party)
|
||||
|
||||
/**
|
||||
* Resolve the well known identity of a party. Throws an exception if the party cannot be identified.
|
||||
@ -102,7 +124,10 @@ interface IdentityService {
|
||||
* @return the well known identity.
|
||||
* @throws IllegalArgumentException
|
||||
*/
|
||||
fun requireWellKnownPartyFromAnonymous(party: AbstractParty): Party
|
||||
fun requireWellKnownPartyFromAnonymous(party: AbstractParty): Party {
|
||||
return wellKnownPartyFromAnonymous(party)
|
||||
?: throw IllegalStateException("Could not deanonymise party ${party.owningKey.toStringShort()}")
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of candidate matches for a given string, with optional fuzzy(ish) matching. Fuzzy matching may
|
||||
|
@ -6,6 +6,9 @@ release, see :doc:`upgrade-notes`.
|
||||
|
||||
Unreleased
|
||||
----------
|
||||
* Docs for IdentityService. assertOwnership updated to correctly state that an UnknownAnonymousPartyException is thrown
|
||||
rather than IllegalStateException.
|
||||
|
||||
* The Corda JPA entities no longer implement java.io.Serializable, as this was causing persistence errors in obscure cases.
|
||||
Java serialization is disabled globally in the node, but in the unlikely event you were relying on these types being Java serializable please contact us.
|
||||
|
||||
|
@ -1,11 +1,78 @@
|
||||
package net.corda.node.services.api
|
||||
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.internal.CertRole
|
||||
import net.corda.core.node.services.IdentityService
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
import net.corda.nodeapi.internal.crypto.x509Certificates
|
||||
import java.security.InvalidAlgorithmParameterException
|
||||
import java.security.cert.CertPathValidatorException
|
||||
import java.security.cert.CertificateExpiredException
|
||||
import java.security.cert.CertificateNotYetValidException
|
||||
import java.security.cert.TrustAnchor
|
||||
|
||||
interface IdentityServiceInternal : IdentityService {
|
||||
|
||||
private companion object {
|
||||
val log = contextLogger()
|
||||
}
|
||||
|
||||
/** This method exists so it can be mocked with doNothing, rather than having to make up a possibly invalid return value. */
|
||||
fun justVerifyAndRegisterIdentity(identity: PartyAndCertificate) {
|
||||
verifyAndRegisterIdentity(identity)
|
||||
}
|
||||
|
||||
fun partiesFromName(query: String, exactMatch: Boolean, x500name: CordaX500Name, results: LinkedHashSet<Party>, party: Party) {
|
||||
val components = listOfNotNull(x500name.commonName, x500name.organisationUnit, x500name.organisation, x500name.locality, x500name.state, x500name.country)
|
||||
components.forEach { component ->
|
||||
if (exactMatch && component == query) {
|
||||
results += party
|
||||
} else if (!exactMatch) {
|
||||
// We can imagine this being a query over a lucene index in future.
|
||||
//
|
||||
// Kostas says: We can easily use the Jaro-Winkler distance metric as it is best suited for short
|
||||
// strings such as entity/company names, and to detect small typos. We can also apply it for city
|
||||
// or any keyword related search in lists of records (not raw text - for raw text we need indexing)
|
||||
// and we can return results in hierarchical order (based on normalised String similarity 0.0-1.0).
|
||||
if (component.contains(query, ignoreCase = true))
|
||||
results += party
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that an identity is valid.
|
||||
*
|
||||
* @param trustAnchor The trust anchor that will verify the identity's validity
|
||||
* @param identity The identity to verify
|
||||
*/
|
||||
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
|
||||
fun verifyAndRegisterIdentity(trustAnchor: TrustAnchor, identity: PartyAndCertificate): PartyAndCertificate? {
|
||||
// Validate the chain first, before we do anything clever with it
|
||||
val identityCertChain = identity.certPath.x509Certificates
|
||||
try {
|
||||
identity.verify(trustAnchor)
|
||||
} catch (e: CertPathValidatorException) {
|
||||
log.warn("Certificate validation failed for ${identity.name} against trusted root ${trustAnchor.trustedCert.subjectX500Principal}.")
|
||||
log.warn("Certificate path :")
|
||||
identityCertChain.reversed().forEachIndexed { index, certificate ->
|
||||
val space = (0 until index).joinToString("") { " " }
|
||||
log.warn("$space${certificate.subjectX500Principal}")
|
||||
}
|
||||
throw e
|
||||
}
|
||||
// Ensure we record the first identity of the same name, first
|
||||
val wellKnownCert = identityCertChain.single { CertRole.extract(it)?.isWellKnown ?: false }
|
||||
if (wellKnownCert != identity.certificate) {
|
||||
val idx = identityCertChain.lastIndexOf(wellKnownCert)
|
||||
val firstPath = X509Utilities.buildCertPath(identityCertChain.slice(idx until identityCertChain.size))
|
||||
verifyAndRegisterIdentity(trustAnchor, PartyAndCertificate(firstPath))
|
||||
}
|
||||
return registerIdentity(identity)
|
||||
}
|
||||
|
||||
fun registerIdentity(identity: PartyAndCertificate): PartyAndCertificate?
|
||||
}
|
||||
|
@ -1,24 +0,0 @@
|
||||
package net.corda.node.services.identity
|
||||
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
|
||||
|
||||
fun partiesFromName(query: String, exactMatch: Boolean, x500name: CordaX500Name, results: LinkedHashSet<Party>, party: Party) {
|
||||
|
||||
val components = listOfNotNull(x500name.commonName, x500name.organisationUnit, x500name.organisation, x500name.locality, x500name.state, x500name.country)
|
||||
components.forEach { component ->
|
||||
if (exactMatch && component == query) {
|
||||
results += party
|
||||
} else if (!exactMatch) {
|
||||
// We can imagine this being a query over a lucene index in future.
|
||||
//
|
||||
// Kostas says: We can easily use the Jaro-Winkler distance metric as it is best suited for short
|
||||
// strings such as entity/company names, and to detect small typos. We can also apply it for city
|
||||
// or any keyword related search in lists of records (not raw text - for raw text we need indexing)
|
||||
// and we can return results in hierarchical order (based on normalised String similarity 0.0-1.0).
|
||||
if (component.contains(query, ignoreCase = true))
|
||||
results += party
|
||||
}
|
||||
}
|
||||
}
|
@ -1,15 +1,12 @@
|
||||
package net.corda.node.services.identity
|
||||
|
||||
import net.corda.core.contracts.PartyAndReference
|
||||
import net.corda.core.crypto.toStringShort
|
||||
import net.corda.core.identity.*
|
||||
import net.corda.core.internal.CertRole
|
||||
import net.corda.core.node.services.IdentityService
|
||||
import net.corda.core.node.services.UnknownAnonymousPartyException
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.core.utilities.trace
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
import net.corda.node.services.api.IdentityServiceInternal
|
||||
import net.corda.nodeapi.internal.crypto.x509Certificates
|
||||
import java.security.InvalidAlgorithmParameterException
|
||||
import java.security.PublicKey
|
||||
@ -22,10 +19,9 @@ import javax.annotation.concurrent.ThreadSafe
|
||||
*
|
||||
* @param identities initial set of identities for the service, typically only used for unit tests.
|
||||
*/
|
||||
// TODO There is duplicated logic between this and PersistentIdentityService
|
||||
@ThreadSafe
|
||||
class InMemoryIdentityService(identities: List<PartyAndCertificate> = emptyList(),
|
||||
override val trustRoot: X509Certificate) : SingletonSerializeAsToken(), IdentityService {
|
||||
override val trustRoot: X509Certificate) : SingletonSerializeAsToken(), IdentityServiceInternal {
|
||||
companion object {
|
||||
private val log = contextLogger()
|
||||
}
|
||||
@ -44,29 +40,10 @@ class InMemoryIdentityService(identities: List<PartyAndCertificate> = emptyList(
|
||||
}
|
||||
|
||||
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
|
||||
override fun verifyAndRegisterIdentity(identity: PartyAndCertificate): PartyAndCertificate? {
|
||||
// Validate the chain first, before we do anything clever with it
|
||||
override fun verifyAndRegisterIdentity(identity: PartyAndCertificate): PartyAndCertificate? = verifyAndRegisterIdentity(trustAnchor, identity)
|
||||
|
||||
override fun registerIdentity(identity: PartyAndCertificate): PartyAndCertificate? {
|
||||
val identityCertChain = identity.certPath.x509Certificates
|
||||
try {
|
||||
identity.verify(trustAnchor)
|
||||
} catch (e: CertPathValidatorException) {
|
||||
log.warn("Certificate validation failed for ${identity.name} against trusted root ${trustAnchor.trustedCert.subjectX500Principal}.")
|
||||
log.warn("Certificate path :")
|
||||
identityCertChain.reversed().forEachIndexed { index, certificate ->
|
||||
val space = (0 until index).joinToString("") { " " }
|
||||
log.warn("$space${certificate.subjectX500Principal}")
|
||||
}
|
||||
throw e
|
||||
}
|
||||
|
||||
// Ensure we record the first identity of the same name, first
|
||||
val wellKnownCert = identityCertChain.single { CertRole.extract(it)?.isWellKnown ?: false }
|
||||
if (wellKnownCert != identity.certificate) {
|
||||
val idx = identityCertChain.lastIndexOf(wellKnownCert)
|
||||
val firstPath = X509Utilities.buildCertPath(identityCertChain.slice(idx until identityCertChain.size))
|
||||
verifyAndRegisterIdentity(PartyAndCertificate(firstPath))
|
||||
}
|
||||
|
||||
log.trace { "Registering identity $identity" }
|
||||
keyToParties[identity.owningKey] = identity
|
||||
// Always keep the first party we registered, as that's the well known identity
|
||||
@ -79,26 +56,7 @@ class InMemoryIdentityService(identities: List<PartyAndCertificate> = emptyList(
|
||||
// We give the caller a copy of the data set to avoid any locking problems
|
||||
override fun getAllIdentities(): Iterable<PartyAndCertificate> = ArrayList(keyToParties.values)
|
||||
|
||||
override fun partyFromKey(key: PublicKey): Party? = keyToParties[key]?.party
|
||||
override fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? = principalToParties[name]?.party
|
||||
override fun wellKnownPartyFromAnonymous(party: AbstractParty): Party? {
|
||||
// The original version of this would return the party as-is if it was a Party (rather than AnonymousParty),
|
||||
// however that means that we don't verify that we know who owns the key. As such as now enforce turning the key
|
||||
// into a party, and from there figure out the well known party.
|
||||
val candidate = partyFromKey(party.owningKey)
|
||||
// TODO: This should be done via the network map cache, which is the authoritative source of well known identities
|
||||
return if (candidate != null) {
|
||||
require(party.nameOrNull() == null || party.nameOrNull() == candidate.name) { "Candidate party $candidate does not match expected $party" }
|
||||
wellKnownPartyFromX500Name(candidate.name)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun wellKnownPartyFromAnonymous(partyRef: PartyAndReference) = wellKnownPartyFromAnonymous(partyRef.party)
|
||||
override fun requireWellKnownPartyFromAnonymous(party: AbstractParty): Party {
|
||||
return wellKnownPartyFromAnonymous(party) ?: throw IllegalStateException("Could not deanonymise party ${party.owningKey.toStringShort()}")
|
||||
}
|
||||
|
||||
override fun partiesFromName(query: String, exactMatch: Boolean): Set<Party> {
|
||||
val results = LinkedHashSet<Party>()
|
||||
@ -107,14 +65,4 @@ class InMemoryIdentityService(identities: List<PartyAndCertificate> = emptyList(
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
@Throws(UnknownAnonymousPartyException::class)
|
||||
override fun assertOwnership(party: Party, anonymousParty: AnonymousParty) {
|
||||
val anonymousIdentity = keyToParties[anonymousParty.owningKey] ?:
|
||||
throw UnknownAnonymousPartyException("Unknown $anonymousParty")
|
||||
val issuingCert = anonymousIdentity.certPath.certificates[1]
|
||||
require(issuingCert.publicKey == party.owningKey) {
|
||||
"Issuing certificate's public key must match the party key ${party.owningKey.toStringShort()}."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,7 @@
|
||||
package net.corda.node.services.identity
|
||||
|
||||
import net.corda.core.contracts.PartyAndReference
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.toStringShort
|
||||
import net.corda.core.identity.*
|
||||
import net.corda.core.internal.CertRole
|
||||
import net.corda.core.internal.hash
|
||||
import net.corda.core.node.services.UnknownAnonymousPartyException
|
||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||
@ -14,7 +11,6 @@ import net.corda.core.utilities.debug
|
||||
import net.corda.node.services.api.IdentityServiceInternal
|
||||
import net.corda.node.utilities.AppendOnlyPersistentMap
|
||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
import net.corda.nodeapi.internal.crypto.x509Certificates
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
|
||||
@ -35,7 +31,6 @@ import javax.persistence.Lob
|
||||
* @param trustRoot certificate from the zone operator for identity on the network.
|
||||
* @param caCertificates list of additional certificates.
|
||||
*/
|
||||
// TODO There is duplicated logic between this and InMemoryIdentityService
|
||||
@ThreadSafe
|
||||
class PersistentIdentityService(override val trustRoot: X509Certificate,
|
||||
private val database: CordaPersistence,
|
||||
@ -127,38 +122,21 @@ class PersistentIdentityService(override val trustRoot: X509Certificate,
|
||||
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
|
||||
override fun verifyAndRegisterIdentity(identity: PartyAndCertificate): PartyAndCertificate? {
|
||||
return database.transaction {
|
||||
|
||||
// Validate the chain first, before we do anything clever with it
|
||||
val identityCertChain = identity.certPath.x509Certificates
|
||||
try {
|
||||
identity.verify(trustAnchor)
|
||||
} catch (e: CertPathValidatorException) {
|
||||
log.warn(e.localizedMessage)
|
||||
log.warn("Path = ")
|
||||
identityCertChain.reversed().forEach {
|
||||
log.warn(it.subjectX500Principal.toString())
|
||||
}
|
||||
throw e
|
||||
}
|
||||
|
||||
// Ensure we record the first identity of the same name, first
|
||||
val wellKnownCert = identityCertChain.single { CertRole.extract(it)?.isWellKnown ?: false }
|
||||
if (wellKnownCert != identity.certificate) {
|
||||
val idx = identityCertChain.lastIndexOf(wellKnownCert)
|
||||
val firstPath = X509Utilities.buildCertPath(identityCertChain.slice(idx until identityCertChain.size))
|
||||
verifyAndRegisterIdentity(PartyAndCertificate(firstPath))
|
||||
}
|
||||
|
||||
log.debug { "Registering identity $identity" }
|
||||
val key = mapToKey(identity)
|
||||
keyToParties.addWithDuplicatesAllowed(key, identity)
|
||||
// Always keep the first party we registered, as that's the well known identity
|
||||
principalToParties.addWithDuplicatesAllowed(identity.name, key, false)
|
||||
val parentId = mapToKey(identityCertChain[1].publicKey)
|
||||
keyToParties[parentId]
|
||||
verifyAndRegisterIdentity(trustAnchor, identity)
|
||||
}
|
||||
}
|
||||
|
||||
override fun registerIdentity(identity: PartyAndCertificate): PartyAndCertificate? {
|
||||
val identityCertChain = identity.certPath.x509Certificates
|
||||
log.debug { "Registering identity $identity" }
|
||||
val key = mapToKey(identity)
|
||||
keyToParties.addWithDuplicatesAllowed(key, identity)
|
||||
// Always keep the first party we registered, as that's the well known identity
|
||||
principalToParties.addWithDuplicatesAllowed(identity.name, key, false)
|
||||
val parentId = mapToKey(identityCertChain[1].publicKey)
|
||||
return keyToParties[parentId]
|
||||
}
|
||||
|
||||
override fun certificateFromKey(owningKey: PublicKey): PartyAndCertificate? = database.transaction { keyToParties[mapToKey(owningKey)] }
|
||||
|
||||
private fun certificateFromCordaX500Name(name: CordaX500Name): PartyAndCertificate? {
|
||||
@ -173,27 +151,9 @@ class PersistentIdentityService(override val trustRoot: X509Certificate,
|
||||
// We give the caller a copy of the data set to avoid any locking problems
|
||||
override fun getAllIdentities(): Iterable<PartyAndCertificate> = database.transaction { keyToParties.allPersisted().map { it.second }.asIterable() }
|
||||
|
||||
override fun partyFromKey(key: PublicKey): Party? = certificateFromKey(key)?.party
|
||||
override fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? = certificateFromCordaX500Name(name)?.party
|
||||
override fun wellKnownPartyFromAnonymous(party: AbstractParty): Party? {
|
||||
return database.transaction {
|
||||
// The original version of this would return the party as-is if it was a Party (rather than AnonymousParty),
|
||||
// however that means that we don't verify that we know who owns the key. As such as now enforce turning the key
|
||||
// into a party, and from there figure out the well known party.
|
||||
val candidate = partyFromKey(party.owningKey)
|
||||
// TODO: This should be done via the network map cache, which is the authoritative source of well known identities
|
||||
if (candidate != null) {
|
||||
wellKnownPartyFromX500Name(candidate.name)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun wellKnownPartyFromAnonymous(partyRef: PartyAndReference) = wellKnownPartyFromAnonymous(partyRef.party)
|
||||
override fun requireWellKnownPartyFromAnonymous(party: AbstractParty): Party {
|
||||
return wellKnownPartyFromAnonymous(party) ?: throw IllegalStateException("Could not deanonymise party ${party.owningKey.toStringShort()}")
|
||||
}
|
||||
override fun wellKnownPartyFromAnonymous(party: AbstractParty): Party? = database.transaction { super.wellKnownPartyFromAnonymous(party) }
|
||||
|
||||
override fun partiesFromName(query: String, exactMatch: Boolean): Set<Party> {
|
||||
return database.transaction {
|
||||
@ -206,13 +166,6 @@ class PersistentIdentityService(override val trustRoot: X509Certificate,
|
||||
}
|
||||
|
||||
@Throws(UnknownAnonymousPartyException::class)
|
||||
override fun assertOwnership(party: Party, anonymousParty: AnonymousParty) {
|
||||
database.transaction {
|
||||
val anonymousIdentity = certificateFromKey(anonymousParty.owningKey) ?: throw UnknownAnonymousPartyException("Unknown $anonymousParty")
|
||||
val issuingCert = anonymousIdentity.certPath.certificates[1]
|
||||
require(issuingCert.publicKey == party.owningKey) {
|
||||
"Issuing certificate's public key must match the party key ${party.owningKey.toStringShort()}."
|
||||
}
|
||||
}
|
||||
}
|
||||
override fun assertOwnership(party: Party, anonymousParty: AnonymousParty) = database.transaction { super.assertOwnership(party, anonymousParty) }
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user