mirror of
https://github.com/corda/corda.git
synced 2025-05-21 01:37:41 +00:00
Creating a PartyAndCertificate only requires a CertPath
This commit is contained in:
parent
3860b22339
commit
dc8d232480
@ -1,6 +1,7 @@
|
||||
@file:JvmName("X500NameUtils")
|
||||
package net.corda.core.crypto
|
||||
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
import org.bouncycastle.asn1.ASN1Encodable
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import org.bouncycastle.asn1.x500.X500NameBuilder
|
||||
@ -57,7 +58,7 @@ val X500Name.locationOrNull: String? get() = try {
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
val X509Certificate.subject: X500Name get() = X509CertificateHolder(encoded).subject
|
||||
val X509Certificate.subject: X500Name get() = toX509CertHolder().subject
|
||||
val X509CertificateHolder.cert: X509Certificate get() = JcaX509CertificateConverter().getCertificate(this)
|
||||
|
||||
/**
|
||||
|
@ -13,8 +13,7 @@ import java.security.PublicKey
|
||||
@CordaSerializable
|
||||
abstract class AbstractParty(val owningKey: PublicKey) {
|
||||
/** Anonymised parties do not include any detail apart from owning key, so equality is dependent solely on the key */
|
||||
override fun equals(other: Any?): Boolean = other is AbstractParty && this.owningKey == other.owningKey
|
||||
|
||||
override fun equals(other: Any?): Boolean = other === this || other is AbstractParty && other.owningKey == owningKey
|
||||
override fun hashCode(): Int = owningKey.hashCode()
|
||||
abstract fun nameOrNull(): X500Name?
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
package net.corda.core.identity
|
||||
|
||||
import net.corda.core.contracts.PartyAndReference
|
||||
import net.corda.core.crypto.toBase58String
|
||||
import net.corda.core.crypto.toStringShort
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
@ -12,11 +11,7 @@ import java.security.PublicKey
|
||||
* information such as name. It is intended to represent a party on the distributed ledger.
|
||||
*/
|
||||
class AnonymousParty(owningKey: PublicKey) : AbstractParty(owningKey) {
|
||||
// Use the key as the bulk of the toString(), but include a human readable identifier as well, so that [Party]
|
||||
// can put in the key and actual name
|
||||
override fun toString() = "${owningKey.toStringShort()} <Anonymous>"
|
||||
|
||||
override fun nameOrNull(): X500Name? = null
|
||||
|
||||
override fun ref(bytes: OpaqueBytes): PartyAndReference = PartyAndReference(this, bytes)
|
||||
override fun toString() = "Anonymous(${owningKey.toStringShort()})"
|
||||
}
|
@ -1,9 +1,11 @@
|
||||
package net.corda.core.identity
|
||||
|
||||
import net.corda.core.contracts.PartyAndReference
|
||||
import net.corda.core.crypto.CertificateAndKeyPair
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.composite.CompositeKey
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import java.security.PublicKey
|
||||
|
||||
/**
|
||||
@ -26,10 +28,9 @@ import java.security.PublicKey
|
||||
* @see CompositeKey
|
||||
*/
|
||||
class Party(val name: X500Name, owningKey: PublicKey) : AbstractParty(owningKey) {
|
||||
constructor(certAndKey: CertificateAndKeyPair) : this(certAndKey.certificate.subject, certAndKey.keyPair.public)
|
||||
override fun toString() = name.toString()
|
||||
override fun nameOrNull(): X500Name? = name
|
||||
|
||||
constructor(certificate: X509CertificateHolder) : this(certificate.subject, Crypto.toSupportedPublicKey(certificate.subjectPublicKeyInfo))
|
||||
override fun nameOrNull(): X500Name = name
|
||||
fun anonymise(): AnonymousParty = AnonymousParty(owningKey)
|
||||
override fun ref(bytes: OpaqueBytes): PartyAndReference = PartyAndReference(this, bytes)
|
||||
override fun toString() = name.toString()
|
||||
}
|
||||
|
@ -1,49 +1,42 @@
|
||||
package net.corda.core.identity
|
||||
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import java.security.PublicKey
|
||||
import java.security.cert.*
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* A full party plus the X.509 certificate and path linking the party back to a trust root. Equality of
|
||||
* [PartyAndCertificate] instances is based on the party only, as certificate and path are data associated with the party,
|
||||
* not part of the identifier themselves. While party and certificate can both be derived from the certificate path,
|
||||
* this class exists in order to ensure the implementation classes of certificates and party public keys are kept stable.
|
||||
* not part of the identifier themselves.
|
||||
*/
|
||||
@CordaSerializable
|
||||
data class PartyAndCertificate(val party: Party,
|
||||
val certificate: X509CertificateHolder,
|
||||
val certPath: CertPath) {
|
||||
constructor(name: X500Name, owningKey: PublicKey, certificate: X509CertificateHolder, certPath: CertPath) : this(Party(name, owningKey), certificate, certPath)
|
||||
val name: X500Name
|
||||
get() = party.name
|
||||
val owningKey: PublicKey
|
||||
get() = party.owningKey
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return if (other is PartyAndCertificate)
|
||||
party == other.party
|
||||
else
|
||||
false
|
||||
//TODO Is VerifiableIdentity a better name?
|
||||
class PartyAndCertificate(val certPath: CertPath) {
|
||||
@Transient val certificate: X509CertificateHolder
|
||||
init {
|
||||
require(certPath.type == "X.509") { "Only X.509 certificates supported" }
|
||||
val certs = certPath.certificates
|
||||
require(certs.size >= 2) { "Certificate path must at least include subject and issuing certificates" }
|
||||
certificate = certs[0].toX509CertHolder()
|
||||
}
|
||||
|
||||
@Transient val party: Party = Party(certificate)
|
||||
|
||||
val owningKey: PublicKey get() = party.owningKey
|
||||
val name: X500Name get() = party.name
|
||||
|
||||
operator fun component1(): Party = party
|
||||
operator fun component2(): X509CertificateHolder = certificate
|
||||
|
||||
override fun equals(other: Any?): Boolean = other === this || other is PartyAndCertificate && other.party == party
|
||||
override fun hashCode(): Int = party.hashCode()
|
||||
override fun toString(): String = party.toString()
|
||||
|
||||
/**
|
||||
* Verify that the given certificate path is valid and leads to the owning key of the party.
|
||||
*/
|
||||
/** Verify the certificate path is valid. */
|
||||
fun verify(trustAnchor: TrustAnchor): PKIXCertPathValidatorResult {
|
||||
require(certPath.certificates.first() is X509Certificate) { "Subject certificate must be an X.509 certificate" }
|
||||
require(Arrays.equals(party.owningKey.encoded, certificate.subjectPublicKeyInfo.encoded)) { "Certificate public key must match party owning key" }
|
||||
require(Arrays.equals(certPath.certificates.first().encoded, certificate.encoded)) { "Certificate path must link to certificate" }
|
||||
|
||||
val validatorParameters = PKIXParameters(setOf(trustAnchor))
|
||||
val parameters = PKIXParameters(setOf(trustAnchor)).apply { isRevocationEnabled = false }
|
||||
val validator = CertPathValidator.getInstance("PKIX")
|
||||
validatorParameters.isRevocationEnabled = false
|
||||
return validator.validate(certPath, validatorParameters) as PKIXCertPathValidatorResult
|
||||
return validator.validate(certPath, parameters) as PKIXCertPathValidatorResult
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package net.corda.core.internal
|
||||
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.sha256
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import org.slf4j.Logger
|
||||
import rx.Observable
|
||||
import rx.Observer
|
||||
@ -165,6 +166,9 @@ fun <T> logElapsedTime(label: String, logger: Logger? = null, body: () -> T): T
|
||||
}
|
||||
}
|
||||
|
||||
fun java.security.cert.Certificate.toX509CertHolder() = X509CertificateHolder(encoded)
|
||||
fun javax.security.cert.Certificate.toX509CertHolder() = X509CertificateHolder(encoded)
|
||||
|
||||
/** Convert a [ByteArrayOutputStream] to [InputStreamAndHash]. */
|
||||
fun ByteArrayOutputStream.toInputStreamAndHash(): InputStreamAndHash {
|
||||
val bytes = toByteArray()
|
||||
|
@ -24,16 +24,17 @@ data class NodeInfo(val addresses: List<NetworkHostAndPort>,
|
||||
val legalIdentityAndCert: PartyAndCertificate, //TODO This field will be removed in future PR which gets rid of services.
|
||||
val legalIdentitiesAndCerts: NonEmptySet<PartyAndCertificate>,
|
||||
val platformVersion: Int,
|
||||
var advertisedServices: List<ServiceEntry> = emptyList(),
|
||||
val advertisedServices: List<ServiceEntry> = emptyList(),
|
||||
val worldMapLocation: WorldMapLocation? = null) {
|
||||
init {
|
||||
require(advertisedServices.none { it.identity == legalIdentityAndCert }) { "Service identities must be different from node legal identity" }
|
||||
require(advertisedServices.none { it.identity == legalIdentityAndCert }) {
|
||||
"Service identities must be different from node legal identity"
|
||||
}
|
||||
}
|
||||
val legalIdentity: Party
|
||||
get() = legalIdentityAndCert.party
|
||||
val notaryIdentity: Party
|
||||
get() = advertisedServices.single { it.info.type.isNotary() }.identity.party
|
||||
|
||||
val legalIdentity: Party get() = legalIdentityAndCert.party
|
||||
val notaryIdentity: Party get() = advertisedServices.single { it.info.type.isNotary() }.identity.party
|
||||
fun serviceIdentities(type: ServiceType): List<Party> {
|
||||
return advertisedServices.filter { it.info.type.isSubTypeOf(type) }.map { it.identity.party }
|
||||
return advertisedServices.mapNotNull { if (it.info.type.isSubTypeOf(type)) it.identity.party else null }
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
package net.corda.core.node.services
|
||||
|
||||
import net.corda.core.contracts.PartyAndReference
|
||||
import net.corda.core.identity.*
|
||||
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
|
||||
@ -114,6 +117,6 @@ interface IdentityService {
|
||||
* @param exactMatch If true, a case sensitive match is done against each component of each X.500 name.
|
||||
*/
|
||||
fun partiesFromName(query: String, exactMatch: Boolean): Set<Party>
|
||||
|
||||
class UnknownAnonymousPartyException(msg: String) : Exception(msg)
|
||||
}
|
||||
|
||||
class UnknownAnonymousPartyException(msg: String) : Exception(msg)
|
||||
|
@ -0,0 +1,26 @@
|
||||
package net.corda.core.identity
|
||||
|
||||
import net.corda.core.crypto.entropyToKeyPair
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.testing.getTestPartyAndCertificate
|
||||
import net.corda.testing.withTestSerialization
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import org.junit.Test
|
||||
import java.math.BigInteger
|
||||
|
||||
class PartyAndCertificateTest {
|
||||
@Test
|
||||
fun `kryo serialisation`() {
|
||||
withTestSerialization {
|
||||
val original = getTestPartyAndCertificate(Party(
|
||||
X500Name("CN=Test Corp,O=Test Corp,L=Madrid,C=ES"),
|
||||
entropyToKeyPair(BigInteger.valueOf(83)).public))
|
||||
val copy = original.serialize().deserialize()
|
||||
assertThat(copy).isEqualTo(original).isNotSameAs(original)
|
||||
assertThat(copy.certPath).isEqualTo(original.certPath)
|
||||
assertThat(copy.certificate).isEqualTo(original.certificate)
|
||||
}
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ import de.javakaffee.kryoserializers.BitSetSerializer
|
||||
import de.javakaffee.kryoserializers.UnmodifiableCollectionsSerializer
|
||||
import de.javakaffee.kryoserializers.guava.*
|
||||
import net.corda.core.crypto.composite.CompositeKey
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.node.CordaPluginRegistry
|
||||
import net.corda.core.serialization.SerializeAsToken
|
||||
import net.corda.core.serialization.SerializedBytes
|
||||
@ -59,6 +60,12 @@ object DefaultKryoCustomizer {
|
||||
|
||||
instantiatorStrategy = CustomInstantiatorStrategy()
|
||||
|
||||
// Required for HashCheckingStream (de)serialization.
|
||||
// Note that return type should be specifically set to InputStream, otherwise it may not work, i.e. val aStream : InputStream = HashCheckingStream(...).
|
||||
addDefaultSerializer(InputStream::class.java, InputStreamSerializer)
|
||||
addDefaultSerializer(SerializeAsToken::class.java, SerializeAsTokenSerializer<SerializeAsToken>())
|
||||
addDefaultSerializer(Logger::class.java, LoggerSerializer)
|
||||
|
||||
// WARNING: reordering the registrations here will cause a change in the serialized form, since classes
|
||||
// with custom serializers get written as registration ids. This will break backwards-compatibility.
|
||||
// Please add any new registrations to the end.
|
||||
@ -68,50 +75,31 @@ object DefaultKryoCustomizer {
|
||||
register(SignedTransaction::class.java, SignedTransactionSerializer)
|
||||
register(WireTransaction::class.java, WireTransactionSerializer)
|
||||
register(SerializedBytes::class.java, SerializedBytesSerializer)
|
||||
|
||||
UnmodifiableCollectionsSerializer.registerSerializers(this)
|
||||
ImmutableListSerializer.registerSerializers(this)
|
||||
ImmutableSetSerializer.registerSerializers(this)
|
||||
ImmutableSortedSetSerializer.registerSerializers(this)
|
||||
ImmutableMapSerializer.registerSerializers(this)
|
||||
ImmutableMultimapSerializer.registerSerializers(this)
|
||||
|
||||
// InputStream subclasses whitelisting, required for attachments.
|
||||
register(BufferedInputStream::class.java, InputStreamSerializer)
|
||||
register(Class.forName("sun.net.www.protocol.jar.JarURLConnection\$JarURLInputStream"), InputStreamSerializer)
|
||||
|
||||
noReferencesWithin<WireTransaction>()
|
||||
|
||||
register(ECPublicKeyImpl::class.java, ECPublicKeyImplSerializer)
|
||||
register(EdDSAPublicKey::class.java, Ed25519PublicKeySerializer)
|
||||
register(EdDSAPrivateKey::class.java, Ed25519PrivateKeySerializer)
|
||||
|
||||
// Using a custom serializer for compactness
|
||||
register(CompositeKey::class.java, CompositeKeySerializer)
|
||||
|
||||
register(CompositeKey::class.java, CompositeKeySerializer) // Using a custom serializer for compactness
|
||||
// Exceptions. We don't bother sending the stack traces as the client will fill in its own anyway.
|
||||
register(Array<StackTraceElement>::class, read = { _, _ -> emptyArray() }, write = { _, _, _ -> })
|
||||
|
||||
// This ensures a NonEmptySetSerializer is constructed with an initial value.
|
||||
register(NonEmptySet::class.java, NonEmptySetSerializer)
|
||||
|
||||
addDefaultSerializer(SerializeAsToken::class.java, SerializeAsTokenSerializer<SerializeAsToken>())
|
||||
|
||||
register(BitSet::class.java, BitSetSerializer())
|
||||
register(Class::class.java, ClassSerializer)
|
||||
|
||||
addDefaultSerializer(Logger::class.java, LoggerSerializer)
|
||||
|
||||
register(FileInputStream::class.java, InputStreamSerializer)
|
||||
// Required for HashCheckingStream (de)serialization.
|
||||
// Note that return type should be specifically set to InputStream, otherwise it may not work, i.e. val aStream : InputStream = HashCheckingStream(...).
|
||||
addDefaultSerializer(InputStream::class.java, InputStreamSerializer)
|
||||
|
||||
register(CertPath::class.java, CertPathSerializer)
|
||||
register(X509CertPath::class.java, CertPathSerializer)
|
||||
register(X500Name::class.java, X500NameSerializer)
|
||||
register(X509CertificateHolder::class.java, X509CertificateSerializer)
|
||||
|
||||
register(BCECPrivateKey::class.java, PrivateKeySerializer)
|
||||
register(BCECPublicKey::class.java, PublicKeySerializer)
|
||||
register(BCRSAPrivateCrtKey::class.java, PrivateKeySerializer)
|
||||
@ -119,8 +107,8 @@ object DefaultKryoCustomizer {
|
||||
register(BCSphincs256PrivateKey::class.java, PrivateKeySerializer)
|
||||
register(BCSphincs256PublicKey::class.java, PublicKeySerializer)
|
||||
register(sun.security.ec.ECPublicKeyImpl::class.java, PublicKeySerializer)
|
||||
|
||||
register(NotaryChangeWireTransaction::class.java, NotaryChangeWireTransactionSerializer)
|
||||
register(PartyAndCertificate::class.java, PartyAndCertificateSerializer)
|
||||
|
||||
val customization = KryoSerializationCustomization(this)
|
||||
pluginRegistries.forEach { it.customizeSerialization(customization) }
|
||||
@ -139,6 +127,15 @@ object DefaultKryoCustomizer {
|
||||
}
|
||||
}
|
||||
|
||||
private object PartyAndCertificateSerializer : Serializer<PartyAndCertificate>() {
|
||||
override fun write(kryo: Kryo, output: Output, obj: PartyAndCertificate) {
|
||||
kryo.writeClassAndObject(output, obj.certPath)
|
||||
}
|
||||
override fun read(kryo: Kryo, input: Input, type: Class<PartyAndCertificate>): PartyAndCertificate {
|
||||
return PartyAndCertificate(kryo.readClassAndObject(input) as CertPath)
|
||||
}
|
||||
}
|
||||
|
||||
private object NonEmptySetSerializer : Serializer<NonEmptySet<Any>>() {
|
||||
override fun write(kryo: Kryo, output: Output, obj: NonEmptySet<Any>) {
|
||||
// Write out the contents as normal
|
||||
|
@ -2,6 +2,7 @@ package net.corda.node.internal
|
||||
|
||||
import com.codahale.metrics.MetricRegistry
|
||||
import com.google.common.annotations.VisibleForTesting
|
||||
import com.google.common.collect.Lists
|
||||
import com.google.common.collect.MutableClassToInstanceMap
|
||||
import com.google.common.util.concurrent.MoreExecutors
|
||||
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner
|
||||
@ -66,7 +67,6 @@ import net.corda.node.utilities.*
|
||||
import net.corda.node.utilities.AddOrRemove.ADD
|
||||
import org.apache.activemq.artemis.utils.ReusableLatch
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import org.slf4j.Logger
|
||||
import rx.Observable
|
||||
import java.io.IOException
|
||||
@ -88,6 +88,9 @@ import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.TimeUnit.SECONDS
|
||||
import java.util.stream.Collectors.toList
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.collections.component1
|
||||
import kotlin.collections.component2
|
||||
import kotlin.collections.set
|
||||
import kotlin.reflect.KClass
|
||||
import net.corda.core.crypto.generateKeyPair as cryptoGenerateKeyPair
|
||||
|
||||
@ -417,8 +420,9 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
||||
checkpointStorage = DBCheckpointStorage()
|
||||
_services = ServiceHubInternalImpl()
|
||||
attachments = NodeAttachmentService(configuration.dataSourceProperties, services.monitoringService.metrics, configuration.database)
|
||||
network = makeMessagingService()
|
||||
info = makeInfo()
|
||||
val legalIdentity = obtainIdentity("identity", configuration.myLegalName)
|
||||
network = makeMessagingService(legalIdentity)
|
||||
info = makeInfo(legalIdentity)
|
||||
|
||||
val tokenizableServices = mutableListOf(attachments, network, services.vaultService, services.vaultQueryService,
|
||||
services.keyManagementService, services.identityService, platformClock, services.schedulerService)
|
||||
@ -486,12 +490,11 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
||||
HibernateObserver(services.vaultService.rawUpdates, HibernateConfiguration(services.schemaService, configuration.database ?: Properties(), {services.identityService}))
|
||||
}
|
||||
|
||||
private fun makeInfo(): NodeInfo {
|
||||
private fun makeInfo(legalIdentity: PartyAndCertificate): NodeInfo {
|
||||
val advertisedServiceEntries = makeServiceEntries()
|
||||
val legalIdentity = obtainLegalIdentity()
|
||||
val allIdentitiesSet = (advertisedServiceEntries.map { it.identity } + legalIdentity).toNonEmptySet()
|
||||
val allIdentities = (advertisedServiceEntries.map { it.identity } + legalIdentity).toNonEmptySet()
|
||||
val addresses = myAddresses() // TODO There is no support for multiple IP addresses yet.
|
||||
return NodeInfo(addresses, legalIdentity, allIdentitiesSet, platformVersion, advertisedServiceEntries, findMyLocation())
|
||||
return NodeInfo(addresses, legalIdentity, allIdentities, platformVersion, advertisedServiceEntries, findMyLocation())
|
||||
}
|
||||
|
||||
/**
|
||||
@ -502,7 +505,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
||||
return advertisedServices.map {
|
||||
val serviceId = it.type.id
|
||||
val serviceName = it.name ?: X500Name("${configuration.myLegalName},OU=$serviceId")
|
||||
val identity = obtainKeyPair(serviceId, serviceName).first
|
||||
val identity = obtainIdentity(serviceId, serviceName)
|
||||
ServiceEntry(it, identity)
|
||||
}
|
||||
}
|
||||
@ -613,8 +616,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
||||
val instant = platformClock.instant()
|
||||
val expires = instant + NetworkMapService.DEFAULT_EXPIRATION_PERIOD
|
||||
val reg = NodeRegistration(info, instant.toEpochMilli(), ADD, expires)
|
||||
val legalIdentityKey = obtainLegalIdentityKey()
|
||||
val request = RegistrationRequest(reg.toWire(services.keyManagementService, legalIdentityKey.public), network.myAddress)
|
||||
val request = RegistrationRequest(reg.toWire(services.keyManagementService, info.legalIdentityAndCert.owningKey), network.myAddress)
|
||||
return network.sendRequest(NetworkMapService.REGISTER_TOPIC, request, networkMapAddress)
|
||||
}
|
||||
|
||||
@ -680,15 +682,11 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
||||
runOnStop.clear()
|
||||
}
|
||||
|
||||
protected abstract fun makeMessagingService(): MessagingService
|
||||
protected abstract fun makeMessagingService(legalIdentity: PartyAndCertificate): MessagingService
|
||||
|
||||
protected abstract fun startMessagingService(rpcOps: RPCOps)
|
||||
|
||||
protected fun obtainLegalIdentity(): PartyAndCertificate = identityKeyPair.first
|
||||
protected fun obtainLegalIdentityKey(): KeyPair = identityKeyPair.second
|
||||
private val identityKeyPair by lazy { obtainKeyPair("identity", configuration.myLegalName) }
|
||||
|
||||
private fun obtainKeyPair(serviceId: String, serviceName: X500Name): Pair<PartyAndCertificate, KeyPair> {
|
||||
private fun obtainIdentity(id: String, name: X500Name): PartyAndCertificate {
|
||||
// Load the private identity key, creating it if necessary. The identity key is a long term well known key that
|
||||
// is distributed to other peers and we use it (or a key signed by it) when we need to do something
|
||||
// "permissioned". The identity file is what gets distributed and contains the node's legal name along with
|
||||
@ -697,50 +695,52 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
||||
|
||||
// TODO: Integrate with Key management service?
|
||||
val keyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword)
|
||||
val privateKeyAlias = "$serviceId-private-key"
|
||||
val compositeKeyAlias = "$serviceId-composite-key"
|
||||
val privateKeyAlias = "$id-private-key"
|
||||
val compositeKeyAlias = "$id-composite-key"
|
||||
|
||||
if (!keyStore.containsAlias(privateKeyAlias)) {
|
||||
val privKeyFile = configuration.baseDirectory / privateKeyAlias
|
||||
val pubIdentityFile = configuration.baseDirectory / "$serviceId-public"
|
||||
val pubIdentityFile = configuration.baseDirectory / "$id-public"
|
||||
val compositeKeyFile = configuration.baseDirectory / compositeKeyAlias
|
||||
// TODO: Remove use of [ServiceIdentityGenerator.generateToDisk].
|
||||
// Get keys from key file.
|
||||
// TODO: this is here to smooth out the key storage transition, remove this migration in future release.
|
||||
if (privKeyFile.exists()) {
|
||||
migrateKeysFromFile(keyStore, serviceName, pubIdentityFile, privKeyFile, compositeKeyFile, privateKeyAlias, compositeKeyAlias)
|
||||
migrateKeysFromFile(keyStore, name, pubIdentityFile, privKeyFile, compositeKeyFile, privateKeyAlias, compositeKeyAlias)
|
||||
} else {
|
||||
log.info("$privateKeyAlias not found in keystore ${configuration.nodeKeystore}, generating fresh key!")
|
||||
keyStore.saveNewKeyPair(serviceName, privateKeyAlias, generateKeyPair())
|
||||
log.info("$privateKeyAlias not found in key store ${configuration.nodeKeystore}, generating fresh key!")
|
||||
keyStore.saveNewKeyPair(name, privateKeyAlias, generateKeyPair())
|
||||
}
|
||||
}
|
||||
|
||||
val (cert, keys) = keyStore.certificateAndKeyPair(privateKeyAlias)
|
||||
// Get keys from keystore.
|
||||
val loadedServiceName = cert.subject
|
||||
if (loadedServiceName != serviceName)
|
||||
throw ConfigurationException("The legal name in the config file doesn't match the stored identity keystore:$serviceName vs $loadedServiceName")
|
||||
val (x509Cert, keys) = keyStore.certificateAndKeyPair(privateKeyAlias)
|
||||
|
||||
// Use composite key instead if exists
|
||||
// TODO: Use configuration to indicate composite key should be used instead of public key for the identity.
|
||||
val (keyPair, certs) = if (keyStore.containsAlias(compositeKeyAlias)) {
|
||||
val compositeKey = Crypto.toSupportedPublicKey(keyStore.getCertificate(compositeKeyAlias).publicKey)
|
||||
val compositeKeyCert = keyStore.getCertificate(compositeKeyAlias)
|
||||
val certificates = if (keyStore.containsAlias(compositeKeyAlias)) {
|
||||
// Use composite key instead if it exists
|
||||
val certificate = keyStore.getCertificate(compositeKeyAlias)
|
||||
// We have to create the certificate chain for the composite key manually, this is because in order to store
|
||||
// the chain in keystore we need a private key, however there are no corresponding private key for composite key.
|
||||
Pair(KeyPair(compositeKey, keys.private), listOf(compositeKeyCert, *keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA)))
|
||||
// the chain in key store we need a private key, however there is no corresponding private key for the composite key.
|
||||
Lists.asList(certificate, keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA))
|
||||
} else {
|
||||
Pair(keys, keyStore.getCertificateChain(privateKeyAlias).toList())
|
||||
keyStore.getCertificateChain(privateKeyAlias).let {
|
||||
check(it[0].toX509CertHolder() == x509Cert) { "Certificates from key store do not line up!" }
|
||||
it.asList()
|
||||
}
|
||||
}
|
||||
val certPath = CertificateFactory.getInstance("X509").generateCertPath(certs)
|
||||
|
||||
val subject = certificates[0].toX509CertHolder().subject
|
||||
if (subject != name)
|
||||
throw ConfigurationException("The name for $id doesn't match what's in the key store: $name vs $subject")
|
||||
|
||||
partyKeys += keys
|
||||
return Pair(PartyAndCertificate(loadedServiceName, keyPair.public, X509CertificateHolder(certs.first().encoded), certPath), keyPair)
|
||||
return PartyAndCertificate(CertificateFactory.getInstance("X509").generateCertPath(certificates))
|
||||
}
|
||||
|
||||
private fun migrateKeysFromFile(keyStore: KeyStoreWrapper, serviceName: X500Name,
|
||||
pubKeyFile: Path, privKeyFile: Path, compositeKeyFile:Path,
|
||||
privateKeyAlias: String, compositeKeyAlias: String) {
|
||||
log.info("Migrating $privateKeyAlias from file to keystore...")
|
||||
log.info("Migrating $privateKeyAlias from file to key store...")
|
||||
// Check that the identity in the config file matches the identity file we have stored to disk.
|
||||
// Load the private key.
|
||||
val publicKey = Crypto.decodePublicKey(pubKeyFile.readAll())
|
||||
@ -753,13 +753,6 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
||||
log.info("Finish migrating $privateKeyAlias from file to keystore.")
|
||||
}
|
||||
|
||||
private fun getTestPartyAndCertificate(party: Party, trustRoot: CertificateAndKeyPair): PartyAndCertificate {
|
||||
val certFactory = CertificateFactory.getInstance("X509")
|
||||
val certHolder = X509Utilities.createCertificate(CertificateType.IDENTITY, trustRoot.certificate, trustRoot.keyPair, party.name, party.owningKey)
|
||||
val certPath = certFactory.generateCertPath(listOf(certHolder.cert, trustRoot.certificate.cert))
|
||||
return PartyAndCertificate(party, certHolder, certPath)
|
||||
}
|
||||
|
||||
protected open fun generateKeyPair() = cryptoGenerateKeyPair()
|
||||
|
||||
private inner class ServiceHubInternalImpl : ServiceHubInternal, SingletonSerializeAsToken() {
|
||||
|
@ -2,6 +2,7 @@ package net.corda.node.internal
|
||||
|
||||
import com.codahale.metrics.JmxReporter
|
||||
import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.internal.concurrent.doneFuture
|
||||
import net.corda.core.internal.concurrent.flatMap
|
||||
import net.corda.core.internal.concurrent.openFuture
|
||||
@ -133,7 +134,7 @@ open class Node(override val configuration: FullNodeConfiguration,
|
||||
|
||||
private lateinit var userService: RPCUserService
|
||||
|
||||
override fun makeMessagingService(): MessagingService {
|
||||
override fun makeMessagingService(legalIdentity: PartyAndCertificate): MessagingService {
|
||||
userService = RPCUserServiceImpl(configuration.rpcUsers)
|
||||
|
||||
val (serverAddress, advertisedAddress) = with(configuration) {
|
||||
@ -147,7 +148,7 @@ open class Node(override val configuration: FullNodeConfiguration,
|
||||
|
||||
printBasicNodeInfo("Incoming connection address", advertisedAddress.toString())
|
||||
|
||||
val myIdentityOrNullIfNetworkMapService = if (networkMapAddress != null) obtainLegalIdentity().owningKey else null
|
||||
val myIdentityOrNullIfNetworkMapService = if (networkMapAddress != null) legalIdentity.owningKey else null
|
||||
return NodeMessagingClient(
|
||||
configuration,
|
||||
versionInfo,
|
||||
|
@ -7,7 +7,9 @@ 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.internal.toX509CertHolder
|
||||
import net.corda.core.node.services.IdentityService
|
||||
import net.corda.core.node.services.UnknownAnonymousPartyException
|
||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.core.utilities.trace
|
||||
@ -16,16 +18,13 @@ import org.bouncycastle.cert.X509CertificateHolder
|
||||
import java.security.InvalidAlgorithmParameterException
|
||||
import java.security.PublicKey
|
||||
import java.security.cert.*
|
||||
import java.util.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import javax.annotation.concurrent.ThreadSafe
|
||||
import kotlin.collections.LinkedHashSet
|
||||
|
||||
/**
|
||||
* Simple identity service which caches parties and provides functionality for efficient lookup.
|
||||
*
|
||||
* @param identities initial set of identities for the service, typically only used for unit tests.
|
||||
* @param certPaths initial set of certificate paths for the service, typically only used for unit tests.
|
||||
*/
|
||||
@ThreadSafe
|
||||
class InMemoryIdentityService(identities: Iterable<PartyAndCertificate> = emptySet(),
|
||||
@ -43,7 +42,7 @@ class InMemoryIdentityService(identities: Iterable<PartyAndCertificate> = emptyS
|
||||
* Certificate store for certificate authority and intermediary certificates.
|
||||
*/
|
||||
override val caCertStore: CertStore
|
||||
override val trustRootHolder = X509CertificateHolder(trustRoot.encoded)
|
||||
override val trustRootHolder = trustRoot.toX509CertHolder()
|
||||
override val trustAnchor: TrustAnchor = TrustAnchor(trustRoot, null)
|
||||
private val keyToParties = ConcurrentHashMap<PublicKey, PartyAndCertificate>()
|
||||
private val principalToParties = ConcurrentHashMap<X500Name, PartyAndCertificate>()
|
||||
@ -54,7 +53,6 @@ class InMemoryIdentityService(identities: Iterable<PartyAndCertificate> = emptyS
|
||||
keyToParties.putAll(identities.associateBy { it.owningKey } )
|
||||
principalToParties.putAll(identities.associateBy { it.name })
|
||||
confidentialIdentities.forEach { identity ->
|
||||
require(identity.certPath.certificates.size >= 2) { "Certificate path must at least include subject and issuing certificates" }
|
||||
principalToParties.computeIfAbsent(identity.name) { identity }
|
||||
}
|
||||
}
|
||||
@ -66,13 +64,10 @@ class InMemoryIdentityService(identities: Iterable<PartyAndCertificate> = emptyS
|
||||
// TODO: Check the certificate validation logic
|
||||
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
|
||||
override fun verifyAndRegisterIdentity(identity: PartyAndCertificate): PartyAndCertificate? {
|
||||
require(identity.certPath.certificates.size >= 2) { "Certificate path must at least include subject and issuing certificates" }
|
||||
// Validate the chain first, before we do anything clever with it
|
||||
identity.verify(trustAnchor)
|
||||
|
||||
log.trace { "Registering identity $identity" }
|
||||
require(Arrays.equals(identity.certificate.subjectPublicKeyInfo.encoded, identity.owningKey.encoded)) { "Party certificate must end with party's public key" }
|
||||
|
||||
keyToParties[identity.owningKey] = identity
|
||||
// Always keep the first party we registered, as that's the well known identity
|
||||
principalToParties.computeIfAbsent(identity.name) { identity }
|
||||
@ -83,7 +78,7 @@ class InMemoryIdentityService(identities: Iterable<PartyAndCertificate> = emptyS
|
||||
override fun certificateFromParty(party: Party): PartyAndCertificate = principalToParties[party.name] ?: throw IllegalArgumentException("Unknown identity ${party.name}")
|
||||
|
||||
// We give the caller a copy of the data set to avoid any locking problems
|
||||
override fun getAllIdentities(): Iterable<PartyAndCertificate> = java.util.ArrayList(keyToParties.values)
|
||||
override fun getAllIdentities(): Iterable<PartyAndCertificate> = ArrayList(keyToParties.values)
|
||||
|
||||
override fun partyFromKey(key: PublicKey): Party? = keyToParties[key]?.party
|
||||
override fun partyFromX500Name(principal: X500Name): Party? = principalToParties[principal]?.party
|
||||
@ -128,13 +123,13 @@ class InMemoryIdentityService(identities: Iterable<PartyAndCertificate> = emptyS
|
||||
return results
|
||||
}
|
||||
|
||||
@Throws(IdentityService.UnknownAnonymousPartyException::class)
|
||||
@Throws(UnknownAnonymousPartyException::class)
|
||||
override fun assertOwnership(party: Party, anonymousParty: AnonymousParty) {
|
||||
val path = keyToParties[anonymousParty.owningKey]?.certPath ?: throw IdentityService.UnknownAnonymousPartyException("Unknown anonymous party ${anonymousParty.owningKey.toStringShort()}")
|
||||
require(path.certificates.size > 1) { "Certificate path must contain at least two certificates" }
|
||||
val actual = path.certificates[1]
|
||||
require(actual is X509Certificate && actual.publicKey == party.owningKey) { "Next certificate in the path must match the party key ${party.owningKey.toStringShort()}." }
|
||||
val target = path.certificates.first()
|
||||
require(target is X509Certificate && target.publicKey == anonymousParty.owningKey) { "Certificate path starts with a certificate for the anonymous party" }
|
||||
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()}."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package net.corda.node.services.keys
|
||||
import net.corda.core.crypto.ContentSignerBuilder
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.cert
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.node.services.IdentityService
|
||||
import net.corda.core.utilities.days
|
||||
@ -35,10 +34,11 @@ fun freshCertificate(identityService: IdentityService,
|
||||
revocationEnabled: Boolean = false): PartyAndCertificate {
|
||||
val issuerCertificate = issuer.certificate
|
||||
val window = X509Utilities.getCertificateValidityWindow(Duration.ZERO, 3650.days, issuerCertificate)
|
||||
val ourCertificate = X509Utilities.createCertificate(CertificateType.IDENTITY, issuerCertificate.subject, issuerSigner, issuer.name, subjectPublicKey, window)
|
||||
val ourCertificate = X509Utilities.createCertificate(CertificateType.IDENTITY, issuerCertificate.subject,
|
||||
issuerSigner, issuer.name, subjectPublicKey, window)
|
||||
val certFactory = CertificateFactory.getInstance("X509")
|
||||
val ourCertPath = certFactory.generateCertPath(listOf(ourCertificate.cert) + issuer.certPath.certificates)
|
||||
val anonymisedIdentity = PartyAndCertificate(Party(issuer.name, subjectPublicKey), ourCertificate, ourCertPath)
|
||||
val anonymisedIdentity = PartyAndCertificate(ourCertPath)
|
||||
identityService.verifyAndRegisterIdentity(anonymisedIdentity)
|
||||
return anonymisedIdentity
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import net.corda.core.internal.ThreadBox
|
||||
import net.corda.core.internal.concurrent.openFuture
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.noneOrSingle
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.node.services.NetworkMapCache
|
||||
import net.corda.core.node.services.NetworkMapCache.MapChange
|
||||
@ -25,7 +26,6 @@ import net.corda.node.services.messaging.NodeLoginModule.Companion.VERIFIER_ROLE
|
||||
import net.corda.node.utilities.X509Utilities
|
||||
import net.corda.node.utilities.X509Utilities.CORDA_CLIENT_TLS
|
||||
import net.corda.node.utilities.X509Utilities.CORDA_ROOT_CA
|
||||
import net.corda.node.utilities.getX509Certificate
|
||||
import net.corda.node.utilities.loadKeyStore
|
||||
import net.corda.nodeapi.*
|
||||
import net.corda.nodeapi.ArtemisMessagingComponent.Companion.NODE_USER
|
||||
@ -52,7 +52,6 @@ import org.apache.activemq.artemis.spi.core.security.jaas.RolePrincipal
|
||||
import org.apache.activemq.artemis.spi.core.security.jaas.UserPrincipal
|
||||
import org.apache.activemq.artemis.utils.ConfigurationHelper
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import rx.Subscription
|
||||
import java.io.IOException
|
||||
import java.math.BigInteger
|
||||
@ -273,12 +272,7 @@ class ArtemisMessagingServer(override val config: NodeConfiguration,
|
||||
private fun createArtemisSecurityManager(): ActiveMQJAASSecurityManager {
|
||||
val keyStore = loadKeyStore(config.sslKeystore, config.keyStorePassword)
|
||||
val trustStore = loadKeyStore(config.trustStoreFile, config.trustStorePassword)
|
||||
val ourCertificate = keyStore.getX509Certificate(CORDA_CLIENT_TLS)
|
||||
|
||||
// This is a sanity check and should not fail unless things have been misconfigured
|
||||
require(ourCertificate.subject == config.myLegalName) {
|
||||
"Legal name does not match with our subject CN: ${ourCertificate.subject}"
|
||||
}
|
||||
val defaultCertPolicies = mapOf(
|
||||
PEER_ROLE to CertificateChainCheckPolicy.RootMustMatch,
|
||||
NODE_ROLE to CertificateChainCheckPolicy.LeafMustMatch,
|
||||
@ -512,12 +506,12 @@ private class VerifyingNettyConnector(configuration: MutableMap<String, Any>,
|
||||
"misconfiguration by the remote peer or an SSL man-in-the-middle attack!"
|
||||
}
|
||||
// Make sure certificate has the same name.
|
||||
val peerCertificate = X509CertificateHolder(session.peerCertificateChain.first().encoded)
|
||||
val peerCertificate = session.peerCertificateChain[0].toX509CertHolder()
|
||||
require(peerCertificate.subject == expectedLegalName) {
|
||||
"Peer has wrong subject name in the certificate - expected $expectedLegalName but got ${peerCertificate.subject}. This is either a fatal " +
|
||||
"misconfiguration by the remote peer or an SSL man-in-the-middle attack!"
|
||||
}
|
||||
X509Utilities.validateCertificateChain(X509CertificateHolder(session.localCertificates.last().encoded), *session.peerCertificates)
|
||||
X509Utilities.validateCertificateChain(session.localCertificates.last().toX509CertHolder(), *session.peerCertificates)
|
||||
server.onTcpConnection(peerLegalName)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
connection.close()
|
||||
|
@ -21,6 +21,7 @@ import java.util.Collections.synchronizedMap
|
||||
class PersistentNetworkMapService(services: ServiceHubInternal, minimumPlatformVersion: Int)
|
||||
: AbstractNetworkMapService(services, minimumPlatformVersion) {
|
||||
|
||||
// Only the node_party_path column is needed to reconstruct a PartyAndCertificate but we have the others for human readability
|
||||
private object Table : JDBCHashedTable("${NODE_DATABASE_PREFIX}network_map_nodes") {
|
||||
val nodeParty = partyAndCertificate("node_party_name", "node_party_key", "node_party_certificate", "node_party_path")
|
||||
val registrationInfo = blob("node_registration_info")
|
||||
@ -28,16 +29,15 @@ class PersistentNetworkMapService(services: ServiceHubInternal, minimumPlatformV
|
||||
|
||||
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): PartyAndCertificate = PartyAndCertificate(X500Name(row[table.nodeParty.name]), row[table.nodeParty.owningKey],
|
||||
row[table.nodeParty.certificate], row[table.nodeParty.certPath])
|
||||
override fun keyFromRow(row: ResultRow): PartyAndCertificate = PartyAndCertificate(row[table.nodeParty.certPath])
|
||||
|
||||
override fun valueFromRow(row: ResultRow): NodeRegistrationInfo = deserializeFromBlob(row[table.registrationInfo])
|
||||
|
||||
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
|
||||
insert[table.nodeParty.certPath] = entry.key.certPath
|
||||
}
|
||||
|
||||
override fun addValueToInsert(insert: InsertStatement, entry: Map.Entry<PartyAndCertificate, NodeRegistrationInfo>, finalizables: MutableList<() -> Unit>) {
|
||||
|
@ -1,8 +1,11 @@
|
||||
package net.corda.node.utilities
|
||||
|
||||
import net.corda.core.crypto.*
|
||||
import net.corda.core.crypto.CertificateAndKeyPair
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.cert
|
||||
import net.corda.core.internal.exists
|
||||
import net.corda.core.internal.read
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
import net.corda.core.internal.write
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
@ -145,8 +148,8 @@ fun KeyStore.getCertificateAndKeyPair(alias: String, keyPassword: String): Certi
|
||||
* @return The X509Certificate found in the KeyStore under the specified alias.
|
||||
*/
|
||||
fun KeyStore.getX509Certificate(alias: String): X509CertificateHolder {
|
||||
val encoded = getCertificate(alias)?.encoded ?: throw IllegalArgumentException("No certificate under alias \"$alias\"")
|
||||
return X509CertificateHolder(encoded)
|
||||
val certificate = getCertificate(alias) ?: throw IllegalArgumentException("No certificate under alias \"$alias\"")
|
||||
return certificate.toX509CertHolder()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -206,5 +209,5 @@ class KeyStoreWrapper(private val storePath: Path, private val storePassword: St
|
||||
|
||||
fun getCertificate(alias: String): Certificate = keyStore.getCertificate(alias)
|
||||
|
||||
fun certificateAndKeyPair(alias: String) = keyStore.getCertificateAndKeyPair(alias, storePassword)
|
||||
fun certificateAndKeyPair(alias: String): CertificateAndKeyPair = keyStore.getCertificateAndKeyPair(alias, storePassword)
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import net.corda.core.crypto.generateKeyPair
|
||||
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.UnknownAnonymousPartyException
|
||||
import net.corda.node.services.identity.InMemoryIdentityService
|
||||
import net.corda.node.utilities.CertificateType
|
||||
import net.corda.node.utilities.X509Utilities
|
||||
@ -90,10 +90,10 @@ class InMemoryIdentityServiceTests {
|
||||
val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val service = InMemoryIdentityService(trustRoot = DUMMY_CA.certificate)
|
||||
// TODO: Generate certificate with an EdDSA key rather than ECDSA
|
||||
val identity = Party(CertificateAndKeyPair(rootCert, rootKey))
|
||||
val identity = Party(rootCert)
|
||||
val txIdentity = AnonymousParty(txKey.public)
|
||||
|
||||
assertFailsWith<IdentityService.UnknownAnonymousPartyException> {
|
||||
assertFailsWith<UnknownAnonymousPartyException> {
|
||||
service.assertOwnership(identity, txIdentity)
|
||||
}
|
||||
}
|
||||
@ -107,7 +107,7 @@ class InMemoryIdentityServiceTests {
|
||||
fun `get anonymous identity by key`() {
|
||||
val trustRoot = DUMMY_CA
|
||||
val (alice, aliceTxIdentity) = createParty(ALICE.name, trustRoot)
|
||||
val (bob, bobTxIdentity) = createParty(ALICE.name, trustRoot)
|
||||
val (_, bobTxIdentity) = createParty(ALICE.name, trustRoot)
|
||||
|
||||
// Now we have identities, construct the service and let it know about both
|
||||
val service = InMemoryIdentityService(setOf(alice), emptySet(), trustRoot.certificate.cert)
|
||||
@ -163,7 +163,7 @@ class InMemoryIdentityServiceTests {
|
||||
val txKey = Crypto.generateKeyPair()
|
||||
val txCert = X509Utilities.createCertificate(CertificateType.IDENTITY, issuer.certificate, issuerKeyPair, x500Name, txKey.public)
|
||||
val txCertPath = certFactory.generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates)
|
||||
return Pair(issuer, PartyAndCertificate(Party(x500Name, txKey.public), txCert, txCertPath))
|
||||
return Pair(issuer, PartyAndCertificate(txCertPath))
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -9,12 +9,12 @@ import net.corda.core.crypto.cert
|
||||
import net.corda.core.crypto.commonName
|
||||
import net.corda.core.internal.exists
|
||||
import net.corda.core.internal.toTypedArray
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
import net.corda.node.utilities.X509Utilities
|
||||
import net.corda.node.utilities.loadKeyStore
|
||||
import net.corda.testing.ALICE
|
||||
import net.corda.testing.getTestX509Name
|
||||
import net.corda.testing.testNodeConfiguration
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
@ -68,7 +68,7 @@ class NetworkRegistrationHelperTest {
|
||||
assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_TLS))
|
||||
val certificateChain = getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
|
||||
assertEquals(3, certificateChain.size)
|
||||
assertEquals(listOf("CORDA_CLIENT_CA", "CORDA_INTERMEDIATE_CA", "CORDA_ROOT_CA"), certificateChain.map { X509CertificateHolder(it.encoded).subject.commonName })
|
||||
assertEquals(listOf("CORDA_CLIENT_CA", "CORDA_INTERMEDIATE_CA", "CORDA_ROOT_CA"), certificateChain.map { it.toX509CertHolder().subject.commonName })
|
||||
}
|
||||
|
||||
sslKeystore.run {
|
||||
@ -78,7 +78,7 @@ class NetworkRegistrationHelperTest {
|
||||
assertTrue(containsAlias(X509Utilities.CORDA_CLIENT_TLS))
|
||||
val certificateChain = getCertificateChain(X509Utilities.CORDA_CLIENT_TLS)
|
||||
assertEquals(4, certificateChain.size)
|
||||
assertEquals(listOf("CORDA_CLIENT_CA", "CORDA_CLIENT_CA", "CORDA_INTERMEDIATE_CA", "CORDA_ROOT_CA"), certificateChain.map { X509CertificateHolder(it.encoded).subject.commonName })
|
||||
assertEquals(listOf("CORDA_CLIENT_CA", "CORDA_CLIENT_CA", "CORDA_INTERMEDIATE_CA", "CORDA_ROOT_CA"), certificateChain.map { it.toX509CertHolder().subject.commonName })
|
||||
}
|
||||
|
||||
trustStore.run {
|
||||
|
@ -3,11 +3,9 @@ package net.corda.traderdemo.flow
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.contracts.CommercialPaper
|
||||
import net.corda.core.contracts.Amount
|
||||
import net.corda.finance.`issued by`
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.flows.FinalityFlow
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.InitiatingFlow
|
||||
import net.corda.core.flows.StartableByRPC
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
@ -16,13 +14,13 @@ import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.core.utilities.ProgressTracker
|
||||
import net.corda.core.utilities.days
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.finance.`issued by`
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Flow for the Bank of Corda node to issue some commercial paper to the seller's node, to sell to the buyer.
|
||||
*/
|
||||
@InitiatingFlow
|
||||
@StartableByRPC
|
||||
class CommercialPaperIssueFlow(val amount: Amount<Currency>,
|
||||
val issueRef: OpaqueBytes,
|
||||
|
@ -207,7 +207,7 @@ fun getTestPartyAndCertificate(party: Party, trustRoot: CertificateAndKeyPair =
|
||||
val certFactory = CertificateFactory.getInstance("X509")
|
||||
val certHolder = X509Utilities.createCertificate(CertificateType.IDENTITY, trustRoot.certificate, trustRoot.keyPair, party.name, party.owningKey)
|
||||
val certPath = certFactory.generateCertPath(listOf(certHolder.cert, trustRoot.certificate.cert))
|
||||
return PartyAndCertificate(party, certHolder, certPath)
|
||||
return PartyAndCertificate(certPath)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -149,7 +149,7 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
|
||||
|
||||
// We only need to override the messaging service here, as currently everything that hits disk does so
|
||||
// through the java.nio API which we are already mocking via Jimfs.
|
||||
override fun makeMessagingService(): MessagingService {
|
||||
override fun makeMessagingService(legalIdentity: PartyAndCertificate): MessagingService {
|
||||
require(id >= 0) { "Node ID must be zero or positive, was passed: " + id }
|
||||
return mockNet.messagingNetwork.createNodeWithID(
|
||||
!mockNet.threadPerNode,
|
||||
|
Loading…
x
Reference in New Issue
Block a user