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")
|
@file:JvmName("X500NameUtils")
|
||||||
package net.corda.core.crypto
|
package net.corda.core.crypto
|
||||||
|
|
||||||
|
import net.corda.core.internal.toX509CertHolder
|
||||||
import org.bouncycastle.asn1.ASN1Encodable
|
import org.bouncycastle.asn1.ASN1Encodable
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
import org.bouncycastle.asn1.x500.X500NameBuilder
|
import org.bouncycastle.asn1.x500.X500NameBuilder
|
||||||
@ -57,7 +58,7 @@ val X500Name.locationOrNull: String? get() = try {
|
|||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
val X509Certificate.subject: X500Name get() = X509CertificateHolder(encoded).subject
|
val X509Certificate.subject: X500Name get() = toX509CertHolder().subject
|
||||||
val X509CertificateHolder.cert: X509Certificate get() = JcaX509CertificateConverter().getCertificate(this)
|
val X509CertificateHolder.cert: X509Certificate get() = JcaX509CertificateConverter().getCertificate(this)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -13,8 +13,7 @@ import java.security.PublicKey
|
|||||||
@CordaSerializable
|
@CordaSerializable
|
||||||
abstract class AbstractParty(val owningKey: PublicKey) {
|
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 */
|
/** 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()
|
override fun hashCode(): Int = owningKey.hashCode()
|
||||||
abstract fun nameOrNull(): X500Name?
|
abstract fun nameOrNull(): X500Name?
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package net.corda.core.identity
|
package net.corda.core.identity
|
||||||
|
|
||||||
import net.corda.core.contracts.PartyAndReference
|
import net.corda.core.contracts.PartyAndReference
|
||||||
import net.corda.core.crypto.toBase58String
|
|
||||||
import net.corda.core.crypto.toStringShort
|
import net.corda.core.crypto.toStringShort
|
||||||
import net.corda.core.utilities.OpaqueBytes
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
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.
|
* information such as name. It is intended to represent a party on the distributed ledger.
|
||||||
*/
|
*/
|
||||||
class AnonymousParty(owningKey: PublicKey) : AbstractParty(owningKey) {
|
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 nameOrNull(): X500Name? = null
|
||||||
|
|
||||||
override fun ref(bytes: OpaqueBytes): PartyAndReference = PartyAndReference(this, bytes)
|
override fun ref(bytes: OpaqueBytes): PartyAndReference = PartyAndReference(this, bytes)
|
||||||
|
override fun toString() = "Anonymous(${owningKey.toStringShort()})"
|
||||||
}
|
}
|
@ -1,9 +1,11 @@
|
|||||||
package net.corda.core.identity
|
package net.corda.core.identity
|
||||||
|
|
||||||
import net.corda.core.contracts.PartyAndReference
|
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 net.corda.core.utilities.OpaqueBytes
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
|
import org.bouncycastle.cert.X509CertificateHolder
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -26,10 +28,9 @@ import java.security.PublicKey
|
|||||||
* @see CompositeKey
|
* @see CompositeKey
|
||||||
*/
|
*/
|
||||||
class Party(val name: X500Name, owningKey: PublicKey) : AbstractParty(owningKey) {
|
class Party(val name: X500Name, owningKey: PublicKey) : AbstractParty(owningKey) {
|
||||||
constructor(certAndKey: CertificateAndKeyPair) : this(certAndKey.certificate.subject, certAndKey.keyPair.public)
|
constructor(certificate: X509CertificateHolder) : this(certificate.subject, Crypto.toSupportedPublicKey(certificate.subjectPublicKeyInfo))
|
||||||
override fun toString() = name.toString()
|
override fun nameOrNull(): X500Name = name
|
||||||
override fun nameOrNull(): X500Name? = name
|
|
||||||
|
|
||||||
fun anonymise(): AnonymousParty = AnonymousParty(owningKey)
|
fun anonymise(): AnonymousParty = AnonymousParty(owningKey)
|
||||||
override fun ref(bytes: OpaqueBytes): PartyAndReference = PartyAndReference(this, bytes)
|
override fun ref(bytes: OpaqueBytes): PartyAndReference = PartyAndReference(this, bytes)
|
||||||
|
override fun toString() = name.toString()
|
||||||
}
|
}
|
||||||
|
@ -1,49 +1,42 @@
|
|||||||
package net.corda.core.identity
|
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.asn1.x500.X500Name
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
import org.bouncycastle.cert.X509CertificateHolder
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.security.cert.*
|
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
|
* 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,
|
* [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,
|
* not part of the identifier themselves.
|
||||||
* this class exists in order to ensure the implementation classes of certificates and party public keys are kept stable.
|
|
||||||
*/
|
*/
|
||||||
@CordaSerializable
|
//TODO Is VerifiableIdentity a better name?
|
||||||
data class PartyAndCertificate(val party: Party,
|
class PartyAndCertificate(val certPath: CertPath) {
|
||||||
val certificate: X509CertificateHolder,
|
@Transient val certificate: X509CertificateHolder
|
||||||
val certPath: CertPath) {
|
init {
|
||||||
constructor(name: X500Name, owningKey: PublicKey, certificate: X509CertificateHolder, certPath: CertPath) : this(Party(name, owningKey), certificate, certPath)
|
require(certPath.type == "X.509") { "Only X.509 certificates supported" }
|
||||||
val name: X500Name
|
val certs = certPath.certificates
|
||||||
get() = party.name
|
require(certs.size >= 2) { "Certificate path must at least include subject and issuing certificates" }
|
||||||
val owningKey: PublicKey
|
certificate = certs[0].toX509CertHolder()
|
||||||
get() = party.owningKey
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
return if (other is PartyAndCertificate)
|
|
||||||
party == other.party
|
|
||||||
else
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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 hashCode(): Int = party.hashCode()
|
||||||
override fun toString(): String = party.toString()
|
override fun toString(): String = party.toString()
|
||||||
|
|
||||||
/**
|
/** Verify the certificate path is valid. */
|
||||||
* Verify that the given certificate path is valid and leads to the owning key of the party.
|
|
||||||
*/
|
|
||||||
fun verify(trustAnchor: TrustAnchor): PKIXCertPathValidatorResult {
|
fun verify(trustAnchor: TrustAnchor): PKIXCertPathValidatorResult {
|
||||||
require(certPath.certificates.first() is X509Certificate) { "Subject certificate must be an X.509 certificate" }
|
val parameters = PKIXParameters(setOf(trustAnchor)).apply { isRevocationEnabled = false }
|
||||||
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 validator = CertPathValidator.getInstance("PKIX")
|
val validator = CertPathValidator.getInstance("PKIX")
|
||||||
validatorParameters.isRevocationEnabled = false
|
return validator.validate(certPath, parameters) as PKIXCertPathValidatorResult
|
||||||
return validator.validate(certPath, validatorParameters) as PKIXCertPathValidatorResult
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package net.corda.core.internal
|
|||||||
|
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.sha256
|
import net.corda.core.crypto.sha256
|
||||||
|
import org.bouncycastle.cert.X509CertificateHolder
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import rx.Observer
|
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]. */
|
/** Convert a [ByteArrayOutputStream] to [InputStreamAndHash]. */
|
||||||
fun ByteArrayOutputStream.toInputStreamAndHash(): InputStreamAndHash {
|
fun ByteArrayOutputStream.toInputStreamAndHash(): InputStreamAndHash {
|
||||||
val bytes = toByteArray()
|
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 legalIdentityAndCert: PartyAndCertificate, //TODO This field will be removed in future PR which gets rid of services.
|
||||||
val legalIdentitiesAndCerts: NonEmptySet<PartyAndCertificate>,
|
val legalIdentitiesAndCerts: NonEmptySet<PartyAndCertificate>,
|
||||||
val platformVersion: Int,
|
val platformVersion: Int,
|
||||||
var advertisedServices: List<ServiceEntry> = emptyList(),
|
val advertisedServices: List<ServiceEntry> = emptyList(),
|
||||||
val worldMapLocation: WorldMapLocation? = null) {
|
val worldMapLocation: WorldMapLocation? = null) {
|
||||||
init {
|
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 legalIdentity: Party get() = legalIdentityAndCert.party
|
||||||
val notaryIdentity: Party
|
val notaryIdentity: Party get() = advertisedServices.single { it.info.type.isNotary() }.identity.party
|
||||||
get() = advertisedServices.single { it.info.type.isNotary() }.identity.party
|
|
||||||
fun serviceIdentities(type: ServiceType): List<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
|
package net.corda.core.node.services
|
||||||
|
|
||||||
import net.corda.core.contracts.PartyAndReference
|
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.asn1.x500.X500Name
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
import org.bouncycastle.cert.X509CertificateHolder
|
||||||
import java.security.InvalidAlgorithmParameterException
|
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.
|
* @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>
|
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.UnmodifiableCollectionsSerializer
|
||||||
import de.javakaffee.kryoserializers.guava.*
|
import de.javakaffee.kryoserializers.guava.*
|
||||||
import net.corda.core.crypto.composite.CompositeKey
|
import net.corda.core.crypto.composite.CompositeKey
|
||||||
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
import net.corda.core.node.CordaPluginRegistry
|
import net.corda.core.node.CordaPluginRegistry
|
||||||
import net.corda.core.serialization.SerializeAsToken
|
import net.corda.core.serialization.SerializeAsToken
|
||||||
import net.corda.core.serialization.SerializedBytes
|
import net.corda.core.serialization.SerializedBytes
|
||||||
@ -59,6 +60,12 @@ object DefaultKryoCustomizer {
|
|||||||
|
|
||||||
instantiatorStrategy = CustomInstantiatorStrategy()
|
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
|
// 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.
|
// with custom serializers get written as registration ids. This will break backwards-compatibility.
|
||||||
// Please add any new registrations to the end.
|
// Please add any new registrations to the end.
|
||||||
@ -68,50 +75,31 @@ object DefaultKryoCustomizer {
|
|||||||
register(SignedTransaction::class.java, SignedTransactionSerializer)
|
register(SignedTransaction::class.java, SignedTransactionSerializer)
|
||||||
register(WireTransaction::class.java, WireTransactionSerializer)
|
register(WireTransaction::class.java, WireTransactionSerializer)
|
||||||
register(SerializedBytes::class.java, SerializedBytesSerializer)
|
register(SerializedBytes::class.java, SerializedBytesSerializer)
|
||||||
|
|
||||||
UnmodifiableCollectionsSerializer.registerSerializers(this)
|
UnmodifiableCollectionsSerializer.registerSerializers(this)
|
||||||
ImmutableListSerializer.registerSerializers(this)
|
ImmutableListSerializer.registerSerializers(this)
|
||||||
ImmutableSetSerializer.registerSerializers(this)
|
ImmutableSetSerializer.registerSerializers(this)
|
||||||
ImmutableSortedSetSerializer.registerSerializers(this)
|
ImmutableSortedSetSerializer.registerSerializers(this)
|
||||||
ImmutableMapSerializer.registerSerializers(this)
|
ImmutableMapSerializer.registerSerializers(this)
|
||||||
ImmutableMultimapSerializer.registerSerializers(this)
|
ImmutableMultimapSerializer.registerSerializers(this)
|
||||||
|
|
||||||
// InputStream subclasses whitelisting, required for attachments.
|
// InputStream subclasses whitelisting, required for attachments.
|
||||||
register(BufferedInputStream::class.java, InputStreamSerializer)
|
register(BufferedInputStream::class.java, InputStreamSerializer)
|
||||||
register(Class.forName("sun.net.www.protocol.jar.JarURLConnection\$JarURLInputStream"), InputStreamSerializer)
|
register(Class.forName("sun.net.www.protocol.jar.JarURLConnection\$JarURLInputStream"), InputStreamSerializer)
|
||||||
|
|
||||||
noReferencesWithin<WireTransaction>()
|
noReferencesWithin<WireTransaction>()
|
||||||
|
|
||||||
register(ECPublicKeyImpl::class.java, ECPublicKeyImplSerializer)
|
register(ECPublicKeyImpl::class.java, ECPublicKeyImplSerializer)
|
||||||
register(EdDSAPublicKey::class.java, Ed25519PublicKeySerializer)
|
register(EdDSAPublicKey::class.java, Ed25519PublicKeySerializer)
|
||||||
register(EdDSAPrivateKey::class.java, Ed25519PrivateKeySerializer)
|
register(EdDSAPrivateKey::class.java, Ed25519PrivateKeySerializer)
|
||||||
|
register(CompositeKey::class.java, CompositeKeySerializer) // Using a custom serializer for compactness
|
||||||
// Using a custom serializer for compactness
|
|
||||||
register(CompositeKey::class.java, CompositeKeySerializer)
|
|
||||||
|
|
||||||
// Exceptions. We don't bother sending the stack traces as the client will fill in its own anyway.
|
// 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 = { _, _, _ -> })
|
register(Array<StackTraceElement>::class, read = { _, _ -> emptyArray() }, write = { _, _, _ -> })
|
||||||
|
|
||||||
// This ensures a NonEmptySetSerializer is constructed with an initial value.
|
// This ensures a NonEmptySetSerializer is constructed with an initial value.
|
||||||
register(NonEmptySet::class.java, NonEmptySetSerializer)
|
register(NonEmptySet::class.java, NonEmptySetSerializer)
|
||||||
|
|
||||||
addDefaultSerializer(SerializeAsToken::class.java, SerializeAsTokenSerializer<SerializeAsToken>())
|
|
||||||
|
|
||||||
register(BitSet::class.java, BitSetSerializer())
|
register(BitSet::class.java, BitSetSerializer())
|
||||||
register(Class::class.java, ClassSerializer)
|
register(Class::class.java, ClassSerializer)
|
||||||
|
|
||||||
addDefaultSerializer(Logger::class.java, LoggerSerializer)
|
|
||||||
|
|
||||||
register(FileInputStream::class.java, InputStreamSerializer)
|
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(CertPath::class.java, CertPathSerializer)
|
||||||
register(X509CertPath::class.java, CertPathSerializer)
|
register(X509CertPath::class.java, CertPathSerializer)
|
||||||
register(X500Name::class.java, X500NameSerializer)
|
register(X500Name::class.java, X500NameSerializer)
|
||||||
register(X509CertificateHolder::class.java, X509CertificateSerializer)
|
register(X509CertificateHolder::class.java, X509CertificateSerializer)
|
||||||
|
|
||||||
register(BCECPrivateKey::class.java, PrivateKeySerializer)
|
register(BCECPrivateKey::class.java, PrivateKeySerializer)
|
||||||
register(BCECPublicKey::class.java, PublicKeySerializer)
|
register(BCECPublicKey::class.java, PublicKeySerializer)
|
||||||
register(BCRSAPrivateCrtKey::class.java, PrivateKeySerializer)
|
register(BCRSAPrivateCrtKey::class.java, PrivateKeySerializer)
|
||||||
@ -119,8 +107,8 @@ object DefaultKryoCustomizer {
|
|||||||
register(BCSphincs256PrivateKey::class.java, PrivateKeySerializer)
|
register(BCSphincs256PrivateKey::class.java, PrivateKeySerializer)
|
||||||
register(BCSphincs256PublicKey::class.java, PublicKeySerializer)
|
register(BCSphincs256PublicKey::class.java, PublicKeySerializer)
|
||||||
register(sun.security.ec.ECPublicKeyImpl::class.java, PublicKeySerializer)
|
register(sun.security.ec.ECPublicKeyImpl::class.java, PublicKeySerializer)
|
||||||
|
|
||||||
register(NotaryChangeWireTransaction::class.java, NotaryChangeWireTransactionSerializer)
|
register(NotaryChangeWireTransaction::class.java, NotaryChangeWireTransactionSerializer)
|
||||||
|
register(PartyAndCertificate::class.java, PartyAndCertificateSerializer)
|
||||||
|
|
||||||
val customization = KryoSerializationCustomization(this)
|
val customization = KryoSerializationCustomization(this)
|
||||||
pluginRegistries.forEach { it.customizeSerialization(customization) }
|
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>>() {
|
private object NonEmptySetSerializer : Serializer<NonEmptySet<Any>>() {
|
||||||
override fun write(kryo: Kryo, output: Output, obj: NonEmptySet<Any>) {
|
override fun write(kryo: Kryo, output: Output, obj: NonEmptySet<Any>) {
|
||||||
// Write out the contents as normal
|
// Write out the contents as normal
|
||||||
|
@ -2,6 +2,7 @@ package net.corda.node.internal
|
|||||||
|
|
||||||
import com.codahale.metrics.MetricRegistry
|
import com.codahale.metrics.MetricRegistry
|
||||||
import com.google.common.annotations.VisibleForTesting
|
import com.google.common.annotations.VisibleForTesting
|
||||||
|
import com.google.common.collect.Lists
|
||||||
import com.google.common.collect.MutableClassToInstanceMap
|
import com.google.common.collect.MutableClassToInstanceMap
|
||||||
import com.google.common.util.concurrent.MoreExecutors
|
import com.google.common.util.concurrent.MoreExecutors
|
||||||
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner
|
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner
|
||||||
@ -66,7 +67,6 @@ import net.corda.node.utilities.*
|
|||||||
import net.corda.node.utilities.AddOrRemove.ADD
|
import net.corda.node.utilities.AddOrRemove.ADD
|
||||||
import org.apache.activemq.artemis.utils.ReusableLatch
|
import org.apache.activemq.artemis.utils.ReusableLatch
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
@ -88,6 +88,9 @@ import java.util.concurrent.ExecutorService
|
|||||||
import java.util.concurrent.TimeUnit.SECONDS
|
import java.util.concurrent.TimeUnit.SECONDS
|
||||||
import java.util.stream.Collectors.toList
|
import java.util.stream.Collectors.toList
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
|
import kotlin.collections.component1
|
||||||
|
import kotlin.collections.component2
|
||||||
|
import kotlin.collections.set
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import net.corda.core.crypto.generateKeyPair as cryptoGenerateKeyPair
|
import net.corda.core.crypto.generateKeyPair as cryptoGenerateKeyPair
|
||||||
|
|
||||||
@ -417,8 +420,9 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
|||||||
checkpointStorage = DBCheckpointStorage()
|
checkpointStorage = DBCheckpointStorage()
|
||||||
_services = ServiceHubInternalImpl()
|
_services = ServiceHubInternalImpl()
|
||||||
attachments = NodeAttachmentService(configuration.dataSourceProperties, services.monitoringService.metrics, configuration.database)
|
attachments = NodeAttachmentService(configuration.dataSourceProperties, services.monitoringService.metrics, configuration.database)
|
||||||
network = makeMessagingService()
|
val legalIdentity = obtainIdentity("identity", configuration.myLegalName)
|
||||||
info = makeInfo()
|
network = makeMessagingService(legalIdentity)
|
||||||
|
info = makeInfo(legalIdentity)
|
||||||
|
|
||||||
val tokenizableServices = mutableListOf(attachments, network, services.vaultService, services.vaultQueryService,
|
val tokenizableServices = mutableListOf(attachments, network, services.vaultService, services.vaultQueryService,
|
||||||
services.keyManagementService, services.identityService, platformClock, services.schedulerService)
|
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}))
|
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 advertisedServiceEntries = makeServiceEntries()
|
||||||
val legalIdentity = obtainLegalIdentity()
|
val allIdentities = (advertisedServiceEntries.map { it.identity } + legalIdentity).toNonEmptySet()
|
||||||
val allIdentitiesSet = (advertisedServiceEntries.map { it.identity } + legalIdentity).toNonEmptySet()
|
|
||||||
val addresses = myAddresses() // TODO There is no support for multiple IP addresses yet.
|
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 {
|
return advertisedServices.map {
|
||||||
val serviceId = it.type.id
|
val serviceId = it.type.id
|
||||||
val serviceName = it.name ?: X500Name("${configuration.myLegalName},OU=$serviceId")
|
val serviceName = it.name ?: X500Name("${configuration.myLegalName},OU=$serviceId")
|
||||||
val identity = obtainKeyPair(serviceId, serviceName).first
|
val identity = obtainIdentity(serviceId, serviceName)
|
||||||
ServiceEntry(it, identity)
|
ServiceEntry(it, identity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -613,8 +616,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
|||||||
val instant = platformClock.instant()
|
val instant = platformClock.instant()
|
||||||
val expires = instant + NetworkMapService.DEFAULT_EXPIRATION_PERIOD
|
val expires = instant + NetworkMapService.DEFAULT_EXPIRATION_PERIOD
|
||||||
val reg = NodeRegistration(info, instant.toEpochMilli(), ADD, expires)
|
val reg = NodeRegistration(info, instant.toEpochMilli(), ADD, expires)
|
||||||
val legalIdentityKey = obtainLegalIdentityKey()
|
val request = RegistrationRequest(reg.toWire(services.keyManagementService, info.legalIdentityAndCert.owningKey), network.myAddress)
|
||||||
val request = RegistrationRequest(reg.toWire(services.keyManagementService, legalIdentityKey.public), network.myAddress)
|
|
||||||
return network.sendRequest(NetworkMapService.REGISTER_TOPIC, request, networkMapAddress)
|
return network.sendRequest(NetworkMapService.REGISTER_TOPIC, request, networkMapAddress)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -680,15 +682,11 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
|||||||
runOnStop.clear()
|
runOnStop.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract fun makeMessagingService(): MessagingService
|
protected abstract fun makeMessagingService(legalIdentity: PartyAndCertificate): MessagingService
|
||||||
|
|
||||||
protected abstract fun startMessagingService(rpcOps: RPCOps)
|
protected abstract fun startMessagingService(rpcOps: RPCOps)
|
||||||
|
|
||||||
protected fun obtainLegalIdentity(): PartyAndCertificate = identityKeyPair.first
|
private fun obtainIdentity(id: String, name: X500Name): PartyAndCertificate {
|
||||||
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> {
|
|
||||||
// Load the private identity key, creating it if necessary. The identity key is a long term well known key that
|
// 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
|
// 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
|
// "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?
|
// TODO: Integrate with Key management service?
|
||||||
val keyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword)
|
val keyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword)
|
||||||
val privateKeyAlias = "$serviceId-private-key"
|
val privateKeyAlias = "$id-private-key"
|
||||||
val compositeKeyAlias = "$serviceId-composite-key"
|
val compositeKeyAlias = "$id-composite-key"
|
||||||
|
|
||||||
if (!keyStore.containsAlias(privateKeyAlias)) {
|
if (!keyStore.containsAlias(privateKeyAlias)) {
|
||||||
val privKeyFile = configuration.baseDirectory / privateKeyAlias
|
val privKeyFile = configuration.baseDirectory / privateKeyAlias
|
||||||
val pubIdentityFile = configuration.baseDirectory / "$serviceId-public"
|
val pubIdentityFile = configuration.baseDirectory / "$id-public"
|
||||||
val compositeKeyFile = configuration.baseDirectory / compositeKeyAlias
|
val compositeKeyFile = configuration.baseDirectory / compositeKeyAlias
|
||||||
// TODO: Remove use of [ServiceIdentityGenerator.generateToDisk].
|
// TODO: Remove use of [ServiceIdentityGenerator.generateToDisk].
|
||||||
// Get keys from key file.
|
// Get keys from key file.
|
||||||
// TODO: this is here to smooth out the key storage transition, remove this migration in future release.
|
// TODO: this is here to smooth out the key storage transition, remove this migration in future release.
|
||||||
if (privKeyFile.exists()) {
|
if (privKeyFile.exists()) {
|
||||||
migrateKeysFromFile(keyStore, serviceName, pubIdentityFile, privKeyFile, compositeKeyFile, privateKeyAlias, compositeKeyAlias)
|
migrateKeysFromFile(keyStore, name, pubIdentityFile, privKeyFile, compositeKeyFile, privateKeyAlias, compositeKeyAlias)
|
||||||
} else {
|
} else {
|
||||||
log.info("$privateKeyAlias not found in keystore ${configuration.nodeKeystore}, generating fresh key!")
|
log.info("$privateKeyAlias not found in key store ${configuration.nodeKeystore}, generating fresh key!")
|
||||||
keyStore.saveNewKeyPair(serviceName, privateKeyAlias, generateKeyPair())
|
keyStore.saveNewKeyPair(name, privateKeyAlias, generateKeyPair())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val (cert, keys) = keyStore.certificateAndKeyPair(privateKeyAlias)
|
val (x509Cert, 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")
|
|
||||||
|
|
||||||
// Use composite key instead if exists
|
|
||||||
// TODO: Use configuration to indicate composite key should be used instead of public key for the identity.
|
// 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 certificates = if (keyStore.containsAlias(compositeKeyAlias)) {
|
||||||
val compositeKey = Crypto.toSupportedPublicKey(keyStore.getCertificate(compositeKeyAlias).publicKey)
|
// Use composite key instead if it exists
|
||||||
val compositeKeyCert = keyStore.getCertificate(compositeKeyAlias)
|
val certificate = keyStore.getCertificate(compositeKeyAlias)
|
||||||
// We have to create the certificate chain for the composite key manually, this is because in order to store
|
// 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.
|
// the chain in key store we need a private key, however there is no corresponding private key for the composite key.
|
||||||
Pair(KeyPair(compositeKey, keys.private), listOf(compositeKeyCert, *keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA)))
|
Lists.asList(certificate, keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA))
|
||||||
} else {
|
} 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
|
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,
|
private fun migrateKeysFromFile(keyStore: KeyStoreWrapper, serviceName: X500Name,
|
||||||
pubKeyFile: Path, privKeyFile: Path, compositeKeyFile:Path,
|
pubKeyFile: Path, privKeyFile: Path, compositeKeyFile:Path,
|
||||||
privateKeyAlias: String, compositeKeyAlias: String) {
|
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.
|
// Check that the identity in the config file matches the identity file we have stored to disk.
|
||||||
// Load the private key.
|
// Load the private key.
|
||||||
val publicKey = Crypto.decodePublicKey(pubKeyFile.readAll())
|
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.")
|
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()
|
protected open fun generateKeyPair() = cryptoGenerateKeyPair()
|
||||||
|
|
||||||
private inner class ServiceHubInternalImpl : ServiceHubInternal, SingletonSerializeAsToken() {
|
private inner class ServiceHubInternalImpl : ServiceHubInternal, SingletonSerializeAsToken() {
|
||||||
|
@ -2,6 +2,7 @@ package net.corda.node.internal
|
|||||||
|
|
||||||
import com.codahale.metrics.JmxReporter
|
import com.codahale.metrics.JmxReporter
|
||||||
import net.corda.core.concurrent.CordaFuture
|
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.doneFuture
|
||||||
import net.corda.core.internal.concurrent.flatMap
|
import net.corda.core.internal.concurrent.flatMap
|
||||||
import net.corda.core.internal.concurrent.openFuture
|
import net.corda.core.internal.concurrent.openFuture
|
||||||
@ -133,7 +134,7 @@ open class Node(override val configuration: FullNodeConfiguration,
|
|||||||
|
|
||||||
private lateinit var userService: RPCUserService
|
private lateinit var userService: RPCUserService
|
||||||
|
|
||||||
override fun makeMessagingService(): MessagingService {
|
override fun makeMessagingService(legalIdentity: PartyAndCertificate): MessagingService {
|
||||||
userService = RPCUserServiceImpl(configuration.rpcUsers)
|
userService = RPCUserServiceImpl(configuration.rpcUsers)
|
||||||
|
|
||||||
val (serverAddress, advertisedAddress) = with(configuration) {
|
val (serverAddress, advertisedAddress) = with(configuration) {
|
||||||
@ -147,7 +148,7 @@ open class Node(override val configuration: FullNodeConfiguration,
|
|||||||
|
|
||||||
printBasicNodeInfo("Incoming connection address", advertisedAddress.toString())
|
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(
|
return NodeMessagingClient(
|
||||||
configuration,
|
configuration,
|
||||||
versionInfo,
|
versionInfo,
|
||||||
|
@ -7,7 +7,9 @@ import net.corda.core.identity.AbstractParty
|
|||||||
import net.corda.core.identity.AnonymousParty
|
import net.corda.core.identity.AnonymousParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.identity.PartyAndCertificate
|
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.IdentityService
|
||||||
|
import net.corda.core.node.services.UnknownAnonymousPartyException
|
||||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||||
import net.corda.core.utilities.loggerFor
|
import net.corda.core.utilities.loggerFor
|
||||||
import net.corda.core.utilities.trace
|
import net.corda.core.utilities.trace
|
||||||
@ -16,16 +18,13 @@ import org.bouncycastle.cert.X509CertificateHolder
|
|||||||
import java.security.InvalidAlgorithmParameterException
|
import java.security.InvalidAlgorithmParameterException
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.security.cert.*
|
import java.security.cert.*
|
||||||
import java.util.*
|
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import javax.annotation.concurrent.ThreadSafe
|
import javax.annotation.concurrent.ThreadSafe
|
||||||
import kotlin.collections.LinkedHashSet
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple identity service which caches parties and provides functionality for efficient lookup.
|
* 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 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
|
@ThreadSafe
|
||||||
class InMemoryIdentityService(identities: Iterable<PartyAndCertificate> = emptySet(),
|
class InMemoryIdentityService(identities: Iterable<PartyAndCertificate> = emptySet(),
|
||||||
@ -43,7 +42,7 @@ class InMemoryIdentityService(identities: Iterable<PartyAndCertificate> = emptyS
|
|||||||
* Certificate store for certificate authority and intermediary certificates.
|
* Certificate store for certificate authority and intermediary certificates.
|
||||||
*/
|
*/
|
||||||
override val caCertStore: CertStore
|
override val caCertStore: CertStore
|
||||||
override val trustRootHolder = X509CertificateHolder(trustRoot.encoded)
|
override val trustRootHolder = trustRoot.toX509CertHolder()
|
||||||
override val trustAnchor: TrustAnchor = TrustAnchor(trustRoot, null)
|
override val trustAnchor: TrustAnchor = TrustAnchor(trustRoot, null)
|
||||||
private val keyToParties = ConcurrentHashMap<PublicKey, PartyAndCertificate>()
|
private val keyToParties = ConcurrentHashMap<PublicKey, PartyAndCertificate>()
|
||||||
private val principalToParties = ConcurrentHashMap<X500Name, PartyAndCertificate>()
|
private val principalToParties = ConcurrentHashMap<X500Name, PartyAndCertificate>()
|
||||||
@ -54,7 +53,6 @@ class InMemoryIdentityService(identities: Iterable<PartyAndCertificate> = emptyS
|
|||||||
keyToParties.putAll(identities.associateBy { it.owningKey } )
|
keyToParties.putAll(identities.associateBy { it.owningKey } )
|
||||||
principalToParties.putAll(identities.associateBy { it.name })
|
principalToParties.putAll(identities.associateBy { it.name })
|
||||||
confidentialIdentities.forEach { identity ->
|
confidentialIdentities.forEach { identity ->
|
||||||
require(identity.certPath.certificates.size >= 2) { "Certificate path must at least include subject and issuing certificates" }
|
|
||||||
principalToParties.computeIfAbsent(identity.name) { identity }
|
principalToParties.computeIfAbsent(identity.name) { identity }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -66,13 +64,10 @@ class InMemoryIdentityService(identities: Iterable<PartyAndCertificate> = emptyS
|
|||||||
// TODO: Check the certificate validation logic
|
// TODO: Check the certificate validation logic
|
||||||
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
|
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
|
||||||
override fun verifyAndRegisterIdentity(identity: PartyAndCertificate): PartyAndCertificate? {
|
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
|
// Validate the chain first, before we do anything clever with it
|
||||||
identity.verify(trustAnchor)
|
identity.verify(trustAnchor)
|
||||||
|
|
||||||
log.trace { "Registering identity $identity" }
|
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
|
keyToParties[identity.owningKey] = identity
|
||||||
// Always keep the first party we registered, as that's the well known identity
|
// Always keep the first party we registered, as that's the well known identity
|
||||||
principalToParties.computeIfAbsent(identity.name) { 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}")
|
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
|
// 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 partyFromKey(key: PublicKey): Party? = keyToParties[key]?.party
|
||||||
override fun partyFromX500Name(principal: X500Name): Party? = principalToParties[principal]?.party
|
override fun partyFromX500Name(principal: X500Name): Party? = principalToParties[principal]?.party
|
||||||
@ -128,13 +123,13 @@ class InMemoryIdentityService(identities: Iterable<PartyAndCertificate> = emptyS
|
|||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IdentityService.UnknownAnonymousPartyException::class)
|
@Throws(UnknownAnonymousPartyException::class)
|
||||||
override fun assertOwnership(party: Party, anonymousParty: AnonymousParty) {
|
override fun assertOwnership(party: Party, anonymousParty: AnonymousParty) {
|
||||||
val path = keyToParties[anonymousParty.owningKey]?.certPath ?: throw IdentityService.UnknownAnonymousPartyException("Unknown anonymous party ${anonymousParty.owningKey.toStringShort()}")
|
val anonymousIdentity = keyToParties[anonymousParty.owningKey] ?:
|
||||||
require(path.certificates.size > 1) { "Certificate path must contain at least two certificates" }
|
throw UnknownAnonymousPartyException("Unknown $anonymousParty")
|
||||||
val actual = path.certificates[1]
|
val issuingCert = anonymousIdentity.certPath.certificates[1]
|
||||||
require(actual is X509Certificate && actual.publicKey == party.owningKey) { "Next certificate in the path must match the party key ${party.owningKey.toStringShort()}." }
|
require(issuingCert.publicKey == party.owningKey) {
|
||||||
val target = path.certificates.first()
|
"Issuing certificate's public key must match the party key ${party.owningKey.toStringShort()}."
|
||||||
require(target is X509Certificate && target.publicKey == anonymousParty.owningKey) { "Certificate path starts with a certificate for the anonymous party" }
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ package net.corda.node.services.keys
|
|||||||
import net.corda.core.crypto.ContentSignerBuilder
|
import net.corda.core.crypto.ContentSignerBuilder
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.crypto.cert
|
import net.corda.core.crypto.cert
|
||||||
import net.corda.core.identity.Party
|
|
||||||
import net.corda.core.identity.PartyAndCertificate
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
import net.corda.core.node.services.IdentityService
|
import net.corda.core.node.services.IdentityService
|
||||||
import net.corda.core.utilities.days
|
import net.corda.core.utilities.days
|
||||||
@ -35,10 +34,11 @@ fun freshCertificate(identityService: IdentityService,
|
|||||||
revocationEnabled: Boolean = false): PartyAndCertificate {
|
revocationEnabled: Boolean = false): PartyAndCertificate {
|
||||||
val issuerCertificate = issuer.certificate
|
val issuerCertificate = issuer.certificate
|
||||||
val window = X509Utilities.getCertificateValidityWindow(Duration.ZERO, 3650.days, issuerCertificate)
|
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 certFactory = CertificateFactory.getInstance("X509")
|
||||||
val ourCertPath = certFactory.generateCertPath(listOf(ourCertificate.cert) + issuer.certPath.certificates)
|
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)
|
identityService.verifyAndRegisterIdentity(anonymisedIdentity)
|
||||||
return 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.concurrent.openFuture
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.internal.noneOrSingle
|
import net.corda.core.internal.noneOrSingle
|
||||||
|
import net.corda.core.internal.toX509CertHolder
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.node.services.NetworkMapCache
|
import net.corda.core.node.services.NetworkMapCache
|
||||||
import net.corda.core.node.services.NetworkMapCache.MapChange
|
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
|
||||||
import net.corda.node.utilities.X509Utilities.CORDA_CLIENT_TLS
|
import net.corda.node.utilities.X509Utilities.CORDA_CLIENT_TLS
|
||||||
import net.corda.node.utilities.X509Utilities.CORDA_ROOT_CA
|
import net.corda.node.utilities.X509Utilities.CORDA_ROOT_CA
|
||||||
import net.corda.node.utilities.getX509Certificate
|
|
||||||
import net.corda.node.utilities.loadKeyStore
|
import net.corda.node.utilities.loadKeyStore
|
||||||
import net.corda.nodeapi.*
|
import net.corda.nodeapi.*
|
||||||
import net.corda.nodeapi.ArtemisMessagingComponent.Companion.NODE_USER
|
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.spi.core.security.jaas.UserPrincipal
|
||||||
import org.apache.activemq.artemis.utils.ConfigurationHelper
|
import org.apache.activemq.artemis.utils.ConfigurationHelper
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
|
||||||
import rx.Subscription
|
import rx.Subscription
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
@ -273,12 +272,7 @@ class ArtemisMessagingServer(override val config: NodeConfiguration,
|
|||||||
private fun createArtemisSecurityManager(): ActiveMQJAASSecurityManager {
|
private fun createArtemisSecurityManager(): ActiveMQJAASSecurityManager {
|
||||||
val keyStore = loadKeyStore(config.sslKeystore, config.keyStorePassword)
|
val keyStore = loadKeyStore(config.sslKeystore, config.keyStorePassword)
|
||||||
val trustStore = loadKeyStore(config.trustStoreFile, config.trustStorePassword)
|
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(
|
val defaultCertPolicies = mapOf(
|
||||||
PEER_ROLE to CertificateChainCheckPolicy.RootMustMatch,
|
PEER_ROLE to CertificateChainCheckPolicy.RootMustMatch,
|
||||||
NODE_ROLE to CertificateChainCheckPolicy.LeafMustMatch,
|
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!"
|
"misconfiguration by the remote peer or an SSL man-in-the-middle attack!"
|
||||||
}
|
}
|
||||||
// Make sure certificate has the same name.
|
// Make sure certificate has the same name.
|
||||||
val peerCertificate = X509CertificateHolder(session.peerCertificateChain.first().encoded)
|
val peerCertificate = session.peerCertificateChain[0].toX509CertHolder()
|
||||||
require(peerCertificate.subject == expectedLegalName) {
|
require(peerCertificate.subject == expectedLegalName) {
|
||||||
"Peer has wrong subject name in the certificate - expected $expectedLegalName but got ${peerCertificate.subject}. This is either a fatal " +
|
"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!"
|
"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)
|
server.onTcpConnection(peerLegalName)
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
connection.close()
|
connection.close()
|
||||||
|
@ -21,6 +21,7 @@ import java.util.Collections.synchronizedMap
|
|||||||
class PersistentNetworkMapService(services: ServiceHubInternal, minimumPlatformVersion: Int)
|
class PersistentNetworkMapService(services: ServiceHubInternal, minimumPlatformVersion: Int)
|
||||||
: AbstractNetworkMapService(services, minimumPlatformVersion) {
|
: 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") {
|
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 nodeParty = partyAndCertificate("node_party_name", "node_party_key", "node_party_certificate", "node_party_path")
|
||||||
val registrationInfo = blob("node_registration_info")
|
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) {
|
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
|
// 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],
|
override fun keyFromRow(row: ResultRow): PartyAndCertificate = PartyAndCertificate(row[table.nodeParty.certPath])
|
||||||
row[table.nodeParty.certificate], row[table.nodeParty.certPath])
|
|
||||||
|
|
||||||
override fun valueFromRow(row: ResultRow): NodeRegistrationInfo = deserializeFromBlob(row[table.registrationInfo])
|
override fun valueFromRow(row: ResultRow): NodeRegistrationInfo = deserializeFromBlob(row[table.registrationInfo])
|
||||||
|
|
||||||
override fun addKeyToInsert(insert: InsertStatement, entry: Map.Entry<PartyAndCertificate, 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.name] = entry.key.name.toString()
|
||||||
insert[table.nodeParty.owningKey] = entry.key.owningKey
|
insert[table.nodeParty.owningKey] = entry.key.owningKey
|
||||||
insert[table.nodeParty.certPath] = entry.key.certPath
|
|
||||||
insert[table.nodeParty.certificate] = entry.key.certificate
|
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>) {
|
override fun addValueToInsert(insert: InsertStatement, entry: Map.Entry<PartyAndCertificate, NodeRegistrationInfo>, finalizables: MutableList<() -> Unit>) {
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
package net.corda.node.utilities
|
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.exists
|
||||||
import net.corda.core.internal.read
|
import net.corda.core.internal.read
|
||||||
|
import net.corda.core.internal.toX509CertHolder
|
||||||
import net.corda.core.internal.write
|
import net.corda.core.internal.write
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
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.
|
* @return The X509Certificate found in the KeyStore under the specified alias.
|
||||||
*/
|
*/
|
||||||
fun KeyStore.getX509Certificate(alias: String): X509CertificateHolder {
|
fun KeyStore.getX509Certificate(alias: String): X509CertificateHolder {
|
||||||
val encoded = getCertificate(alias)?.encoded ?: throw IllegalArgumentException("No certificate under alias \"$alias\"")
|
val certificate = getCertificate(alias) ?: throw IllegalArgumentException("No certificate under alias \"$alias\"")
|
||||||
return X509CertificateHolder(encoded)
|
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 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.AnonymousParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.identity.PartyAndCertificate
|
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.services.identity.InMemoryIdentityService
|
||||||
import net.corda.node.utilities.CertificateType
|
import net.corda.node.utilities.CertificateType
|
||||||
import net.corda.node.utilities.X509Utilities
|
import net.corda.node.utilities.X509Utilities
|
||||||
@ -90,10 +90,10 @@ class InMemoryIdentityServiceTests {
|
|||||||
val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val service = InMemoryIdentityService(trustRoot = DUMMY_CA.certificate)
|
val service = InMemoryIdentityService(trustRoot = DUMMY_CA.certificate)
|
||||||
// TODO: Generate certificate with an EdDSA key rather than ECDSA
|
// 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)
|
val txIdentity = AnonymousParty(txKey.public)
|
||||||
|
|
||||||
assertFailsWith<IdentityService.UnknownAnonymousPartyException> {
|
assertFailsWith<UnknownAnonymousPartyException> {
|
||||||
service.assertOwnership(identity, txIdentity)
|
service.assertOwnership(identity, txIdentity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -107,7 +107,7 @@ class InMemoryIdentityServiceTests {
|
|||||||
fun `get anonymous identity by key`() {
|
fun `get anonymous identity by key`() {
|
||||||
val trustRoot = DUMMY_CA
|
val trustRoot = DUMMY_CA
|
||||||
val (alice, aliceTxIdentity) = createParty(ALICE.name, trustRoot)
|
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
|
// Now we have identities, construct the service and let it know about both
|
||||||
val service = InMemoryIdentityService(setOf(alice), emptySet(), trustRoot.certificate.cert)
|
val service = InMemoryIdentityService(setOf(alice), emptySet(), trustRoot.certificate.cert)
|
||||||
@ -163,7 +163,7 @@ class InMemoryIdentityServiceTests {
|
|||||||
val txKey = Crypto.generateKeyPair()
|
val txKey = Crypto.generateKeyPair()
|
||||||
val txCert = X509Utilities.createCertificate(CertificateType.IDENTITY, issuer.certificate, issuerKeyPair, x500Name, txKey.public)
|
val txCert = X509Utilities.createCertificate(CertificateType.IDENTITY, issuer.certificate, issuerKeyPair, x500Name, txKey.public)
|
||||||
val txCertPath = certFactory.generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates)
|
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.crypto.commonName
|
||||||
import net.corda.core.internal.exists
|
import net.corda.core.internal.exists
|
||||||
import net.corda.core.internal.toTypedArray
|
import net.corda.core.internal.toTypedArray
|
||||||
|
import net.corda.core.internal.toX509CertHolder
|
||||||
import net.corda.node.utilities.X509Utilities
|
import net.corda.node.utilities.X509Utilities
|
||||||
import net.corda.node.utilities.loadKeyStore
|
import net.corda.node.utilities.loadKeyStore
|
||||||
import net.corda.testing.ALICE
|
import net.corda.testing.ALICE
|
||||||
import net.corda.testing.getTestX509Name
|
import net.corda.testing.getTestX509Name
|
||||||
import net.corda.testing.testNodeConfiguration
|
import net.corda.testing.testNodeConfiguration
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.rules.TemporaryFolder
|
import org.junit.rules.TemporaryFolder
|
||||||
@ -68,7 +68,7 @@ class NetworkRegistrationHelperTest {
|
|||||||
assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_TLS))
|
assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_TLS))
|
||||||
val certificateChain = getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
|
val certificateChain = getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
|
||||||
assertEquals(3, certificateChain.size)
|
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 {
|
sslKeystore.run {
|
||||||
@ -78,7 +78,7 @@ class NetworkRegistrationHelperTest {
|
|||||||
assertTrue(containsAlias(X509Utilities.CORDA_CLIENT_TLS))
|
assertTrue(containsAlias(X509Utilities.CORDA_CLIENT_TLS))
|
||||||
val certificateChain = getCertificateChain(X509Utilities.CORDA_CLIENT_TLS)
|
val certificateChain = getCertificateChain(X509Utilities.CORDA_CLIENT_TLS)
|
||||||
assertEquals(4, certificateChain.size)
|
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 {
|
trustStore.run {
|
||||||
|
@ -3,11 +3,9 @@ package net.corda.traderdemo.flow
|
|||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import net.corda.contracts.CommercialPaper
|
import net.corda.contracts.CommercialPaper
|
||||||
import net.corda.core.contracts.Amount
|
import net.corda.core.contracts.Amount
|
||||||
import net.corda.finance.`issued by`
|
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.flows.FinalityFlow
|
import net.corda.core.flows.FinalityFlow
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.flows.InitiatingFlow
|
|
||||||
import net.corda.core.flows.StartableByRPC
|
import net.corda.core.flows.StartableByRPC
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.transactions.SignedTransaction
|
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.ProgressTracker
|
||||||
import net.corda.core.utilities.days
|
import net.corda.core.utilities.days
|
||||||
import net.corda.core.utilities.seconds
|
import net.corda.core.utilities.seconds
|
||||||
|
import net.corda.finance.`issued by`
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.*
|
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.
|
* Flow for the Bank of Corda node to issue some commercial paper to the seller's node, to sell to the buyer.
|
||||||
*/
|
*/
|
||||||
@InitiatingFlow
|
|
||||||
@StartableByRPC
|
@StartableByRPC
|
||||||
class CommercialPaperIssueFlow(val amount: Amount<Currency>,
|
class CommercialPaperIssueFlow(val amount: Amount<Currency>,
|
||||||
val issueRef: OpaqueBytes,
|
val issueRef: OpaqueBytes,
|
||||||
|
@ -207,7 +207,7 @@ fun getTestPartyAndCertificate(party: Party, trustRoot: CertificateAndKeyPair =
|
|||||||
val certFactory = CertificateFactory.getInstance("X509")
|
val certFactory = CertificateFactory.getInstance("X509")
|
||||||
val certHolder = X509Utilities.createCertificate(CertificateType.IDENTITY, trustRoot.certificate, trustRoot.keyPair, party.name, party.owningKey)
|
val certHolder = X509Utilities.createCertificate(CertificateType.IDENTITY, trustRoot.certificate, trustRoot.keyPair, party.name, party.owningKey)
|
||||||
val certPath = certFactory.generateCertPath(listOf(certHolder.cert, trustRoot.certificate.cert))
|
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
|
// 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.
|
// 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 }
|
require(id >= 0) { "Node ID must be zero or positive, was passed: " + id }
|
||||||
return mockNet.messagingNetwork.createNodeWithID(
|
return mockNet.messagingNetwork.createNodeWithID(
|
||||||
!mockNet.threadPerNode,
|
!mockNet.threadPerNode,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user