mirror of
https://github.com/corda/corda.git
synced 2024-12-28 00:38:55 +00:00
Merge remote-tracking branch 'open/master' into shams-os-merge-040118
# Conflicts: # node/src/integration-test/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt # testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt # testing/test-utils/src/main/kotlin/net/corda/testing/dsl/TransactionDSLInterpreter.kt
This commit is contained in:
commit
13619df0b1
@ -3579,6 +3579,9 @@ public static final class net.corda.client.jackson.StringToMethodCallParser$Unpa
|
|||||||
public <init>(String)
|
public <init>(String)
|
||||||
@org.jetbrains.annotations.NotNull public final String getMethodName()
|
@org.jetbrains.annotations.NotNull public final String getMethodName()
|
||||||
##
|
##
|
||||||
|
public static interface net.corda.testing.node.InMemoryMessagingNetwork$LatencyCalculator
|
||||||
|
@org.jetbrains.annotations.NotNull public abstract java.time.Duration between(net.corda.core.messaging.SingleMessageRecipient, net.corda.core.messaging.SingleMessageRecipient)
|
||||||
|
##
|
||||||
public final class net.corda.client.rpc.CordaRPCClient extends java.lang.Object
|
public final class net.corda.client.rpc.CordaRPCClient extends java.lang.Object
|
||||||
public <init>(net.corda.core.utilities.NetworkHostAndPort)
|
public <init>(net.corda.core.utilities.NetworkHostAndPort)
|
||||||
public <init>(net.corda.core.utilities.NetworkHostAndPort, net.corda.client.rpc.CordaRPCClientConfiguration)
|
public <init>(net.corda.core.utilities.NetworkHostAndPort, net.corda.client.rpc.CordaRPCClientConfiguration)
|
||||||
|
@ -10,7 +10,6 @@ import net.corda.core.identity.AnonymousParty
|
|||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
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.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import net.corda.core.serialization.SerializedBytes
|
import net.corda.core.serialization.SerializedBytes
|
||||||
@ -18,14 +17,8 @@ import net.corda.core.serialization.deserialize
|
|||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.utilities.ProgressTracker
|
import net.corda.core.utilities.ProgressTracker
|
||||||
import net.corda.core.utilities.unwrap
|
import net.corda.core.utilities.unwrap
|
||||||
import org.bouncycastle.asn1.DERSet
|
|
||||||
import org.bouncycastle.asn1.pkcs.CertificationRequestInfo
|
|
||||||
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
|
|
||||||
import java.io.ByteArrayOutputStream
|
|
||||||
import java.nio.charset.Charset
|
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.security.SignatureException
|
import java.security.SignatureException
|
||||||
import java.security.cert.CertPath
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -80,8 +80,10 @@ data class CordaX500Name(val commonName: String?,
|
|||||||
const val MAX_LENGTH_STATE = 64
|
const val MAX_LENGTH_STATE = 64
|
||||||
const val MAX_LENGTH_ORGANISATION_UNIT = 64
|
const val MAX_LENGTH_ORGANISATION_UNIT = 64
|
||||||
const val MAX_LENGTH_COMMON_NAME = 64
|
const val MAX_LENGTH_COMMON_NAME = 64
|
||||||
|
|
||||||
private val supportedAttributes = setOf(BCStyle.O, BCStyle.C, BCStyle.L, BCStyle.CN, BCStyle.ST, BCStyle.OU)
|
private val supportedAttributes = setOf(BCStyle.O, BCStyle.C, BCStyle.L, BCStyle.CN, BCStyle.ST, BCStyle.OU)
|
||||||
private val countryCodes: Set<String> = ImmutableSet.copyOf(Locale.getISOCountries() + unspecifiedCountry)
|
private val countryCodes: Set<String> = ImmutableSet.copyOf(Locale.getISOCountries() + unspecifiedCountry)
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun build(principal: X500Principal): CordaX500Name {
|
fun build(principal: X500Principal): CordaX500Name {
|
||||||
val x500Name = X500Name.getInstance(principal.encoded)
|
val x500Name = X500Name.getInstance(principal.encoded)
|
||||||
@ -115,20 +117,12 @@ data class CordaX500Name(val commonName: String?,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transient
|
@Transient
|
||||||
private var x500Cache: X500Name? = null
|
private var _x500Principal: X500Principal? = null
|
||||||
|
|
||||||
val x500Principal: X500Principal
|
/** Return the [X500Principal] equivalent of this name. */
|
||||||
get() {
|
val x500Principal: X500Principal get() {
|
||||||
if (x500Cache == null) {
|
return _x500Principal ?: X500Principal(this.x500Name.encoded).also { _x500Principal = it }
|
||||||
x500Cache = this.x500Name
|
|
||||||
}
|
|
||||||
return X500Principal(x500Cache!!.encoded)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
if (x500Cache == null) {
|
|
||||||
x500Cache = this.x500Name
|
|
||||||
}
|
|
||||||
return x500Cache.toString()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun toString(): String = x500Principal.toString()
|
||||||
}
|
}
|
@ -10,8 +10,9 @@ import net.corda.core.node.ServicesForResolution
|
|||||||
import net.corda.core.serialization.SerializationContext
|
import net.corda.core.serialization.SerializationContext
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.transactions.WireTransaction
|
import net.corda.core.transactions.WireTransaction
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter
|
import org.bouncycastle.asn1.x500.X500NameBuilder
|
||||||
|
import org.bouncycastle.asn1.x500.style.BCStyle
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import rx.Observer
|
import rx.Observer
|
||||||
@ -26,8 +27,6 @@ import java.nio.charset.Charset
|
|||||||
import java.nio.charset.StandardCharsets.UTF_8
|
import java.nio.charset.StandardCharsets.UTF_8
|
||||||
import java.nio.file.*
|
import java.nio.file.*
|
||||||
import java.nio.file.attribute.FileAttribute
|
import java.nio.file.attribute.FileAttribute
|
||||||
import java.security.cert.Certificate
|
|
||||||
import java.security.cert.X509Certificate
|
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.temporal.Temporal
|
import java.time.temporal.Temporal
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -186,9 +185,6 @@ fun <T> logElapsedTime(label: String, logger: Logger? = null, body: () -> T): T
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Certificate.toX509CertHolder() = X509CertificateHolder(encoded)
|
|
||||||
val X509CertificateHolder.cert: X509Certificate get() = JcaX509CertificateConverter().getCertificate(this)
|
|
||||||
|
|
||||||
/** Convert a [ByteArrayOutputStream] to [InputStreamAndHash]. */
|
/** Convert a [ByteArrayOutputStream] to [InputStreamAndHash]. */
|
||||||
fun ByteArrayOutputStream.toInputStreamAndHash(): InputStreamAndHash {
|
fun ByteArrayOutputStream.toInputStreamAndHash(): InputStreamAndHash {
|
||||||
val bytes = toByteArray()
|
val bytes = toByteArray()
|
||||||
@ -320,6 +316,22 @@ fun ExecutorService.join() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the underlying X.500 name from this Corda-safe X.500 name. These are guaranteed to have a consistent
|
||||||
|
* ordering, such that their `toString()` function returns the same value every time for the same [CordaX500Name].
|
||||||
|
*/
|
||||||
|
val CordaX500Name.x500Name: X500Name
|
||||||
|
get() {
|
||||||
|
return X500NameBuilder(BCStyle.INSTANCE).apply {
|
||||||
|
addRDN(BCStyle.C, country)
|
||||||
|
state?.let { addRDN(BCStyle.ST, it) }
|
||||||
|
addRDN(BCStyle.L, locality)
|
||||||
|
addRDN(BCStyle.O, organisation)
|
||||||
|
organisationUnit?.let { addRDN(BCStyle.OU, it) }
|
||||||
|
commonName?.let { addRDN(BCStyle.CN, it) }
|
||||||
|
}.build()
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
val CordaX500Name.Companion.unspecifiedCountry
|
val CordaX500Name.Companion.unspecifiedCountry
|
||||||
|
@ -1,34 +0,0 @@
|
|||||||
@file:JvmName("X500NameUtils")
|
|
||||||
|
|
||||||
package net.corda.core.internal
|
|
||||||
|
|
||||||
import net.corda.core.identity.CordaX500Name
|
|
||||||
import org.bouncycastle.asn1.ASN1ObjectIdentifier
|
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
|
||||||
import org.bouncycastle.asn1.x500.X500NameBuilder
|
|
||||||
import org.bouncycastle.asn1.x500.style.BCStyle
|
|
||||||
|
|
||||||
val X500Name.commonName: String? get() = getRDNValueString(BCStyle.CN)
|
|
||||||
val X500Name.state: String? get() = getRDNValueString(BCStyle.ST)
|
|
||||||
val X500Name.organisation: String get() = getRDNValueString(BCStyle.O) ?: throw IllegalArgumentException("Malformed X500 name, organisation attribute (O) cannot be empty.")
|
|
||||||
val X500Name.locality: String get() = getRDNValueString(BCStyle.L) ?: throw IllegalArgumentException("Malformed X500 name, locality attribute (L) cannot be empty.")
|
|
||||||
val X500Name.country: String get() = getRDNValueString(BCStyle.C) ?: throw IllegalArgumentException("Malformed X500 name, country attribute (C) cannot be empty.")
|
|
||||||
|
|
||||||
private fun X500Name.getRDNValueString(identifier: ASN1ObjectIdentifier): String? = getRDNs(identifier).firstOrNull()?.first?.value?.toString()
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the underlying X.500 name from this Corda-safe X.500 name. These are guaranteed to have a consistent
|
|
||||||
* ordering, such that their `toString()` function returns the same value every time for the same [CordaX500Name].
|
|
||||||
*/
|
|
||||||
val CordaX500Name.x500Name: X500Name
|
|
||||||
get() {
|
|
||||||
return X500NameBuilder(BCStyle.INSTANCE).apply {
|
|
||||||
addRDN(BCStyle.C, country)
|
|
||||||
state?.let { addRDN(BCStyle.ST, it) }
|
|
||||||
addRDN(BCStyle.L, locality)
|
|
||||||
addRDN(BCStyle.O, organisation)
|
|
||||||
organisationUnit?.let { addRDN(BCStyle.OU, it) }
|
|
||||||
commonName?.let { addRDN(BCStyle.CN, it) }
|
|
||||||
}.build()
|
|
||||||
}
|
|
@ -1,8 +1,6 @@
|
|||||||
package net.corda.core.crypto
|
package net.corda.core.crypto
|
||||||
|
|
||||||
import net.corda.core.crypto.CompositeKey.NodeAndWeight
|
import net.corda.core.crypto.CompositeKey.NodeAndWeight
|
||||||
import net.corda.core.identity.CordaX500Name
|
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.internal.declaredField
|
import net.corda.core.internal.declaredField
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
@ -15,6 +13,7 @@ import org.junit.Rule
|
|||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.rules.TemporaryFolder
|
import org.junit.rules.TemporaryFolder
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertFailsWith
|
import kotlin.test.assertFailsWith
|
||||||
import kotlin.test.assertFalse
|
import kotlin.test.assertFalse
|
||||||
@ -334,7 +333,7 @@ class CompositeKeyTests {
|
|||||||
|
|
||||||
// Create self sign CA.
|
// Create self sign CA.
|
||||||
val caKeyPair = Crypto.generateKeyPair()
|
val caKeyPair = Crypto.generateKeyPair()
|
||||||
val caName = CordaX500Name(commonName = "Test CA", organisation = "R3 Ltd", locality = "London", country = "GB")
|
val caName = X500Principal("CN=Test CA,O=R3 Ltd,L=London,C=GB")
|
||||||
val ca = X509Utilities.createSelfSignedCACertificate(caName, caKeyPair)
|
val ca = X509Utilities.createSelfSignedCACertificate(caName, caKeyPair)
|
||||||
|
|
||||||
// Sign the composite key with the self sign CA.
|
// Sign the composite key with the self sign CA.
|
||||||
@ -343,7 +342,7 @@ class CompositeKeyTests {
|
|||||||
// Store certificate to keystore.
|
// Store certificate to keystore.
|
||||||
val keystorePath = tempFolder.root.toPath() / "keystore.jks"
|
val keystorePath = tempFolder.root.toPath() / "keystore.jks"
|
||||||
val keystore = loadOrCreateKeyStore(keystorePath, "password")
|
val keystore = loadOrCreateKeyStore(keystorePath, "password")
|
||||||
keystore.setCertificateEntry("CompositeKey", compositeKeyCert.cert)
|
keystore.setCertificateEntry("CompositeKey", compositeKeyCert)
|
||||||
keystore.save(keystorePath, "password")
|
keystore.save(keystorePath, "password")
|
||||||
|
|
||||||
// Load keystore from disk.
|
// Load keystore from disk.
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package net.corda.core.crypto
|
package net.corda.core.crypto
|
||||||
|
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.nodeapi.internal.crypto.*
|
import net.corda.nodeapi.internal.crypto.*
|
||||||
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
@ -14,6 +13,7 @@ import java.security.KeyStore
|
|||||||
import java.security.cert.CertPathValidator
|
import java.security.cert.CertPathValidator
|
||||||
import java.security.cert.CertPathValidatorException
|
import java.security.cert.CertPathValidatorException
|
||||||
import java.security.cert.PKIXParameters
|
import java.security.cert.PKIXParameters
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
import kotlin.test.assertFailsWith
|
import kotlin.test.assertFailsWith
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
@ -26,17 +26,22 @@ class X509NameConstraintsTest {
|
|||||||
CertificateType.NODE_CA,
|
CertificateType.NODE_CA,
|
||||||
intermediateCa.certificate,
|
intermediateCa.certificate,
|
||||||
intermediateCa.keyPair,
|
intermediateCa.keyPair,
|
||||||
CordaX500Name("Corda Client CA", "R3 Ltd", "London", "GB"),
|
CordaX500Name("Corda Client CA", "R3 Ltd", "London", "GB").x500Principal,
|
||||||
nodeCaKeyPair.public,
|
nodeCaKeyPair.public,
|
||||||
nameConstraints = nameConstraints)
|
nameConstraints = nameConstraints)
|
||||||
|
|
||||||
val keyPass = "password"
|
val keyPass = "password"
|
||||||
val trustStore = KeyStore.getInstance(KEYSTORE_TYPE)
|
val trustStore = KeyStore.getInstance(KEYSTORE_TYPE)
|
||||||
trustStore.load(null, keyPass.toCharArray())
|
trustStore.load(null, keyPass.toCharArray())
|
||||||
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCa.certificate.cert)
|
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCa.certificate)
|
||||||
|
|
||||||
val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, subjectName, tlsKeyPair.public)
|
val tlsCert = X509Utilities.createCertificate(
|
||||||
|
CertificateType.TLS,
|
||||||
|
nodeCaCert,
|
||||||
|
nodeCaKeyPair,
|
||||||
|
X500Principal(subjectName.encoded),
|
||||||
|
tlsKeyPair.public)
|
||||||
|
|
||||||
val keyStore = KeyStore.getInstance(KEYSTORE_TYPE)
|
val keyStore = KeyStore.getInstance(KEYSTORE_TYPE)
|
||||||
keyStore.load(null, keyPass.toCharArray())
|
keyStore.load(null, keyPass.toCharArray())
|
||||||
|
@ -7,30 +7,36 @@ import kotlin.test.assertNull
|
|||||||
|
|
||||||
class CordaX500NameTest {
|
class CordaX500NameTest {
|
||||||
@Test
|
@Test
|
||||||
fun `parse service name with organisational unit`() {
|
fun `service name with organisational unit`() {
|
||||||
val name = CordaX500Name.parse("O=Bank A, L=New York, C=US, OU=Org Unit, CN=Service Name")
|
val name = CordaX500Name.parse("O=Bank A, L=New York, C=US, OU=Org Unit, CN=Service Name")
|
||||||
assertEquals("Service Name", name.commonName)
|
assertEquals("Service Name", name.commonName)
|
||||||
assertEquals("Org Unit", name.organisationUnit)
|
assertEquals("Org Unit", name.organisationUnit)
|
||||||
assertEquals("Bank A", name.organisation)
|
assertEquals("Bank A", name.organisation)
|
||||||
assertEquals("New York", name.locality)
|
assertEquals("New York", name.locality)
|
||||||
|
assertEquals(CordaX500Name.parse(name.toString()), name)
|
||||||
|
assertEquals(CordaX500Name.build(name.x500Principal), name)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `parse service name`() {
|
fun `service name`() {
|
||||||
val name = CordaX500Name.parse("O=Bank A, L=New York, C=US, CN=Service Name")
|
val name = CordaX500Name.parse("O=Bank A, L=New York, C=US, CN=Service Name")
|
||||||
assertEquals("Service Name", name.commonName)
|
assertEquals("Service Name", name.commonName)
|
||||||
assertNull(name.organisationUnit)
|
assertNull(name.organisationUnit)
|
||||||
assertEquals("Bank A", name.organisation)
|
assertEquals("Bank A", name.organisation)
|
||||||
assertEquals("New York", name.locality)
|
assertEquals("New York", name.locality)
|
||||||
|
assertEquals(CordaX500Name.parse(name.toString()), name)
|
||||||
|
assertEquals(CordaX500Name.build(name.x500Principal), name)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `parse legal entity name`() {
|
fun `legal entity name`() {
|
||||||
val name = CordaX500Name.parse("O=Bank A, L=New York, C=US")
|
val name = CordaX500Name.parse("O=Bank A, L=New York, C=US")
|
||||||
assertNull(name.commonName)
|
assertNull(name.commonName)
|
||||||
assertNull(name.organisationUnit)
|
assertNull(name.organisationUnit)
|
||||||
assertEquals("Bank A", name.organisation)
|
assertEquals("Bank A", name.organisation)
|
||||||
assertEquals("New York", name.locality)
|
assertEquals("New York", name.locality)
|
||||||
|
assertEquals(CordaX500Name.parse(name.toString()), name)
|
||||||
|
assertEquals(CordaX500Name.build(name.x500Principal), name)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
package net.corda.core.identity
|
package net.corda.core.identity
|
||||||
|
|
||||||
import net.corda.core.crypto.entropyToKeyPair
|
import net.corda.core.crypto.entropyToKeyPair
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.internal.read
|
import net.corda.core.internal.read
|
||||||
import net.corda.core.serialization.deserialize
|
import net.corda.core.serialization.deserialize
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.nodeapi.internal.crypto.KEYSTORE_TYPE
|
import net.corda.nodeapi.internal.crypto.KEYSTORE_TYPE
|
||||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||||
import net.corda.nodeapi.internal.crypto.save
|
import net.corda.nodeapi.internal.crypto.save
|
||||||
import net.corda.testing.DEV_CA
|
import net.corda.testing.DEV_ROOT_CA
|
||||||
import net.corda.testing.SerializationEnvironmentRule
|
import net.corda.testing.SerializationEnvironmentRule
|
||||||
import net.corda.testing.getTestPartyAndCertificate
|
import net.corda.testing.getTestPartyAndCertificate
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
@ -25,8 +24,8 @@ class PartyAndCertificateTest {
|
|||||||
val testSerialization = SerializationEnvironmentRule()
|
val testSerialization = SerializationEnvironmentRule()
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `should reject a path with no roles`() {
|
fun `reject a path with no roles`() {
|
||||||
val path = X509CertificateFactory().generateCertPath(DEV_CA.certificate.cert)
|
val path = X509CertificateFactory().generateCertPath(DEV_ROOT_CA.certificate)
|
||||||
assertFailsWith<IllegalArgumentException> { PartyAndCertificate(path) }
|
assertFailsWith<IllegalArgumentException> { PartyAndCertificate(path) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,7 +103,7 @@ Go to the terminal window displaying the CRaSH shell of PartyA. Typing ``help``
|
|||||||
commands.
|
commands.
|
||||||
|
|
||||||
.. note:: Local terminal shell is available only in a development mode. In production environment SSH server can be enabled.
|
.. note:: Local terminal shell is available only in a development mode. In production environment SSH server can be enabled.
|
||||||
More about SSH and how to connect can be found on :doc:`Shell` page.
|
More about SSH and how to connect can be found on the :doc:`shell` page.
|
||||||
|
|
||||||
We want to create an IOU of 100 with PartyB. We start the ``IOUFlow`` by typing:
|
We want to create an IOU of 100 with PartyB. We start the ``IOUFlow`` by typing:
|
||||||
|
|
||||||
|
@ -192,9 +192,6 @@ The following 3rd party types are supported.
|
|||||||
|
|
||||||
org.apache.activemq.artemis.api.core.SimpleString
|
org.apache.activemq.artemis.api.core.SimpleString
|
||||||
|
|
||||||
org.bouncycastle.asn1.x500.X500Name
|
|
||||||
org.bouncycastle.cert.X509CertificateHolder
|
|
||||||
|
|
||||||
Corda Types
|
Corda Types
|
||||||
```````````
|
```````````
|
||||||
|
|
||||||
|
@ -5,10 +5,8 @@ import net.corda.core.crypto.Crypto
|
|||||||
import net.corda.core.crypto.generateKeyPair
|
import net.corda.core.crypto.generateKeyPair
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.internal.createDirectories
|
import net.corda.core.internal.createDirectories
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.internal.toX509CertHolder
|
|
||||||
import net.corda.core.utilities.trace
|
import net.corda.core.utilities.trace
|
||||||
import net.corda.nodeapi.internal.config.NodeSSLConfiguration
|
import net.corda.nodeapi.internal.config.NodeSSLConfiguration
|
||||||
import net.corda.nodeapi.internal.crypto.*
|
import net.corda.nodeapi.internal.crypto.*
|
||||||
@ -39,10 +37,10 @@ object DevIdentityGenerator {
|
|||||||
// TODO The passwords for the dev key stores are spread everywhere and should be constants in a single location
|
// TODO The passwords for the dev key stores are spread everywhere and should be constants in a single location
|
||||||
val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
|
val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
|
||||||
val intermediateCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
|
val intermediateCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
|
||||||
val rootCert = caKeyStore.getCertificate(X509Utilities.CORDA_ROOT_CA)
|
val rootCert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA)
|
||||||
|
|
||||||
nodeSslConfig.certificatesDirectory.createDirectories()
|
nodeSslConfig.certificatesDirectory.createDirectories()
|
||||||
nodeSslConfig.createDevKeyStores(rootCert.toX509CertHolder(), intermediateCa, legalName)
|
nodeSslConfig.createDevKeyStores(rootCert, intermediateCa, legalName)
|
||||||
|
|
||||||
val keyStoreWrapper = KeyStoreWrapper(nodeSslConfig.nodeKeystore, nodeSslConfig.keyStorePassword)
|
val keyStoreWrapper = KeyStoreWrapper(nodeSslConfig.nodeKeystore, nodeSslConfig.keyStorePassword)
|
||||||
val identity = keyStoreWrapper.storeLegalIdentity(legalName, "$NODE_IDENTITY_ALIAS_PREFIX-private-key", Crypto.generateKeyPair())
|
val identity = keyStoreWrapper.storeLegalIdentity(legalName, "$NODE_IDENTITY_ALIAS_PREFIX-private-key", Crypto.generateKeyPair())
|
||||||
@ -62,16 +60,21 @@ object DevIdentityGenerator {
|
|||||||
|
|
||||||
keyPairs.zip(dirs) { keyPair, nodeDir ->
|
keyPairs.zip(dirs) { keyPair, nodeDir ->
|
||||||
val (serviceKeyCert, compositeKeyCert) = listOf(keyPair.public, compositeKey).map { publicKey ->
|
val (serviceKeyCert, compositeKeyCert) = listOf(keyPair.public, compositeKey).map { publicKey ->
|
||||||
X509Utilities.createCertificate(CertificateType.SERVICE_IDENTITY, intermediateCa.certificate, intermediateCa.keyPair, notaryName, publicKey)
|
X509Utilities.createCertificate(
|
||||||
|
CertificateType.SERVICE_IDENTITY,
|
||||||
|
intermediateCa.certificate,
|
||||||
|
intermediateCa.keyPair,
|
||||||
|
notaryName.x500Principal,
|
||||||
|
publicKey)
|
||||||
}
|
}
|
||||||
val distServKeyStoreFile = (nodeDir / "certificates").createDirectories() / "distributedService.jks"
|
val distServKeyStoreFile = (nodeDir / "certificates").createDirectories() / "distributedService.jks"
|
||||||
val keystore = loadOrCreateKeyStore(distServKeyStoreFile, "cordacadevpass")
|
val keystore = loadOrCreateKeyStore(distServKeyStoreFile, "cordacadevpass")
|
||||||
keystore.setCertificateEntry("$DISTRIBUTED_NOTARY_ALIAS_PREFIX-composite-key", compositeKeyCert.cert)
|
keystore.setCertificateEntry("$DISTRIBUTED_NOTARY_ALIAS_PREFIX-composite-key", compositeKeyCert)
|
||||||
keystore.setKeyEntry(
|
keystore.setKeyEntry(
|
||||||
"$DISTRIBUTED_NOTARY_ALIAS_PREFIX-private-key",
|
"$DISTRIBUTED_NOTARY_ALIAS_PREFIX-private-key",
|
||||||
keyPair.private,
|
keyPair.private,
|
||||||
"cordacadevkeypass".toCharArray(),
|
"cordacadevkeypass".toCharArray(),
|
||||||
arrayOf(serviceKeyCert.cert, intermediateCa.certificate.cert, rootCert))
|
arrayOf(serviceKeyCert, intermediateCa.certificate, rootCert))
|
||||||
keystore.save(distServKeyStoreFile, "cordacadevpass")
|
keystore.save(distServKeyStoreFile, "cordacadevpass")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,13 +8,13 @@ import net.corda.nodeapi.internal.crypto.*
|
|||||||
import org.bouncycastle.asn1.x509.GeneralName
|
import org.bouncycastle.asn1.x509.GeneralName
|
||||||
import org.bouncycastle.asn1.x509.GeneralSubtree
|
import org.bouncycastle.asn1.x509.GeneralSubtree
|
||||||
import org.bouncycastle.asn1.x509.NameConstraints
|
import org.bouncycastle.asn1.x509.NameConstraints
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
import java.security.cert.X509Certificate
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the node and SSL key stores needed by a node. The node key store will be populated with a node CA cert (using
|
* Create the node and SSL key stores needed by a node. The node key store will be populated with a node CA cert (using
|
||||||
* the given legal name), and the SSL key store will store the TLS cert which is a sub-cert of the node CA.
|
* the given legal name), and the SSL key store will store the TLS cert which is a sub-cert of the node CA.
|
||||||
*/
|
*/
|
||||||
fun SSLConfiguration.createDevKeyStores(rootCert: X509CertificateHolder, intermediateCa: CertificateAndKeyPair, legalName: CordaX500Name) {
|
fun SSLConfiguration.createDevKeyStores(rootCert: X509Certificate, intermediateCa: CertificateAndKeyPair, legalName: CordaX500Name) {
|
||||||
val (nodeCaCert, nodeCaKeyPair) = createDevNodeCa(intermediateCa, legalName)
|
val (nodeCaCert, nodeCaKeyPair) = createDevNodeCa(intermediateCa, legalName)
|
||||||
|
|
||||||
loadOrCreateKeyStore(nodeKeystore, keyStorePassword).apply {
|
loadOrCreateKeyStore(nodeKeystore, keyStorePassword).apply {
|
||||||
@ -27,7 +27,7 @@ fun SSLConfiguration.createDevKeyStores(rootCert: X509CertificateHolder, interme
|
|||||||
}
|
}
|
||||||
|
|
||||||
val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, legalName, tlsKeyPair.public)
|
val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, legalName.x500Principal, tlsKeyPair.public)
|
||||||
|
|
||||||
loadOrCreateKeyStore(sslKeystore, keyStorePassword).apply {
|
loadOrCreateKeyStore(sslKeystore, keyStorePassword).apply {
|
||||||
addOrReplaceKey(
|
addOrReplaceKey(
|
||||||
@ -50,7 +50,7 @@ fun createDevNodeCa(intermediateCa: CertificateAndKeyPair, legalName: CordaX500N
|
|||||||
CertificateType.NODE_CA,
|
CertificateType.NODE_CA,
|
||||||
intermediateCa.certificate,
|
intermediateCa.certificate,
|
||||||
intermediateCa.keyPair,
|
intermediateCa.keyPair,
|
||||||
legalName,
|
legalName.x500Principal,
|
||||||
keyPair.public,
|
keyPair.public,
|
||||||
nameConstraints = nameConstraints)
|
nameConstraints = nameConstraints)
|
||||||
return CertificateAndKeyPair(cert, keyPair)
|
return CertificateAndKeyPair(cert, keyPair)
|
||||||
|
@ -3,8 +3,9 @@
|
|||||||
package net.corda.nodeapi.internal.crypto
|
package net.corda.nodeapi.internal.crypto
|
||||||
|
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.internal.*
|
import net.corda.core.internal.exists
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
import net.corda.core.internal.read
|
||||||
|
import net.corda.core.internal.write
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
@ -67,18 +68,6 @@ fun loadKeyStore(input: InputStream, storePassword: String): KeyStore {
|
|||||||
return keyStore
|
return keyStore
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper extension method to add, or overwrite any key data in store.
|
|
||||||
* @param alias name to record the private key and certificate chain under.
|
|
||||||
* @param key cryptographic key to store.
|
|
||||||
* @param password password for unlocking the key entry in the future. This does not have to be the same password as any keys stored,
|
|
||||||
* but for SSL purposes this is recommended.
|
|
||||||
* @param chain the sequence of certificates starting with the public key certificate for this key and extending to the root CA cert.
|
|
||||||
*/
|
|
||||||
fun KeyStore.addOrReplaceKey(alias: String, key: Key, password: CharArray, chain: Array<out X509CertificateHolder>) {
|
|
||||||
addOrReplaceKey(alias, key, password, chain.map { it.cert }.toTypedArray<Certificate>())
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper extension method to add, or overwrite any key data in store.
|
* Helper extension method to add, or overwrite any key data in store.
|
||||||
* @param alias name to record the private key and certificate chain under.
|
* @param alias name to record the private key and certificate chain under.
|
||||||
@ -132,9 +121,9 @@ fun KeyStore.getKeyPair(alias: String, keyPassword: String): KeyPair = getCertif
|
|||||||
* @param keyPassword The password for the PrivateKey (not the store access password).
|
* @param keyPassword The password for the PrivateKey (not the store access password).
|
||||||
*/
|
*/
|
||||||
fun KeyStore.getCertificateAndKeyPair(alias: String, keyPassword: String): CertificateAndKeyPair {
|
fun KeyStore.getCertificateAndKeyPair(alias: String, keyPassword: String): CertificateAndKeyPair {
|
||||||
val cert = getX509Certificate(alias).toX509CertHolder()
|
val certificate = getX509Certificate(alias)
|
||||||
val publicKey = Crypto.toSupportedPublicKey(cert.subjectPublicKeyInfo)
|
val publicKey = Crypto.toSupportedPublicKey(certificate.publicKey)
|
||||||
return CertificateAndKeyPair(cert, KeyPair(publicKey, getSupportedKey(alias, keyPassword)))
|
return CertificateAndKeyPair(certificate, KeyPair(publicKey, getSupportedKey(alias, keyPassword)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2,7 +2,6 @@ package net.corda.nodeapi.internal.crypto
|
|||||||
|
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.identity.PartyAndCertificate
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.internal.read
|
import net.corda.core.internal.read
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
@ -15,8 +14,13 @@ class KeyStoreWrapper(private val storePath: Path, private val storePassword: St
|
|||||||
fun storeLegalIdentity(legalName: CordaX500Name, alias: String, keyPair: KeyPair): PartyAndCertificate {
|
fun storeLegalIdentity(legalName: CordaX500Name, alias: String, keyPair: KeyPair): PartyAndCertificate {
|
||||||
val nodeCaCertChain = keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
|
val nodeCaCertChain = keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
|
||||||
val nodeCa = getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)
|
val nodeCa = getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)
|
||||||
val identityCert = X509Utilities.createCertificate(CertificateType.LEGAL_IDENTITY, nodeCa.certificate, nodeCa.keyPair, legalName, keyPair.public)
|
val identityCert = X509Utilities.createCertificate(
|
||||||
val identityCertPath = X509CertificateFactory().generateCertPath(identityCert.cert, *nodeCaCertChain)
|
CertificateType.LEGAL_IDENTITY,
|
||||||
|
nodeCa.certificate,
|
||||||
|
nodeCa.keyPair,
|
||||||
|
legalName.x500Principal,
|
||||||
|
keyPair.public)
|
||||||
|
val identityCertPath = X509CertificateFactory().generateCertPath(identityCert, *nodeCaCertChain)
|
||||||
// Assume key password = store password.
|
// Assume key password = store password.
|
||||||
keyStore.addOrReplaceKey(alias, keyPair.private, storePassword.toCharArray(), identityCertPath.certificates.toTypedArray())
|
keyStore.addOrReplaceKey(alias, keyPair.private, storePassword.toCharArray(), identityCertPath.certificates.toTypedArray())
|
||||||
keyStore.save(storePath, storePassword)
|
keyStore.save(storePath, storePassword)
|
||||||
|
@ -4,12 +4,14 @@ import net.corda.core.CordaOID
|
|||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.crypto.SignatureScheme
|
import net.corda.core.crypto.SignatureScheme
|
||||||
import net.corda.core.crypto.random63BitValue
|
import net.corda.core.crypto.random63BitValue
|
||||||
|
import net.corda.core.internal.CertRole
|
||||||
|
import net.corda.core.internal.reader
|
||||||
|
import net.corda.core.internal.writer
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.*
|
import net.corda.core.internal.*
|
||||||
import net.corda.core.utilities.days
|
import net.corda.core.utilities.days
|
||||||
import net.corda.core.utilities.millis
|
import net.corda.core.utilities.millis
|
||||||
import org.bouncycastle.asn1.*
|
import org.bouncycastle.asn1.*
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
|
||||||
import org.bouncycastle.asn1.x500.style.BCStyle
|
import org.bouncycastle.asn1.x500.style.BCStyle
|
||||||
import org.bouncycastle.asn1.x509.*
|
import org.bouncycastle.asn1.x509.*
|
||||||
import org.bouncycastle.asn1.x509.Extension
|
import org.bouncycastle.asn1.x509.Extension
|
||||||
@ -34,6 +36,7 @@ import java.time.Duration
|
|||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.temporal.ChronoUnit
|
import java.time.temporal.ChronoUnit
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
|
|
||||||
object X509Utilities {
|
object X509Utilities {
|
||||||
val DEFAULT_IDENTITY_SIGNATURE_SCHEME = Crypto.EDDSA_ED25519_SHA512
|
val DEFAULT_IDENTITY_SIGNATURE_SCHEME = Crypto.EDDSA_ED25519_SHA512
|
||||||
@ -74,7 +77,7 @@ object X509Utilities {
|
|||||||
* @param after duration to roll forward returned end date relative to current date.
|
* @param after duration to roll forward returned end date relative to current date.
|
||||||
* @param parent if provided certificate whose validity should bound the date interval returned.
|
* @param parent if provided certificate whose validity should bound the date interval returned.
|
||||||
*/
|
*/
|
||||||
fun getCertificateValidityWindow(before: Duration, after: Duration, parent: X509CertificateHolder? = null): Pair<Date, Date> {
|
fun getCertificateValidityWindow(before: Duration, after: Duration, parent: X509Certificate? = null): Pair<Date, Date> {
|
||||||
val startOfDayUTC = Instant.now().truncatedTo(ChronoUnit.DAYS)
|
val startOfDayUTC = Instant.now().truncatedTo(ChronoUnit.DAYS)
|
||||||
val notBefore = max(startOfDayUTC - before, parent?.notBefore)
|
val notBefore = max(startOfDayUTC - before, parent?.notBefore)
|
||||||
val notAfter = min(startOfDayUTC + after, parent?.notAfter)
|
val notAfter = min(startOfDayUTC + after, parent?.notAfter)
|
||||||
@ -85,60 +88,11 @@ object X509Utilities {
|
|||||||
* Create a de novo root self-signed X509 v3 CA cert.
|
* Create a de novo root self-signed X509 v3 CA cert.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun createSelfSignedCACertificate(subject: CordaX500Name,
|
fun createSelfSignedCACertificate(subject: X500Principal,
|
||||||
keyPair: KeyPair,
|
keyPair: KeyPair,
|
||||||
validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW): X509CertificateHolder {
|
validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW): X509Certificate {
|
||||||
val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second)
|
val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second)
|
||||||
return createCertificate(CertificateType.ROOT_CA, subject.x500Name, keyPair, subject.x500Name, keyPair.public, window)
|
return createCertificate(CertificateType.ROOT_CA, subject, keyPair, subject, keyPair.public, window)
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a X509 v3 certificate for use as a CA or for TLS. This does not require a [CordaX500Name] because the
|
|
||||||
* constraints are inappropriate for TLS/CA usage, however as a result this is unsuitable for Corda identity
|
|
||||||
* certificate generation.
|
|
||||||
*
|
|
||||||
* @param issuerCertificate The Public certificate of the root CA above this used to sign it.
|
|
||||||
* @param issuerKeyPair The KeyPair of the root CA above this used to sign it.
|
|
||||||
* @param subject subject of the generated certificate.
|
|
||||||
* @param subjectPublicKey subject's public key.
|
|
||||||
* @param validityWindow The certificate's validity window. Default to [DEFAULT_VALIDITY_WINDOW] if not provided.
|
|
||||||
* @return A data class is returned containing the new intermediate CA Cert and its KeyPair for signing downstream certificates.
|
|
||||||
* Note the generated certificate tree is capped at max depth of 1 below this to be in line with commercially available certificates.
|
|
||||||
*/
|
|
||||||
@JvmStatic
|
|
||||||
fun createCertificate(certificateType: CertificateType,
|
|
||||||
issuerCertificate: X509CertificateHolder,
|
|
||||||
issuerKeyPair: KeyPair,
|
|
||||||
subject: CordaX500Name,
|
|
||||||
subjectPublicKey: PublicKey,
|
|
||||||
validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW,
|
|
||||||
nameConstraints: NameConstraints? = null): X509CertificateHolder {
|
|
||||||
return createCertificate(certificateType, issuerCertificate, issuerKeyPair, subject.x500Name, subjectPublicKey, validityWindow, nameConstraints)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a X509 v3 certificate for use as a CA or for TLS. This does not require a [CordaX500Name] because the
|
|
||||||
* constraints are inappropriate for TLS/CA usage, however as a result this is unsuitable for Corda identity
|
|
||||||
* certificate generation.
|
|
||||||
*
|
|
||||||
* @param issuerCertificate The Public certificate of the root CA above this used to sign it.
|
|
||||||
* @param issuerKeyPair The KeyPair of the root CA above this used to sign it.
|
|
||||||
* @param subject subject of the generated certificate.
|
|
||||||
* @param subjectPublicKey subject's public key.
|
|
||||||
* @param validityWindow The certificate's validity window. Default to [DEFAULT_VALIDITY_WINDOW] if not provided.
|
|
||||||
* @return A data class is returned containing the new intermediate CA Cert and its KeyPair for signing downstream certificates.
|
|
||||||
* Note the generated certificate tree is capped at max depth of 1 below this to be in line with commercially available certificates.
|
|
||||||
*/
|
|
||||||
@JvmStatic
|
|
||||||
fun createCertificate(certificateType: CertificateType,
|
|
||||||
issuerCertificate: X509CertificateHolder,
|
|
||||||
issuerKeyPair: KeyPair,
|
|
||||||
subject: X500Name,
|
|
||||||
subjectPublicKey: PublicKey,
|
|
||||||
validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW,
|
|
||||||
nameConstraints: NameConstraints? = null): X509CertificateHolder {
|
|
||||||
val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second, issuerCertificate)
|
|
||||||
return createCertificate(certificateType, issuerCertificate.subject, issuerKeyPair, subject, subjectPublicKey, window, nameConstraints)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(CertPathValidatorException::class)
|
@Throws(CertPathValidatorException::class)
|
||||||
@ -153,13 +107,13 @@ object X509Utilities {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper method to store a .pem/.cer format file copy of a certificate if required for import into a PC/Mac, or for inspection.
|
* Helper method to store a .pem/.cer format file copy of a certificate if required for import into a PC/Mac, or for inspection.
|
||||||
* @param x509Certificate certificate to save.
|
* @param certificate certificate to save.
|
||||||
* @param file Target file.
|
* @param file Target file.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun saveCertificateAsPEMFile(x509Certificate: X509Certificate, file: Path) {
|
fun saveCertificateAsPEMFile(certificate: X509Certificate, file: Path) {
|
||||||
JcaPEMWriter(file.writer()).use {
|
JcaPEMWriter(file.writer()).use {
|
||||||
it.writeObject(x509Certificate)
|
it.writeObject(certificate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,9 +126,10 @@ object X509Utilities {
|
|||||||
fun loadCertificateFromPEMFile(file: Path): X509Certificate {
|
fun loadCertificateFromPEMFile(file: Path): X509Certificate {
|
||||||
return file.reader().use {
|
return file.reader().use {
|
||||||
val pemObject = PemReader(it).readPemObject()
|
val pemObject = PemReader(it).readPemObject()
|
||||||
val certHolder = X509CertificateHolder(pemObject.content)
|
X509CertificateHolder(pemObject.content).run {
|
||||||
certHolder.isValidOn(Date())
|
isValidOn(Date())
|
||||||
certHolder.cert
|
toJca()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,38 +142,18 @@ object X509Utilities {
|
|||||||
* @param validityWindow the time period the certificate is valid for.
|
* @param validityWindow the time period the certificate is valid for.
|
||||||
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
|
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
|
||||||
*/
|
*/
|
||||||
fun createCertificate(certificateType: CertificateType,
|
fun createPartialCertificate(certificateType: CertificateType,
|
||||||
issuer: CordaX500Name,
|
issuer: X500Principal,
|
||||||
subject: CordaX500Name,
|
subject: X500Principal,
|
||||||
subjectPublicKey: PublicKey,
|
subjectPublicKey: PublicKey,
|
||||||
validityWindow: Pair<Date, Date>,
|
validityWindow: Pair<Date, Date>,
|
||||||
nameConstraints: NameConstraints? = null): X509v3CertificateBuilder {
|
nameConstraints: NameConstraints? = null): X509v3CertificateBuilder {
|
||||||
return createCertificate(certificateType, issuer.x500Name, subject.x500Name, subjectPublicKey, validityWindow, nameConstraints)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build a partial X.509 certificate ready for signing.
|
|
||||||
*
|
|
||||||
* @param issuer name of the issuing entity.
|
|
||||||
* @param subject name of the certificate subject.
|
|
||||||
* @param subjectPublicKey public key of the certificate subject.
|
|
||||||
* @param validityWindow the time period the certificate is valid for.
|
|
||||||
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
|
|
||||||
*/
|
|
||||||
internal fun createCertificate(certificateType: CertificateType,
|
|
||||||
issuer: X500Name,
|
|
||||||
subject: X500Name,
|
|
||||||
subjectPublicKey: PublicKey,
|
|
||||||
validityWindow: Pair<Date, Date>,
|
|
||||||
nameConstraints: NameConstraints? = null): X509v3CertificateBuilder {
|
|
||||||
|
|
||||||
val serial = BigInteger.valueOf(random63BitValue())
|
val serial = BigInteger.valueOf(random63BitValue())
|
||||||
val keyPurposes = DERSequence(ASN1EncodableVector().apply { certificateType.purposes.forEach { add(it) } })
|
val keyPurposes = DERSequence(ASN1EncodableVector().apply { certificateType.purposes.forEach { add(it) } })
|
||||||
val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(subjectPublicKey.encoded))
|
val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(subjectPublicKey.encoded))
|
||||||
val role = certificateType.role
|
val role = certificateType.role
|
||||||
|
|
||||||
val builder = JcaX509v3CertificateBuilder(issuer, serial, validityWindow.first, validityWindow.second,
|
val builder = JcaX509v3CertificateBuilder(issuer, serial, validityWindow.first, validityWindow.second, subject, subjectPublicKey)
|
||||||
subject, subjectPublicKey)
|
|
||||||
.addExtension(Extension.subjectKeyIdentifier, false, BcX509ExtensionUtils().createSubjectKeyIdentifier(subjectPublicKeyInfo))
|
.addExtension(Extension.subjectKeyIdentifier, false, BcX509ExtensionUtils().createSubjectKeyIdentifier(subjectPublicKeyInfo))
|
||||||
.addExtension(Extension.basicConstraints, certificateType.isCA, BasicConstraints(certificateType.isCA))
|
.addExtension(Extension.basicConstraints, certificateType.isCA, BasicConstraints(certificateType.isCA))
|
||||||
.addExtension(Extension.keyUsage, false, certificateType.keyUsage)
|
.addExtension(Extension.keyUsage, false, certificateType.keyUsage)
|
||||||
@ -234,6 +169,37 @@ object X509Utilities {
|
|||||||
return builder
|
return builder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a X509 v3 certificate using the given issuer certificate and key pair.
|
||||||
|
*
|
||||||
|
* @param issuerCertificate The Public certificate of the root CA above this used to sign it.
|
||||||
|
* @param issuerKeyPair The KeyPair of the root CA above this used to sign it.
|
||||||
|
* @param subject subject of the generated certificate.
|
||||||
|
* @param subjectPublicKey subject's public key.
|
||||||
|
* @param validityWindow The certificate's validity window. Default to [DEFAULT_VALIDITY_WINDOW] if not provided.
|
||||||
|
* @return A data class is returned containing the new intermediate CA Cert and its KeyPair for signing downstream certificates.
|
||||||
|
* Note the generated certificate tree is capped at max depth of 1 below this to be in line with commercially available certificates.
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun createCertificate(certificateType: CertificateType,
|
||||||
|
issuerCertificate: X509Certificate,
|
||||||
|
issuerKeyPair: KeyPair,
|
||||||
|
subject: X500Principal,
|
||||||
|
subjectPublicKey: PublicKey,
|
||||||
|
validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW,
|
||||||
|
nameConstraints: NameConstraints? = null): X509Certificate {
|
||||||
|
val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second, issuerCertificate)
|
||||||
|
return createCertificate(
|
||||||
|
certificateType,
|
||||||
|
issuerCertificate.subjectX500Principal,
|
||||||
|
issuerKeyPair,
|
||||||
|
subject,
|
||||||
|
subjectPublicKey,
|
||||||
|
window,
|
||||||
|
nameConstraints
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build and sign an X.509 certificate with the given signer.
|
* Build and sign an X.509 certificate with the given signer.
|
||||||
*
|
*
|
||||||
@ -245,15 +211,16 @@ object X509Utilities {
|
|||||||
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
|
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
|
||||||
*/
|
*/
|
||||||
fun createCertificate(certificateType: CertificateType,
|
fun createCertificate(certificateType: CertificateType,
|
||||||
issuer: X500Name,
|
issuer: X500Principal,
|
||||||
issuerSigner: ContentSigner,
|
issuerSigner: ContentSigner,
|
||||||
subject: CordaX500Name,
|
subject: X500Principal,
|
||||||
subjectPublicKey: PublicKey,
|
subjectPublicKey: PublicKey,
|
||||||
validityWindow: Pair<Date, Date>,
|
validityWindow: Pair<Date, Date>,
|
||||||
nameConstraints: NameConstraints? = null): X509CertificateHolder {
|
nameConstraints: NameConstraints? = null): X509Certificate {
|
||||||
val builder = createCertificate(certificateType, issuer, subject.x500Name, subjectPublicKey, validityWindow, nameConstraints)
|
val builder = createPartialCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints)
|
||||||
return builder.build(issuerSigner).apply {
|
return builder.build(issuerSigner).run {
|
||||||
require(isValidOn(Date()))
|
require(isValidOn(Date()))
|
||||||
|
toJca()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,39 +235,47 @@ object X509Utilities {
|
|||||||
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
|
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
|
||||||
*/
|
*/
|
||||||
fun createCertificate(certificateType: CertificateType,
|
fun createCertificate(certificateType: CertificateType,
|
||||||
issuer: X500Name,
|
issuer: X500Principal,
|
||||||
issuerKeyPair: KeyPair,
|
issuerKeyPair: KeyPair,
|
||||||
subject: X500Name,
|
subject: X500Principal,
|
||||||
subjectPublicKey: PublicKey,
|
subjectPublicKey: PublicKey,
|
||||||
validityWindow: Pair<Date, Date>,
|
validityWindow: Pair<Date, Date>,
|
||||||
nameConstraints: NameConstraints? = null): X509CertificateHolder {
|
nameConstraints: NameConstraints? = null): X509Certificate {
|
||||||
val signatureScheme = Crypto.findSignatureScheme(issuerKeyPair.private)
|
val signatureScheme = Crypto.findSignatureScheme(issuerKeyPair.private)
|
||||||
val provider = Crypto.findProvider(signatureScheme.providerName)
|
val provider = Crypto.findProvider(signatureScheme.providerName)
|
||||||
val builder = createCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints)
|
|
||||||
|
|
||||||
val signer = ContentSignerBuilder.build(signatureScheme, issuerKeyPair.private, provider)
|
val signer = ContentSignerBuilder.build(signatureScheme, issuerKeyPair.private, provider)
|
||||||
return builder.build(signer).apply {
|
val builder = createPartialCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints)
|
||||||
|
return builder.build(signer).run {
|
||||||
require(isValidOn(Date()))
|
require(isValidOn(Date()))
|
||||||
require(isSignatureValid(JcaContentVerifierProviderBuilder().build(issuerKeyPair.public)))
|
require(isSignatureValid(JcaContentVerifierProviderBuilder().build(issuerKeyPair.public)))
|
||||||
|
toJca()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create certificate signing request using provided information.
|
* Create certificate signing request using provided information.
|
||||||
*/
|
*/
|
||||||
private fun createCertificateSigningRequest(subject: CordaX500Name,
|
private fun createCertificateSigningRequest(subject: X500Principal,
|
||||||
email: String,
|
email: String,
|
||||||
keyPair: KeyPair,
|
keyPair: KeyPair,
|
||||||
signatureScheme: SignatureScheme): PKCS10CertificationRequest {
|
signatureScheme: SignatureScheme): PKCS10CertificationRequest {
|
||||||
val signer = ContentSignerBuilder.build(signatureScheme, keyPair.private, Crypto.findProvider(signatureScheme.providerName))
|
val signer = ContentSignerBuilder.build(signatureScheme, keyPair.private, Crypto.findProvider(signatureScheme.providerName))
|
||||||
return JcaPKCS10CertificationRequestBuilder(subject.x500Name, keyPair.public).addAttribute(BCStyle.E, DERUTF8String(email)).build(signer)
|
return JcaPKCS10CertificationRequestBuilder(subject, keyPair.public).addAttribute(BCStyle.E, DERUTF8String(email)).build(signer)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createCertificateSigningRequest(subject: CordaX500Name, email: String, keyPair: KeyPair): PKCS10CertificationRequest {
|
fun createCertificateSigningRequest(subject: X500Principal, email: String, keyPair: KeyPair): PKCS10CertificationRequest {
|
||||||
return createCertificateSigningRequest(subject, email, keyPair, DEFAULT_TLS_SIGNATURE_SCHEME)
|
return createCertificateSigningRequest(subject, email, keyPair, DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a [X509Certificate] into Bouncycastle's [X509CertificateHolder].
|
||||||
|
*
|
||||||
|
* NOTE: To avoid unnecessary copying use [X509Certificate] where possible.
|
||||||
|
*/
|
||||||
|
fun X509Certificate.toBc() = X509CertificateHolder(encoded)
|
||||||
|
fun X509CertificateHolder.toJca(): X509Certificate = X509CertificateFactory().generateCertificate(encoded.inputStream())
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps a [CertificateFactory] to remove boilerplate. It's unclear whether [CertificateFactory] is threadsafe so best
|
* Wraps a [CertificateFactory] to remove boilerplate. It's unclear whether [CertificateFactory] is threadsafe so best
|
||||||
* so assume this class is not.
|
* so assume this class is not.
|
||||||
@ -396,4 +371,4 @@ enum class CertificateType(val keyUsage: KeyUsage, vararg val purposes: KeyPurpo
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class CertificateAndKeyPair(val certificate: X509CertificateHolder, val keyPair: KeyPair)
|
data class CertificateAndKeyPair(val certificate: X509Certificate, val keyPair: KeyPair)
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package net.corda.nodeapi.internal.serialization.amqp
|
package net.corda.nodeapi.internal.serialization.amqp
|
||||||
|
|
||||||
import net.corda.core.serialization.ClassWhitelist
|
|
||||||
import net.corda.core.serialization.CordaSerializable
|
|
||||||
import com.google.common.primitives.Primitives
|
import com.google.common.primitives.Primitives
|
||||||
import com.google.common.reflect.TypeToken
|
import com.google.common.reflect.TypeToken
|
||||||
|
import net.corda.core.serialization.ClassWhitelist
|
||||||
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import net.corda.core.serialization.SerializationContext
|
import net.corda.core.serialization.SerializationContext
|
||||||
import org.apache.qpid.proton.codec.Data
|
import org.apache.qpid.proton.codec.Data
|
||||||
import java.beans.IndexedPropertyDescriptor
|
import java.beans.IndexedPropertyDescriptor
|
||||||
@ -81,10 +81,17 @@ private fun <T : Any> propertiesForSerializationFromConstructor(kotlinConstructo
|
|||||||
val rc: MutableList<PropertySerializer> = ArrayList(kotlinConstructor.parameters.size)
|
val rc: MutableList<PropertySerializer> = ArrayList(kotlinConstructor.parameters.size)
|
||||||
for (param in kotlinConstructor.parameters) {
|
for (param in kotlinConstructor.parameters) {
|
||||||
val name = param.name ?: throw NotSerializableException("Constructor parameter of $clazz has no name.")
|
val name = param.name ?: throw NotSerializableException("Constructor parameter of $clazz has no name.")
|
||||||
|
|
||||||
val matchingProperty = properties[name] ?:
|
val matchingProperty = properties[name] ?:
|
||||||
throw NotSerializableException("No property matching constructor parameter named '$name' of '$clazz'. " +
|
try {
|
||||||
"If using Java, check that you have the -parameters option specified in the Java compiler. " +
|
clazz.getDeclaredField(param.name)
|
||||||
"Alternately, provide a proxy serializer (SerializationCustomSerializer) if recompiling isn't an option")
|
throw NotSerializableException("Property '$name' or it's getter is non public, this renders class '$clazz' unserializable")
|
||||||
|
} catch (e: NoSuchFieldException) {
|
||||||
|
throw NotSerializableException("No property matching constructor parameter named '$name' of '$clazz'. " +
|
||||||
|
"If using Java, check that you have the -parameters option specified in the Java compiler. " +
|
||||||
|
"Alternately, provide a proxy serializer (SerializationCustomSerializer) if recompiling isn't an option")
|
||||||
|
}
|
||||||
|
|
||||||
// Check that the method has a getter in java.
|
// Check that the method has a getter in java.
|
||||||
val getter = matchingProperty.readMethod ?: throw NotSerializableException("Property has no getter method for $name of $clazz. " +
|
val getter = matchingProperty.readMethod ?: throw NotSerializableException("Property has no getter method for $name of $clazz. " +
|
||||||
"If using Java and the parameter name looks anonymous, check that you have the -parameters option specified in the Java compiler." +
|
"If using Java and the parameter name looks anonymous, check that you have the -parameters option specified in the Java compiler." +
|
||||||
|
@ -0,0 +1,40 @@
|
|||||||
|
package net.corda.nodeapi.internal.serialization.amqp;
|
||||||
|
|
||||||
|
import net.corda.nodeapi.internal.serialization.AllWhitelist;
|
||||||
|
import org.assertj.core.api.Assertions;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.NotSerializableException;
|
||||||
|
|
||||||
|
public class ErrorMessageTests {
|
||||||
|
private String errMsg(String property, String testname) {
|
||||||
|
return "Property '"
|
||||||
|
+ property
|
||||||
|
+ "' or it's getter is non public, this renders class 'class "
|
||||||
|
+ testname
|
||||||
|
+ "$C' unserializable -> class "
|
||||||
|
+ testname
|
||||||
|
+ "$C";
|
||||||
|
}
|
||||||
|
|
||||||
|
static class C {
|
||||||
|
public Integer a;
|
||||||
|
|
||||||
|
public C(Integer a) {
|
||||||
|
this.a = a;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer getA() { return this.a; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testJavaConstructorAnnotations() {
|
||||||
|
SerializerFactory factory1 = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader());
|
||||||
|
SerializationOutput ser = new SerializationOutput(factory1);
|
||||||
|
|
||||||
|
Assertions.assertThatThrownBy(() -> ser.serialize(new C(1)))
|
||||||
|
.isInstanceOf(NotSerializableException.class)
|
||||||
|
.hasMessage(errMsg("a", getClass().getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -4,10 +4,7 @@ import net.corda.core.crypto.Crypto
|
|||||||
import net.corda.core.crypto.Crypto.EDDSA_ED25519_SHA512
|
import net.corda.core.crypto.Crypto.EDDSA_ED25519_SHA512
|
||||||
import net.corda.core.crypto.Crypto.generateKeyPair
|
import net.corda.core.crypto.Crypto.generateKeyPair
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.internal.toTypedArray
|
|
||||||
import net.corda.core.internal.x500Name
|
|
||||||
import net.corda.core.serialization.SerializationContext
|
import net.corda.core.serialization.SerializationContext
|
||||||
import net.corda.core.serialization.deserialize
|
import net.corda.core.serialization.deserialize
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
@ -23,12 +20,9 @@ import net.corda.testing.BOB_NAME
|
|||||||
import net.corda.testing.TestIdentity
|
import net.corda.testing.TestIdentity
|
||||||
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
|
||||||
import org.bouncycastle.asn1.x509.BasicConstraints
|
import org.bouncycastle.asn1.x509.BasicConstraints
|
||||||
import org.bouncycastle.asn1.x509.Extension
|
import org.bouncycastle.asn1.x509.Extension
|
||||||
import org.bouncycastle.asn1.x509.KeyUsage
|
import org.bouncycastle.asn1.x509.KeyUsage
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
|
||||||
import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder
|
|
||||||
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
|
||||||
@ -42,18 +36,16 @@ import java.security.SecureRandom
|
|||||||
import java.security.cert.CertPath
|
import java.security.cert.CertPath
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.stream.Stream
|
|
||||||
import javax.net.ssl.*
|
import javax.net.ssl.*
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
|
|
||||||
class X509UtilitiesTest {
|
class X509UtilitiesTest {
|
||||||
private companion object {
|
private companion object {
|
||||||
val ALICE = TestIdentity(ALICE_NAME, 70).party
|
val ALICE = TestIdentity(ALICE_NAME, 70).party
|
||||||
val bob = TestIdentity(BOB_NAME, 80)
|
val BOB = TestIdentity(BOB_NAME, 80)
|
||||||
val MEGA_CORP = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).party
|
val MEGA_CORP = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).party
|
||||||
val BOB get() = bob.party
|
|
||||||
val BOB_PUBKEY get() = bob.publicKey
|
|
||||||
val CIPHER_SUITES = arrayOf(
|
val CIPHER_SUITES = arrayOf(
|
||||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||||
@ -63,27 +55,30 @@ class X509UtilitiesTest {
|
|||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
@JvmField
|
@JvmField
|
||||||
val tempFolder: TemporaryFolder = TemporaryFolder()
|
val tempFolder = TemporaryFolder()
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `create valid self-signed CA certificate`() {
|
fun `create valid self-signed CA certificate`() {
|
||||||
val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val caCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Test Cert", organisation = "R3 Ltd", locality = "London", country = "GB"), caKey)
|
val subject = X500Principal("CN=Test Cert,O=R3 Ltd,L=London,C=GB")
|
||||||
assertEquals(X500Name("CN=Test Cert,O=R3 Ltd,L=London,C=GB"), caCert.subject) // using our subject common name
|
val caCert = X509Utilities.createSelfSignedCACertificate(subject, caKey)
|
||||||
assertEquals(caCert.issuer, caCert.subject) //self-signed
|
assertEquals(subject, caCert.subjectX500Principal) // using our subject common name
|
||||||
caCert.isValidOn(Date()) // throws on verification problems
|
assertEquals(caCert.issuerX500Principal, caCert.subjectX500Principal) //self-signed
|
||||||
caCert.isSignatureValid(JcaContentVerifierProviderBuilder().build(caKey.public)) // throws on verification problems
|
caCert.checkValidity(Date()) // throws on verification problems
|
||||||
val basicConstraints = BasicConstraints.getInstance(caCert.getExtension(Extension.basicConstraints).parsedValue)
|
caCert.verify(caKey.public) // throws on verification problems
|
||||||
val keyUsage = KeyUsage.getInstance(caCert.getExtension(Extension.keyUsage).parsedValue)
|
caCert.toBc().run {
|
||||||
assertFalse { keyUsage.hasUsages(5) } // Bit 5 == keyCertSign according to ASN.1 spec (see full comment on KeyUsage property)
|
val basicConstraints = BasicConstraints.getInstance(getExtension(Extension.basicConstraints).parsedValue)
|
||||||
assertNull(basicConstraints.pathLenConstraint) // No length constraint specified on this CA certificate
|
val keyUsage = KeyUsage.getInstance(getExtension(Extension.keyUsage).parsedValue)
|
||||||
|
assertFalse { keyUsage.hasUsages(5) } // Bit 5 == keyCertSign according to ASN.1 spec (see full comment on KeyUsage property)
|
||||||
|
assertNull(basicConstraints.pathLenConstraint) // No length constraint specified on this CA certificate
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `load and save a PEM file certificate`() {
|
fun `load and save a PEM file certificate`() {
|
||||||
val tmpCertificateFile = tempFile("cacert.pem")
|
val tmpCertificateFile = tempFile("cacert.pem")
|
||||||
val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val caCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Test Cert", organisation = "R3 Ltd", locality = "London", country = "GB"), caKey).cert
|
val caCert = X509Utilities.createSelfSignedCACertificate(X500Principal("CN=Test Cert,O=R3 Ltd,L=London,C=GB"), caKey)
|
||||||
X509Utilities.saveCertificateAsPEMFile(caCert, tmpCertificateFile)
|
X509Utilities.saveCertificateAsPEMFile(caCert, tmpCertificateFile)
|
||||||
val readCertificate = X509Utilities.loadCertificateFromPEMFile(tmpCertificateFile)
|
val readCertificate = X509Utilities.loadCertificateFromPEMFile(tmpCertificateFile)
|
||||||
assertEquals(caCert, readCertificate)
|
assertEquals(caCert, readCertificate)
|
||||||
@ -92,18 +87,20 @@ class X509UtilitiesTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `create valid server certificate chain`() {
|
fun `create valid server certificate chain`() {
|
||||||
val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val caCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Test CA Cert", organisation = "R3 Ltd", locality = "London", country = "GB"), caKey)
|
val caCert = X509Utilities.createSelfSignedCACertificate(X500Principal("CN=Test CA Cert,O=R3 Ltd,L=London,C=GB"), caKey)
|
||||||
val subject = CordaX500Name(commonName = "Server Cert", organisation = "R3 Ltd", locality = "London", country = "GB")
|
val subject = X500Principal("CN=Server Cert,O=R3 Ltd,L=London,C=GB")
|
||||||
val keyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val keyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val serverCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, caKey, subject, keyPair.public)
|
val serverCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, caKey, subject, keyPair.public)
|
||||||
assertEquals(X500Name("C=GB,L=London,O=R3 Ltd,CN=Server Cert"), serverCert.subject) // using our subject common name
|
assertEquals(subject, serverCert.subjectX500Principal) // using our subject common name
|
||||||
assertEquals(caCert.issuer, serverCert.issuer) // Issued by our CA cert
|
assertEquals(caCert.issuerX500Principal, serverCert.issuerX500Principal) // Issued by our CA cert
|
||||||
serverCert.isValidOn(Date()) // throws on verification problems
|
serverCert.checkValidity(Date()) // throws on verification problems
|
||||||
serverCert.isSignatureValid(JcaContentVerifierProviderBuilder().build(caKey.public)) // throws on verification problems
|
serverCert.verify(caKey.public) // throws on verification problems
|
||||||
val basicConstraints = BasicConstraints.getInstance(serverCert.getExtension(Extension.basicConstraints).parsedValue)
|
serverCert.toBc().run {
|
||||||
val keyUsage = KeyUsage.getInstance(serverCert.getExtension(Extension.keyUsage).parsedValue)
|
val basicConstraints = BasicConstraints.getInstance(getExtension(Extension.basicConstraints).parsedValue)
|
||||||
assertFalse { keyUsage.hasUsages(5) } // Bit 5 == keyCertSign according to ASN.1 spec (see full comment on KeyUsage property)
|
val keyUsage = KeyUsage.getInstance(getExtension(Extension.keyUsage).parsedValue)
|
||||||
assertNull(basicConstraints.pathLenConstraint) // Non-CA certificate
|
assertFalse { keyUsage.hasUsages(5) } // Bit 5 == keyCertSign according to ASN.1 spec (see full comment on KeyUsage property)
|
||||||
|
assertNull(basicConstraints.pathLenConstraint) // Non-CA certificate
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -111,15 +108,14 @@ class X509UtilitiesTest {
|
|||||||
val tmpKeyStore = tempFile("keystore.jks")
|
val tmpKeyStore = tempFile("keystore.jks")
|
||||||
|
|
||||||
val keyPair = generateKeyPair(EDDSA_ED25519_SHA512)
|
val keyPair = generateKeyPair(EDDSA_ED25519_SHA512)
|
||||||
val testName = CordaX500Name(commonName = "Test", organisation = "R3 Ltd", locality = "London", country = "GB")
|
val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB")
|
||||||
val selfSignCert = X509Utilities.createSelfSignedCACertificate(testName, keyPair)
|
val selfSignCert = X509Utilities.createSelfSignedCACertificate(testName, keyPair)
|
||||||
|
|
||||||
assertTrue(Arrays.equals(selfSignCert.subjectPublicKeyInfo.encoded, keyPair.public.encoded))
|
assertTrue(Arrays.equals(selfSignCert.publicKey.encoded, keyPair.public.encoded))
|
||||||
|
|
||||||
// Save the EdDSA private key with self sign cert in the keystore.
|
// Save the EdDSA private key with self sign cert in the keystore.
|
||||||
val keyStore = loadOrCreateKeyStore(tmpKeyStore, "keystorepass")
|
val keyStore = loadOrCreateKeyStore(tmpKeyStore, "keystorepass")
|
||||||
keyStore.setKeyEntry("Key", keyPair.private, "password".toCharArray(),
|
keyStore.setKeyEntry("Key", keyPair.private, "password".toCharArray(), arrayOf(selfSignCert))
|
||||||
Stream.of(selfSignCert).map { it.cert }.toTypedArray())
|
|
||||||
keyStore.save(tmpKeyStore, "keystorepass")
|
keyStore.save(tmpKeyStore, "keystorepass")
|
||||||
|
|
||||||
// Load the keystore from file and make sure keys are intact.
|
// Load the keystore from file and make sure keys are intact.
|
||||||
@ -137,15 +133,14 @@ class X509UtilitiesTest {
|
|||||||
fun `signing EdDSA key with EcDSA certificate`() {
|
fun `signing EdDSA key with EcDSA certificate`() {
|
||||||
val tmpKeyStore = tempFile("keystore.jks")
|
val tmpKeyStore = tempFile("keystore.jks")
|
||||||
val ecDSAKey = generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
|
val ecDSAKey = generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
|
||||||
val testName = CordaX500Name(commonName = "Test", organisation = "R3 Ltd", locality = "London", country = "GB")
|
val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB")
|
||||||
val ecDSACert = X509Utilities.createSelfSignedCACertificate(testName, ecDSAKey)
|
val ecDSACert = X509Utilities.createSelfSignedCACertificate(testName, ecDSAKey)
|
||||||
val edDSAKeypair = generateKeyPair(EDDSA_ED25519_SHA512)
|
val edDSAKeypair = generateKeyPair(EDDSA_ED25519_SHA512)
|
||||||
val edDSACert = X509Utilities.createCertificate(CertificateType.TLS, ecDSACert, ecDSAKey, X500Name("CN=TestEdDSA"), edDSAKeypair.public)
|
val edDSACert = X509Utilities.createCertificate(CertificateType.TLS, ecDSACert, ecDSAKey, BOB.name.x500Principal, edDSAKeypair.public)
|
||||||
|
|
||||||
// Save the EdDSA private key with cert chains.
|
// Save the EdDSA private key with cert chains.
|
||||||
val keyStore = loadOrCreateKeyStore(tmpKeyStore, "keystorepass")
|
val keyStore = loadOrCreateKeyStore(tmpKeyStore, "keystorepass")
|
||||||
keyStore.setKeyEntry("Key", edDSAKeypair.private, "password".toCharArray(),
|
keyStore.setKeyEntry("Key", edDSAKeypair.private, "password".toCharArray(), arrayOf(ecDSACert, edDSACert))
|
||||||
Stream.of(ecDSACert, edDSACert).map { it.cert }.toTypedArray())
|
|
||||||
keyStore.save(tmpKeyStore, "keystorepass")
|
keyStore.save(tmpKeyStore, "keystorepass")
|
||||||
|
|
||||||
// Load the keystore from file and make sure keys are intact.
|
// Load the keystore from file and make sure keys are intact.
|
||||||
@ -179,23 +174,22 @@ class X509UtilitiesTest {
|
|||||||
val serverKeyStore = loadKeyStore(sslConfig.nodeKeystore, sslConfig.keyStorePassword)
|
val serverKeyStore = loadKeyStore(sslConfig.nodeKeystore, sslConfig.keyStorePassword)
|
||||||
val (serverCert, serverKeyPair) = serverKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, sslConfig.keyStorePassword)
|
val (serverCert, serverKeyPair) = serverKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, sslConfig.keyStorePassword)
|
||||||
|
|
||||||
serverCert.cert.checkValidity()
|
serverCert.checkValidity()
|
||||||
serverCert.cert.verify(intermediateCa.certificate.cert.publicKey)
|
serverCert.verify(intermediateCa.certificate.publicKey)
|
||||||
assertThat(CordaX500Name.parse(serverCert.subject.toString())).isEqualTo(MEGA_CORP.name)
|
assertThat(CordaX500Name.build(serverCert.subjectX500Principal)).isEqualTo(MEGA_CORP.name)
|
||||||
|
|
||||||
// Load back SSL certificate
|
// Load back SSL certificate
|
||||||
val sslKeyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword)
|
val sslKeyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword)
|
||||||
val (sslCert) = sslKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_TLS, sslConfig.keyStorePassword)
|
val (sslCert) = sslKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_TLS, sslConfig.keyStorePassword)
|
||||||
|
|
||||||
sslCert.cert.checkValidity()
|
sslCert.checkValidity()
|
||||||
sslCert.cert.verify(serverCert.cert.publicKey)
|
sslCert.verify(serverCert.publicKey)
|
||||||
assertThat(CordaX500Name.parse(sslCert.subject.toString())).isEqualTo(MEGA_CORP.name)
|
assertThat(CordaX500Name.build(sslCert.subjectX500Principal)).isEqualTo(MEGA_CORP.name)
|
||||||
|
|
||||||
// Now sign something with private key and verify against certificate public key
|
// Now sign something with private key and verify against certificate public key
|
||||||
val testData = "123456".toByteArray()
|
val testData = "123456".toByteArray()
|
||||||
val signature = Crypto.doSign(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME, serverKeyPair.private, testData)
|
val signature = Crypto.doSign(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME, serverKeyPair.private, testData)
|
||||||
val publicKey = Crypto.toSupportedPublicKey(serverCert.subjectPublicKeyInfo)
|
assertTrue { Crypto.isValid(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME, serverCert.publicKey, signature, testData) }
|
||||||
assertTrue { Crypto.isValid(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME, publicKey, signature, testData) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -210,7 +204,7 @@ class X509UtilitiesTest {
|
|||||||
|
|
||||||
// Generate server cert and private key and populate another keystore suitable for SSL
|
// Generate server cert and private key and populate another keystore suitable for SSL
|
||||||
sslConfig.createDevKeyStores(rootCa.certificate, intermediateCa, MEGA_CORP.name)
|
sslConfig.createDevKeyStores(rootCa.certificate, intermediateCa, MEGA_CORP.name)
|
||||||
sslConfig.createTrustStore(rootCa.certificate.cert)
|
sslConfig.createTrustStore(rootCa.certificate)
|
||||||
|
|
||||||
val keyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword)
|
val keyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword)
|
||||||
val trustStore = loadKeyStore(sslConfig.trustStoreFile, sslConfig.trustStorePassword)
|
val trustStore = loadKeyStore(sslConfig.trustStoreFile, sslConfig.trustStorePassword)
|
||||||
@ -303,10 +297,10 @@ class X509UtilitiesTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `get correct private key type from Keystore`() {
|
fun `get correct private key type from Keystore`() {
|
||||||
val keyPair = generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
|
val keyPair = generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
|
||||||
val testName = CordaX500Name(commonName = "Test", organisation = "R3 Ltd", locality = "London", country = "GB")
|
val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB")
|
||||||
val selfSignCert = X509Utilities.createSelfSignedCACertificate(testName, keyPair)
|
val selfSignCert = X509Utilities.createSelfSignedCACertificate(testName, keyPair)
|
||||||
val keyStore = loadOrCreateKeyStore(tempFile("testKeystore.jks"), "keystorepassword")
|
val keyStore = loadOrCreateKeyStore(tempFile("testKeystore.jks"), "keystorepassword")
|
||||||
keyStore.setKeyEntry("Key", keyPair.private, "keypassword".toCharArray(), arrayOf(selfSignCert.cert))
|
keyStore.setKeyEntry("Key", keyPair.private, "keypassword".toCharArray(), arrayOf(selfSignCert))
|
||||||
|
|
||||||
val keyFromKeystore = keyStore.getKey("Key", "keypassword".toCharArray())
|
val keyFromKeystore = keyStore.getKey("Key", "keypassword".toCharArray())
|
||||||
val keyFromKeystoreCasted = keyStore.getSupportedKey("Key", "keypassword")
|
val keyFromKeystoreCasted = keyStore.getSupportedKey("Key", "keypassword")
|
||||||
@ -316,7 +310,7 @@ class X509UtilitiesTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `serialize - deserialize X509CertififcateHolder`() {
|
fun `serialize - deserialize X509Certififcate`() {
|
||||||
val factory = SerializationFactoryImpl().apply { registerScheme(KryoServerSerializationScheme()) }
|
val factory = SerializationFactoryImpl().apply { registerScheme(KryoServerSerializationScheme()) }
|
||||||
val context = SerializationContextImpl(KryoHeaderV0_1,
|
val context = SerializationContextImpl(KryoHeaderV0_1,
|
||||||
javaClass.classLoader,
|
javaClass.classLoader,
|
||||||
@ -324,9 +318,9 @@ class X509UtilitiesTest {
|
|||||||
emptyMap(),
|
emptyMap(),
|
||||||
true,
|
true,
|
||||||
SerializationContext.UseCase.P2P)
|
SerializationContext.UseCase.P2P)
|
||||||
val expected: X509CertificateHolder = X509Utilities.createSelfSignedCACertificate(ALICE.name, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
|
val expected = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Principal, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
|
||||||
val serialized = expected.serialize(factory, context).bytes
|
val serialized = expected.serialize(factory, context).bytes
|
||||||
val actual: X509CertificateHolder = serialized.deserialize(factory, context)
|
val actual = serialized.deserialize<X509Certificate>(factory, context)
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -340,9 +334,9 @@ class X509UtilitiesTest {
|
|||||||
true,
|
true,
|
||||||
SerializationContext.UseCase.P2P)
|
SerializationContext.UseCase.P2P)
|
||||||
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val rootCACert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootCAKey)
|
val rootCACert = X509Utilities.createSelfSignedCACertificate(ALICE_NAME.x500Principal, rootCAKey)
|
||||||
val certificate = X509Utilities.createCertificate(CertificateType.TLS, rootCACert, rootCAKey, BOB.name.x500Name, BOB_PUBKEY)
|
val certificate = X509Utilities.createCertificate(CertificateType.TLS, rootCACert, rootCAKey, BOB_NAME.x500Principal, BOB.publicKey)
|
||||||
val expected = X509CertificateFactory().generateCertPath(certificate.cert, rootCACert.cert)
|
val expected = X509CertificateFactory().generateCertPath(certificate, rootCACert)
|
||||||
val serialized = expected.serialize(factory, context).bytes
|
val serialized = expected.serialize(factory, context).bytes
|
||||||
val actual: CertPath = serialized.deserialize(factory, context)
|
val actual: CertPath = serialized.deserialize(factory, context)
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
|
@ -0,0 +1,69 @@
|
|||||||
|
package net.corda.nodeapi.internal.serialization.amqp
|
||||||
|
|
||||||
|
import org.assertj.core.api.Assertions
|
||||||
|
import org.junit.Test
|
||||||
|
import java.io.NotSerializableException
|
||||||
|
|
||||||
|
class ErrorMessagesTests {
|
||||||
|
companion object {
|
||||||
|
val VERBOSE get() = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun errMsg(property:String, testname: String) =
|
||||||
|
"Property '$property' or it's getter is non public, this renders class 'class $testname\$C' unserializable -> class $testname\$C"
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun privateProperty() {
|
||||||
|
data class C(private val a: Int)
|
||||||
|
|
||||||
|
val sf = testDefaultFactory()
|
||||||
|
|
||||||
|
val testname = "${javaClass.name}\$${testName()}"
|
||||||
|
|
||||||
|
Assertions.assertThatThrownBy {
|
||||||
|
TestSerializationOutput(VERBOSE, sf).serialize(C(1))
|
||||||
|
}.isInstanceOf(NotSerializableException::class.java).hasMessage(errMsg("a", testname))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun privateProperty2() {
|
||||||
|
data class C(val a: Int, private val b: Int)
|
||||||
|
|
||||||
|
val sf = testDefaultFactory()
|
||||||
|
|
||||||
|
val testname = "${javaClass.name}\$${testName()}"
|
||||||
|
|
||||||
|
Assertions.assertThatThrownBy {
|
||||||
|
TestSerializationOutput(VERBOSE, sf).serialize(C(1, 2))
|
||||||
|
}.isInstanceOf(NotSerializableException::class.java).hasMessage(errMsg("b", testname))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun privateProperty3() {
|
||||||
|
// despite b being private, the getter we've added is public and thus allows for the serialisation
|
||||||
|
// of the object
|
||||||
|
data class C(val a: Int, private val b: Int) {
|
||||||
|
public fun getB() = b
|
||||||
|
}
|
||||||
|
|
||||||
|
val sf = testDefaultFactory()
|
||||||
|
|
||||||
|
val testname = "${javaClass.name}\$${testName()}"
|
||||||
|
|
||||||
|
val bytes = TestSerializationOutput(VERBOSE, sf).serialize(C(1, 2))
|
||||||
|
val c = DeserializationInput(sf).deserialize(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun protectedProperty() {
|
||||||
|
data class C(protected val a: Int)
|
||||||
|
|
||||||
|
val sf = testDefaultFactory()
|
||||||
|
|
||||||
|
val testname = "${javaClass.name}\$${testName()}"
|
||||||
|
|
||||||
|
Assertions.assertThatThrownBy {
|
||||||
|
TestSerializationOutput(VERBOSE, sf).serialize(C(1))
|
||||||
|
}.isInstanceOf(NotSerializableException::class.java).hasMessage(errMsg("a", testname))
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,6 @@
|
|||||||
package net.corda.node
|
package net.corda.node
|
||||||
|
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.identity.CordaX500Name
|
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.node.services.config.configureDevKeyAndTrustStores
|
import net.corda.node.services.config.configureDevKeyAndTrustStores
|
||||||
@ -17,6 +15,10 @@ import org.junit.ClassRule
|
|||||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertFailsWith
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
class NodeKeystoreCheckTest : IntegrationTest() {
|
class NodeKeystoreCheckTest : IntegrationTest() {
|
||||||
companion object {
|
companion object {
|
||||||
@ -59,10 +61,10 @@ class NodeKeystoreCheckTest : IntegrationTest() {
|
|||||||
|
|
||||||
// Self signed root
|
// Self signed root
|
||||||
val badRootKeyPair = Crypto.generateKeyPair()
|
val badRootKeyPair = Crypto.generateKeyPair()
|
||||||
val badRoot = X509Utilities.createSelfSignedCACertificate(CordaX500Name("Bad Root", "Lodnon", "GB"), badRootKeyPair)
|
val badRoot = X509Utilities.createSelfSignedCACertificate(X500Principal("O=Bad Root,L=Lodnon,C=GB"), badRootKeyPair)
|
||||||
val nodeCA = keystore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, config.keyStorePassword)
|
val nodeCA = keystore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, config.keyStorePassword)
|
||||||
val badNodeCACert = X509Utilities.createCertificate(CertificateType.NODE_CA, badRoot, badRootKeyPair, ALICE_NAME, nodeCA.keyPair.public)
|
val badNodeCACert = X509Utilities.createCertificate(CertificateType.NODE_CA, badRoot, badRootKeyPair, ALICE_NAME.x500Principal, nodeCA.keyPair.public)
|
||||||
keystore.setKeyEntry(X509Utilities.CORDA_CLIENT_CA, nodeCA.keyPair.private, config.keyStorePassword.toCharArray(), arrayOf(badNodeCACert.cert, badRoot.cert))
|
keystore.setKeyEntry(X509Utilities.CORDA_CLIENT_CA, nodeCA.keyPair.private, config.keyStorePassword.toCharArray(), arrayOf(badNodeCACert, badRoot))
|
||||||
keystore.save(config.nodeKeystore, config.keyStorePassword)
|
keystore.save(config.nodeKeystore, config.keyStorePassword)
|
||||||
|
|
||||||
assertThatThrownBy {
|
assertThatThrownBy {
|
||||||
|
@ -59,10 +59,10 @@ class ProtonWrapperTests {
|
|||||||
amqpClient.start()
|
amqpClient.start()
|
||||||
val serverConnect = serverConnected.get()
|
val serverConnect = serverConnected.get()
|
||||||
assertEquals(true, serverConnect.connected)
|
assertEquals(true, serverConnect.connected)
|
||||||
assertEquals(BOB_NAME, CordaX500Name.parse(serverConnect.remoteCert!!.subject.toString()))
|
assertEquals(BOB_NAME, CordaX500Name.build(serverConnect.remoteCert!!.subjectX500Principal))
|
||||||
val clientConnect = clientConnected.get()
|
val clientConnect = clientConnected.get()
|
||||||
assertEquals(true, clientConnect.connected)
|
assertEquals(true, clientConnect.connected)
|
||||||
assertEquals(ALICE_NAME, CordaX500Name.parse(clientConnect.remoteCert!!.subject.toString()))
|
assertEquals(ALICE_NAME, CordaX500Name.build(clientConnect.remoteCert!!.subjectX500Principal))
|
||||||
val msg = amqpClient.createMessage("Test".toByteArray(),
|
val msg = amqpClient.createMessage("Test".toByteArray(),
|
||||||
"p2p.inbound",
|
"p2p.inbound",
|
||||||
ALICE_NAME.toString(),
|
ALICE_NAME.toString(),
|
||||||
@ -102,10 +102,10 @@ class ProtonWrapperTests {
|
|||||||
amqpClient.start()
|
amqpClient.start()
|
||||||
val serverConn1 = serverConnected.get()
|
val serverConn1 = serverConnected.get()
|
||||||
assertEquals(true, serverConn1.connected)
|
assertEquals(true, serverConn1.connected)
|
||||||
assertEquals(BOB_NAME, CordaX500Name.parse(serverConn1.remoteCert!!.subject.toString()))
|
assertEquals(BOB_NAME, CordaX500Name.build(serverConn1.remoteCert!!.subjectX500Principal))
|
||||||
val connState1 = clientConnected.next()
|
val connState1 = clientConnected.next()
|
||||||
assertEquals(true, connState1.connected)
|
assertEquals(true, connState1.connected)
|
||||||
assertEquals(ALICE_NAME, CordaX500Name.parse(connState1.remoteCert!!.subject.toString()))
|
assertEquals(ALICE_NAME, CordaX500Name.build(connState1.remoteCert!!.subjectX500Principal))
|
||||||
assertEquals(serverPort, connState1.remoteAddress.port)
|
assertEquals(serverPort, connState1.remoteAddress.port)
|
||||||
|
|
||||||
// Fail over
|
// Fail over
|
||||||
@ -116,10 +116,10 @@ class ProtonWrapperTests {
|
|||||||
assertEquals(serverPort, connState2.remoteAddress.port)
|
assertEquals(serverPort, connState2.remoteAddress.port)
|
||||||
val serverConn2 = serverConnected2.get()
|
val serverConn2 = serverConnected2.get()
|
||||||
assertEquals(true, serverConn2.connected)
|
assertEquals(true, serverConn2.connected)
|
||||||
assertEquals(BOB_NAME, CordaX500Name.parse(serverConn2.remoteCert!!.subject.toString()))
|
assertEquals(BOB_NAME, CordaX500Name.build(serverConn2.remoteCert!!.subjectX500Principal))
|
||||||
val connState3 = clientConnected.next()
|
val connState3 = clientConnected.next()
|
||||||
assertEquals(true, connState3.connected)
|
assertEquals(true, connState3.connected)
|
||||||
assertEquals(ALICE_NAME, CordaX500Name.parse(connState3.remoteCert!!.subject.toString()))
|
assertEquals(ALICE_NAME, CordaX500Name.build(connState3.remoteCert!!.subjectX500Principal))
|
||||||
assertEquals(serverPort2, connState3.remoteAddress.port)
|
assertEquals(serverPort2, connState3.remoteAddress.port)
|
||||||
|
|
||||||
// Fail back
|
// Fail back
|
||||||
@ -130,10 +130,10 @@ class ProtonWrapperTests {
|
|||||||
assertEquals(serverPort2, connState4.remoteAddress.port)
|
assertEquals(serverPort2, connState4.remoteAddress.port)
|
||||||
val serverConn3 = serverConnected.get()
|
val serverConn3 = serverConnected.get()
|
||||||
assertEquals(true, serverConn3.connected)
|
assertEquals(true, serverConn3.connected)
|
||||||
assertEquals(BOB_NAME, CordaX500Name.parse(serverConn3.remoteCert!!.subject.toString()))
|
assertEquals(BOB_NAME, CordaX500Name.build(serverConn3.remoteCert!!.subjectX500Principal))
|
||||||
val connState5 = clientConnected.next()
|
val connState5 = clientConnected.next()
|
||||||
assertEquals(true, connState5.connected)
|
assertEquals(true, connState5.connected)
|
||||||
assertEquals(ALICE_NAME, CordaX500Name.parse(connState5.remoteCert!!.subject.toString()))
|
assertEquals(ALICE_NAME, CordaX500Name.build(connState5.remoteCert!!.subjectX500Principal))
|
||||||
assertEquals(serverPort, connState5.remoteAddress.port)
|
assertEquals(serverPort, connState5.remoteAddress.port)
|
||||||
} finally {
|
} finally {
|
||||||
amqpClient.close()
|
amqpClient.close()
|
||||||
@ -149,7 +149,7 @@ class ProtonWrapperTests {
|
|||||||
val clientConnected = amqpClient.onConnection.toFuture()
|
val clientConnected = amqpClient.onConnection.toFuture()
|
||||||
amqpClient.start()
|
amqpClient.start()
|
||||||
assertEquals(true, clientConnected.get().connected)
|
assertEquals(true, clientConnected.get().connected)
|
||||||
assertEquals(CHARLIE_NAME, CordaX500Name.parse(clientConnected.get().remoteCert!!.subject.toString()))
|
assertEquals(CHARLIE_NAME, CordaX500Name.build(clientConnected.get().remoteCert!!.subjectX500Principal))
|
||||||
val artemis = artemisClient.started!!
|
val artemis = artemisClient.started!!
|
||||||
val sendAddress = "p2p.inbound"
|
val sendAddress = "p2p.inbound"
|
||||||
artemis.session.createQueue(sendAddress, RoutingType.MULTICAST, "queue", true)
|
artemis.session.createQueue(sendAddress, RoutingType.MULTICAST, "queue", true)
|
||||||
@ -180,13 +180,13 @@ class ProtonWrapperTests {
|
|||||||
amqpClient1.start()
|
amqpClient1.start()
|
||||||
val connection1 = connectionEvents.next()
|
val connection1 = connectionEvents.next()
|
||||||
assertEquals(true, connection1.connected)
|
assertEquals(true, connection1.connected)
|
||||||
val connection1ID = CordaX500Name.parse(connection1.remoteCert!!.subject.toString())
|
val connection1ID = CordaX500Name.build(connection1.remoteCert!!.subjectX500Principal)
|
||||||
assertEquals("client 0", connection1ID.organisationUnit)
|
assertEquals("client 0", connection1ID.organisationUnit)
|
||||||
val source1 = connection1.remoteAddress
|
val source1 = connection1.remoteAddress
|
||||||
amqpClient2.start()
|
amqpClient2.start()
|
||||||
val connection2 = connectionEvents.next()
|
val connection2 = connectionEvents.next()
|
||||||
assertEquals(true, connection2.connected)
|
assertEquals(true, connection2.connected)
|
||||||
val connection2ID = CordaX500Name.parse(connection2.remoteCert!!.subject.toString())
|
val connection2ID = CordaX500Name.build(connection2.remoteCert!!.subjectX500Principal)
|
||||||
assertEquals("client 1", connection2ID.organisationUnit)
|
assertEquals("client 1", connection2ID.organisationUnit)
|
||||||
val source2 = connection2.remoteAddress
|
val source2 = connection2.remoteAddress
|
||||||
// Stopping one shouldn't disconnect the other
|
// Stopping one shouldn't disconnect the other
|
||||||
@ -207,7 +207,7 @@ class ProtonWrapperTests {
|
|||||||
amqpClient1.start()
|
amqpClient1.start()
|
||||||
val connection5 = connectionEvents.next()
|
val connection5 = connectionEvents.next()
|
||||||
assertEquals(true, connection5.connected)
|
assertEquals(true, connection5.connected)
|
||||||
val connection5ID = CordaX500Name.parse(connection5.remoteCert!!.subject.toString())
|
val connection5ID = CordaX500Name.build(connection5.remoteCert!!.subjectX500Principal)
|
||||||
assertEquals("client 0", connection5ID.organisationUnit)
|
assertEquals("client 0", connection5ID.organisationUnit)
|
||||||
assertEquals(true, amqpClient1.connected)
|
assertEquals(true, amqpClient1.connected)
|
||||||
assertEquals(false, amqpClient2.connected)
|
assertEquals(false, amqpClient2.connected)
|
||||||
@ -252,7 +252,8 @@ class ProtonWrapperTests {
|
|||||||
|
|
||||||
val clientTruststore = loadKeyStore(clientConfig.trustStoreFile, clientConfig.trustStorePassword)
|
val clientTruststore = loadKeyStore(clientConfig.trustStoreFile, clientConfig.trustStorePassword)
|
||||||
val clientKeystore = loadKeyStore(clientConfig.sslKeystore, clientConfig.keyStorePassword)
|
val clientKeystore = loadKeyStore(clientConfig.sslKeystore, clientConfig.keyStorePassword)
|
||||||
val amqpClient = AMQPClient(listOf(NetworkHostAndPort("localhost", serverPort),
|
return AMQPClient(
|
||||||
|
listOf(NetworkHostAndPort("localhost", serverPort),
|
||||||
NetworkHostAndPort("localhost", serverPort2),
|
NetworkHostAndPort("localhost", serverPort2),
|
||||||
NetworkHostAndPort("localhost", artemisPort)),
|
NetworkHostAndPort("localhost", artemisPort)),
|
||||||
setOf(ALICE_NAME, CHARLIE_NAME),
|
setOf(ALICE_NAME, CHARLIE_NAME),
|
||||||
@ -261,7 +262,6 @@ class ProtonWrapperTests {
|
|||||||
clientKeystore,
|
clientKeystore,
|
||||||
clientConfig.keyStorePassword,
|
clientConfig.keyStorePassword,
|
||||||
clientTruststore, true)
|
clientTruststore, true)
|
||||||
return amqpClient
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createSharedThreadsClient(sharedEventGroup: EventLoopGroup, id: Int): AMQPClient {
|
private fun createSharedThreadsClient(sharedEventGroup: EventLoopGroup, id: Int): AMQPClient {
|
||||||
@ -275,14 +275,14 @@ class ProtonWrapperTests {
|
|||||||
|
|
||||||
val clientTruststore = loadKeyStore(clientConfig.trustStoreFile, clientConfig.trustStorePassword)
|
val clientTruststore = loadKeyStore(clientConfig.trustStoreFile, clientConfig.trustStorePassword)
|
||||||
val clientKeystore = loadKeyStore(clientConfig.sslKeystore, clientConfig.keyStorePassword)
|
val clientKeystore = loadKeyStore(clientConfig.sslKeystore, clientConfig.keyStorePassword)
|
||||||
val amqpClient = AMQPClient(listOf(NetworkHostAndPort("localhost", serverPort)),
|
return AMQPClient(
|
||||||
|
listOf(NetworkHostAndPort("localhost", serverPort)),
|
||||||
setOf(ALICE_NAME),
|
setOf(ALICE_NAME),
|
||||||
PEER_USER,
|
PEER_USER,
|
||||||
PEER_USER,
|
PEER_USER,
|
||||||
clientKeystore,
|
clientKeystore,
|
||||||
clientConfig.keyStorePassword,
|
clientConfig.keyStorePassword,
|
||||||
clientTruststore, true, sharedEventGroup)
|
clientTruststore, true, sharedEventGroup)
|
||||||
return amqpClient
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createServer(port: Int, name: CordaX500Name = ALICE_NAME): AMQPServer {
|
private fun createServer(port: Int, name: CordaX500Name = ALICE_NAME): AMQPServer {
|
||||||
@ -296,14 +296,13 @@ class ProtonWrapperTests {
|
|||||||
|
|
||||||
val serverTruststore = loadKeyStore(serverConfig.trustStoreFile, serverConfig.trustStorePassword)
|
val serverTruststore = loadKeyStore(serverConfig.trustStoreFile, serverConfig.trustStorePassword)
|
||||||
val serverKeystore = loadKeyStore(serverConfig.sslKeystore, serverConfig.keyStorePassword)
|
val serverKeystore = loadKeyStore(serverConfig.sslKeystore, serverConfig.keyStorePassword)
|
||||||
val amqpServer = AMQPServer("0.0.0.0",
|
return AMQPServer(
|
||||||
|
"0.0.0.0",
|
||||||
port,
|
port,
|
||||||
PEER_USER,
|
PEER_USER,
|
||||||
PEER_USER,
|
PEER_USER,
|
||||||
serverKeystore,
|
serverKeystore,
|
||||||
serverConfig.keyStorePassword,
|
serverConfig.keyStorePassword,
|
||||||
serverTruststore)
|
serverTruststore)
|
||||||
return amqpServer
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
@ -2,9 +2,7 @@ package net.corda.node.utilities.registration
|
|||||||
|
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.internal.concurrent.transpose
|
import net.corda.core.internal.concurrent.transpose
|
||||||
import net.corda.core.internal.toX509CertHolder
|
|
||||||
import net.corda.core.messaging.startFlow
|
import net.corda.core.messaging.startFlow
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.core.utilities.OpaqueBytes
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
@ -38,8 +36,10 @@ import java.security.KeyPair
|
|||||||
import java.security.cert.CertPath
|
import java.security.cert.CertPath
|
||||||
import java.security.cert.CertPathValidatorException
|
import java.security.cert.CertPathValidatorException
|
||||||
import java.security.cert.Certificate
|
import java.security.cert.Certificate
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
import java.util.zip.ZipEntry
|
import java.util.zip.ZipEntry
|
||||||
import java.util.zip.ZipOutputStream
|
import java.util.zip.ZipOutputStream
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
import javax.ws.rs.*
|
import javax.ws.rs.*
|
||||||
import javax.ws.rs.core.MediaType
|
import javax.ws.rs.core.MediaType
|
||||||
import javax.ws.rs.core.Response
|
import javax.ws.rs.core.Response
|
||||||
@ -59,14 +59,13 @@ class NodeRegistrationTest : IntegrationTest() {
|
|||||||
val testSerialization = SerializationEnvironmentRule(true)
|
val testSerialization = SerializationEnvironmentRule(true)
|
||||||
|
|
||||||
private val portAllocation = PortAllocation.Incremental(13000)
|
private val portAllocation = PortAllocation.Incremental(13000)
|
||||||
private val registrationHandler = RegistrationHandler(ROOT_CA)
|
private val registrationHandler = RegistrationHandler(DEV_ROOT_CA)
|
||||||
|
|
||||||
private lateinit var server: NetworkMapServer
|
private lateinit var server: NetworkMapServer
|
||||||
private lateinit var serverHostAndPort: NetworkHostAndPort
|
private lateinit var serverHostAndPort: NetworkHostAndPort
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun startServer() {
|
fun startServer() {
|
||||||
server = NetworkMapServer(1.minutes, portAllocation.nextHostAndPort(), ROOT_CA, "localhost", registrationHandler)
|
server = NetworkMapServer(1.minutes, portAllocation.nextHostAndPort(), DEV_ROOT_CA, "localhost", registrationHandler)
|
||||||
serverHostAndPort = server.start()
|
serverHostAndPort = server.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,7 +79,7 @@ class NodeRegistrationTest : IntegrationTest() {
|
|||||||
val compatibilityZone = CompatibilityZoneParams(
|
val compatibilityZone = CompatibilityZoneParams(
|
||||||
URL("http://$serverHostAndPort"),
|
URL("http://$serverHostAndPort"),
|
||||||
publishNotaries = { server.networkParameters = testNetworkParameters(it) },
|
publishNotaries = { server.networkParameters = testNetworkParameters(it) },
|
||||||
rootCert = ROOT_CA.certificate.cert)
|
rootCert = DEV_ROOT_CA.certificate)
|
||||||
internalDriver(
|
internalDriver(
|
||||||
portAllocation = portAllocation,
|
portAllocation = portAllocation,
|
||||||
compatibilityZone = compatibilityZone,
|
compatibilityZone = compatibilityZone,
|
||||||
@ -116,12 +115,12 @@ class NodeRegistrationTest : IntegrationTest() {
|
|||||||
@Test
|
@Test
|
||||||
fun `node registration wrong root cert`() {
|
fun `node registration wrong root cert`() {
|
||||||
val someRootCert = X509Utilities.createSelfSignedCACertificate(
|
val someRootCert = X509Utilities.createSelfSignedCACertificate(
|
||||||
CordaX500Name("Integration Test Corda Node Root CA", "R3 Ltd", "London", "GB"),
|
X500Principal("CN=Integration Test Corda Node Root CA,O=R3 Ltd,L=London,C=GB"),
|
||||||
Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
|
Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
|
||||||
val compatibilityZone = CompatibilityZoneParams(
|
val compatibilityZone = CompatibilityZoneParams(
|
||||||
URL("http://$serverHostAndPort"),
|
URL("http://$serverHostAndPort"),
|
||||||
publishNotaries = { server.networkParameters = testNetworkParameters(it) },
|
publishNotaries = { server.networkParameters = testNetworkParameters(it) },
|
||||||
rootCert = someRootCert.cert)
|
rootCert = someRootCert)
|
||||||
internalDriver(
|
internalDriver(
|
||||||
portAllocation = portAllocation,
|
portAllocation = portAllocation,
|
||||||
compatibilityZone = compatibilityZone,
|
compatibilityZone = compatibilityZone,
|
||||||
@ -149,7 +148,7 @@ class RegistrationHandler(private val rootCertAndKeyPair: CertificateAndKeyPair)
|
|||||||
val (certPath, name) = createSignedClientCertificate(
|
val (certPath, name) = createSignedClientCertificate(
|
||||||
certificationRequest,
|
certificationRequest,
|
||||||
rootCertAndKeyPair.keyPair,
|
rootCertAndKeyPair.keyPair,
|
||||||
arrayOf(rootCertAndKeyPair.certificate.cert))
|
arrayOf(rootCertAndKeyPair.certificate))
|
||||||
require(!name.organisation.contains("\\s".toRegex())) { "Whitespace in the organisation name not supported" }
|
require(!name.organisation.contains("\\s".toRegex())) { "Whitespace in the organisation name not supported" }
|
||||||
certPaths[name.organisation] = certPath
|
certPaths[name.organisation] = certPath
|
||||||
return Response.ok(name.organisation).build()
|
return Response.ok(name.organisation).build()
|
||||||
@ -183,12 +182,12 @@ class RegistrationHandler(private val rootCertAndKeyPair: CertificateAndKeyPair)
|
|||||||
val name = CordaX500Name.parse(request.subject.toString())
|
val name = CordaX500Name.parse(request.subject.toString())
|
||||||
val nodeCaCert = X509Utilities.createCertificate(
|
val nodeCaCert = X509Utilities.createCertificate(
|
||||||
CertificateType.NODE_CA,
|
CertificateType.NODE_CA,
|
||||||
caCertPath.first().toX509CertHolder(),
|
caCertPath[0] as X509Certificate ,
|
||||||
caKeyPair,
|
caKeyPair,
|
||||||
name,
|
name.x500Principal,
|
||||||
request.publicKey,
|
request.publicKey,
|
||||||
nameConstraints = null)
|
nameConstraints = null)
|
||||||
val certPath = X509CertificateFactory().generateCertPath(nodeCaCert.cert, *caCertPath)
|
val certPath = X509CertificateFactory().generateCertPath(nodeCaCert, *caCertPath)
|
||||||
return Pair(certPath, name)
|
return Pair(certPath, name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,22 +94,34 @@ class MQSecurityAsNodeTest : MQSecurityTest() {
|
|||||||
javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"),
|
javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"),
|
||||||
"cordacadevpass")
|
"cordacadevpass")
|
||||||
|
|
||||||
val rootCACert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA).toX509CertHolder()
|
val rootCACert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA)
|
||||||
val intermediateCA = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
|
val intermediateCA = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
|
||||||
val clientKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
|
||||||
|
|
||||||
|
val clientKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
// Set name constrain to the legal name.
|
// Set name constrain to the legal name.
|
||||||
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.x500Name))), arrayOf())
|
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.x500Name))), arrayOf())
|
||||||
val clientCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, intermediateCA.certificate,
|
val clientCACert = X509Utilities.createCertificate(
|
||||||
intermediateCA.keyPair, legalName, clientKey.public, nameConstraints = nameConstraints)
|
CertificateType.INTERMEDIATE_CA,
|
||||||
val tlsKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
intermediateCA.certificate,
|
||||||
|
intermediateCA.keyPair,
|
||||||
|
legalName.x500Principal,
|
||||||
|
clientKeyPair.public,
|
||||||
|
nameConstraints = nameConstraints)
|
||||||
|
|
||||||
|
val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
// Using different x500 name in the TLS cert which is not allowed in the name constraints.
|
// Using different x500 name in the TLS cert which is not allowed in the name constraints.
|
||||||
val clientTLSCert = X509Utilities.createCertificate(CertificateType.TLS, clientCACert, clientKey, CordaX500Name("MiniCorp", "London", "GB"), tlsKey.public)
|
val clientTLSCert = X509Utilities.createCertificate(
|
||||||
|
CertificateType.TLS,
|
||||||
|
clientCACert,
|
||||||
|
clientKeyPair,
|
||||||
|
CordaX500Name("MiniCorp", "London", "GB").x500Principal,
|
||||||
|
tlsKeyPair.public)
|
||||||
|
|
||||||
val keyPass = keyStorePassword.toCharArray()
|
val keyPass = keyStorePassword.toCharArray()
|
||||||
val clientCAKeystore = loadOrCreateKeyStore(nodeKeystore, keyStorePassword)
|
val clientCAKeystore = loadOrCreateKeyStore(nodeKeystore, keyStorePassword)
|
||||||
clientCAKeystore.addOrReplaceKey(
|
clientCAKeystore.addOrReplaceKey(
|
||||||
X509Utilities.CORDA_CLIENT_CA,
|
X509Utilities.CORDA_CLIENT_CA,
|
||||||
clientKey.private,
|
clientKeyPair.private,
|
||||||
keyPass,
|
keyPass,
|
||||||
arrayOf(clientCACert, intermediateCA.certificate, rootCACert))
|
arrayOf(clientCACert, intermediateCA.certificate, rootCACert))
|
||||||
clientCAKeystore.save(nodeKeystore, keyStorePassword)
|
clientCAKeystore.save(nodeKeystore, keyStorePassword)
|
||||||
@ -117,7 +129,7 @@ class MQSecurityAsNodeTest : MQSecurityTest() {
|
|||||||
val tlsKeystore = loadOrCreateKeyStore(sslKeystore, keyStorePassword)
|
val tlsKeystore = loadOrCreateKeyStore(sslKeystore, keyStorePassword)
|
||||||
tlsKeystore.addOrReplaceKey(
|
tlsKeystore.addOrReplaceKey(
|
||||||
X509Utilities.CORDA_CLIENT_TLS,
|
X509Utilities.CORDA_CLIENT_TLS,
|
||||||
tlsKey.private,
|
tlsKeyPair.private,
|
||||||
keyPass,
|
keyPass,
|
||||||
arrayOf(clientTLSCert, clientCACert, intermediateCA.certificate, rootCACert))
|
arrayOf(clientTLSCert, clientCACert, intermediateCA.certificate, rootCACert))
|
||||||
tlsKeystore.save(sslKeystore, keyStorePassword)
|
tlsKeystore.save(sslKeystore, keyStorePassword)
|
||||||
|
@ -726,7 +726,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
|||||||
val caKeyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword)
|
val caKeyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword)
|
||||||
val trustRoot = trustStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA)
|
val trustRoot = trustStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA)
|
||||||
val clientCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)
|
val clientCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)
|
||||||
val caCertificates = arrayOf(identityCert, clientCa.certificate.cert)
|
val caCertificates = arrayOf(identityCert, clientCa.certificate)
|
||||||
return PersistentIdentityService(trustRoot, *caCertificates)
|
return PersistentIdentityService(trustRoot, *caCertificates)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -784,7 +784,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
|||||||
listOf(certificate) + keyStore.getCertificateChain(privateKeyAlias).drop(1)
|
listOf(certificate) + keyStore.getCertificateChain(privateKeyAlias).drop(1)
|
||||||
} else {
|
} else {
|
||||||
keyStore.getCertificateChain(privateKeyAlias).let {
|
keyStore.getCertificateChain(privateKeyAlias).let {
|
||||||
check(it[0].toX509CertHolder() == x509Cert) { "Certificates from key store do not line up!" }
|
check(it[0] == x509Cert) { "Certificates from key store do not line up!" }
|
||||||
it.asList()
|
it.asList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ import io.netty.handler.ssl.SslHandler
|
|||||||
import io.netty.handler.ssl.SslHandshakeCompletionEvent
|
import io.netty.handler.ssl.SslHandshakeCompletionEvent
|
||||||
import io.netty.util.ReferenceCountUtil
|
import io.netty.util.ReferenceCountUtil
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.toX509CertHolder
|
|
||||||
import net.corda.core.utilities.debug
|
import net.corda.core.utilities.debug
|
||||||
import net.corda.node.internal.protonwrapper.engine.EventProcessor
|
import net.corda.node.internal.protonwrapper.engine.EventProcessor
|
||||||
import net.corda.node.internal.protonwrapper.messages.ReceivedMessage
|
import net.corda.node.internal.protonwrapper.messages.ReceivedMessage
|
||||||
@ -19,9 +18,9 @@ import org.apache.qpid.proton.engine.ProtonJTransport
|
|||||||
import org.apache.qpid.proton.engine.Transport
|
import org.apache.qpid.proton.engine.Transport
|
||||||
import org.apache.qpid.proton.engine.impl.ProtocolTracer
|
import org.apache.qpid.proton.engine.impl.ProtocolTracer
|
||||||
import org.apache.qpid.proton.framing.TransportFrame
|
import org.apache.qpid.proton.framing.TransportFrame
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import java.net.InetSocketAddress
|
import java.net.InetSocketAddress
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An instance of AMQPChannelHandler sits inside the netty pipeline and controls the socket level lifecycle.
|
* An instance of AMQPChannelHandler sits inside the netty pipeline and controls the socket level lifecycle.
|
||||||
@ -38,20 +37,20 @@ internal class AMQPChannelHandler(private val serverMode: Boolean,
|
|||||||
private val onReceive: (ReceivedMessage) -> Unit) : ChannelDuplexHandler() {
|
private val onReceive: (ReceivedMessage) -> Unit) : ChannelDuplexHandler() {
|
||||||
private val log = LoggerFactory.getLogger(allowedRemoteLegalNames?.firstOrNull()?.toString() ?: "AMQPChannelHandler")
|
private val log = LoggerFactory.getLogger(allowedRemoteLegalNames?.firstOrNull()?.toString() ?: "AMQPChannelHandler")
|
||||||
private lateinit var remoteAddress: InetSocketAddress
|
private lateinit var remoteAddress: InetSocketAddress
|
||||||
private lateinit var localCert: X509CertificateHolder
|
private lateinit var localCert: X509Certificate
|
||||||
private lateinit var remoteCert: X509CertificateHolder
|
private lateinit var remoteCert: X509Certificate
|
||||||
private var eventProcessor: EventProcessor? = null
|
private var eventProcessor: EventProcessor? = null
|
||||||
|
|
||||||
override fun channelActive(ctx: ChannelHandlerContext) {
|
override fun channelActive(ctx: ChannelHandlerContext) {
|
||||||
val ch = ctx.channel()
|
val ch = ctx.channel()
|
||||||
remoteAddress = ch.remoteAddress() as InetSocketAddress
|
remoteAddress = ch.remoteAddress() as InetSocketAddress
|
||||||
val localAddress = ch.localAddress() as InetSocketAddress
|
val localAddress = ch.localAddress() as InetSocketAddress
|
||||||
log.info("New client connection ${ch.id()} from ${remoteAddress} to ${localAddress}")
|
log.info("New client connection ${ch.id()} from $remoteAddress to $localAddress")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createAMQPEngine(ctx: ChannelHandlerContext) {
|
private fun createAMQPEngine(ctx: ChannelHandlerContext) {
|
||||||
val ch = ctx.channel()
|
val ch = ctx.channel()
|
||||||
eventProcessor = EventProcessor(ch, serverMode, localCert.subject.toString(), remoteCert.subject.toString(), userName, password)
|
eventProcessor = EventProcessor(ch, serverMode, localCert.subjectX500Principal.toString(), remoteCert.subjectX500Principal.toString(), userName, password)
|
||||||
val connection = eventProcessor!!.connection
|
val connection = eventProcessor!!.connection
|
||||||
val transport = connection.transport as ProtonJTransport
|
val transport = connection.transport as ProtonJTransport
|
||||||
if (trace) {
|
if (trace) {
|
||||||
@ -71,7 +70,7 @@ internal class AMQPChannelHandler(private val serverMode: Boolean,
|
|||||||
|
|
||||||
override fun channelInactive(ctx: ChannelHandlerContext) {
|
override fun channelInactive(ctx: ChannelHandlerContext) {
|
||||||
val ch = ctx.channel()
|
val ch = ctx.channel()
|
||||||
log.info("Closed client connection ${ch.id()} from ${remoteAddress} to ${ch.localAddress()}")
|
log.info("Closed client connection ${ch.id()} from $remoteAddress to ${ch.localAddress()}")
|
||||||
onClose(Pair(ch as SocketChannel, ConnectionChange(remoteAddress, null, false)))
|
onClose(Pair(ch as SocketChannel, ConnectionChange(remoteAddress, null, false)))
|
||||||
eventProcessor?.close()
|
eventProcessor?.close()
|
||||||
ctx.fireChannelInactive()
|
ctx.fireChannelInactive()
|
||||||
@ -81,12 +80,12 @@ internal class AMQPChannelHandler(private val serverMode: Boolean,
|
|||||||
if (evt is SslHandshakeCompletionEvent) {
|
if (evt is SslHandshakeCompletionEvent) {
|
||||||
if (evt.isSuccess) {
|
if (evt.isSuccess) {
|
||||||
val sslHandler = ctx.pipeline().get(SslHandler::class.java)
|
val sslHandler = ctx.pipeline().get(SslHandler::class.java)
|
||||||
localCert = sslHandler.engine().session.localCertificates.first().toX509CertHolder()
|
localCert = sslHandler.engine().session.localCertificates[0] as X509Certificate
|
||||||
remoteCert = sslHandler.engine().session.peerCertificates.first().toX509CertHolder()
|
remoteCert = sslHandler.engine().session.peerCertificates[0] as X509Certificate
|
||||||
try {
|
try {
|
||||||
val remoteX500Name = CordaX500Name.parse(remoteCert.subject.toString())
|
val remoteX500Name = CordaX500Name.build(remoteCert.subjectX500Principal)
|
||||||
require(allowedRemoteLegalNames == null || remoteX500Name in allowedRemoteLegalNames)
|
require(allowedRemoteLegalNames == null || remoteX500Name in allowedRemoteLegalNames)
|
||||||
log.info("handshake completed subject: ${remoteX500Name}")
|
log.info("handshake completed subject: $remoteX500Name")
|
||||||
} catch (ex: IllegalArgumentException) {
|
} catch (ex: IllegalArgumentException) {
|
||||||
log.error("Invalid certificate subject", ex)
|
log.error("Invalid certificate subject", ex)
|
||||||
ctx.close()
|
ctx.close()
|
||||||
@ -124,7 +123,7 @@ internal class AMQPChannelHandler(private val serverMode: Boolean,
|
|||||||
require(inetAddress == remoteAddress) {
|
require(inetAddress == remoteAddress) {
|
||||||
"Message for incorrect endpoint"
|
"Message for incorrect endpoint"
|
||||||
}
|
}
|
||||||
require(CordaX500Name.parse(msg.destinationLegalName) == CordaX500Name.parse(remoteCert.subject.toString())) {
|
require(CordaX500Name.parse(msg.destinationLegalName) == CordaX500Name.build(remoteCert.subjectX500Principal)) {
|
||||||
"Message for incorrect legal identity"
|
"Message for incorrect legal identity"
|
||||||
}
|
}
|
||||||
log.debug { "channel write ${msg.applicationProperties["_AMQ_DUPL_ID"]}" }
|
log.debug { "channel write ${msg.applicationProperties["_AMQ_DUPL_ID"]}" }
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package net.corda.node.internal.protonwrapper.netty
|
package net.corda.node.internal.protonwrapper.netty
|
||||||
|
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
|
||||||
import java.net.InetSocketAddress
|
import java.net.InetSocketAddress
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
|
|
||||||
data class ConnectionChange(val remoteAddress: InetSocketAddress, val remoteCert: X509CertificateHolder?, val connected: Boolean)
|
data class ConnectionChange(val remoteAddress: InetSocketAddress, val remoteCert: X509Certificate?, val connected: Boolean)
|
@ -11,7 +11,6 @@ import net.corda.core.identity.CordaX500Name
|
|||||||
import net.corda.core.internal.createDirectories
|
import net.corda.core.internal.createDirectories
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.internal.exists
|
import net.corda.core.internal.exists
|
||||||
import net.corda.core.internal.toX509CertHolder
|
|
||||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
import net.corda.nodeapi.internal.config.SSLConfiguration
|
||||||
import net.corda.nodeapi.internal.config.toProperties
|
import net.corda.nodeapi.internal.config.toProperties
|
||||||
import net.corda.nodeapi.internal.createDevKeyStores
|
import net.corda.nodeapi.internal.createDevKeyStores
|
||||||
@ -73,7 +72,7 @@ fun SSLConfiguration.configureDevKeyAndTrustStores(myLegalName: CordaX500Name) {
|
|||||||
}
|
}
|
||||||
if (!sslKeystore.exists() || !nodeKeystore.exists()) {
|
if (!sslKeystore.exists() || !nodeKeystore.exists()) {
|
||||||
val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
|
val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
|
||||||
val rootCert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA).toX509CertHolder()
|
val rootCert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA)
|
||||||
val intermediateCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
|
val intermediateCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
|
||||||
createDevKeyStores(rootCert, intermediateCa, myLegalName)
|
createDevKeyStores(rootCert, intermediateCa, myLegalName)
|
||||||
|
|
||||||
|
@ -4,15 +4,12 @@ import net.corda.core.contracts.PartyAndReference
|
|||||||
import net.corda.core.crypto.toStringShort
|
import net.corda.core.crypto.toStringShort
|
||||||
import net.corda.core.identity.*
|
import net.corda.core.identity.*
|
||||||
import net.corda.core.internal.CertRole
|
import net.corda.core.internal.CertRole
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.internal.toX509CertHolder
|
|
||||||
import net.corda.core.node.services.UnknownAnonymousPartyException
|
import net.corda.core.node.services.UnknownAnonymousPartyException
|
||||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||||
import net.corda.core.utilities.contextLogger
|
import net.corda.core.utilities.contextLogger
|
||||||
import net.corda.core.utilities.trace
|
import net.corda.core.utilities.trace
|
||||||
import net.corda.node.services.api.IdentityServiceInternal
|
import net.corda.node.services.api.IdentityServiceInternal
|
||||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||||
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.*
|
||||||
@ -27,7 +24,7 @@ import javax.annotation.concurrent.ThreadSafe
|
|||||||
// TODO There is duplicated logic between this and PersistentIdentityService
|
// TODO There is duplicated logic between this and PersistentIdentityService
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
class InMemoryIdentityService(identities: Array<out PartyAndCertificate>,
|
class InMemoryIdentityService(identities: Array<out PartyAndCertificate>,
|
||||||
trustRoot: X509CertificateHolder) : SingletonSerializeAsToken(), IdentityServiceInternal {
|
override val trustRoot: X509Certificate) : SingletonSerializeAsToken(), IdentityServiceInternal {
|
||||||
companion object {
|
companion object {
|
||||||
private val log = contextLogger()
|
private val log = contextLogger()
|
||||||
}
|
}
|
||||||
@ -35,14 +32,12 @@ class InMemoryIdentityService(identities: Array<out PartyAndCertificate>,
|
|||||||
/**
|
/**
|
||||||
* Certificate store for certificate authority and intermediary certificates.
|
* Certificate store for certificate authority and intermediary certificates.
|
||||||
*/
|
*/
|
||||||
override val caCertStore: CertStore
|
override val caCertStore: CertStore = CertStore.getInstance("Collection", CollectionCertStoreParameters(setOf(trustRoot)))
|
||||||
override val trustRoot = trustRoot.cert
|
override val trustAnchor: TrustAnchor = TrustAnchor(trustRoot, null)
|
||||||
override val trustAnchor: TrustAnchor = TrustAnchor(this.trustRoot, null)
|
|
||||||
private val keyToParties = ConcurrentHashMap<PublicKey, PartyAndCertificate>()
|
private val keyToParties = ConcurrentHashMap<PublicKey, PartyAndCertificate>()
|
||||||
private val principalToParties = ConcurrentHashMap<CordaX500Name, PartyAndCertificate>()
|
private val principalToParties = ConcurrentHashMap<CordaX500Name, PartyAndCertificate>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
caCertStore = CertStore.getInstance("Collection", CollectionCertStoreParameters(setOf(this.trustRoot)))
|
|
||||||
keyToParties.putAll(identities.associateBy { it.owningKey })
|
keyToParties.putAll(identities.associateBy { it.owningKey })
|
||||||
principalToParties.putAll(identities.associateBy { it.name })
|
principalToParties.putAll(identities.associateBy { it.name })
|
||||||
}
|
}
|
||||||
@ -57,7 +52,7 @@ class InMemoryIdentityService(identities: Array<out PartyAndCertificate>,
|
|||||||
log.warn("Certificate path :")
|
log.warn("Certificate path :")
|
||||||
identity.certPath.certificates.reversed().forEachIndexed { index, certificate ->
|
identity.certPath.certificates.reversed().forEachIndexed { index, certificate ->
|
||||||
val space = (0 until index).joinToString("") { " " }
|
val space = (0 until index).joinToString("") { " " }
|
||||||
log.warn("$space${certificate.toX509CertHolder().subject}")
|
log.warn("$space${(certificate as X509Certificate).subjectX500Principal}")
|
||||||
}
|
}
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,6 @@ import net.corda.core.crypto.SecureHash
|
|||||||
import net.corda.core.crypto.toStringShort
|
import net.corda.core.crypto.toStringShort
|
||||||
import net.corda.core.identity.*
|
import net.corda.core.identity.*
|
||||||
import net.corda.core.internal.CertRole
|
import net.corda.core.internal.CertRole
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.internal.toX509CertHolder
|
|
||||||
import net.corda.core.node.services.UnknownAnonymousPartyException
|
import net.corda.core.node.services.UnknownAnonymousPartyException
|
||||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||||
import net.corda.core.utilities.MAX_HASH_HEX_SIZE
|
import net.corda.core.utilities.MAX_HASH_HEX_SIZE
|
||||||
@ -16,7 +14,6 @@ import net.corda.node.services.api.IdentityServiceInternal
|
|||||||
import net.corda.node.utilities.AppendOnlyPersistentMap
|
import net.corda.node.utilities.AppendOnlyPersistentMap
|
||||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||||
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
|
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
|
||||||
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.*
|
||||||
@ -30,7 +27,6 @@ import javax.persistence.Lob
|
|||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
class PersistentIdentityService(override val trustRoot: X509Certificate,
|
class PersistentIdentityService(override val trustRoot: X509Certificate,
|
||||||
vararg caCertificates: X509Certificate) : SingletonSerializeAsToken(), IdentityServiceInternal {
|
vararg caCertificates: X509Certificate) : SingletonSerializeAsToken(), IdentityServiceInternal {
|
||||||
constructor(trustRoot: X509CertificateHolder) : this(trustRoot.cert)
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val log = contextLogger()
|
private val log = contextLogger()
|
||||||
@ -121,7 +117,7 @@ class PersistentIdentityService(override val trustRoot: X509Certificate,
|
|||||||
log.warn(e.localizedMessage)
|
log.warn(e.localizedMessage)
|
||||||
log.warn("Path = ")
|
log.warn("Path = ")
|
||||||
identity.certPath.certificates.reversed().forEach {
|
identity.certPath.certificates.reversed().forEach {
|
||||||
log.warn(it.toX509CertHolder().subject.toString())
|
log.warn((it as X509Certificate).subjectX500Principal.toString())
|
||||||
}
|
}
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,6 @@ package net.corda.node.services.keys
|
|||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.identity.PartyAndCertificate
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
import net.corda.core.internal.CertRole
|
import net.corda.core.internal.CertRole
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.internal.toX509CertHolder
|
|
||||||
import net.corda.core.utilities.days
|
import net.corda.core.utilities.days
|
||||||
import net.corda.node.services.api.IdentityServiceInternal
|
import net.corda.node.services.api.IdentityServiceInternal
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
@ -36,11 +34,16 @@ fun freshCertificate(identityService: IdentityServiceInternal,
|
|||||||
revocationEnabled: Boolean = false): PartyAndCertificate {
|
revocationEnabled: Boolean = false): PartyAndCertificate {
|
||||||
val issuerRole = CertRole.extract(issuer.certificate)
|
val issuerRole = CertRole.extract(issuer.certificate)
|
||||||
require(issuerRole == CertRole.LEGAL_IDENTITY) { "Confidential identities can only be issued from well known identities, provided issuer ${issuer.name} has role $issuerRole" }
|
require(issuerRole == CertRole.LEGAL_IDENTITY) { "Confidential identities can only be issued from well known identities, provided issuer ${issuer.name} has role $issuerRole" }
|
||||||
val issuerCert = issuer.certificate.toX509CertHolder()
|
val issuerCert = issuer.certificate
|
||||||
val window = X509Utilities.getCertificateValidityWindow(Duration.ZERO, 3650.days, issuerCert)
|
val window = X509Utilities.getCertificateValidityWindow(Duration.ZERO, 3650.days, issuerCert)
|
||||||
val ourCertificate = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuerCert.subject,
|
val ourCertificate = X509Utilities.createCertificate(
|
||||||
issuerSigner, issuer.name, subjectPublicKey, window)
|
CertificateType.CONFIDENTIAL_LEGAL_IDENTITY,
|
||||||
val ourCertPath = X509CertificateFactory().generateCertPath(listOf(ourCertificate.cert) + issuer.certPath.certificates)
|
issuerCert.subjectX500Principal,
|
||||||
|
issuerSigner,
|
||||||
|
issuer.name.x500Principal,
|
||||||
|
subjectPublicKey,
|
||||||
|
window)
|
||||||
|
val ourCertPath = X509CertificateFactory().generateCertPath(listOf(ourCertificate) + issuer.certPath.certificates)
|
||||||
val anonymisedIdentity = PartyAndCertificate(ourCertPath)
|
val anonymisedIdentity = PartyAndCertificate(ourCertPath)
|
||||||
identityService.justVerifyAndRegisterIdentity(anonymisedIdentity)
|
identityService.justVerifyAndRegisterIdentity(anonymisedIdentity)
|
||||||
return anonymisedIdentity
|
return anonymisedIdentity
|
||||||
|
@ -22,9 +22,9 @@ import java.security.cert.X509Certificate
|
|||||||
* needed.
|
* needed.
|
||||||
*/
|
*/
|
||||||
class NetworkRegistrationHelper(private val config: NodeConfiguration, private val certService: NetworkRegistrationService) {
|
class NetworkRegistrationHelper(private val config: NodeConfiguration, private val certService: NetworkRegistrationService) {
|
||||||
companion object {
|
private companion object {
|
||||||
val pollInterval = 10.seconds
|
val pollInterval = 10.seconds
|
||||||
val SELF_SIGNED_PRIVATE_KEY = "Self Signed Private Key"
|
const val SELF_SIGNED_PRIVATE_KEY = "Self Signed Private Key"
|
||||||
}
|
}
|
||||||
|
|
||||||
private val requestIdStore = config.certificatesDirectory / "certificate-request-id.txt"
|
private val requestIdStore = config.certificatesDirectory / "certificate-request-id.txt"
|
||||||
@ -62,54 +62,81 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v
|
|||||||
*/
|
*/
|
||||||
fun buildKeystore() {
|
fun buildKeystore() {
|
||||||
config.certificatesDirectory.createDirectories()
|
config.certificatesDirectory.createDirectories()
|
||||||
val caKeyStore = loadOrCreateKeyStore(config.nodeKeystore, keystorePassword)
|
val nodeKeyStore = loadOrCreateKeyStore(config.nodeKeystore, keystorePassword)
|
||||||
if (!caKeyStore.containsAlias(CORDA_CLIENT_CA)) {
|
if (nodeKeyStore.containsAlias(CORDA_CLIENT_CA)) {
|
||||||
// Create or load self signed keypair from the key store.
|
|
||||||
// We use the self sign certificate to store the key temporarily in the keystore while waiting for the request approval.
|
|
||||||
if (!caKeyStore.containsAlias(SELF_SIGNED_PRIVATE_KEY)) {
|
|
||||||
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
|
||||||
val selfSignCert = X509Utilities.createSelfSignedCACertificate(config.myLegalName, keyPair)
|
|
||||||
// Save to the key store.
|
|
||||||
caKeyStore.addOrReplaceKey(SELF_SIGNED_PRIVATE_KEY, keyPair.private, privateKeyPassword.toCharArray(),
|
|
||||||
arrayOf(selfSignCert))
|
|
||||||
caKeyStore.save(config.nodeKeystore, keystorePassword)
|
|
||||||
}
|
|
||||||
val keyPair = caKeyStore.getKeyPair(SELF_SIGNED_PRIVATE_KEY, privateKeyPassword)
|
|
||||||
val requestId = submitOrResumeCertificateSigningRequest(keyPair)
|
|
||||||
|
|
||||||
val certificates = try {
|
|
||||||
pollServerForCertificates(requestId)
|
|
||||||
} catch (certificateRequestException: CertificateRequestException) {
|
|
||||||
System.err.println(certificateRequestException.message)
|
|
||||||
System.err.println("Please make sure the details in configuration file are correct and try again.")
|
|
||||||
System.err.println("Corda node will now terminate.")
|
|
||||||
requestIdStore.deleteIfExists()
|
|
||||||
throw certificateRequestException
|
|
||||||
}
|
|
||||||
|
|
||||||
println("Certificate signing request approved, storing private key with the certificate chain.")
|
|
||||||
// Save private key and certificate chain to the key store.
|
|
||||||
caKeyStore.addOrReplaceKey(CORDA_CLIENT_CA, keyPair.private, privateKeyPassword.toCharArray(), certificates)
|
|
||||||
caKeyStore.deleteEntry(SELF_SIGNED_PRIVATE_KEY)
|
|
||||||
caKeyStore.save(config.nodeKeystore, keystorePassword)
|
|
||||||
println("Node private key and certificate stored in ${config.nodeKeystore}.")
|
|
||||||
|
|
||||||
println("Checking root of the certificate path is what we expect.")
|
|
||||||
X509Utilities.validateCertificateChain(rootCert, *certificates)
|
|
||||||
|
|
||||||
println("Generating SSL certificate for node messaging service.")
|
|
||||||
val sslKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
|
||||||
val caCert = caKeyStore.getX509Certificate(CORDA_CLIENT_CA).toX509CertHolder()
|
|
||||||
val sslCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, keyPair, CordaX500Name.build(caCert.cert.subjectX500Principal), sslKey.public)
|
|
||||||
val sslKeyStore = loadOrCreateKeyStore(config.sslKeystore, keystorePassword)
|
|
||||||
sslKeyStore.addOrReplaceKey(CORDA_CLIENT_TLS, sslKey.private, privateKeyPassword.toCharArray(), arrayOf(sslCert.cert, *certificates))
|
|
||||||
sslKeyStore.save(config.sslKeystore, config.keyStorePassword)
|
|
||||||
println("SSL private key and certificate stored in ${config.sslKeystore}.")
|
|
||||||
// All done, clean up temp files.
|
|
||||||
requestIdStore.deleteIfExists()
|
|
||||||
} else {
|
|
||||||
println("Certificate already exists, Corda node will now terminate...")
|
println("Certificate already exists, Corda node will now terminate...")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create or load self signed keypair from the key store.
|
||||||
|
// We use the self sign certificate to store the key temporarily in the keystore while waiting for the request approval.
|
||||||
|
if (!nodeKeyStore.containsAlias(SELF_SIGNED_PRIVATE_KEY)) {
|
||||||
|
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
|
val selfSignCert = X509Utilities.createSelfSignedCACertificate(config.myLegalName.x500Principal, keyPair)
|
||||||
|
// Save to the key store.
|
||||||
|
nodeKeyStore.addOrReplaceKey(SELF_SIGNED_PRIVATE_KEY, keyPair.private, privateKeyPassword.toCharArray(),
|
||||||
|
arrayOf(selfSignCert))
|
||||||
|
nodeKeyStore.save(config.nodeKeystore, keystorePassword)
|
||||||
|
}
|
||||||
|
|
||||||
|
val keyPair = nodeKeyStore.getKeyPair(SELF_SIGNED_PRIVATE_KEY, privateKeyPassword)
|
||||||
|
val requestId = submitOrResumeCertificateSigningRequest(keyPair)
|
||||||
|
|
||||||
|
val certificates = try {
|
||||||
|
pollServerForCertificates(requestId)
|
||||||
|
} catch (certificateRequestException: CertificateRequestException) {
|
||||||
|
System.err.println(certificateRequestException.message)
|
||||||
|
System.err.println("Please make sure the details in configuration file are correct and try again.")
|
||||||
|
System.err.println("Corda node will now terminate.")
|
||||||
|
requestIdStore.deleteIfExists()
|
||||||
|
throw certificateRequestException
|
||||||
|
}
|
||||||
|
|
||||||
|
val nodeCaCert = certificates[0] as X509Certificate
|
||||||
|
|
||||||
|
val nodeCaSubject = try {
|
||||||
|
CordaX500Name.build(nodeCaCert.subjectX500Principal)
|
||||||
|
} catch (e: IllegalArgumentException) {
|
||||||
|
throw CertificateRequestException("Received node CA cert has invalid subject name: ${e.message}")
|
||||||
|
}
|
||||||
|
if (nodeCaSubject != config.myLegalName) {
|
||||||
|
throw CertificateRequestException("Subject of received node CA cert doesn't match with node legal name: $nodeCaSubject")
|
||||||
|
}
|
||||||
|
|
||||||
|
val nodeCaCertRole = try {
|
||||||
|
CertRole.extract(nodeCaCert)
|
||||||
|
} catch (e: IllegalArgumentException) {
|
||||||
|
throw CertificateRequestException("Unable to extract cert role from received node CA cert: ${e.message}")
|
||||||
|
}
|
||||||
|
if (nodeCaCertRole != CertRole.NODE_CA) {
|
||||||
|
throw CertificateRequestException("Received node CA cert has invalid role: $nodeCaCertRole")
|
||||||
|
}
|
||||||
|
|
||||||
|
println("Checking root of the certificate path is what we expect.")
|
||||||
|
X509Utilities.validateCertificateChain(rootCert, *certificates)
|
||||||
|
|
||||||
|
println("Certificate signing request approved, storing private key with the certificate chain.")
|
||||||
|
// Save private key and certificate chain to the key store.
|
||||||
|
nodeKeyStore.addOrReplaceKey(CORDA_CLIENT_CA, keyPair.private, privateKeyPassword.toCharArray(), certificates)
|
||||||
|
nodeKeyStore.deleteEntry(SELF_SIGNED_PRIVATE_KEY)
|
||||||
|
nodeKeyStore.save(config.nodeKeystore, keystorePassword)
|
||||||
|
println("Node private key and certificate stored in ${config.nodeKeystore}.")
|
||||||
|
|
||||||
|
println("Generating SSL certificate for node messaging service.")
|
||||||
|
val sslKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
|
val sslCert = X509Utilities.createCertificate(
|
||||||
|
CertificateType.TLS,
|
||||||
|
nodeCaCert,
|
||||||
|
keyPair,
|
||||||
|
config.myLegalName.x500Principal,
|
||||||
|
sslKeyPair.public)
|
||||||
|
val sslKeyStore = loadOrCreateKeyStore(config.sslKeystore, keystorePassword)
|
||||||
|
sslKeyStore.addOrReplaceKey(CORDA_CLIENT_TLS, sslKeyPair.private, privateKeyPassword.toCharArray(), arrayOf(sslCert, *certificates))
|
||||||
|
sslKeyStore.save(config.sslKeystore, config.keyStorePassword)
|
||||||
|
println("SSL private key and certificate stored in ${config.sslKeystore}.")
|
||||||
|
|
||||||
|
// All done, clean up temp files.
|
||||||
|
requestIdStore.deleteIfExists()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -138,7 +165,7 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v
|
|||||||
private fun submitOrResumeCertificateSigningRequest(keyPair: KeyPair): String {
|
private fun submitOrResumeCertificateSigningRequest(keyPair: KeyPair): String {
|
||||||
// Retrieve request id from file if exists, else post a request to server.
|
// Retrieve request id from file if exists, else post a request to server.
|
||||||
return if (!requestIdStore.exists()) {
|
return if (!requestIdStore.exists()) {
|
||||||
val request = X509Utilities.createCertificateSigningRequest(config.myLegalName, config.emailAddress, keyPair)
|
val request = X509Utilities.createCertificateSigningRequest(config.myLegalName.x500Principal, config.emailAddress, keyPair)
|
||||||
val writer = StringWriter()
|
val writer = StringWriter()
|
||||||
JcaPEMWriter(writer).use {
|
JcaPEMWriter(writer).use {
|
||||||
it.writeObject(PemObject("CERTIFICATE REQUEST", request.encoded))
|
it.writeObject(PemObject("CERTIFICATE REQUEST", request.encoded))
|
||||||
|
@ -6,8 +6,6 @@ import net.corda.core.identity.AnonymousParty
|
|||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
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.cert
|
|
||||||
import net.corda.core.internal.toX509CertHolder
|
|
||||||
import net.corda.core.node.services.UnknownAnonymousPartyException
|
import net.corda.core.node.services.UnknownAnonymousPartyException
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||||
@ -32,7 +30,7 @@ class InMemoryIdentityServiceTests {
|
|||||||
val BOB get() = bob.party
|
val BOB get() = bob.party
|
||||||
val BOB_IDENTITY get() = bob.identity
|
val BOB_IDENTITY get() = bob.identity
|
||||||
val BOB_PUBKEY get() = bob.publicKey
|
val BOB_PUBKEY get() = bob.publicKey
|
||||||
fun createService(vararg identities: PartyAndCertificate) = InMemoryIdentityService(identities, DEV_TRUST_ROOT)
|
fun createService(vararg identities: PartyAndCertificate) = InMemoryIdentityService(identities, DEV_ROOT_CA.certificate)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
@ -100,11 +98,11 @@ class InMemoryIdentityServiceTests {
|
|||||||
@Test
|
@Test
|
||||||
fun `assert unknown anonymous key is unrecognised`() {
|
fun `assert unknown anonymous key is unrecognised`() {
|
||||||
val rootKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val rootKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootKey)
|
val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Principal, rootKey)
|
||||||
val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val service = createService()
|
val service = createService()
|
||||||
// TODO: Generate certificate with an EdDSA key rather than ECDSA
|
// TODO: Generate certificate with an EdDSA key rather than ECDSA
|
||||||
val identity = Party(rootCert.cert)
|
val identity = Party(rootCert)
|
||||||
val txIdentity = AnonymousParty(txKey.public)
|
val txIdentity = AnonymousParty(txKey.public)
|
||||||
|
|
||||||
assertFailsWith<UnknownAnonymousPartyException> {
|
assertFailsWith<UnknownAnonymousPartyException> {
|
||||||
@ -159,8 +157,8 @@ class InMemoryIdentityServiceTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
assertFailsWith<IllegalArgumentException> {
|
assertFailsWith<IllegalArgumentException> {
|
||||||
val owningKey = Crypto.decodePublicKey(DEV_CA.certificate.subjectPublicKeyInfo.encoded)
|
val owningKey = DEV_INTERMEDIATE_CA.certificate.publicKey
|
||||||
val subject = CordaX500Name.build(DEV_CA.certificate.cert.subjectX500Principal)
|
val subject = CordaX500Name.build(DEV_INTERMEDIATE_CA.certificate.subjectX500Principal)
|
||||||
service.assertOwnership(Party(subject, owningKey), anonymousAlice.party.anonymise())
|
service.assertOwnership(Party(subject, owningKey), anonymousAlice.party.anonymise())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -168,9 +166,14 @@ class InMemoryIdentityServiceTests {
|
|||||||
private fun createConfidentialIdentity(x500Name: CordaX500Name): Pair<PartyAndCertificate, PartyAndCertificate> {
|
private fun createConfidentialIdentity(x500Name: CordaX500Name): Pair<PartyAndCertificate, PartyAndCertificate> {
|
||||||
val issuerKeyPair = generateKeyPair()
|
val issuerKeyPair = generateKeyPair()
|
||||||
val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public)
|
val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public)
|
||||||
val txKey = Crypto.generateKeyPair()
|
val txKeyPair = Crypto.generateKeyPair()
|
||||||
val txCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuer.certificate.toX509CertHolder(), issuerKeyPair, x500Name, txKey.public)
|
val txCert = X509Utilities.createCertificate(
|
||||||
val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates)
|
CertificateType.CONFIDENTIAL_LEGAL_IDENTITY,
|
||||||
|
issuer.certificate,
|
||||||
|
issuerKeyPair,
|
||||||
|
x500Name.x500Principal,
|
||||||
|
txKeyPair.public)
|
||||||
|
val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert) + issuer.certPath.certificates)
|
||||||
return Pair(issuer, PartyAndCertificate(txCertPath))
|
return Pair(issuer, PartyAndCertificate(txCertPath))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,8 +6,6 @@ import net.corda.core.identity.AnonymousParty
|
|||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
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.cert
|
|
||||||
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.node.services.UnknownAnonymousPartyException
|
||||||
import net.corda.node.internal.configureDatabase
|
import net.corda.node.internal.configureDatabase
|
||||||
@ -50,7 +48,7 @@ class PersistentIdentityServiceTests {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
identityService = PersistentIdentityService(DEV_TRUST_ROOT)
|
identityService = PersistentIdentityService(DEV_ROOT_CA.certificate)
|
||||||
database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), identityService)
|
database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), identityService)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,9 +141,9 @@ class PersistentIdentityServiceTests {
|
|||||||
@Test
|
@Test
|
||||||
fun `assert unknown anonymous key is unrecognised`() {
|
fun `assert unknown anonymous key is unrecognised`() {
|
||||||
val rootKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val rootKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootKey)
|
val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Principal, rootKey)
|
||||||
val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_IDENTITY_SIGNATURE_SCHEME)
|
val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_IDENTITY_SIGNATURE_SCHEME)
|
||||||
val identity = Party(rootCert.cert)
|
val identity = Party(rootCert)
|
||||||
val txIdentity = AnonymousParty(txKey.public)
|
val txIdentity = AnonymousParty(txKey.public)
|
||||||
|
|
||||||
assertFailsWith<UnknownAnonymousPartyException> {
|
assertFailsWith<UnknownAnonymousPartyException> {
|
||||||
@ -219,9 +217,9 @@ class PersistentIdentityServiceTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
assertFailsWith<IllegalArgumentException> {
|
assertFailsWith<IllegalArgumentException> {
|
||||||
val owningKey = Crypto.decodePublicKey(DEV_CA.certificate.subjectPublicKeyInfo.encoded)
|
val owningKey = DEV_INTERMEDIATE_CA.certificate.publicKey
|
||||||
database.transaction {
|
database.transaction {
|
||||||
val subject = CordaX500Name.build(DEV_CA.certificate.cert.subjectX500Principal)
|
val subject = CordaX500Name.build(DEV_INTERMEDIATE_CA.certificate.subjectX500Principal)
|
||||||
identityService.assertOwnership(Party(subject, owningKey), anonymousAlice.party.anonymise())
|
identityService.assertOwnership(Party(subject, owningKey), anonymousAlice.party.anonymise())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -243,7 +241,7 @@ class PersistentIdentityServiceTests {
|
|||||||
|
|
||||||
// Create new identity service mounted onto same DB
|
// Create new identity service mounted onto same DB
|
||||||
val newPersistentIdentityService = database.transaction {
|
val newPersistentIdentityService = database.transaction {
|
||||||
PersistentIdentityService(DEV_TRUST_ROOT)
|
PersistentIdentityService(DEV_ROOT_CA.certificate)
|
||||||
}
|
}
|
||||||
|
|
||||||
database.transaction {
|
database.transaction {
|
||||||
@ -266,8 +264,8 @@ class PersistentIdentityServiceTests {
|
|||||||
val issuerKeyPair = generateKeyPair()
|
val issuerKeyPair = generateKeyPair()
|
||||||
val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public)
|
val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public)
|
||||||
val txKey = Crypto.generateKeyPair()
|
val txKey = Crypto.generateKeyPair()
|
||||||
val txCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuer.certificate.toX509CertHolder(), issuerKeyPair, x500Name, txKey.public)
|
val txCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuer.certificate, issuerKeyPair, x500Name.x500Principal, txKey.public)
|
||||||
val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates)
|
val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert) + issuer.certPath.certificates)
|
||||||
return Pair(issuer, PartyAndCertificate(txCertPath))
|
return Pair(issuer, PartyAndCertificate(txCertPath))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,12 +2,11 @@ package net.corda.node.services.network
|
|||||||
|
|
||||||
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 net.corda.core.internal.cert
|
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.utilities.seconds
|
import net.corda.core.utilities.seconds
|
||||||
import net.corda.testing.ALICE_NAME
|
import net.corda.testing.ALICE_NAME
|
||||||
import net.corda.testing.BOB_NAME
|
import net.corda.testing.BOB_NAME
|
||||||
import net.corda.testing.DEV_TRUST_ROOT
|
import net.corda.testing.DEV_ROOT_CA
|
||||||
import net.corda.testing.SerializationEnvironmentRule
|
import net.corda.testing.SerializationEnvironmentRule
|
||||||
import net.corda.testing.driver.PortAllocation
|
import net.corda.testing.driver.PortAllocation
|
||||||
import net.corda.testing.internal.createNodeInfoAndSigned
|
import net.corda.testing.internal.createNodeInfoAndSigned
|
||||||
@ -35,7 +34,7 @@ class NetworkMapClientTest {
|
|||||||
fun setUp() {
|
fun setUp() {
|
||||||
server = NetworkMapServer(cacheTimeout, PortAllocation.Incremental(10000).nextHostAndPort())
|
server = NetworkMapServer(cacheTimeout, PortAllocation.Incremental(10000).nextHostAndPort())
|
||||||
val hostAndPort = server.start()
|
val hostAndPort = server.start()
|
||||||
networkMapClient = NetworkMapClient(URL("http://${hostAndPort.host}:${hostAndPort.port}"), DEV_TRUST_ROOT.cert)
|
networkMapClient = NetworkMapClient(URL("http://${hostAndPort.host}:${hostAndPort.port}"), DEV_ROOT_CA.certificate)
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
@ -18,6 +18,7 @@ import java.net.ServerSocket
|
|||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.security.KeyStore
|
import java.security.KeyStore
|
||||||
import javax.net.ssl.*
|
import javax.net.ssl.*
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
|
|
||||||
@ -51,9 +52,9 @@ class TLSAuthenticationTests {
|
|||||||
val tempFolder: TemporaryFolder = TemporaryFolder()
|
val tempFolder: TemporaryFolder = TemporaryFolder()
|
||||||
|
|
||||||
// Root CA.
|
// Root CA.
|
||||||
private val ROOT_X500 = CordaX500Name(commonName = "Root_CA_1", organisation = "R3CEV", locality = "London", country = "GB")
|
private val ROOT_X500 = X500Principal("CN=Root_CA_1,O=R3CEV,L=London,C=GB")
|
||||||
// Intermediate CA.
|
// Intermediate CA.
|
||||||
private val INTERMEDIATE_X500 = CordaX500Name(commonName = "Intermediate_CA_1", organisation = "R3CEV", locality = "London", country = "GB")
|
private val INTERMEDIATE_X500 = X500Principal("CN=Intermediate_CA_1,O=R3CEV,L=London,C=GB")
|
||||||
// TLS server (client1).
|
// TLS server (client1).
|
||||||
private val CLIENT_1_X500 = CordaX500Name(commonName = "Client_1", organisation = "R3CEV", locality = "London", country = "GB")
|
private val CLIENT_1_X500 = CordaX500Name(commonName = "Client_1", organisation = "R3CEV", locality = "London", country = "GB")
|
||||||
// TLS client (client2).
|
// TLS client (client2).
|
||||||
@ -274,7 +275,7 @@ class TLSAuthenticationTests {
|
|||||||
CertificateType.NODE_CA,
|
CertificateType.NODE_CA,
|
||||||
intermediateCACert,
|
intermediateCACert,
|
||||||
intermediateCAKeyPair,
|
intermediateCAKeyPair,
|
||||||
CLIENT_1_X500,
|
CLIENT_1_X500.x500Principal,
|
||||||
client1CAKeyPair.public
|
client1CAKeyPair.public
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -283,7 +284,7 @@ class TLSAuthenticationTests {
|
|||||||
CertificateType.TLS,
|
CertificateType.TLS,
|
||||||
client1CACert,
|
client1CACert,
|
||||||
client1CAKeyPair,
|
client1CAKeyPair,
|
||||||
CLIENT_1_X500,
|
CLIENT_1_X500.x500Principal,
|
||||||
client1TLSKeyPair.public
|
client1TLSKeyPair.public
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -301,7 +302,7 @@ class TLSAuthenticationTests {
|
|||||||
CertificateType.NODE_CA,
|
CertificateType.NODE_CA,
|
||||||
intermediateCACert,
|
intermediateCACert,
|
||||||
intermediateCAKeyPair,
|
intermediateCAKeyPair,
|
||||||
CLIENT_2_X500,
|
CLIENT_2_X500.x500Principal,
|
||||||
client2CAKeyPair.public
|
client2CAKeyPair.public
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -310,7 +311,7 @@ class TLSAuthenticationTests {
|
|||||||
CertificateType.TLS,
|
CertificateType.TLS,
|
||||||
client2CACert,
|
client2CACert,
|
||||||
client2CAKeyPair,
|
client2CAKeyPair,
|
||||||
CLIENT_2_X500,
|
CLIENT_2_X500.x500Principal,
|
||||||
client2TLSKeyPair.public
|
client2TLSKeyPair.public
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -323,8 +324,8 @@ class TLSAuthenticationTests {
|
|||||||
// client2TLSKeyStore.save(client2TLSKeyStorePath, PASSWORD)
|
// client2TLSKeyStore.save(client2TLSKeyStorePath, PASSWORD)
|
||||||
|
|
||||||
val trustStore = loadOrCreateKeyStore(trustStorePath, PASSWORD)
|
val trustStore = loadOrCreateKeyStore(trustStorePath, PASSWORD)
|
||||||
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCACert.cert)
|
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCACert)
|
||||||
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_INTERMEDIATE_CA, intermediateCACert.cert)
|
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_INTERMEDIATE_CA, intermediateCACert)
|
||||||
// trustStore.save(trustStorePath, PASSWORD)
|
// trustStore.save(trustStorePath, PASSWORD)
|
||||||
|
|
||||||
val client1SSLContext = sslContext(client1TLSKeyStore, PASSWORD, trustStore)
|
val client1SSLContext = sslContext(client1TLSKeyStore, PASSWORD, trustStore)
|
||||||
|
@ -9,21 +9,23 @@ import com.nhaarman.mockito_kotlin.whenever
|
|||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.internal.createDirectories
|
import net.corda.core.internal.createDirectories
|
||||||
|
import net.corda.core.internal.x500Name
|
||||||
import net.corda.node.services.config.NodeConfiguration
|
import net.corda.node.services.config.NodeConfiguration
|
||||||
import net.corda.nodeapi.internal.crypto.*
|
import net.corda.nodeapi.internal.crypto.*
|
||||||
import net.corda.testing.ALICE_NAME
|
import net.corda.testing.ALICE_NAME
|
||||||
import net.corda.testing.internal.createDevNodeCaCertPath
|
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
||||||
import net.corda.testing.internal.rigorousMock
|
import net.corda.testing.internal.rigorousMock
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.*
|
||||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
import org.bouncycastle.asn1.x509.GeneralName
|
||||||
|
import org.bouncycastle.asn1.x509.GeneralSubtree
|
||||||
|
import org.bouncycastle.asn1.x509.NameConstraints
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.security.cert.CertPathValidatorException
|
import java.security.cert.CertPathValidatorException
|
||||||
import java.security.cert.Certificate
|
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
import kotlin.test.assertFalse
|
import kotlin.test.assertFalse
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
@ -32,18 +34,10 @@ class NetworkRegistrationHelperTest {
|
|||||||
private val requestId = SecureHash.randomSHA256().toString()
|
private val requestId = SecureHash.randomSHA256().toString()
|
||||||
private val nodeLegalName = ALICE_NAME
|
private val nodeLegalName = ALICE_NAME
|
||||||
|
|
||||||
private lateinit var rootCaCert: X509Certificate
|
|
||||||
private lateinit var intermediateCaCert: X509Certificate
|
|
||||||
private lateinit var nodeCaCert: X509Certificate
|
|
||||||
private lateinit var config: NodeConfiguration
|
private lateinit var config: NodeConfiguration
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun init() {
|
fun init() {
|
||||||
val (rootCa, intermediateCa, nodeCa) = createDevNodeCaCertPath(nodeLegalName)
|
|
||||||
this.rootCaCert = rootCa.certificate.cert
|
|
||||||
this.intermediateCaCert = intermediateCa.certificate.cert
|
|
||||||
this.nodeCaCert = nodeCa.certificate.cert
|
|
||||||
|
|
||||||
val baseDirectory = fs.getPath("/baseDir").createDirectories()
|
val baseDirectory = fs.getPath("/baseDir").createDirectories()
|
||||||
abstract class AbstractNodeConfiguration : NodeConfiguration
|
abstract class AbstractNodeConfiguration : NodeConfiguration
|
||||||
config = rigorousMock<AbstractNodeConfiguration>().also {
|
config = rigorousMock<AbstractNodeConfiguration>().also {
|
||||||
@ -66,9 +60,10 @@ class NetworkRegistrationHelperTest {
|
|||||||
assertThat(config.sslKeystore).doesNotExist()
|
assertThat(config.sslKeystore).doesNotExist()
|
||||||
assertThat(config.trustStoreFile).doesNotExist()
|
assertThat(config.trustStoreFile).doesNotExist()
|
||||||
|
|
||||||
saveTrustStoreWithRootCa(rootCaCert)
|
val nodeCaCertPath = createNodeCaCertPath()
|
||||||
|
|
||||||
createRegistrationHelper().buildKeystore()
|
saveTrustStoreWithRootCa(nodeCaCertPath.last())
|
||||||
|
createRegistrationHelper(nodeCaCertPath).buildKeystore()
|
||||||
|
|
||||||
val nodeKeystore = loadKeyStore(config.nodeKeystore, config.keyStorePassword)
|
val nodeKeystore = loadKeyStore(config.nodeKeystore, config.keyStorePassword)
|
||||||
val sslKeystore = loadKeyStore(config.sslKeystore, config.keyStorePassword)
|
val sslKeystore = loadKeyStore(config.sslKeystore, config.keyStorePassword)
|
||||||
@ -79,8 +74,7 @@ class NetworkRegistrationHelperTest {
|
|||||||
assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA))
|
assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA))
|
||||||
assertFalse(containsAlias(X509Utilities.CORDA_ROOT_CA))
|
assertFalse(containsAlias(X509Utilities.CORDA_ROOT_CA))
|
||||||
assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_TLS))
|
assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_TLS))
|
||||||
val nodeCaCertChain = getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
|
assertThat(getCertificateChain(X509Utilities.CORDA_CLIENT_CA)).containsExactly(*nodeCaCertPath)
|
||||||
assertThat(nodeCaCertChain).containsExactly(nodeCaCert, intermediateCaCert, rootCaCert)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sslKeystore.run {
|
sslKeystore.run {
|
||||||
@ -92,40 +86,77 @@ class NetworkRegistrationHelperTest {
|
|||||||
assertThat(nodeTlsCertChain).hasSize(4)
|
assertThat(nodeTlsCertChain).hasSize(4)
|
||||||
// The TLS cert has the same subject as the node CA cert
|
// The TLS cert has the same subject as the node CA cert
|
||||||
assertThat(CordaX500Name.build((nodeTlsCertChain[0] as X509Certificate).subjectX500Principal)).isEqualTo(nodeLegalName)
|
assertThat(CordaX500Name.build((nodeTlsCertChain[0] as X509Certificate).subjectX500Principal)).isEqualTo(nodeLegalName)
|
||||||
assertThat(nodeTlsCertChain.drop(1)).containsExactly(nodeCaCert, intermediateCaCert, rootCaCert)
|
assertThat(nodeTlsCertChain.drop(1)).containsExactly(*nodeCaCertPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
trustStore.run {
|
trustStore.run {
|
||||||
assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_CA))
|
assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_CA))
|
||||||
assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA))
|
assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA))
|
||||||
assertTrue(containsAlias(X509Utilities.CORDA_ROOT_CA))
|
assertTrue(containsAlias(X509Utilities.CORDA_ROOT_CA))
|
||||||
val trustStoreRootCaCert = getCertificate(X509Utilities.CORDA_ROOT_CA)
|
assertThat(getCertificate(X509Utilities.CORDA_ROOT_CA)).isEqualTo(nodeCaCertPath.last())
|
||||||
assertThat(trustStoreRootCaCert).isEqualTo(rootCaCert)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `missing truststore`() {
|
fun `missing truststore`() {
|
||||||
|
val nodeCaCertPath = createNodeCaCertPath()
|
||||||
assertThatThrownBy {
|
assertThatThrownBy {
|
||||||
createRegistrationHelper()
|
createRegistrationHelper(nodeCaCertPath)
|
||||||
}.hasMessageContaining("This file must contain the root CA cert of your compatibility zone. Please contact your CZ operator.")
|
}.hasMessageContaining("This file must contain the root CA cert of your compatibility zone. Please contact your CZ operator.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `node CA with incorrect cert role`() {
|
||||||
|
val nodeCaCertPath = createNodeCaCertPath(type = CertificateType.TLS)
|
||||||
|
saveTrustStoreWithRootCa(nodeCaCertPath.last())
|
||||||
|
val registrationHelper = createRegistrationHelper(nodeCaCertPath)
|
||||||
|
assertThatExceptionOfType(CertificateRequestException::class.java)
|
||||||
|
.isThrownBy { registrationHelper.buildKeystore() }
|
||||||
|
.withMessageContaining(CertificateType.TLS.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `node CA with incorrect subject`() {
|
||||||
|
val invalidName = CordaX500Name("Foo", "MU", "GB")
|
||||||
|
val nodeCaCertPath = createNodeCaCertPath(legalName = invalidName)
|
||||||
|
saveTrustStoreWithRootCa(nodeCaCertPath.last())
|
||||||
|
val registrationHelper = createRegistrationHelper(nodeCaCertPath)
|
||||||
|
assertThatExceptionOfType(CertificateRequestException::class.java)
|
||||||
|
.isThrownBy { registrationHelper.buildKeystore() }
|
||||||
|
.withMessageContaining(invalidName.toString())
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `wrong root cert in truststore`() {
|
fun `wrong root cert in truststore`() {
|
||||||
val rootKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val wrongRootCert = X509Utilities.createSelfSignedCACertificate(
|
||||||
val rootCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name("Foo", "MU", "GB"), rootKeyPair)
|
X500Principal("O=Foo,L=MU,C=GB"),
|
||||||
saveTrustStoreWithRootCa(rootCert.cert)
|
Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
|
||||||
val registrationHelper = createRegistrationHelper()
|
saveTrustStoreWithRootCa(wrongRootCert)
|
||||||
|
val registrationHelper = createRegistrationHelper(createNodeCaCertPath())
|
||||||
assertThatThrownBy {
|
assertThatThrownBy {
|
||||||
registrationHelper.buildKeystore()
|
registrationHelper.buildKeystore()
|
||||||
}.isInstanceOf(CertPathValidatorException::class.java)
|
}.isInstanceOf(CertPathValidatorException::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createRegistrationHelper(): NetworkRegistrationHelper {
|
private fun createNodeCaCertPath(type: CertificateType = CertificateType.NODE_CA,
|
||||||
|
legalName: CordaX500Name = nodeLegalName): Array<X509Certificate> {
|
||||||
|
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
|
||||||
|
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
|
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.x500Name))), arrayOf())
|
||||||
|
val nodeCaCert = X509Utilities.createCertificate(
|
||||||
|
type,
|
||||||
|
intermediateCa.certificate,
|
||||||
|
intermediateCa.keyPair,
|
||||||
|
legalName.x500Principal,
|
||||||
|
keyPair.public,
|
||||||
|
nameConstraints = nameConstraints)
|
||||||
|
return arrayOf(nodeCaCert, intermediateCa.certificate, rootCa.certificate)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createRegistrationHelper(response: Array<X509Certificate>): NetworkRegistrationHelper {
|
||||||
val certService = rigorousMock<NetworkRegistrationService>().also {
|
val certService = rigorousMock<NetworkRegistrationService>().also {
|
||||||
doReturn(requestId).whenever(it).submitRequest(any())
|
doReturn(requestId).whenever(it).submitRequest(any())
|
||||||
doReturn(arrayOf<Certificate>(nodeCaCert, intermediateCaCert, rootCaCert)).whenever(it).retrieveCertificates(eq(requestId))
|
doReturn(response).whenever(it).retrieveCertificates(eq(requestId))
|
||||||
}
|
}
|
||||||
return NetworkRegistrationHelper(config, certService)
|
return NetworkRegistrationHelper(config, certService)
|
||||||
}
|
}
|
||||||
|
@ -9,11 +9,12 @@ webapp which provides REST API and web frontend. Application communicate using C
|
|||||||
|
|
||||||
To run from the command line in Unix:
|
To run from the command line in Unix:
|
||||||
1. Run ``./gradlew samples:irs-demo:cordapp:deployNodes`` to install configs and a command line tool under
|
1. Run ``./gradlew samples:irs-demo:cordapp:deployNodes`` to install configs and a command line tool under
|
||||||
``samples/irs-demo/build``
|
``samples/irs-demo/cordapp/build``
|
||||||
2. Run ``./gradlew samples:irs-demo:web:deployWebapps`` to install configs and tools for running webservers
|
2. Run ``./gradlew samples:irs-demo:web:deployWebapps`` to install configs and tools for running webservers
|
||||||
3. Move to the ``samples/irs-demo/`` directory
|
3. Move to the ``samples/irs-demo/`` directory
|
||||||
4. Run ``./cordapp/build/nodes/runnodes`` to open up three new terminals with the three nodes (you may have to install xterm)
|
4. Run ``./cordapp/build/nodes/runnodes`` to open up three new terminals with the three nodes (you may have to install xterm)
|
||||||
5. Run ``./web/build/webapps/runwebapps`` to open three more terminals for associated webserver
|
5. On Linux, run ``./web/build/webapps/runwebapps`` to open three more terminals for associated webservers. On macOS,
|
||||||
|
use the following command instead: ``osascript ./web/build/webapps/runwebapps.scpt``
|
||||||
|
|
||||||
To run from the command line in Windows:
|
To run from the command line in Windows:
|
||||||
|
|
||||||
|
@ -1,8 +1,180 @@
|
|||||||
# SIMM Valuation Demo
|
# SIMM and Portfolio Demo - aka the Initial Margin Agreement Demo
|
||||||
|
|
||||||
See our [main documentation site](https://docs.corda.net/initial-margin-agreement.html) regarding the SIMM valuation and agreement on a distributed ledger.
|
## Background and SIMM Introduction
|
||||||
|
|
||||||
|
This app is a demonstration of how Corda can be used for the real world requirement of initial margin calculation and
|
||||||
|
agreement; featuring the integration of complex and industry proven third party libraries into Corda nodes.
|
||||||
|
|
||||||
|
SIMM is an acronym for "Standard Initial Margin Model". It is effectively the calculation of a "margin" that is paid
|
||||||
|
by one party to another when they agree a trade on certain types of transaction.
|
||||||
|
|
||||||
|
The SIMM was introduced to standardise the calculation of how much margin counterparties charge each other on their
|
||||||
|
bilateral transactions. Before SIMM, each counterparty computed margins according to its own model and it was made it very
|
||||||
|
difficult to agree the exact margin with the counterparty that faces the same trade on the other side.
|
||||||
|
|
||||||
|
To enact this, in September 2016, the ISDA committee - with full backing from various governing bodies -
|
||||||
|
[issued a ruling on what is known as the ISDA SIMM ™ model](http://www2.isda.org/news/isda-simm-deployed-today-new-industry-standard-for-calculating-initial-margin-widely-adopted-by-market-participants)
|
||||||
|
a way of fairly and consistently calculating this margin. Any parties wishing to trade a financial product that is
|
||||||
|
covered under this ruling would, independently, use this model and calculate their margin payment requirement,
|
||||||
|
agree it with their trading counterparty and then pay (or receive, depending on the results of this calculation)
|
||||||
|
this amount. In the case of disagreement that is not resolved in a timely fashion, this payment would increase
|
||||||
|
and so therefore it is in the parties' interest to reach agreement in as short as time frame as possible.
|
||||||
|
|
||||||
|
To be more accurate, the SIMM calculation is not performed on just one trade - it is calculated on an aggregate of
|
||||||
|
intermediary values (which in this model are sensitivities to risk factors) from a portfolio of trades; therefore
|
||||||
|
the input to a SIMM is actually this data, not the individual trades themselves.
|
||||||
|
|
||||||
|
Also note that implementations of the SIMM are actually protected and subject to license restrictions by ISDA
|
||||||
|
(this is due to the model itself being protected). We were fortunate enough to technically partner with
|
||||||
|
[OpenGamma](http://www.opengamma.com) who allowed us to demonstrate the SIMM process using their proprietary model.
|
||||||
|
In the source code released, we have replaced their analytics engine with very simple stub functions that allow
|
||||||
|
the process to run without actually calculating correct values, and can easily be swapped out in place for their real libraries.
|
||||||
|
|
||||||
|
## What happens in the demo (notionally)
|
||||||
|
|
||||||
|
|
||||||
|
Preliminaries
|
||||||
|
- Ensure that there are a number of live trades with another party based on financial products that are covered under the
|
||||||
|
ISDA SIMM agreement (if none, then use the demo to enter some simple trades as described below).
|
||||||
|
|
||||||
|
Initial Margin Agreement Process
|
||||||
|
- Agree that one will be performing the margining calculation against a portfolio of trades with another party, and agree the trades in that portfolio. In practice, one node will start the flow but it does not matter which node does.
|
||||||
|
- Individually (at the node level), identify the data (static, reference etc) one will need in order to be able to calculate the metrics on those trades
|
||||||
|
- Confirm with the other counterparty the dataset from the above set
|
||||||
|
- Calculate any intermediary steps and values needed for the margin calculation (ie sensitivities to risk factors)
|
||||||
|
- Agree on the results of these steps
|
||||||
|
- Calculate the initial margin
|
||||||
|
- Agree on the calculation of the above with the other party
|
||||||
|
- In practice, pay (or receive) this margin (omitted for the sake of complexity for this example)
|
||||||
|
|
||||||
|
## Demo execution (step by step)
|
||||||
|
|
||||||
|
|
||||||
|
**Setting up the Corda infrastructure**
|
||||||
|
|
||||||
|
To run from the command line in Unix:
|
||||||
|
|
||||||
|
1. Deploy the nodes using ``./gradlew samples:simm-valuation-demo:deployNodes``
|
||||||
|
2. Run the nodes using ``./samples/simm-valuation-demo/build/nodes/runnodes``
|
||||||
|
|
||||||
|
To run from the command line in Windows:
|
||||||
|
|
||||||
|
1. Deploy the nodes using ``gradlew samples:simm-valuation-demo:deployNodes``
|
||||||
|
2. Run the nodes using ``samples\simm-valuation-demo\build\nodes\runnodes``
|
||||||
|
|
||||||
|
**Getting Bank A's details**
|
||||||
|
|
||||||
|
From the command line run
|
||||||
|
|
||||||
|
curl http://localhost:10005/api/simmvaluationdemo/whoami
|
||||||
|
|
||||||
|
The response should be something like
|
||||||
|
|
||||||
|
{
|
||||||
|
"self" : {
|
||||||
|
"id" : "8Kqd4oWdx4KQGHGQW3FwXHQpjiv7cHaSsaAWMwRrK25bBJj792Z4rag7EtA",
|
||||||
|
"text" : "C=GB,L=London,O=Bank A"
|
||||||
|
},
|
||||||
|
"counterparties" : [
|
||||||
|
{
|
||||||
|
"id" : "8Kqd4oWdx4KQGHGL1DzULumUmZyyokeSGJDY1n5M6neUfAj2sjbf65wYwQM",
|
||||||
|
"text" : "C=JP,L=Tokyo,O=Bank C"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id" : "8Kqd4oWdx4KQGHGTBm34eCM2nrpcWKeM1ZG3DUYat3JTFUQTwB3Lv2WbPM8",
|
||||||
|
"text" : "C=US,L=New York,O=Bank B"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Now, if we ask the same question of Bank C we will see that it's id matches the id for Bank C as a counter
|
||||||
|
party to Bank A and Bank A will appear as a counter party
|
||||||
|
|
||||||
|
curl -i -H "Content-Type: application/json" -X GET http://localhost:10011/api/simmvaluationdemo/whoami
|
||||||
|
|
||||||
|
**Creating a trade with Bank C**
|
||||||
|
|
||||||
|
In what follows, we assume we are Bank A (which is listening on port 10005)
|
||||||
|
|
||||||
|
Notice the id field in the output of the ``whoami`` command. We are going to use the id assocatied
|
||||||
|
with Bank C, one of our counter parties, to create a trade. The general command for this is:
|
||||||
|
|
||||||
|
curl -i -H "Content-Type: application/json" -X PUT -d <<<JSON representation of the trade>>> http://localhost:10005/api/simmvaluationdemo/<<<counter party id>>>/trades
|
||||||
|
|
||||||
|
where the representation of the trade is
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
"id" : "trade1",
|
||||||
|
"description" : "desc",
|
||||||
|
"tradeDate" : [ 2016, 6, 6 ],
|
||||||
|
"convention" : "EUR_FIXED_1Y_EURIBOR_3M",
|
||||||
|
"startDate" : [ 2016, 6, 6 ],
|
||||||
|
"endDate" : [ 2020, 1, 2 ],
|
||||||
|
"buySell" : "BUY",
|
||||||
|
"notional" : "1000",
|
||||||
|
"fixedRate" : "0.1"
|
||||||
|
}
|
||||||
|
|
||||||
|
Continuing our example, the specific command we would run is
|
||||||
|
|
||||||
|
curl -i -H "Content-Type: application/json" \
|
||||||
|
-X PUT \
|
||||||
|
-d '{"id":"trade1","description" : "desc","tradeDate" : [ 2016, 6, 6 ], "convention" : "EUR_FIXED_1Y_EURIBOR_3M", "startDate" : [ 2016, 6, 6 ], "endDate" : [ 2020, 1, 2 ], "buySell" : "BUY", "notional" : "1000", "fixedRate" : "0.1"}' \
|
||||||
|
http://localhost:10005/api/simmvaluationdemo/8Kqd4oWdx4KQGHGL1DzULumUmZyyokeSGJDY1n5M6neUfAj2sjbf65wYwQM/trades
|
||||||
|
|
||||||
|
With an expected response of
|
||||||
|
|
||||||
|
HTTP/1.1 202 Accepted
|
||||||
|
Date: Thu, 28 Sep 2017 17:19:39 GMT
|
||||||
|
Content-Type: text/plain
|
||||||
|
Access-Control-Allow-Origin: *
|
||||||
|
Content-Length: 2
|
||||||
|
Server: Jetty(9.3.9.v20160517)
|
||||||
|
|
||||||
|
**Verifying trade completion**
|
||||||
|
|
||||||
|
With the trade completed and stored by both parties, the complete list of trades with our couterparty can be seen with the following command
|
||||||
|
|
||||||
|
curl -X GET http://localhost:10005/api/simmvaluationdemo/<<<counter party id>>>/trades
|
||||||
|
|
||||||
|
The command for our example, using Bank A, would thus be
|
||||||
|
|
||||||
|
curl -X GET http://localhost:10005/api/simmvaluationdemo/8Kqd4oWdx4KQGHGL1DzULumUmZyyokeSGJDY1n5M6neUfAj2sjbf65wYwQM/trades
|
||||||
|
|
||||||
|
whilst a specific trade can be seen with
|
||||||
|
|
||||||
|
|
||||||
|
curl -X GET http://localhost:10005/api/simmvaluationdemo/<<<counter party id>>>/trades/<<<trade id>>>
|
||||||
|
|
||||||
|
If we look at the trade we created above, we assigned it the id "trade1", the complete command in this case would be
|
||||||
|
|
||||||
|
curl -X GET http://localhost:10005/api/simmvaluationdemo/8Kqd4oWdx4KQGHGL1DzULumUmZyyokeSGJDY1n5M6neUfAj2sjbf65wYwQM/trades/trade1
|
||||||
|
|
||||||
|
**Generating a valuation**
|
||||||
|
|
||||||
|
curl -i -H "Content-Type: application/json" \
|
||||||
|
-X POST \
|
||||||
|
-d <<<JSON representation>>>
|
||||||
|
http://localhost:10005/api/simmvaluationdemo/<<<counter party id>>>/portfolio/valuations/calculate
|
||||||
|
|
||||||
|
Again, the specific command to continue our example would be
|
||||||
|
|
||||||
|
curl -i -H "Content-Type: application/json" \
|
||||||
|
-X POST \
|
||||||
|
-d '{"valuationDate":[2016,6,6]}' \
|
||||||
|
http://localhost:10005/api/simmvaluationdemo/8Kqd4oWdx4KQGHGL1DzLumUmZyyokeSGJDY1n5M6neUfAj2sjbf65wYwQM/portfolio/valuations/calculate
|
||||||
|
|
||||||
|
**Viewing a valuation**
|
||||||
|
|
||||||
|
In the same way we can ask for specific instances of trades with a counter party, we can request details of valuations
|
||||||
|
|
||||||
|
curl -i -H "Content-Type: application/json" -X GET http://localhost:10005/api/simmvaluationdemo/<<<counter party id>>>/portfolio/valuations
|
||||||
|
|
||||||
|
The specific command for out Bank A example is
|
||||||
|
|
||||||
|
curl -i -H "Content-Type: application/json" \
|
||||||
|
-X GET http://localhost:10005/api/simmvaluationdemo/8Kqd4oWdx4KQGHGL1DzULumUmZyyokeSGJDY1n5M6neUfAj2sjbf65YwQM/portfolio/valuations
|
||||||
|
|
||||||
|
|
||||||
## SIMM Library Licensing
|
## SIMM Library Licensing
|
||||||
|
@ -2,6 +2,7 @@ apply plugin: 'kotlin'
|
|||||||
apply plugin: 'kotlin-jpa'
|
apply plugin: 'kotlin-jpa'
|
||||||
apply plugin: 'net.corda.plugins.quasar-utils'
|
apply plugin: 'net.corda.plugins.quasar-utils'
|
||||||
apply plugin: 'net.corda.plugins.publish-utils'
|
apply plugin: 'net.corda.plugins.publish-utils'
|
||||||
|
apply plugin: 'net.corda.plugins.api-scanner'
|
||||||
apply plugin: 'com.jfrog.artifactory'
|
apply plugin: 'com.jfrog.artifactory'
|
||||||
|
|
||||||
//noinspection GroovyAssignabilityCheck
|
//noinspection GroovyAssignabilityCheck
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
package net.corda.testing.driver
|
package net.corda.testing.driver
|
||||||
|
|
||||||
import net.corda.client.rpc.CordaRPCClient
|
import net.corda.client.rpc.CordaRPCClient
|
||||||
|
import net.corda.core.DoNotImplement
|
||||||
import net.corda.core.concurrent.CordaFuture
|
import net.corda.core.concurrent.CordaFuture
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
@ -30,6 +31,7 @@ import java.util.concurrent.atomic.AtomicInteger
|
|||||||
*/
|
*/
|
||||||
data class NotaryHandle(val identity: Party, val validating: Boolean, val nodeHandles: CordaFuture<List<NodeHandle>>)
|
data class NotaryHandle(val identity: Party, val validating: Boolean, val nodeHandles: CordaFuture<List<NodeHandle>>)
|
||||||
|
|
||||||
|
@DoNotImplement
|
||||||
sealed class NodeHandle {
|
sealed class NodeHandle {
|
||||||
abstract val nodeInfo: NodeInfo
|
abstract val nodeInfo: NodeInfo
|
||||||
/**
|
/**
|
||||||
@ -90,6 +92,7 @@ data class WebserverHandle(
|
|||||||
val process: Process
|
val process: Process
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@DoNotImplement
|
||||||
sealed class PortAllocation {
|
sealed class PortAllocation {
|
||||||
abstract fun nextPort(): Int
|
abstract fun nextPort(): Int
|
||||||
fun nextHostAndPort() = NetworkHostAndPort("localhost", nextPort())
|
fun nextHostAndPort() = NetworkHostAndPort("localhost", nextPort())
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package net.corda.testing.driver
|
package net.corda.testing.driver
|
||||||
|
|
||||||
|
import net.corda.core.DoNotImplement
|
||||||
import net.corda.core.concurrent.CordaFuture
|
import net.corda.core.concurrent.CordaFuture
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
@ -10,6 +11,7 @@ import net.corda.nodeapi.internal.config.User
|
|||||||
import net.corda.testing.node.NotarySpec
|
import net.corda.testing.node.NotarySpec
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
@DoNotImplement
|
||||||
interface DriverDSL {
|
interface DriverDSL {
|
||||||
/** Returns a list of [NotaryHandle]s matching the list of [NotarySpec]s passed into [driver]. */
|
/** Returns a list of [NotaryHandle]s matching the list of [NotarySpec]s passed into [driver]. */
|
||||||
val notaryHandles: List<NotaryHandle>
|
val notaryHandles: List<NotaryHandle>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package net.corda.testing.node
|
package net.corda.testing.node
|
||||||
|
|
||||||
|
import net.corda.core.DoNotImplement
|
||||||
import net.corda.core.crypto.CompositeKey
|
import net.corda.core.crypto.CompositeKey
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
@ -123,7 +124,7 @@ class InMemoryMessagingNetwork internal constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LatencyCalculator {
|
interface LatencyCalculator { // XXX: Used?
|
||||||
fun between(sender: SingleMessageRecipient, receiver: SingleMessageRecipient): Duration
|
fun between(sender: SingleMessageRecipient, receiver: SingleMessageRecipient): Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,6 +182,7 @@ class InMemoryMessagingNetwork internal constructor(
|
|||||||
/**
|
/**
|
||||||
* Mock service loadbalancing
|
* Mock service loadbalancing
|
||||||
*/
|
*/
|
||||||
|
@DoNotImplement
|
||||||
sealed class ServicePeerAllocationStrategy {
|
sealed class ServicePeerAllocationStrategy {
|
||||||
abstract fun <A> pickNext(service: ServiceHandle, pickFrom: List<A>): A
|
abstract fun <A> pickNext(service: ServiceHandle, pickFrom: List<A>): A
|
||||||
class Random(val random: SplittableRandom = SplittableRandom()) : ServicePeerAllocationStrategy() {
|
class Random(val random: SplittableRandom = SplittableRandom()) : ServicePeerAllocationStrategy() {
|
||||||
|
@ -4,6 +4,7 @@ import com.google.common.jimfs.Configuration.unix
|
|||||||
import com.google.common.jimfs.Jimfs
|
import com.google.common.jimfs.Jimfs
|
||||||
import com.nhaarman.mockito_kotlin.doReturn
|
import com.nhaarman.mockito_kotlin.doReturn
|
||||||
import com.nhaarman.mockito_kotlin.whenever
|
import com.nhaarman.mockito_kotlin.whenever
|
||||||
|
import net.corda.core.DoNotImplement
|
||||||
import net.corda.core.crypto.entropyToKeyPair
|
import net.corda.core.crypto.entropyToKeyPair
|
||||||
import net.corda.core.crypto.random63BitValue
|
import net.corda.core.crypto.random63BitValue
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
@ -492,6 +493,7 @@ fun StartedNode<MockNetwork.MockNode>.setMessagingServiceSpy(messagingServiceSpy
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun mockNodeConfiguration(): NodeConfiguration {
|
private fun mockNodeConfiguration(): NodeConfiguration {
|
||||||
|
@DoNotImplement
|
||||||
abstract class AbstractNodeConfiguration : NodeConfiguration
|
abstract class AbstractNodeConfiguration : NodeConfiguration
|
||||||
return rigorousMock<AbstractNodeConfiguration>().also {
|
return rigorousMock<AbstractNodeConfiguration>().also {
|
||||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||||
|
@ -23,6 +23,7 @@ import net.corda.core.transactions.SignedTransaction
|
|||||||
import net.corda.node.VersionInfo
|
import net.corda.node.VersionInfo
|
||||||
import net.corda.node.internal.configureDatabase
|
import net.corda.node.internal.configureDatabase
|
||||||
import net.corda.node.internal.cordapp.CordappLoader
|
import net.corda.node.internal.cordapp.CordappLoader
|
||||||
|
import net.corda.node.services.api.IdentityServiceInternal
|
||||||
import net.corda.node.services.api.SchemaService
|
import net.corda.node.services.api.SchemaService
|
||||||
import net.corda.node.services.api.VaultServiceInternal
|
import net.corda.node.services.api.VaultServiceInternal
|
||||||
import net.corda.node.services.api.WritableTransactionStorage
|
import net.corda.node.services.api.WritableTransactionStorage
|
||||||
@ -34,12 +35,12 @@ import net.corda.node.services.schema.HibernateObserver
|
|||||||
import net.corda.node.services.schema.NodeSchemaService
|
import net.corda.node.services.schema.NodeSchemaService
|
||||||
import net.corda.node.services.transactions.InMemoryTransactionVerifierService
|
import net.corda.node.services.transactions.InMemoryTransactionVerifierService
|
||||||
import net.corda.node.services.vault.NodeVaultService
|
import net.corda.node.services.vault.NodeVaultService
|
||||||
import net.corda.node.services.api.IdentityServiceInternal
|
|
||||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||||
import net.corda.nodeapi.internal.persistence.HibernateConfiguration
|
import net.corda.nodeapi.internal.persistence.HibernateConfiguration
|
||||||
import net.corda.nodeapi.internal.persistence.TransactionIsolationLevel
|
import net.corda.nodeapi.internal.persistence.TransactionIsolationLevel
|
||||||
import net.corda.testing.*
|
import net.corda.testing.DEV_ROOT_CA
|
||||||
|
import net.corda.testing.TestIdentity
|
||||||
import net.corda.testing.database.DatabaseConstants
|
import net.corda.testing.database.DatabaseConstants
|
||||||
import net.corda.testing.database.DatabaseConstants.DATA_SOURCE_CLASSNAME
|
import net.corda.testing.database.DatabaseConstants.DATA_SOURCE_CLASSNAME
|
||||||
import net.corda.testing.database.DatabaseConstants.DATA_SOURCE_PASSWORD
|
import net.corda.testing.database.DatabaseConstants.DATA_SOURCE_PASSWORD
|
||||||
@ -59,7 +60,7 @@ import java.sql.Connection
|
|||||||
import java.time.Clock
|
import java.time.Clock
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
fun makeTestIdentityService(vararg identities: PartyAndCertificate) = InMemoryIdentityService(identities, DEV_TRUST_ROOT)
|
fun makeTestIdentityService(vararg identities: PartyAndCertificate) = InMemoryIdentityService(identities, DEV_ROOT_CA.certificate)
|
||||||
/**
|
/**
|
||||||
* A singleton utility that only provides a mock identity, key and storage service. However, this is sufficient for
|
* A singleton utility that only provides a mock identity, key and storage service. However, this is sufficient for
|
||||||
* building chains of transactions and verifying them. It isn't sufficient for testing flows however.
|
* building chains of transactions and verifying them. It isn't sufficient for testing flows however.
|
||||||
@ -197,7 +198,7 @@ class MockKeyManagementService(val identityService: IdentityServiceInternal,
|
|||||||
|
|
||||||
override val keys: Set<PublicKey> get() = keyStore.keys
|
override val keys: Set<PublicKey> get() = keyStore.keys
|
||||||
|
|
||||||
val nextKeys = LinkedList<KeyPair>()
|
private val nextKeys = LinkedList<KeyPair>()
|
||||||
|
|
||||||
override fun freshKey(): PublicKey {
|
override fun freshKey(): PublicKey {
|
||||||
val k = nextKeys.poll() ?: generateKeyPair()
|
val k = nextKeys.poll() ?: generateKeyPair()
|
||||||
@ -259,11 +260,10 @@ open class MockTransactionStorage : WritableTransactionStorage, SingletonSeriali
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun <T : SerializeAsToken> createMockCordaService(serviceHub: MockServices, serviceConstructor: (AppServiceHub) -> T): T {
|
fun <T : SerializeAsToken> createMockCordaService(serviceHub: MockServices, serviceConstructor: (AppServiceHub) -> T): T {
|
||||||
class MockAppServiceHubImpl<T : SerializeAsToken>(val serviceHub: MockServices, serviceConstructor: (AppServiceHub) -> T) : AppServiceHub, ServiceHub by serviceHub {
|
class MockAppServiceHubImpl<out T : SerializeAsToken>(val serviceHub: MockServices, serviceConstructor: (AppServiceHub) -> T) : AppServiceHub, ServiceHub by serviceHub {
|
||||||
val serviceInstance: T
|
val serviceInstance: T = serviceConstructor(this)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
serviceInstance = serviceConstructor(this)
|
|
||||||
serviceHub.cordappServices.putInstance(serviceInstance.javaClass, serviceInstance)
|
serviceHub.cordappServices.putInstance(serviceInstance.javaClass, serviceInstance)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package net.corda.testing.node
|
package net.corda.testing.node
|
||||||
|
|
||||||
|
import net.corda.core.DoNotImplement
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.node.services.config.VerifierType
|
import net.corda.node.services.config.VerifierType
|
||||||
import net.corda.nodeapi.internal.config.User
|
import net.corda.nodeapi.internal.config.User
|
||||||
@ -12,6 +13,7 @@ data class NotarySpec(
|
|||||||
val cluster: ClusterSpec? = null
|
val cluster: ClusterSpec? = null
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@DoNotImplement
|
||||||
sealed class ClusterSpec {
|
sealed class ClusterSpec {
|
||||||
abstract val clusterSize: Int
|
abstract val clusterSize: Int
|
||||||
|
|
||||||
|
@ -2,8 +2,6 @@ package net.corda.testing.node.internal.network
|
|||||||
|
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.internal.toX509CertHolder
|
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.serialization.deserialize
|
import net.corda.core.serialization.deserialize
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
@ -16,7 +14,7 @@ import net.corda.nodeapi.internal.network.DigitalSignatureWithCert
|
|||||||
import net.corda.nodeapi.internal.network.NetworkMap
|
import net.corda.nodeapi.internal.network.NetworkMap
|
||||||
import net.corda.nodeapi.internal.network.NetworkParameters
|
import net.corda.nodeapi.internal.network.NetworkParameters
|
||||||
import net.corda.nodeapi.internal.network.SignedNetworkMap
|
import net.corda.nodeapi.internal.network.SignedNetworkMap
|
||||||
import net.corda.testing.ROOT_CA
|
import net.corda.testing.DEV_ROOT_CA
|
||||||
import org.eclipse.jetty.server.Server
|
import org.eclipse.jetty.server.Server
|
||||||
import org.eclipse.jetty.server.ServerConnector
|
import org.eclipse.jetty.server.ServerConnector
|
||||||
import org.eclipse.jetty.server.handler.HandlerCollection
|
import org.eclipse.jetty.server.handler.HandlerCollection
|
||||||
@ -29,6 +27,7 @@ import java.io.InputStream
|
|||||||
import java.net.InetSocketAddress
|
import java.net.InetSocketAddress
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
import javax.ws.rs.*
|
import javax.ws.rs.*
|
||||||
import javax.ws.rs.core.MediaType
|
import javax.ws.rs.core.MediaType
|
||||||
import javax.ws.rs.core.Response
|
import javax.ws.rs.core.Response
|
||||||
@ -36,7 +35,7 @@ import javax.ws.rs.core.Response.ok
|
|||||||
|
|
||||||
class NetworkMapServer(cacheTimeout: Duration,
|
class NetworkMapServer(cacheTimeout: Duration,
|
||||||
hostAndPort: NetworkHostAndPort,
|
hostAndPort: NetworkHostAndPort,
|
||||||
rootCa: CertificateAndKeyPair = ROOT_CA, // Default to ROOT_CA for testing.
|
rootCa: CertificateAndKeyPair = DEV_ROOT_CA,
|
||||||
private val myHostNameValue: String = "test.host.name",
|
private val myHostNameValue: String = "test.host.name",
|
||||||
vararg additionalServices: Any) : Closeable {
|
vararg additionalServices: Any) : Closeable {
|
||||||
companion object {
|
companion object {
|
||||||
@ -48,12 +47,12 @@ class NetworkMapServer(cacheTimeout: Duration,
|
|||||||
CertificateType.NETWORK_MAP,
|
CertificateType.NETWORK_MAP,
|
||||||
rootCAKeyAndCert.certificate,
|
rootCAKeyAndCert.certificate,
|
||||||
rootCAKeyAndCert.keyPair,
|
rootCAKeyAndCert.keyPair,
|
||||||
CordaX500Name("Corda Network Map", "R3 Ltd", "London","GB"),
|
X500Principal("CN=Corda Network Map,O=R3 Ltd,L=London,C=GB"),
|
||||||
networkMapKey.public).cert
|
networkMapKey.public)
|
||||||
// Check that the certificate validates. Nodes will perform this check upon receiving a network map,
|
// Check that the certificate validates. Nodes will perform this check upon receiving a network map,
|
||||||
// it's better to fail here than there.
|
// it's better to fail here than there.
|
||||||
X509Utilities.validateCertificateChain(rootCAKeyAndCert.certificate.cert, networkMapCert)
|
X509Utilities.validateCertificateChain(rootCAKeyAndCert.certificate, networkMapCert)
|
||||||
return CertificateAndKeyPair(networkMapCert.toX509CertHolder(), networkMapKey)
|
return CertificateAndKeyPair(networkMapCert, networkMapKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,7 +129,7 @@ class NetworkMapServer(cacheTimeout: Duration,
|
|||||||
val networkMap = NetworkMap(nodeInfoMap.keys.toList(), parametersHash)
|
val networkMap = NetworkMap(nodeInfoMap.keys.toList(), parametersHash)
|
||||||
val serializedNetworkMap = networkMap.serialize()
|
val serializedNetworkMap = networkMap.serialize()
|
||||||
val signature = Crypto.doSign(networkMapKeyAndCert.keyPair.private, serializedNetworkMap.bytes)
|
val signature = Crypto.doSign(networkMapKeyAndCert.keyPair.private, serializedNetworkMap.bytes)
|
||||||
val signedNetworkMap = SignedNetworkMap(networkMap.serialize(), DigitalSignatureWithCert(networkMapKeyAndCert.certificate.cert, signature))
|
val signedNetworkMap = SignedNetworkMap(networkMap.serialize(), DigitalSignatureWithCert(networkMapKeyAndCert.certificate, signature))
|
||||||
return Response.ok(signedNetworkMap.serialize().bytes).header("Cache-Control", "max-age=${cacheTimeout.seconds}").build()
|
return Response.ok(signedNetworkMap.serialize().bytes).header("Cache-Control", "max-age=${cacheTimeout.seconds}").build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
apply plugin: 'net.corda.plugins.publish-utils'
|
apply plugin: 'net.corda.plugins.publish-utils'
|
||||||
|
apply plugin: 'net.corda.plugins.api-scanner'
|
||||||
apply plugin: 'com.jfrog.artifactory'
|
apply plugin: 'com.jfrog.artifactory'
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
@ -2,6 +2,7 @@ apply plugin: 'kotlin'
|
|||||||
apply plugin: 'kotlin-jpa'
|
apply plugin: 'kotlin-jpa'
|
||||||
apply plugin: 'net.corda.plugins.quasar-utils'
|
apply plugin: 'net.corda.plugins.quasar-utils'
|
||||||
apply plugin: 'net.corda.plugins.publish-utils'
|
apply plugin: 'net.corda.plugins.publish-utils'
|
||||||
|
apply plugin: 'net.corda.plugins.api-scanner'
|
||||||
apply plugin: 'com.jfrog.artifactory'
|
apply plugin: 'com.jfrog.artifactory'
|
||||||
|
|
||||||
description 'Testing utilities for Corda'
|
description 'Testing utilities for Corda'
|
||||||
|
@ -12,7 +12,6 @@ import net.corda.core.crypto.toStringShort
|
|||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
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.cert
|
|
||||||
import net.corda.core.internal.unspecifiedCountry
|
import net.corda.core.internal.unspecifiedCountry
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
@ -21,10 +20,13 @@ import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
|||||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
import org.bouncycastle.asn1.x509.GeneralName
|
||||||
|
import org.bouncycastle.asn1.x509.GeneralSubtree
|
||||||
|
import org.bouncycastle.asn1.x509.NameConstraints
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -76,8 +78,8 @@ fun getFreeLocalPorts(hostName: String, numberToAlloc: Int): List<NetworkHostAnd
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getTestPartyAndCertificate(party: Party): PartyAndCertificate {
|
fun getTestPartyAndCertificate(party: Party): PartyAndCertificate {
|
||||||
val trustRoot: X509CertificateHolder = DEV_TRUST_ROOT
|
val trustRoot: X509Certificate = DEV_ROOT_CA.certificate
|
||||||
val intermediate: CertificateAndKeyPair = DEV_CA
|
val intermediate: CertificateAndKeyPair = DEV_INTERMEDIATE_CA
|
||||||
|
|
||||||
val (nodeCaCert, nodeCaKeyPair) = createDevNodeCa(intermediate, party.name)
|
val (nodeCaCert, nodeCaKeyPair) = createDevNodeCa(intermediate, party.name)
|
||||||
|
|
||||||
@ -85,11 +87,10 @@ fun getTestPartyAndCertificate(party: Party): PartyAndCertificate {
|
|||||||
CertificateType.LEGAL_IDENTITY,
|
CertificateType.LEGAL_IDENTITY,
|
||||||
nodeCaCert,
|
nodeCaCert,
|
||||||
nodeCaKeyPair,
|
nodeCaKeyPair,
|
||||||
party.name,
|
party.name.x500Principal,
|
||||||
party.owningKey)
|
party.owningKey)
|
||||||
|
|
||||||
val pathElements = listOf(identityCert, nodeCaCert, intermediate.certificate, trustRoot)
|
val certPath = X509CertificateFactory().generateCertPath(identityCert, nodeCaCert, intermediate.certificate, trustRoot)
|
||||||
val certPath = X509CertificateFactory().generateCertPath(pathElements.map(X509CertificateHolder::cert))
|
|
||||||
return PartyAndCertificate(certPath)
|
return PartyAndCertificate(certPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.corda.testing
|
package net.corda.testing
|
||||||
|
|
||||||
import com.google.common.util.concurrent.SettableFuture
|
import com.google.common.util.concurrent.SettableFuture
|
||||||
|
import net.corda.core.DoNotImplement
|
||||||
import net.corda.core.internal.uncheckedCast
|
import net.corda.core.internal.uncheckedCast
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
@ -189,6 +190,7 @@ fun <S, E : Any> S.genericExpectEvents(
|
|||||||
finishFuture.getOrThrow()
|
finishFuture.getOrThrow()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DoNotImplement
|
||||||
sealed class ExpectCompose<out E> {
|
sealed class ExpectCompose<out E> {
|
||||||
internal class Single<out E, T : E>(val expect: Expect<E, T>) : ExpectCompose<E>()
|
internal class Single<out E, T : E>(val expect: Expect<E, T>) : ExpectCompose<E>()
|
||||||
internal class Sequential<out E>(val sequence: List<ExpectCompose<E>>) : ExpectCompose<E>()
|
internal class Sequential<out E>(val sequence: List<ExpectCompose<E>>) : ExpectCompose<E>()
|
||||||
|
@ -2,6 +2,7 @@ package net.corda.testing
|
|||||||
|
|
||||||
import com.nhaarman.mockito_kotlin.*
|
import com.nhaarman.mockito_kotlin.*
|
||||||
import net.corda.client.rpc.internal.KryoClientSerializationScheme
|
import net.corda.client.rpc.internal.KryoClientSerializationScheme
|
||||||
|
import net.corda.core.DoNotImplement
|
||||||
import net.corda.core.internal.staticField
|
import net.corda.core.internal.staticField
|
||||||
import net.corda.core.serialization.internal.*
|
import net.corda.core.serialization.internal.*
|
||||||
import net.corda.node.serialization.KryoServerSerializationScheme
|
import net.corda.node.serialization.KryoServerSerializationScheme
|
||||||
@ -62,6 +63,7 @@ class SerializationEnvironmentRule(private val inheritable: Boolean = false) : T
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DoNotImplement
|
||||||
interface GlobalSerializationEnvironment : SerializationEnvironment {
|
interface GlobalSerializationEnvironment : SerializationEnvironment {
|
||||||
/** Unset this environment. */
|
/** Unset this environment. */
|
||||||
fun unset()
|
fun unset()
|
||||||
|
@ -6,12 +6,10 @@ import net.corda.core.contracts.Command
|
|||||||
import net.corda.core.contracts.TypeOnlyCommandData
|
import net.corda.core.contracts.TypeOnlyCommandData
|
||||||
import net.corda.core.crypto.generateKeyPair
|
import net.corda.core.crypto.generateKeyPair
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.toX509CertHolder
|
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
import net.corda.nodeapi.internal.crypto.getCertificateAndKeyPair
|
import net.corda.nodeapi.internal.crypto.getCertificateAndKeyPair
|
||||||
import net.corda.nodeapi.internal.crypto.loadKeyStore
|
import net.corda.nodeapi.internal.crypto.loadKeyStore
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
@ -34,22 +32,17 @@ val ALICE_NAME = CordaX500Name("Alice Corp", "Madrid", "ES")
|
|||||||
val BOB_NAME = CordaX500Name("Bob Plc", "Rome", "IT")
|
val BOB_NAME = CordaX500Name("Bob Plc", "Rome", "IT")
|
||||||
@JvmField
|
@JvmField
|
||||||
val CHARLIE_NAME = CordaX500Name("Charlie Ltd", "Athens", "GR")
|
val CHARLIE_NAME = CordaX500Name("Charlie Ltd", "Athens", "GR")
|
||||||
val DEV_CA: CertificateAndKeyPair by lazy {
|
val DEV_INTERMEDIATE_CA: CertificateAndKeyPair by lazy {
|
||||||
// TODO: Should be identity scheme
|
// TODO: Should be identity scheme
|
||||||
val caKeyStore = loadKeyStore(ClassLoader.getSystemResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
|
val caKeyStore = loadKeyStore(ClassLoader.getSystemResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
|
||||||
caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
|
caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
|
||||||
}
|
}
|
||||||
|
|
||||||
val ROOT_CA: CertificateAndKeyPair by lazy {
|
val DEV_ROOT_CA: CertificateAndKeyPair by lazy {
|
||||||
// TODO: Should be identity scheme
|
// TODO: Should be identity scheme
|
||||||
val caKeyStore = loadKeyStore(ClassLoader.getSystemResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
|
val caKeyStore = loadKeyStore(ClassLoader.getSystemResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
|
||||||
caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_ROOT_CA, "cordacadevkeypass")
|
caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_ROOT_CA, "cordacadevkeypass")
|
||||||
}
|
}
|
||||||
val DEV_TRUST_ROOT: X509CertificateHolder by lazy {
|
|
||||||
// TODO: Should be identity scheme
|
|
||||||
val caKeyStore = loadKeyStore(ClassLoader.getSystemResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
|
|
||||||
caKeyStore.getCertificateChain(X509Utilities.CORDA_INTERMEDIATE_CA).last().toX509CertHolder()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun dummyCommand(vararg signers: PublicKey = arrayOf(generateKeyPair().public)) = Command<TypeOnlyCommandData>(DummyCommandData, signers.toList())
|
fun dummyCommand(vararg signers: PublicKey = arrayOf(generateKeyPair().public)) = Command<TypeOnlyCommandData>(DummyCommandData, signers.toList())
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package net.corda.testing.contracts
|
package net.corda.testing.contracts
|
||||||
|
|
||||||
|
import net.corda.core.DoNotImplement
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.identity.AbstractParty
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
@ -12,6 +13,7 @@ data class DummyContract(val blank: Any? = null) : Contract {
|
|||||||
|
|
||||||
val PROGRAM_ID = "net.corda.testing.contracts.DummyContract"
|
val PROGRAM_ID = "net.corda.testing.contracts.DummyContract"
|
||||||
|
|
||||||
|
@DoNotImplement // State is effectively a sealed class.
|
||||||
interface State : ContractState {
|
interface State : ContractState {
|
||||||
val magicNumber: Int
|
val magicNumber: Int
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package net.corda.testing.dsl
|
package net.corda.testing.dsl
|
||||||
|
|
||||||
|
import net.corda.core.DoNotImplement
|
||||||
import net.corda.core.contracts.ContractState
|
import net.corda.core.contracts.ContractState
|
||||||
import net.corda.core.contracts.StateAndRef
|
import net.corda.core.contracts.StateAndRef
|
||||||
import net.corda.core.contracts.TransactionState
|
import net.corda.core.contracts.TransactionState
|
||||||
@ -14,6 +15,7 @@ import java.io.InputStream
|
|||||||
* This interface defines output state lookup by label. It is split from the interpreter interfaces so that outputs may
|
* This interface defines output state lookup by label. It is split from the interpreter interfaces so that outputs may
|
||||||
* be looked up both in ledger{..} and transaction{..} blocks.
|
* be looked up both in ledger{..} and transaction{..} blocks.
|
||||||
*/
|
*/
|
||||||
|
@DoNotImplement
|
||||||
interface OutputStateLookup {
|
interface OutputStateLookup {
|
||||||
/**
|
/**
|
||||||
* Retrieves an output previously defined by [TransactionDSLInterpreter.output] with a label passed in.
|
* Retrieves an output previously defined by [TransactionDSLInterpreter.output] with a label passed in.
|
||||||
@ -27,6 +29,7 @@ interface OutputStateLookup {
|
|||||||
/**
|
/**
|
||||||
* This interface asserts that the DSL at hand is capable of verifying its underlying construct(ledger/transaction).
|
* This interface asserts that the DSL at hand is capable of verifying its underlying construct(ledger/transaction).
|
||||||
*/
|
*/
|
||||||
|
@DoNotImplement
|
||||||
interface Verifies {
|
interface Verifies {
|
||||||
/**
|
/**
|
||||||
* Verifies the ledger/transaction, throws if the verification fails.
|
* Verifies the ledger/transaction, throws if the verification fails.
|
||||||
@ -83,6 +86,7 @@ interface Verifies {
|
|||||||
*
|
*
|
||||||
* TODO (Kotlin 1.1): Use type synonyms to make the type params less unwieldy
|
* TODO (Kotlin 1.1): Use type synonyms to make the type params less unwieldy
|
||||||
*/
|
*/
|
||||||
|
@DoNotImplement
|
||||||
interface LedgerDSLInterpreter<out T : TransactionDSLInterpreter> : Verifies, OutputStateLookup {
|
interface LedgerDSLInterpreter<out T : TransactionDSLInterpreter> : Verifies, OutputStateLookup {
|
||||||
/**
|
/**
|
||||||
* Creates and adds a transaction to the ledger.
|
* Creates and adds a transaction to the ledger.
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package net.corda.testing.dsl
|
package net.corda.testing.dsl
|
||||||
|
|
||||||
|
import net.corda.core.DoNotImplement
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.cordapp.CordappProvider
|
import net.corda.core.cordapp.CordappProvider
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.*
|
||||||
@ -50,6 +51,7 @@ import kotlin.collections.set
|
|||||||
* will have as the last line either an accept or a failure test. The name is deliberately long to help make sense of
|
* will have as the last line either an accept or a failure test. The name is deliberately long to help make sense of
|
||||||
* the triggered diagnostic.
|
* the triggered diagnostic.
|
||||||
*/
|
*/
|
||||||
|
@DoNotImplement
|
||||||
sealed class EnforceVerifyOrFail {
|
sealed class EnforceVerifyOrFail {
|
||||||
internal object Token : EnforceVerifyOrFail()
|
internal object Token : EnforceVerifyOrFail()
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package net.corda.testing.dsl
|
package net.corda.testing.dsl
|
||||||
|
|
||||||
|
import net.corda.core.DoNotImplement
|
||||||
import net.corda.core.contracts.AlwaysAcceptAttachmentConstraint
|
import net.corda.core.contracts.AlwaysAcceptAttachmentConstraint
|
||||||
import net.corda.core.contracts.Attachment
|
import net.corda.core.contracts.Attachment
|
||||||
import net.corda.core.contracts.AttachmentConstraint
|
import net.corda.core.contracts.AttachmentConstraint
|
||||||
@ -22,6 +23,7 @@ import java.time.Instant
|
|||||||
* @param <R> The return type of [verifies]/[failsWith] and the like. It is generic so that we have control over whether
|
* @param <R> The return type of [verifies]/[failsWith] and the like. It is generic so that we have control over whether
|
||||||
* we want to enforce users to call these methods (see [EnforceVerifyOrFail]) or not.
|
* we want to enforce users to call these methods (see [EnforceVerifyOrFail]) or not.
|
||||||
*/
|
*/
|
||||||
|
@DoNotImplement
|
||||||
interface TransactionDSLInterpreter : Verifies, OutputStateLookup {
|
interface TransactionDSLInterpreter : Verifies, OutputStateLookup {
|
||||||
/**
|
/**
|
||||||
* A reference to the enclosing ledger{..}'s interpreter.
|
* A reference to the enclosing ledger{..}'s interpreter.
|
||||||
|
@ -15,6 +15,7 @@ import org.mockito.internal.stubbing.answers.ThrowsException
|
|||||||
import java.lang.reflect.Modifier
|
import java.lang.reflect.Modifier
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
inline fun <reified T : Any> T.kryoSpecific(reason: String, function: () -> Unit) = if (!AMQP_ENABLED) {
|
inline fun <reified T : Any> T.kryoSpecific(reason: String, function: () -> Unit) = if (!AMQP_ENABLED) {
|
||||||
@ -64,8 +65,8 @@ fun configureTestSSL(legalName: CordaX500Name): SSLConfiguration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val defaultRootCaName = CordaX500Name("Corda Root CA", "R3 Ltd", "London", "GB")
|
private val defaultRootCaName = X500Principal("CN=Corda Root CA,O=R3 Ltd,L=London,C=GB")
|
||||||
private val defaultIntermediateCaName = CordaX500Name("Corda Intermediate CA", "R3 Ltd", "London", "GB")
|
private val defaultIntermediateCaName = X500Principal("CN=Corda Intermediate CA,O=R3 Ltd,L=London,C=GB")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a pair of [CertificateAndKeyPair]s, the first being the root CA and the second the intermediate CA.
|
* Returns a pair of [CertificateAndKeyPair]s, the first being the root CA and the second the intermediate CA.
|
||||||
@ -73,8 +74,8 @@ private val defaultIntermediateCaName = CordaX500Name("Corda Intermediate CA", "
|
|||||||
* @param intermediateCaName The subject name for the intermediate CA cert.
|
* @param intermediateCaName The subject name for the intermediate CA cert.
|
||||||
*/
|
*/
|
||||||
fun createDevIntermediateCaCertPath(
|
fun createDevIntermediateCaCertPath(
|
||||||
rootCaName: CordaX500Name = defaultRootCaName,
|
rootCaName: X500Principal = defaultRootCaName,
|
||||||
intermediateCaName: CordaX500Name = defaultIntermediateCaName
|
intermediateCaName: X500Principal = defaultIntermediateCaName
|
||||||
): Pair<CertificateAndKeyPair, CertificateAndKeyPair> {
|
): Pair<CertificateAndKeyPair, CertificateAndKeyPair> {
|
||||||
val rootKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val rootKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val rootCert = X509Utilities.createSelfSignedCACertificate(rootCaName, rootKeyPair)
|
val rootCert = X509Utilities.createSelfSignedCACertificate(rootCaName, rootKeyPair)
|
||||||
@ -87,7 +88,10 @@ fun createDevIntermediateCaCertPath(
|
|||||||
intermediateCaName,
|
intermediateCaName,
|
||||||
intermediateCaKeyPair.public)
|
intermediateCaKeyPair.public)
|
||||||
|
|
||||||
return Pair(CertificateAndKeyPair(rootCert, rootKeyPair), CertificateAndKeyPair(intermediateCaCert, intermediateCaKeyPair))
|
return Pair(
|
||||||
|
CertificateAndKeyPair(rootCert, rootKeyPair),
|
||||||
|
CertificateAndKeyPair(intermediateCaCert, intermediateCaKeyPair)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -97,8 +101,8 @@ fun createDevIntermediateCaCertPath(
|
|||||||
*/
|
*/
|
||||||
fun createDevNodeCaCertPath(
|
fun createDevNodeCaCertPath(
|
||||||
legalName: CordaX500Name,
|
legalName: CordaX500Name,
|
||||||
rootCaName: CordaX500Name = defaultRootCaName,
|
rootCaName: X500Principal = defaultRootCaName,
|
||||||
intermediateCaName: CordaX500Name = defaultIntermediateCaName
|
intermediateCaName: X500Principal = defaultIntermediateCaName
|
||||||
): Triple<CertificateAndKeyPair, CertificateAndKeyPair, CertificateAndKeyPair> {
|
): Triple<CertificateAndKeyPair, CertificateAndKeyPair, CertificateAndKeyPair> {
|
||||||
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath(rootCaName, intermediateCaName)
|
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath(rootCaName, intermediateCaName)
|
||||||
val nodeCa = createDevNodeCa(intermediateCa, legalName)
|
val nodeCa = createDevNodeCa(intermediateCa, legalName)
|
||||||
|
Loading…
Reference in New Issue
Block a user