mirror of
https://github.com/corda/corda.git
synced 2024-12-27 08:22:35 +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)
|
||||
@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 <init>(net.corda.core.utilities.NetworkHostAndPort)
|
||||
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.Party
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
import net.corda.core.node.services.IdentityService
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
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.utilities.ProgressTracker
|
||||
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.SignatureException
|
||||
import java.security.cert.CertPath
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
|
@ -80,8 +80,10 @@ data class CordaX500Name(val commonName: String?,
|
||||
const val MAX_LENGTH_STATE = 64
|
||||
const val MAX_LENGTH_ORGANISATION_UNIT = 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 countryCodes: Set<String> = ImmutableSet.copyOf(Locale.getISOCountries() + unspecifiedCountry)
|
||||
|
||||
@JvmStatic
|
||||
fun build(principal: X500Principal): CordaX500Name {
|
||||
val x500Name = X500Name.getInstance(principal.encoded)
|
||||
@ -115,20 +117,12 @@ data class CordaX500Name(val commonName: String?,
|
||||
}
|
||||
|
||||
@Transient
|
||||
private var x500Cache: X500Name? = null
|
||||
private var _x500Principal: X500Principal? = null
|
||||
|
||||
val x500Principal: X500Principal
|
||||
get() {
|
||||
if (x500Cache == null) {
|
||||
x500Cache = this.x500Name
|
||||
}
|
||||
return X500Principal(x500Cache!!.encoded)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
if (x500Cache == null) {
|
||||
x500Cache = this.x500Name
|
||||
}
|
||||
return x500Cache.toString()
|
||||
/** Return the [X500Principal] equivalent of this name. */
|
||||
val x500Principal: X500Principal get() {
|
||||
return _x500Principal ?: X500Principal(this.x500Name.encoded).also { _x500Principal = it }
|
||||
}
|
||||
|
||||
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.transactions.TransactionBuilder
|
||||
import net.corda.core.transactions.WireTransaction
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import org.bouncycastle.asn1.x500.X500NameBuilder
|
||||
import org.bouncycastle.asn1.x500.style.BCStyle
|
||||
import org.slf4j.Logger
|
||||
import rx.Observable
|
||||
import rx.Observer
|
||||
@ -26,8 +27,6 @@ import java.nio.charset.Charset
|
||||
import java.nio.charset.StandardCharsets.UTF_8
|
||||
import java.nio.file.*
|
||||
import java.nio.file.attribute.FileAttribute
|
||||
import java.security.cert.Certificate
|
||||
import java.security.cert.X509Certificate
|
||||
import java.time.Duration
|
||||
import java.time.temporal.Temporal
|
||||
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]. */
|
||||
fun ByteArrayOutputStream.toInputStreamAndHash(): InputStreamAndHash {
|
||||
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")
|
||||
@VisibleForTesting
|
||||
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
|
||||
|
||||
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.div
|
||||
import net.corda.core.serialization.serialize
|
||||
@ -15,6 +13,7 @@ import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import java.security.PublicKey
|
||||
import javax.security.auth.x500.X500Principal
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertFalse
|
||||
@ -334,7 +333,7 @@ class CompositeKeyTests {
|
||||
|
||||
// Create self sign CA.
|
||||
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)
|
||||
|
||||
// Sign the composite key with the self sign CA.
|
||||
@ -343,7 +342,7 @@ class CompositeKeyTests {
|
||||
// Store certificate to keystore.
|
||||
val keystorePath = tempFolder.root.toPath() / "keystore.jks"
|
||||
val keystore = loadOrCreateKeyStore(keystorePath, "password")
|
||||
keystore.setCertificateEntry("CompositeKey", compositeKeyCert.cert)
|
||||
keystore.setCertificateEntry("CompositeKey", compositeKeyCert)
|
||||
keystore.save(keystorePath, "password")
|
||||
|
||||
// Load keystore from disk.
|
||||
|
@ -1,7 +1,6 @@
|
||||
package net.corda.core.crypto
|
||||
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.nodeapi.internal.crypto.*
|
||||
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
@ -14,6 +13,7 @@ import java.security.KeyStore
|
||||
import java.security.cert.CertPathValidator
|
||||
import java.security.cert.CertPathValidatorException
|
||||
import java.security.cert.PKIXParameters
|
||||
import javax.security.auth.x500.X500Principal
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@ -26,17 +26,22 @@ class X509NameConstraintsTest {
|
||||
CertificateType.NODE_CA,
|
||||
intermediateCa.certificate,
|
||||
intermediateCa.keyPair,
|
||||
CordaX500Name("Corda Client CA", "R3 Ltd", "London", "GB"),
|
||||
CordaX500Name("Corda Client CA", "R3 Ltd", "London", "GB").x500Principal,
|
||||
nodeCaKeyPair.public,
|
||||
nameConstraints = nameConstraints)
|
||||
|
||||
val keyPass = "password"
|
||||
val trustStore = KeyStore.getInstance(KEYSTORE_TYPE)
|
||||
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 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)
|
||||
keyStore.load(null, keyPass.toCharArray())
|
||||
|
@ -7,30 +7,36 @@ import kotlin.test.assertNull
|
||||
|
||||
class CordaX500NameTest {
|
||||
@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")
|
||||
assertEquals("Service Name", name.commonName)
|
||||
assertEquals("Org Unit", name.organisationUnit)
|
||||
assertEquals("Bank A", name.organisation)
|
||||
assertEquals("New York", name.locality)
|
||||
assertEquals(CordaX500Name.parse(name.toString()), name)
|
||||
assertEquals(CordaX500Name.build(name.x500Principal), name)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parse service name`() {
|
||||
fun `service name`() {
|
||||
val name = CordaX500Name.parse("O=Bank A, L=New York, C=US, CN=Service Name")
|
||||
assertEquals("Service Name", name.commonName)
|
||||
assertNull(name.organisationUnit)
|
||||
assertEquals("Bank A", name.organisation)
|
||||
assertEquals("New York", name.locality)
|
||||
assertEquals(CordaX500Name.parse(name.toString()), name)
|
||||
assertEquals(CordaX500Name.build(name.x500Principal), name)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parse legal entity name`() {
|
||||
fun `legal entity name`() {
|
||||
val name = CordaX500Name.parse("O=Bank A, L=New York, C=US")
|
||||
assertNull(name.commonName)
|
||||
assertNull(name.organisationUnit)
|
||||
assertEquals("Bank A", name.organisation)
|
||||
assertEquals("New York", name.locality)
|
||||
assertEquals(CordaX500Name.parse(name.toString()), name)
|
||||
assertEquals(CordaX500Name.build(name.x500Principal), name)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1,14 +1,13 @@
|
||||
package net.corda.core.identity
|
||||
|
||||
import net.corda.core.crypto.entropyToKeyPair
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.read
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.nodeapi.internal.crypto.KEYSTORE_TYPE
|
||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||
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.getTestPartyAndCertificate
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
@ -25,8 +24,8 @@ class PartyAndCertificateTest {
|
||||
val testSerialization = SerializationEnvironmentRule()
|
||||
|
||||
@Test
|
||||
fun `should reject a path with no roles`() {
|
||||
val path = X509CertificateFactory().generateCertPath(DEV_CA.certificate.cert)
|
||||
fun `reject a path with no roles`() {
|
||||
val path = X509CertificateFactory().generateCertPath(DEV_ROOT_CA.certificate)
|
||||
assertFailsWith<IllegalArgumentException> { PartyAndCertificate(path) }
|
||||
}
|
||||
|
||||
|
@ -103,7 +103,7 @@ Go to the terminal window displaying the CRaSH shell of PartyA. Typing ``help``
|
||||
commands.
|
||||
|
||||
.. 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:
|
||||
|
||||
|
@ -192,9 +192,6 @@ The following 3rd party types are supported.
|
||||
|
||||
org.apache.activemq.artemis.api.core.SimpleString
|
||||
|
||||
org.bouncycastle.asn1.x500.X500Name
|
||||
org.bouncycastle.cert.X509CertificateHolder
|
||||
|
||||
Corda Types
|
||||
```````````
|
||||
|
||||
|
@ -5,10 +5,8 @@ import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.generateKeyPair
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
import net.corda.core.utilities.trace
|
||||
import net.corda.nodeapi.internal.config.NodeSSLConfiguration
|
||||
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
|
||||
val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
|
||||
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.createDevKeyStores(rootCert.toX509CertHolder(), intermediateCa, legalName)
|
||||
nodeSslConfig.createDevKeyStores(rootCert, intermediateCa, legalName)
|
||||
|
||||
val keyStoreWrapper = KeyStoreWrapper(nodeSslConfig.nodeKeystore, nodeSslConfig.keyStorePassword)
|
||||
val identity = keyStoreWrapper.storeLegalIdentity(legalName, "$NODE_IDENTITY_ALIAS_PREFIX-private-key", Crypto.generateKeyPair())
|
||||
@ -62,16 +60,21 @@ object DevIdentityGenerator {
|
||||
|
||||
keyPairs.zip(dirs) { keyPair, nodeDir ->
|
||||
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 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(
|
||||
"$DISTRIBUTED_NOTARY_ALIAS_PREFIX-private-key",
|
||||
keyPair.private,
|
||||
"cordacadevkeypass".toCharArray(),
|
||||
arrayOf(serviceKeyCert.cert, intermediateCa.certificate.cert, rootCert))
|
||||
arrayOf(serviceKeyCert, intermediateCa.certificate, rootCert))
|
||||
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.GeneralSubtree
|
||||
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
|
||||
* 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)
|
||||
|
||||
loadOrCreateKeyStore(nodeKeystore, keyStorePassword).apply {
|
||||
@ -27,7 +27,7 @@ fun SSLConfiguration.createDevKeyStores(rootCert: X509CertificateHolder, interme
|
||||
}
|
||||
|
||||
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 {
|
||||
addOrReplaceKey(
|
||||
@ -50,7 +50,7 @@ fun createDevNodeCa(intermediateCa: CertificateAndKeyPair, legalName: CordaX500N
|
||||
CertificateType.NODE_CA,
|
||||
intermediateCa.certificate,
|
||||
intermediateCa.keyPair,
|
||||
legalName,
|
||||
legalName.x500Principal,
|
||||
keyPair.public,
|
||||
nameConstraints = nameConstraints)
|
||||
return CertificateAndKeyPair(cert, keyPair)
|
||||
|
@ -3,8 +3,9 @@
|
||||
package net.corda.nodeapi.internal.crypto
|
||||
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.internal.*
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import net.corda.core.internal.exists
|
||||
import net.corda.core.internal.read
|
||||
import net.corda.core.internal.write
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
@ -67,18 +68,6 @@ fun loadKeyStore(input: InputStream, storePassword: String): 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.
|
||||
* @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).
|
||||
*/
|
||||
fun KeyStore.getCertificateAndKeyPair(alias: String, keyPassword: String): CertificateAndKeyPair {
|
||||
val cert = getX509Certificate(alias).toX509CertHolder()
|
||||
val publicKey = Crypto.toSupportedPublicKey(cert.subjectPublicKeyInfo)
|
||||
return CertificateAndKeyPair(cert, KeyPair(publicKey, getSupportedKey(alias, keyPassword)))
|
||||
val certificate = getX509Certificate(alias)
|
||||
val publicKey = Crypto.toSupportedPublicKey(certificate.publicKey)
|
||||
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.PartyAndCertificate
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.read
|
||||
import java.nio.file.Path
|
||||
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 {
|
||||
val nodeCaCertChain = keyStore.getCertificateChain(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 identityCertPath = X509CertificateFactory().generateCertPath(identityCert.cert, *nodeCaCertChain)
|
||||
val identityCert = X509Utilities.createCertificate(
|
||||
CertificateType.LEGAL_IDENTITY,
|
||||
nodeCa.certificate,
|
||||
nodeCa.keyPair,
|
||||
legalName.x500Principal,
|
||||
keyPair.public)
|
||||
val identityCertPath = X509CertificateFactory().generateCertPath(identityCert, *nodeCaCertChain)
|
||||
// Assume key password = store password.
|
||||
keyStore.addOrReplaceKey(alias, keyPair.private, storePassword.toCharArray(), identityCertPath.certificates.toTypedArray())
|
||||
keyStore.save(storePath, storePassword)
|
||||
|
@ -4,12 +4,14 @@ import net.corda.core.CordaOID
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.SignatureScheme
|
||||
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.internal.*
|
||||
import net.corda.core.utilities.days
|
||||
import net.corda.core.utilities.millis
|
||||
import org.bouncycastle.asn1.*
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import org.bouncycastle.asn1.x500.style.BCStyle
|
||||
import org.bouncycastle.asn1.x509.*
|
||||
import org.bouncycastle.asn1.x509.Extension
|
||||
@ -34,6 +36,7 @@ import java.time.Duration
|
||||
import java.time.Instant
|
||||
import java.time.temporal.ChronoUnit
|
||||
import java.util.*
|
||||
import javax.security.auth.x500.X500Principal
|
||||
|
||||
object X509Utilities {
|
||||
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 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 notBefore = max(startOfDayUTC - before, parent?.notBefore)
|
||||
val notAfter = min(startOfDayUTC + after, parent?.notAfter)
|
||||
@ -85,60 +88,11 @@ object X509Utilities {
|
||||
* Create a de novo root self-signed X509 v3 CA cert.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun createSelfSignedCACertificate(subject: CordaX500Name,
|
||||
fun createSelfSignedCACertificate(subject: X500Principal,
|
||||
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)
|
||||
return createCertificate(CertificateType.ROOT_CA, subject.x500Name, keyPair, subject.x500Name, 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)
|
||||
return createCertificate(CertificateType.ROOT_CA, subject, keyPair, subject, keyPair.public, window)
|
||||
}
|
||||
|
||||
@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.
|
||||
* @param x509Certificate certificate to save.
|
||||
* @param certificate certificate to save.
|
||||
* @param file Target file.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun saveCertificateAsPEMFile(x509Certificate: X509Certificate, file: Path) {
|
||||
fun saveCertificateAsPEMFile(certificate: X509Certificate, file: Path) {
|
||||
JcaPEMWriter(file.writer()).use {
|
||||
it.writeObject(x509Certificate)
|
||||
it.writeObject(certificate)
|
||||
}
|
||||
}
|
||||
|
||||
@ -172,9 +126,10 @@ object X509Utilities {
|
||||
fun loadCertificateFromPEMFile(file: Path): X509Certificate {
|
||||
return file.reader().use {
|
||||
val pemObject = PemReader(it).readPemObject()
|
||||
val certHolder = X509CertificateHolder(pemObject.content)
|
||||
certHolder.isValidOn(Date())
|
||||
certHolder.cert
|
||||
X509CertificateHolder(pemObject.content).run {
|
||||
isValidOn(Date())
|
||||
toJca()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -187,38 +142,18 @@ object X509Utilities {
|
||||
* @param validityWindow the time period the certificate is valid for.
|
||||
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
|
||||
*/
|
||||
fun createCertificate(certificateType: CertificateType,
|
||||
issuer: CordaX500Name,
|
||||
subject: CordaX500Name,
|
||||
subjectPublicKey: PublicKey,
|
||||
validityWindow: Pair<Date, Date>,
|
||||
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 {
|
||||
|
||||
fun createPartialCertificate(certificateType: CertificateType,
|
||||
issuer: X500Principal,
|
||||
subject: X500Principal,
|
||||
subjectPublicKey: PublicKey,
|
||||
validityWindow: Pair<Date, Date>,
|
||||
nameConstraints: NameConstraints? = null): X509v3CertificateBuilder {
|
||||
val serial = BigInteger.valueOf(random63BitValue())
|
||||
val keyPurposes = DERSequence(ASN1EncodableVector().apply { certificateType.purposes.forEach { add(it) } })
|
||||
val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(subjectPublicKey.encoded))
|
||||
val role = certificateType.role
|
||||
|
||||
val builder = JcaX509v3CertificateBuilder(issuer, serial, validityWindow.first, validityWindow.second,
|
||||
subject, subjectPublicKey)
|
||||
val builder = JcaX509v3CertificateBuilder(issuer, serial, validityWindow.first, validityWindow.second, subject, subjectPublicKey)
|
||||
.addExtension(Extension.subjectKeyIdentifier, false, BcX509ExtensionUtils().createSubjectKeyIdentifier(subjectPublicKeyInfo))
|
||||
.addExtension(Extension.basicConstraints, certificateType.isCA, BasicConstraints(certificateType.isCA))
|
||||
.addExtension(Extension.keyUsage, false, certificateType.keyUsage)
|
||||
@ -234,6 +169,37 @@ object X509Utilities {
|
||||
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.
|
||||
*
|
||||
@ -245,15 +211,16 @@ object X509Utilities {
|
||||
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
|
||||
*/
|
||||
fun createCertificate(certificateType: CertificateType,
|
||||
issuer: X500Name,
|
||||
issuer: X500Principal,
|
||||
issuerSigner: ContentSigner,
|
||||
subject: CordaX500Name,
|
||||
subject: X500Principal,
|
||||
subjectPublicKey: PublicKey,
|
||||
validityWindow: Pair<Date, Date>,
|
||||
nameConstraints: NameConstraints? = null): X509CertificateHolder {
|
||||
val builder = createCertificate(certificateType, issuer, subject.x500Name, subjectPublicKey, validityWindow, nameConstraints)
|
||||
return builder.build(issuerSigner).apply {
|
||||
nameConstraints: NameConstraints? = null): X509Certificate {
|
||||
val builder = createPartialCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints)
|
||||
return builder.build(issuerSigner).run {
|
||||
require(isValidOn(Date()))
|
||||
toJca()
|
||||
}
|
||||
}
|
||||
|
||||
@ -268,39 +235,47 @@ object X509Utilities {
|
||||
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
|
||||
*/
|
||||
fun createCertificate(certificateType: CertificateType,
|
||||
issuer: X500Name,
|
||||
issuer: X500Principal,
|
||||
issuerKeyPair: KeyPair,
|
||||
subject: X500Name,
|
||||
subject: X500Principal,
|
||||
subjectPublicKey: PublicKey,
|
||||
validityWindow: Pair<Date, Date>,
|
||||
nameConstraints: NameConstraints? = null): X509CertificateHolder {
|
||||
nameConstraints: NameConstraints? = null): X509Certificate {
|
||||
val signatureScheme = Crypto.findSignatureScheme(issuerKeyPair.private)
|
||||
val provider = Crypto.findProvider(signatureScheme.providerName)
|
||||
val builder = createCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints)
|
||||
|
||||
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(isSignatureValid(JcaContentVerifierProviderBuilder().build(issuerKeyPair.public)))
|
||||
toJca()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create certificate signing request using provided information.
|
||||
*/
|
||||
private fun createCertificateSigningRequest(subject: CordaX500Name,
|
||||
private fun createCertificateSigningRequest(subject: X500Principal,
|
||||
email: String,
|
||||
keyPair: KeyPair,
|
||||
signatureScheme: SignatureScheme): PKCS10CertificationRequest {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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
|
||||
|
||||
import net.corda.core.serialization.ClassWhitelist
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import com.google.common.primitives.Primitives
|
||||
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 org.apache.qpid.proton.codec.Data
|
||||
import java.beans.IndexedPropertyDescriptor
|
||||
@ -81,10 +81,17 @@ private fun <T : Any> propertiesForSerializationFromConstructor(kotlinConstructo
|
||||
val rc: MutableList<PropertySerializer> = ArrayList(kotlinConstructor.parameters.size)
|
||||
for (param in kotlinConstructor.parameters) {
|
||||
val name = param.name ?: throw NotSerializableException("Constructor parameter of $clazz has no name.")
|
||||
|
||||
val matchingProperty = properties[name] ?:
|
||||
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")
|
||||
try {
|
||||
clazz.getDeclaredField(param.name)
|
||||
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.
|
||||
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." +
|
||||
|
@ -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.generateKeyPair
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.cert
|
||||
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.deserialize
|
||||
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.internal.createDevIntermediateCaCertPath
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import org.bouncycastle.asn1.x509.BasicConstraints
|
||||
import org.bouncycastle.asn1.x509.Extension
|
||||
import org.bouncycastle.asn1.x509.KeyUsage
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
@ -42,18 +36,16 @@ import java.security.SecureRandom
|
||||
import java.security.cert.CertPath
|
||||
import java.security.cert.X509Certificate
|
||||
import java.util.*
|
||||
import java.util.stream.Stream
|
||||
import javax.net.ssl.*
|
||||
import javax.security.auth.x500.X500Principal
|
||||
import kotlin.concurrent.thread
|
||||
import kotlin.test.*
|
||||
|
||||
class X509UtilitiesTest {
|
||||
private companion object {
|
||||
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 BOB get() = bob.party
|
||||
val BOB_PUBKEY get() = bob.publicKey
|
||||
val CIPHER_SUITES = arrayOf(
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
@ -63,27 +55,30 @@ class X509UtilitiesTest {
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
val tempFolder: TemporaryFolder = TemporaryFolder()
|
||||
val tempFolder = TemporaryFolder()
|
||||
|
||||
@Test
|
||||
fun `create valid self-signed CA certificate`() {
|
||||
val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val caCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Test Cert", organisation = "R3 Ltd", locality = "London", country = "GB"), caKey)
|
||||
assertEquals(X500Name("CN=Test Cert,O=R3 Ltd,L=London,C=GB"), caCert.subject) // using our subject common name
|
||||
assertEquals(caCert.issuer, caCert.subject) //self-signed
|
||||
caCert.isValidOn(Date()) // throws on verification problems
|
||||
caCert.isSignatureValid(JcaContentVerifierProviderBuilder().build(caKey.public)) // throws on verification problems
|
||||
val basicConstraints = BasicConstraints.getInstance(caCert.getExtension(Extension.basicConstraints).parsedValue)
|
||||
val keyUsage = KeyUsage.getInstance(caCert.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
|
||||
val subject = X500Principal("CN=Test Cert,O=R3 Ltd,L=London,C=GB")
|
||||
val caCert = X509Utilities.createSelfSignedCACertificate(subject, caKey)
|
||||
assertEquals(subject, caCert.subjectX500Principal) // using our subject common name
|
||||
assertEquals(caCert.issuerX500Principal, caCert.subjectX500Principal) //self-signed
|
||||
caCert.checkValidity(Date()) // throws on verification problems
|
||||
caCert.verify(caKey.public) // throws on verification problems
|
||||
caCert.toBc().run {
|
||||
val basicConstraints = BasicConstraints.getInstance(getExtension(Extension.basicConstraints).parsedValue)
|
||||
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
|
||||
fun `load and save a PEM file certificate`() {
|
||||
val tmpCertificateFile = tempFile("cacert.pem")
|
||||
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)
|
||||
val readCertificate = X509Utilities.loadCertificateFromPEMFile(tmpCertificateFile)
|
||||
assertEquals(caCert, readCertificate)
|
||||
@ -92,18 +87,20 @@ class X509UtilitiesTest {
|
||||
@Test
|
||||
fun `create valid server certificate chain`() {
|
||||
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 subject = CordaX500Name(commonName = "Server Cert", organisation = "R3 Ltd", locality = "London", country = "GB")
|
||||
val caCert = X509Utilities.createSelfSignedCACertificate(X500Principal("CN=Test CA Cert,O=R3 Ltd,L=London,C=GB"), caKey)
|
||||
val subject = X500Principal("CN=Server Cert,O=R3 Ltd,L=London,C=GB")
|
||||
val keyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
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(caCert.issuer, serverCert.issuer) // Issued by our CA cert
|
||||
serverCert.isValidOn(Date()) // throws on verification problems
|
||||
serverCert.isSignatureValid(JcaContentVerifierProviderBuilder().build(caKey.public)) // throws on verification problems
|
||||
val basicConstraints = BasicConstraints.getInstance(serverCert.getExtension(Extension.basicConstraints).parsedValue)
|
||||
val keyUsage = KeyUsage.getInstance(serverCert.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) // Non-CA certificate
|
||||
assertEquals(subject, serverCert.subjectX500Principal) // using our subject common name
|
||||
assertEquals(caCert.issuerX500Principal, serverCert.issuerX500Principal) // Issued by our CA cert
|
||||
serverCert.checkValidity(Date()) // throws on verification problems
|
||||
serverCert.verify(caKey.public) // throws on verification problems
|
||||
serverCert.toBc().run {
|
||||
val basicConstraints = BasicConstraints.getInstance(getExtension(Extension.basicConstraints).parsedValue)
|
||||
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) // Non-CA certificate
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -111,15 +108,14 @@ class X509UtilitiesTest {
|
||||
val tmpKeyStore = tempFile("keystore.jks")
|
||||
|
||||
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)
|
||||
|
||||
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.
|
||||
val keyStore = loadOrCreateKeyStore(tmpKeyStore, "keystorepass")
|
||||
keyStore.setKeyEntry("Key", keyPair.private, "password".toCharArray(),
|
||||
Stream.of(selfSignCert).map { it.cert }.toTypedArray())
|
||||
keyStore.setKeyEntry("Key", keyPair.private, "password".toCharArray(), arrayOf(selfSignCert))
|
||||
keyStore.save(tmpKeyStore, "keystorepass")
|
||||
|
||||
// Load the keystore from file and make sure keys are intact.
|
||||
@ -137,15 +133,14 @@ class X509UtilitiesTest {
|
||||
fun `signing EdDSA key with EcDSA certificate`() {
|
||||
val tmpKeyStore = tempFile("keystore.jks")
|
||||
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 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.
|
||||
val keyStore = loadOrCreateKeyStore(tmpKeyStore, "keystorepass")
|
||||
keyStore.setKeyEntry("Key", edDSAKeypair.private, "password".toCharArray(),
|
||||
Stream.of(ecDSACert, edDSACert).map { it.cert }.toTypedArray())
|
||||
keyStore.setKeyEntry("Key", edDSAKeypair.private, "password".toCharArray(), arrayOf(ecDSACert, edDSACert))
|
||||
keyStore.save(tmpKeyStore, "keystorepass")
|
||||
|
||||
// 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 (serverCert, serverKeyPair) = serverKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, sslConfig.keyStorePassword)
|
||||
|
||||
serverCert.cert.checkValidity()
|
||||
serverCert.cert.verify(intermediateCa.certificate.cert.publicKey)
|
||||
assertThat(CordaX500Name.parse(serverCert.subject.toString())).isEqualTo(MEGA_CORP.name)
|
||||
serverCert.checkValidity()
|
||||
serverCert.verify(intermediateCa.certificate.publicKey)
|
||||
assertThat(CordaX500Name.build(serverCert.subjectX500Principal)).isEqualTo(MEGA_CORP.name)
|
||||
|
||||
// Load back SSL certificate
|
||||
val sslKeyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword)
|
||||
val (sslCert) = sslKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_TLS, sslConfig.keyStorePassword)
|
||||
|
||||
sslCert.cert.checkValidity()
|
||||
sslCert.cert.verify(serverCert.cert.publicKey)
|
||||
assertThat(CordaX500Name.parse(sslCert.subject.toString())).isEqualTo(MEGA_CORP.name)
|
||||
sslCert.checkValidity()
|
||||
sslCert.verify(serverCert.publicKey)
|
||||
assertThat(CordaX500Name.build(sslCert.subjectX500Principal)).isEqualTo(MEGA_CORP.name)
|
||||
|
||||
// Now sign something with private key and verify against certificate public key
|
||||
val testData = "123456".toByteArray()
|
||||
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, publicKey, signature, testData) }
|
||||
assertTrue { Crypto.isValid(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME, serverCert.publicKey, signature, testData) }
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -210,7 +204,7 @@ class X509UtilitiesTest {
|
||||
|
||||
// Generate server cert and private key and populate another keystore suitable for SSL
|
||||
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 trustStore = loadKeyStore(sslConfig.trustStoreFile, sslConfig.trustStorePassword)
|
||||
@ -303,10 +297,10 @@ class X509UtilitiesTest {
|
||||
@Test
|
||||
fun `get correct private key type from Keystore`() {
|
||||
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 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 keyFromKeystoreCasted = keyStore.getSupportedKey("Key", "keypassword")
|
||||
@ -316,7 +310,7 @@ class X509UtilitiesTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `serialize - deserialize X509CertififcateHolder`() {
|
||||
fun `serialize - deserialize X509Certififcate`() {
|
||||
val factory = SerializationFactoryImpl().apply { registerScheme(KryoServerSerializationScheme()) }
|
||||
val context = SerializationContextImpl(KryoHeaderV0_1,
|
||||
javaClass.classLoader,
|
||||
@ -324,9 +318,9 @@ class X509UtilitiesTest {
|
||||
emptyMap(),
|
||||
true,
|
||||
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 actual: X509CertificateHolder = serialized.deserialize(factory, context)
|
||||
val actual = serialized.deserialize<X509Certificate>(factory, context)
|
||||
assertEquals(expected, actual)
|
||||
}
|
||||
|
||||
@ -340,9 +334,9 @@ class X509UtilitiesTest {
|
||||
true,
|
||||
SerializationContext.UseCase.P2P)
|
||||
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val rootCACert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootCAKey)
|
||||
val certificate = X509Utilities.createCertificate(CertificateType.TLS, rootCACert, rootCAKey, BOB.name.x500Name, BOB_PUBKEY)
|
||||
val expected = X509CertificateFactory().generateCertPath(certificate.cert, rootCACert.cert)
|
||||
val rootCACert = X509Utilities.createSelfSignedCACertificate(ALICE_NAME.x500Principal, rootCAKey)
|
||||
val certificate = X509Utilities.createCertificate(CertificateType.TLS, rootCACert, rootCAKey, BOB_NAME.x500Principal, BOB.publicKey)
|
||||
val expected = X509CertificateFactory().generateCertPath(certificate, rootCACert)
|
||||
val serialized = expected.serialize(factory, context).bytes
|
||||
val actual: CertPath = serialized.deserialize(factory, context)
|
||||
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
|
||||
|
||||
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.utilities.getOrThrow
|
||||
import net.corda.node.services.config.configureDevKeyAndTrustStores
|
||||
@ -17,6 +15,10 @@ import org.junit.ClassRule
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.Test
|
||||
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() {
|
||||
companion object {
|
||||
@ -59,10 +61,10 @@ class NodeKeystoreCheckTest : IntegrationTest() {
|
||||
|
||||
// Self signed root
|
||||
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 badNodeCACert = X509Utilities.createCertificate(CertificateType.NODE_CA, badRoot, badRootKeyPair, ALICE_NAME, nodeCA.keyPair.public)
|
||||
keystore.setKeyEntry(X509Utilities.CORDA_CLIENT_CA, nodeCA.keyPair.private, config.keyStorePassword.toCharArray(), arrayOf(badNodeCACert.cert, badRoot.cert))
|
||||
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, badRoot))
|
||||
keystore.save(config.nodeKeystore, config.keyStorePassword)
|
||||
|
||||
assertThatThrownBy {
|
||||
|
@ -59,10 +59,10 @@ class ProtonWrapperTests {
|
||||
amqpClient.start()
|
||||
val serverConnect = serverConnected.get()
|
||||
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()
|
||||
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(),
|
||||
"p2p.inbound",
|
||||
ALICE_NAME.toString(),
|
||||
@ -102,10 +102,10 @@ class ProtonWrapperTests {
|
||||
amqpClient.start()
|
||||
val serverConn1 = serverConnected.get()
|
||||
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()
|
||||
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)
|
||||
|
||||
// Fail over
|
||||
@ -116,10 +116,10 @@ class ProtonWrapperTests {
|
||||
assertEquals(serverPort, connState2.remoteAddress.port)
|
||||
val serverConn2 = serverConnected2.get()
|
||||
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()
|
||||
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)
|
||||
|
||||
// Fail back
|
||||
@ -130,10 +130,10 @@ class ProtonWrapperTests {
|
||||
assertEquals(serverPort2, connState4.remoteAddress.port)
|
||||
val serverConn3 = serverConnected.get()
|
||||
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()
|
||||
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)
|
||||
} finally {
|
||||
amqpClient.close()
|
||||
@ -149,7 +149,7 @@ class ProtonWrapperTests {
|
||||
val clientConnected = amqpClient.onConnection.toFuture()
|
||||
amqpClient.start()
|
||||
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 sendAddress = "p2p.inbound"
|
||||
artemis.session.createQueue(sendAddress, RoutingType.MULTICAST, "queue", true)
|
||||
@ -180,13 +180,13 @@ class ProtonWrapperTests {
|
||||
amqpClient1.start()
|
||||
val connection1 = connectionEvents.next()
|
||||
assertEquals(true, connection1.connected)
|
||||
val connection1ID = CordaX500Name.parse(connection1.remoteCert!!.subject.toString())
|
||||
val connection1ID = CordaX500Name.build(connection1.remoteCert!!.subjectX500Principal)
|
||||
assertEquals("client 0", connection1ID.organisationUnit)
|
||||
val source1 = connection1.remoteAddress
|
||||
amqpClient2.start()
|
||||
val connection2 = connectionEvents.next()
|
||||
assertEquals(true, connection2.connected)
|
||||
val connection2ID = CordaX500Name.parse(connection2.remoteCert!!.subject.toString())
|
||||
val connection2ID = CordaX500Name.build(connection2.remoteCert!!.subjectX500Principal)
|
||||
assertEquals("client 1", connection2ID.organisationUnit)
|
||||
val source2 = connection2.remoteAddress
|
||||
// Stopping one shouldn't disconnect the other
|
||||
@ -207,7 +207,7 @@ class ProtonWrapperTests {
|
||||
amqpClient1.start()
|
||||
val connection5 = connectionEvents.next()
|
||||
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(true, amqpClient1.connected)
|
||||
assertEquals(false, amqpClient2.connected)
|
||||
@ -252,7 +252,8 @@ class ProtonWrapperTests {
|
||||
|
||||
val clientTruststore = loadKeyStore(clientConfig.trustStoreFile, clientConfig.trustStorePassword)
|
||||
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", artemisPort)),
|
||||
setOf(ALICE_NAME, CHARLIE_NAME),
|
||||
@ -261,7 +262,6 @@ class ProtonWrapperTests {
|
||||
clientKeystore,
|
||||
clientConfig.keyStorePassword,
|
||||
clientTruststore, true)
|
||||
return amqpClient
|
||||
}
|
||||
|
||||
private fun createSharedThreadsClient(sharedEventGroup: EventLoopGroup, id: Int): AMQPClient {
|
||||
@ -275,14 +275,14 @@ class ProtonWrapperTests {
|
||||
|
||||
val clientTruststore = loadKeyStore(clientConfig.trustStoreFile, clientConfig.trustStorePassword)
|
||||
val clientKeystore = loadKeyStore(clientConfig.sslKeystore, clientConfig.keyStorePassword)
|
||||
val amqpClient = AMQPClient(listOf(NetworkHostAndPort("localhost", serverPort)),
|
||||
return AMQPClient(
|
||||
listOf(NetworkHostAndPort("localhost", serverPort)),
|
||||
setOf(ALICE_NAME),
|
||||
PEER_USER,
|
||||
PEER_USER,
|
||||
clientKeystore,
|
||||
clientConfig.keyStorePassword,
|
||||
clientTruststore, true, sharedEventGroup)
|
||||
return amqpClient
|
||||
}
|
||||
|
||||
private fun createServer(port: Int, name: CordaX500Name = ALICE_NAME): AMQPServer {
|
||||
@ -296,14 +296,13 @@ class ProtonWrapperTests {
|
||||
|
||||
val serverTruststore = loadKeyStore(serverConfig.trustStoreFile, serverConfig.trustStorePassword)
|
||||
val serverKeystore = loadKeyStore(serverConfig.sslKeystore, serverConfig.keyStorePassword)
|
||||
val amqpServer = AMQPServer("0.0.0.0",
|
||||
return AMQPServer(
|
||||
"0.0.0.0",
|
||||
port,
|
||||
PEER_USER,
|
||||
PEER_USER,
|
||||
serverKeystore,
|
||||
serverConfig.keyStorePassword,
|
||||
serverTruststore)
|
||||
return amqpServer
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,7 @@ package net.corda.node.utilities.registration
|
||||
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.concurrent.transpose
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
import net.corda.core.messaging.startFlow
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
@ -38,8 +36,10 @@ import java.security.KeyPair
|
||||
import java.security.cert.CertPath
|
||||
import java.security.cert.CertPathValidatorException
|
||||
import java.security.cert.Certificate
|
||||
import java.security.cert.X509Certificate
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipOutputStream
|
||||
import javax.security.auth.x500.X500Principal
|
||||
import javax.ws.rs.*
|
||||
import javax.ws.rs.core.MediaType
|
||||
import javax.ws.rs.core.Response
|
||||
@ -59,14 +59,13 @@ class NodeRegistrationTest : IntegrationTest() {
|
||||
val testSerialization = SerializationEnvironmentRule(true)
|
||||
|
||||
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 serverHostAndPort: NetworkHostAndPort
|
||||
|
||||
@Before
|
||||
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()
|
||||
}
|
||||
|
||||
@ -80,7 +79,7 @@ class NodeRegistrationTest : IntegrationTest() {
|
||||
val compatibilityZone = CompatibilityZoneParams(
|
||||
URL("http://$serverHostAndPort"),
|
||||
publishNotaries = { server.networkParameters = testNetworkParameters(it) },
|
||||
rootCert = ROOT_CA.certificate.cert)
|
||||
rootCert = DEV_ROOT_CA.certificate)
|
||||
internalDriver(
|
||||
portAllocation = portAllocation,
|
||||
compatibilityZone = compatibilityZone,
|
||||
@ -116,12 +115,12 @@ class NodeRegistrationTest : IntegrationTest() {
|
||||
@Test
|
||||
fun `node registration wrong root cert`() {
|
||||
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))
|
||||
val compatibilityZone = CompatibilityZoneParams(
|
||||
URL("http://$serverHostAndPort"),
|
||||
publishNotaries = { server.networkParameters = testNetworkParameters(it) },
|
||||
rootCert = someRootCert.cert)
|
||||
rootCert = someRootCert)
|
||||
internalDriver(
|
||||
portAllocation = portAllocation,
|
||||
compatibilityZone = compatibilityZone,
|
||||
@ -149,7 +148,7 @@ class RegistrationHandler(private val rootCertAndKeyPair: CertificateAndKeyPair)
|
||||
val (certPath, name) = createSignedClientCertificate(
|
||||
certificationRequest,
|
||||
rootCertAndKeyPair.keyPair,
|
||||
arrayOf(rootCertAndKeyPair.certificate.cert))
|
||||
arrayOf(rootCertAndKeyPair.certificate))
|
||||
require(!name.organisation.contains("\\s".toRegex())) { "Whitespace in the organisation name not supported" }
|
||||
certPaths[name.organisation] = certPath
|
||||
return Response.ok(name.organisation).build()
|
||||
@ -183,12 +182,12 @@ class RegistrationHandler(private val rootCertAndKeyPair: CertificateAndKeyPair)
|
||||
val name = CordaX500Name.parse(request.subject.toString())
|
||||
val nodeCaCert = X509Utilities.createCertificate(
|
||||
CertificateType.NODE_CA,
|
||||
caCertPath.first().toX509CertHolder(),
|
||||
caCertPath[0] as X509Certificate ,
|
||||
caKeyPair,
|
||||
name,
|
||||
name.x500Principal,
|
||||
request.publicKey,
|
||||
nameConstraints = null)
|
||||
val certPath = X509CertificateFactory().generateCertPath(nodeCaCert.cert, *caCertPath)
|
||||
val certPath = X509CertificateFactory().generateCertPath(nodeCaCert, *caCertPath)
|
||||
return Pair(certPath, name)
|
||||
}
|
||||
}
|
||||
|
@ -94,22 +94,34 @@ class MQSecurityAsNodeTest : MQSecurityTest() {
|
||||
javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"),
|
||||
"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 clientKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
|
||||
val clientKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
// Set name constrain to the legal name.
|
||||
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.x500Name))), arrayOf())
|
||||
val clientCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, intermediateCA.certificate,
|
||||
intermediateCA.keyPair, legalName, clientKey.public, nameConstraints = nameConstraints)
|
||||
val tlsKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val clientCACert = X509Utilities.createCertificate(
|
||||
CertificateType.INTERMEDIATE_CA,
|
||||
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.
|
||||
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 clientCAKeystore = loadOrCreateKeyStore(nodeKeystore, keyStorePassword)
|
||||
clientCAKeystore.addOrReplaceKey(
|
||||
X509Utilities.CORDA_CLIENT_CA,
|
||||
clientKey.private,
|
||||
clientKeyPair.private,
|
||||
keyPass,
|
||||
arrayOf(clientCACert, intermediateCA.certificate, rootCACert))
|
||||
clientCAKeystore.save(nodeKeystore, keyStorePassword)
|
||||
@ -117,7 +129,7 @@ class MQSecurityAsNodeTest : MQSecurityTest() {
|
||||
val tlsKeystore = loadOrCreateKeyStore(sslKeystore, keyStorePassword)
|
||||
tlsKeystore.addOrReplaceKey(
|
||||
X509Utilities.CORDA_CLIENT_TLS,
|
||||
tlsKey.private,
|
||||
tlsKeyPair.private,
|
||||
keyPass,
|
||||
arrayOf(clientTLSCert, clientCACert, intermediateCA.certificate, rootCACert))
|
||||
tlsKeystore.save(sslKeystore, keyStorePassword)
|
||||
|
@ -726,7 +726,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
||||
val caKeyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword)
|
||||
val trustRoot = trustStore.getX509Certificate(X509Utilities.CORDA_ROOT_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)
|
||||
}
|
||||
|
||||
@ -784,7 +784,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
||||
listOf(certificate) + keyStore.getCertificateChain(privateKeyAlias).drop(1)
|
||||
} else {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ import io.netty.handler.ssl.SslHandler
|
||||
import io.netty.handler.ssl.SslHandshakeCompletionEvent
|
||||
import io.netty.util.ReferenceCountUtil
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
import net.corda.core.utilities.debug
|
||||
import net.corda.node.internal.protonwrapper.engine.EventProcessor
|
||||
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.impl.ProtocolTracer
|
||||
import org.apache.qpid.proton.framing.TransportFrame
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.net.InetSocketAddress
|
||||
import java.security.cert.X509Certificate
|
||||
|
||||
/**
|
||||
* 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 log = LoggerFactory.getLogger(allowedRemoteLegalNames?.firstOrNull()?.toString() ?: "AMQPChannelHandler")
|
||||
private lateinit var remoteAddress: InetSocketAddress
|
||||
private lateinit var localCert: X509CertificateHolder
|
||||
private lateinit var remoteCert: X509CertificateHolder
|
||||
private lateinit var localCert: X509Certificate
|
||||
private lateinit var remoteCert: X509Certificate
|
||||
private var eventProcessor: EventProcessor? = null
|
||||
|
||||
override fun channelActive(ctx: ChannelHandlerContext) {
|
||||
val ch = ctx.channel()
|
||||
remoteAddress = ch.remoteAddress() 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) {
|
||||
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 transport = connection.transport as ProtonJTransport
|
||||
if (trace) {
|
||||
@ -71,7 +70,7 @@ internal class AMQPChannelHandler(private val serverMode: Boolean,
|
||||
|
||||
override fun channelInactive(ctx: ChannelHandlerContext) {
|
||||
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)))
|
||||
eventProcessor?.close()
|
||||
ctx.fireChannelInactive()
|
||||
@ -81,12 +80,12 @@ internal class AMQPChannelHandler(private val serverMode: Boolean,
|
||||
if (evt is SslHandshakeCompletionEvent) {
|
||||
if (evt.isSuccess) {
|
||||
val sslHandler = ctx.pipeline().get(SslHandler::class.java)
|
||||
localCert = sslHandler.engine().session.localCertificates.first().toX509CertHolder()
|
||||
remoteCert = sslHandler.engine().session.peerCertificates.first().toX509CertHolder()
|
||||
localCert = sslHandler.engine().session.localCertificates[0] as X509Certificate
|
||||
remoteCert = sslHandler.engine().session.peerCertificates[0] as X509Certificate
|
||||
try {
|
||||
val remoteX500Name = CordaX500Name.parse(remoteCert.subject.toString())
|
||||
val remoteX500Name = CordaX500Name.build(remoteCert.subjectX500Principal)
|
||||
require(allowedRemoteLegalNames == null || remoteX500Name in allowedRemoteLegalNames)
|
||||
log.info("handshake completed subject: ${remoteX500Name}")
|
||||
log.info("handshake completed subject: $remoteX500Name")
|
||||
} catch (ex: IllegalArgumentException) {
|
||||
log.error("Invalid certificate subject", ex)
|
||||
ctx.close()
|
||||
@ -124,7 +123,7 @@ internal class AMQPChannelHandler(private val serverMode: Boolean,
|
||||
require(inetAddress == remoteAddress) {
|
||||
"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"
|
||||
}
|
||||
log.debug { "channel write ${msg.applicationProperties["_AMQ_DUPL_ID"]}" }
|
||||
|
@ -1,6 +1,6 @@
|
||||
package net.corda.node.internal.protonwrapper.netty
|
||||
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
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.div
|
||||
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.toProperties
|
||||
import net.corda.nodeapi.internal.createDevKeyStores
|
||||
@ -73,7 +72,7 @@ fun SSLConfiguration.configureDevKeyAndTrustStores(myLegalName: CordaX500Name) {
|
||||
}
|
||||
if (!sslKeystore.exists() || !nodeKeystore.exists()) {
|
||||
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")
|
||||
createDevKeyStores(rootCert, intermediateCa, myLegalName)
|
||||
|
||||
|
@ -4,15 +4,12 @@ import net.corda.core.contracts.PartyAndReference
|
||||
import net.corda.core.crypto.toStringShort
|
||||
import net.corda.core.identity.*
|
||||
import net.corda.core.internal.CertRole
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
import net.corda.core.node.services.UnknownAnonymousPartyException
|
||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.core.utilities.trace
|
||||
import net.corda.node.services.api.IdentityServiceInternal
|
||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import java.security.InvalidAlgorithmParameterException
|
||||
import java.security.PublicKey
|
||||
import java.security.cert.*
|
||||
@ -27,7 +24,7 @@ import javax.annotation.concurrent.ThreadSafe
|
||||
// TODO There is duplicated logic between this and PersistentIdentityService
|
||||
@ThreadSafe
|
||||
class InMemoryIdentityService(identities: Array<out PartyAndCertificate>,
|
||||
trustRoot: X509CertificateHolder) : SingletonSerializeAsToken(), IdentityServiceInternal {
|
||||
override val trustRoot: X509Certificate) : SingletonSerializeAsToken(), IdentityServiceInternal {
|
||||
companion object {
|
||||
private val log = contextLogger()
|
||||
}
|
||||
@ -35,14 +32,12 @@ class InMemoryIdentityService(identities: Array<out PartyAndCertificate>,
|
||||
/**
|
||||
* Certificate store for certificate authority and intermediary certificates.
|
||||
*/
|
||||
override val caCertStore: CertStore
|
||||
override val trustRoot = trustRoot.cert
|
||||
override val trustAnchor: TrustAnchor = TrustAnchor(this.trustRoot, null)
|
||||
override val caCertStore: CertStore = CertStore.getInstance("Collection", CollectionCertStoreParameters(setOf(trustRoot)))
|
||||
override val trustAnchor: TrustAnchor = TrustAnchor(trustRoot, null)
|
||||
private val keyToParties = ConcurrentHashMap<PublicKey, PartyAndCertificate>()
|
||||
private val principalToParties = ConcurrentHashMap<CordaX500Name, PartyAndCertificate>()
|
||||
|
||||
init {
|
||||
caCertStore = CertStore.getInstance("Collection", CollectionCertStoreParameters(setOf(this.trustRoot)))
|
||||
keyToParties.putAll(identities.associateBy { it.owningKey })
|
||||
principalToParties.putAll(identities.associateBy { it.name })
|
||||
}
|
||||
@ -57,7 +52,7 @@ class InMemoryIdentityService(identities: Array<out PartyAndCertificate>,
|
||||
log.warn("Certificate path :")
|
||||
identity.certPath.certificates.reversed().forEachIndexed { index, certificate ->
|
||||
val space = (0 until index).joinToString("") { " " }
|
||||
log.warn("$space${certificate.toX509CertHolder().subject}")
|
||||
log.warn("$space${(certificate as X509Certificate).subjectX500Principal}")
|
||||
}
|
||||
throw e
|
||||
}
|
||||
|
@ -5,8 +5,6 @@ import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.toStringShort
|
||||
import net.corda.core.identity.*
|
||||
import net.corda.core.internal.CertRole
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
import net.corda.core.node.services.UnknownAnonymousPartyException
|
||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||
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.nodeapi.internal.crypto.X509CertificateFactory
|
||||
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import java.security.InvalidAlgorithmParameterException
|
||||
import java.security.PublicKey
|
||||
import java.security.cert.*
|
||||
@ -30,7 +27,6 @@ import javax.persistence.Lob
|
||||
@ThreadSafe
|
||||
class PersistentIdentityService(override val trustRoot: X509Certificate,
|
||||
vararg caCertificates: X509Certificate) : SingletonSerializeAsToken(), IdentityServiceInternal {
|
||||
constructor(trustRoot: X509CertificateHolder) : this(trustRoot.cert)
|
||||
|
||||
companion object {
|
||||
private val log = contextLogger()
|
||||
@ -121,7 +117,7 @@ class PersistentIdentityService(override val trustRoot: X509Certificate,
|
||||
log.warn(e.localizedMessage)
|
||||
log.warn("Path = ")
|
||||
identity.certPath.certificates.reversed().forEach {
|
||||
log.warn(it.toX509CertHolder().subject.toString())
|
||||
log.warn((it as X509Certificate).subjectX500Principal.toString())
|
||||
}
|
||||
throw e
|
||||
}
|
||||
|
@ -3,8 +3,6 @@ package net.corda.node.services.keys
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
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.node.services.api.IdentityServiceInternal
|
||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||
@ -36,11 +34,16 @@ fun freshCertificate(identityService: IdentityServiceInternal,
|
||||
revocationEnabled: Boolean = false): PartyAndCertificate {
|
||||
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" }
|
||||
val issuerCert = issuer.certificate.toX509CertHolder()
|
||||
val issuerCert = issuer.certificate
|
||||
val window = X509Utilities.getCertificateValidityWindow(Duration.ZERO, 3650.days, issuerCert)
|
||||
val ourCertificate = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuerCert.subject,
|
||||
issuerSigner, issuer.name, subjectPublicKey, window)
|
||||
val ourCertPath = X509CertificateFactory().generateCertPath(listOf(ourCertificate.cert) + issuer.certPath.certificates)
|
||||
val ourCertificate = X509Utilities.createCertificate(
|
||||
CertificateType.CONFIDENTIAL_LEGAL_IDENTITY,
|
||||
issuerCert.subjectX500Principal,
|
||||
issuerSigner,
|
||||
issuer.name.x500Principal,
|
||||
subjectPublicKey,
|
||||
window)
|
||||
val ourCertPath = X509CertificateFactory().generateCertPath(listOf(ourCertificate) + issuer.certPath.certificates)
|
||||
val anonymisedIdentity = PartyAndCertificate(ourCertPath)
|
||||
identityService.justVerifyAndRegisterIdentity(anonymisedIdentity)
|
||||
return anonymisedIdentity
|
||||
|
@ -22,9 +22,9 @@ import java.security.cert.X509Certificate
|
||||
* needed.
|
||||
*/
|
||||
class NetworkRegistrationHelper(private val config: NodeConfiguration, private val certService: NetworkRegistrationService) {
|
||||
companion object {
|
||||
private companion object {
|
||||
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"
|
||||
@ -62,54 +62,81 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v
|
||||
*/
|
||||
fun buildKeystore() {
|
||||
config.certificatesDirectory.createDirectories()
|
||||
val caKeyStore = loadOrCreateKeyStore(config.nodeKeystore, keystorePassword)
|
||||
if (!caKeyStore.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 {
|
||||
val nodeKeyStore = loadOrCreateKeyStore(config.nodeKeystore, keystorePassword)
|
||||
if (nodeKeyStore.containsAlias(CORDA_CLIENT_CA)) {
|
||||
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 {
|
||||
// Retrieve request id from file if exists, else post a request to server.
|
||||
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()
|
||||
JcaPEMWriter(writer).use {
|
||||
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.Party
|
||||
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.nodeapi.internal.crypto.CertificateType
|
||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||
@ -32,7 +30,7 @@ class InMemoryIdentityServiceTests {
|
||||
val BOB get() = bob.party
|
||||
val BOB_IDENTITY get() = bob.identity
|
||||
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
|
||||
@ -100,11 +98,11 @@ class InMemoryIdentityServiceTests {
|
||||
@Test
|
||||
fun `assert unknown anonymous key is unrecognised`() {
|
||||
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 service = createService()
|
||||
// TODO: Generate certificate with an EdDSA key rather than ECDSA
|
||||
val identity = Party(rootCert.cert)
|
||||
val identity = Party(rootCert)
|
||||
val txIdentity = AnonymousParty(txKey.public)
|
||||
|
||||
assertFailsWith<UnknownAnonymousPartyException> {
|
||||
@ -159,8 +157,8 @@ class InMemoryIdentityServiceTests {
|
||||
}
|
||||
|
||||
assertFailsWith<IllegalArgumentException> {
|
||||
val owningKey = Crypto.decodePublicKey(DEV_CA.certificate.subjectPublicKeyInfo.encoded)
|
||||
val subject = CordaX500Name.build(DEV_CA.certificate.cert.subjectX500Principal)
|
||||
val owningKey = DEV_INTERMEDIATE_CA.certificate.publicKey
|
||||
val subject = CordaX500Name.build(DEV_INTERMEDIATE_CA.certificate.subjectX500Principal)
|
||||
service.assertOwnership(Party(subject, owningKey), anonymousAlice.party.anonymise())
|
||||
}
|
||||
}
|
||||
@ -168,9 +166,14 @@ class InMemoryIdentityServiceTests {
|
||||
private fun createConfidentialIdentity(x500Name: CordaX500Name): Pair<PartyAndCertificate, PartyAndCertificate> {
|
||||
val issuerKeyPair = generateKeyPair()
|
||||
val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public)
|
||||
val txKey = Crypto.generateKeyPair()
|
||||
val txCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuer.certificate.toX509CertHolder(), issuerKeyPair, x500Name, txKey.public)
|
||||
val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates)
|
||||
val txKeyPair = Crypto.generateKeyPair()
|
||||
val txCert = X509Utilities.createCertificate(
|
||||
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))
|
||||
}
|
||||
|
||||
|
@ -6,8 +6,6 @@ import net.corda.core.identity.AnonymousParty
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
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.UnknownAnonymousPartyException
|
||||
import net.corda.node.internal.configureDatabase
|
||||
@ -50,7 +48,7 @@ class PersistentIdentityServiceTests {
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
identityService = PersistentIdentityService(DEV_TRUST_ROOT)
|
||||
identityService = PersistentIdentityService(DEV_ROOT_CA.certificate)
|
||||
database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), identityService)
|
||||
}
|
||||
|
||||
@ -143,9 +141,9 @@ class PersistentIdentityServiceTests {
|
||||
@Test
|
||||
fun `assert unknown anonymous key is unrecognised`() {
|
||||
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 identity = Party(rootCert.cert)
|
||||
val identity = Party(rootCert)
|
||||
val txIdentity = AnonymousParty(txKey.public)
|
||||
|
||||
assertFailsWith<UnknownAnonymousPartyException> {
|
||||
@ -219,9 +217,9 @@ class PersistentIdentityServiceTests {
|
||||
}
|
||||
|
||||
assertFailsWith<IllegalArgumentException> {
|
||||
val owningKey = Crypto.decodePublicKey(DEV_CA.certificate.subjectPublicKeyInfo.encoded)
|
||||
val owningKey = DEV_INTERMEDIATE_CA.certificate.publicKey
|
||||
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())
|
||||
}
|
||||
}
|
||||
@ -243,7 +241,7 @@ class PersistentIdentityServiceTests {
|
||||
|
||||
// Create new identity service mounted onto same DB
|
||||
val newPersistentIdentityService = database.transaction {
|
||||
PersistentIdentityService(DEV_TRUST_ROOT)
|
||||
PersistentIdentityService(DEV_ROOT_CA.certificate)
|
||||
}
|
||||
|
||||
database.transaction {
|
||||
@ -266,8 +264,8 @@ class PersistentIdentityServiceTests {
|
||||
val issuerKeyPair = generateKeyPair()
|
||||
val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public)
|
||||
val txKey = Crypto.generateKeyPair()
|
||||
val txCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuer.certificate.toX509CertHolder(), issuerKeyPair, x500Name, txKey.public)
|
||||
val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates)
|
||||
val txCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuer.certificate, issuerKeyPair, x500Name.x500Principal, txKey.public)
|
||||
val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert) + issuer.certPath.certificates)
|
||||
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.sha256
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.testing.ALICE_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.driver.PortAllocation
|
||||
import net.corda.testing.internal.createNodeInfoAndSigned
|
||||
@ -35,7 +34,7 @@ class NetworkMapClientTest {
|
||||
fun setUp() {
|
||||
server = NetworkMapServer(cacheTimeout, PortAllocation.Incremental(10000).nextHostAndPort())
|
||||
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
|
||||
|
@ -18,6 +18,7 @@ import java.net.ServerSocket
|
||||
import java.nio.file.Path
|
||||
import java.security.KeyStore
|
||||
import javax.net.ssl.*
|
||||
import javax.security.auth.x500.X500Principal
|
||||
import kotlin.concurrent.thread
|
||||
import kotlin.test.*
|
||||
|
||||
@ -51,9 +52,9 @@ class TLSAuthenticationTests {
|
||||
val tempFolder: TemporaryFolder = TemporaryFolder()
|
||||
|
||||
// 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.
|
||||
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).
|
||||
private val CLIENT_1_X500 = CordaX500Name(commonName = "Client_1", organisation = "R3CEV", locality = "London", country = "GB")
|
||||
// TLS client (client2).
|
||||
@ -274,7 +275,7 @@ class TLSAuthenticationTests {
|
||||
CertificateType.NODE_CA,
|
||||
intermediateCACert,
|
||||
intermediateCAKeyPair,
|
||||
CLIENT_1_X500,
|
||||
CLIENT_1_X500.x500Principal,
|
||||
client1CAKeyPair.public
|
||||
)
|
||||
|
||||
@ -283,7 +284,7 @@ class TLSAuthenticationTests {
|
||||
CertificateType.TLS,
|
||||
client1CACert,
|
||||
client1CAKeyPair,
|
||||
CLIENT_1_X500,
|
||||
CLIENT_1_X500.x500Principal,
|
||||
client1TLSKeyPair.public
|
||||
)
|
||||
|
||||
@ -301,7 +302,7 @@ class TLSAuthenticationTests {
|
||||
CertificateType.NODE_CA,
|
||||
intermediateCACert,
|
||||
intermediateCAKeyPair,
|
||||
CLIENT_2_X500,
|
||||
CLIENT_2_X500.x500Principal,
|
||||
client2CAKeyPair.public
|
||||
)
|
||||
|
||||
@ -310,7 +311,7 @@ class TLSAuthenticationTests {
|
||||
CertificateType.TLS,
|
||||
client2CACert,
|
||||
client2CAKeyPair,
|
||||
CLIENT_2_X500,
|
||||
CLIENT_2_X500.x500Principal,
|
||||
client2TLSKeyPair.public
|
||||
)
|
||||
|
||||
@ -323,8 +324,8 @@ class TLSAuthenticationTests {
|
||||
// client2TLSKeyStore.save(client2TLSKeyStorePath, PASSWORD)
|
||||
|
||||
val trustStore = loadOrCreateKeyStore(trustStorePath, PASSWORD)
|
||||
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCACert.cert)
|
||||
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_INTERMEDIATE_CA, intermediateCACert.cert)
|
||||
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCACert)
|
||||
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_INTERMEDIATE_CA, intermediateCACert)
|
||||
// trustStore.save(trustStorePath, PASSWORD)
|
||||
|
||||
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.SecureHash
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.x500Name
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.nodeapi.internal.crypto.*
|
||||
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 org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.assertj.core.api.Assertions.*
|
||||
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.Before
|
||||
import org.junit.Test
|
||||
import java.security.cert.CertPathValidatorException
|
||||
import java.security.cert.Certificate
|
||||
import java.security.cert.X509Certificate
|
||||
import javax.security.auth.x500.X500Principal
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@ -32,18 +34,10 @@ class NetworkRegistrationHelperTest {
|
||||
private val requestId = SecureHash.randomSHA256().toString()
|
||||
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
|
||||
|
||||
@Before
|
||||
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()
|
||||
abstract class AbstractNodeConfiguration : NodeConfiguration
|
||||
config = rigorousMock<AbstractNodeConfiguration>().also {
|
||||
@ -66,9 +60,10 @@ class NetworkRegistrationHelperTest {
|
||||
assertThat(config.sslKeystore).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 sslKeystore = loadKeyStore(config.sslKeystore, config.keyStorePassword)
|
||||
@ -79,8 +74,7 @@ class NetworkRegistrationHelperTest {
|
||||
assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA))
|
||||
assertFalse(containsAlias(X509Utilities.CORDA_ROOT_CA))
|
||||
assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_TLS))
|
||||
val nodeCaCertChain = getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
|
||||
assertThat(nodeCaCertChain).containsExactly(nodeCaCert, intermediateCaCert, rootCaCert)
|
||||
assertThat(getCertificateChain(X509Utilities.CORDA_CLIENT_CA)).containsExactly(*nodeCaCertPath)
|
||||
}
|
||||
|
||||
sslKeystore.run {
|
||||
@ -92,40 +86,77 @@ class NetworkRegistrationHelperTest {
|
||||
assertThat(nodeTlsCertChain).hasSize(4)
|
||||
// The TLS cert has the same subject as the node CA cert
|
||||
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 {
|
||||
assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_CA))
|
||||
assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA))
|
||||
assertTrue(containsAlias(X509Utilities.CORDA_ROOT_CA))
|
||||
val trustStoreRootCaCert = getCertificate(X509Utilities.CORDA_ROOT_CA)
|
||||
assertThat(trustStoreRootCaCert).isEqualTo(rootCaCert)
|
||||
assertThat(getCertificate(X509Utilities.CORDA_ROOT_CA)).isEqualTo(nodeCaCertPath.last())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `missing truststore`() {
|
||||
val nodeCaCertPath = createNodeCaCertPath()
|
||||
assertThatThrownBy {
|
||||
createRegistrationHelper()
|
||||
createRegistrationHelper(nodeCaCertPath)
|
||||
}.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
|
||||
fun `wrong root cert in truststore`() {
|
||||
val rootKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val rootCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name("Foo", "MU", "GB"), rootKeyPair)
|
||||
saveTrustStoreWithRootCa(rootCert.cert)
|
||||
val registrationHelper = createRegistrationHelper()
|
||||
val wrongRootCert = X509Utilities.createSelfSignedCACertificate(
|
||||
X500Principal("O=Foo,L=MU,C=GB"),
|
||||
Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
|
||||
saveTrustStoreWithRootCa(wrongRootCert)
|
||||
val registrationHelper = createRegistrationHelper(createNodeCaCertPath())
|
||||
assertThatThrownBy {
|
||||
registrationHelper.buildKeystore()
|
||||
}.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 {
|
||||
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)
|
||||
}
|
||||
|
@ -9,11 +9,12 @@ webapp which provides REST API and web frontend. Application communicate using C
|
||||
|
||||
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
|
||||
``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
|
||||
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)
|
||||
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:
|
||||
|
||||
|
@ -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
|
||||
|
@ -2,6 +2,7 @@ apply plugin: 'kotlin'
|
||||
apply plugin: 'kotlin-jpa'
|
||||
apply plugin: 'net.corda.plugins.quasar-utils'
|
||||
apply plugin: 'net.corda.plugins.publish-utils'
|
||||
apply plugin: 'net.corda.plugins.api-scanner'
|
||||
apply plugin: 'com.jfrog.artifactory'
|
||||
|
||||
//noinspection GroovyAssignabilityCheck
|
||||
|
@ -3,6 +3,7 @@
|
||||
package net.corda.testing.driver
|
||||
|
||||
import net.corda.client.rpc.CordaRPCClient
|
||||
import net.corda.core.DoNotImplement
|
||||
import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
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>>)
|
||||
|
||||
@DoNotImplement
|
||||
sealed class NodeHandle {
|
||||
abstract val nodeInfo: NodeInfo
|
||||
/**
|
||||
@ -90,6 +92,7 @@ data class WebserverHandle(
|
||||
val process: Process
|
||||
)
|
||||
|
||||
@DoNotImplement
|
||||
sealed class PortAllocation {
|
||||
abstract fun nextPort(): Int
|
||||
fun nextHostAndPort() = NetworkHostAndPort("localhost", nextPort())
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.corda.testing.driver
|
||||
|
||||
import net.corda.core.DoNotImplement
|
||||
import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
@ -10,6 +11,7 @@ import net.corda.nodeapi.internal.config.User
|
||||
import net.corda.testing.node.NotarySpec
|
||||
import java.nio.file.Path
|
||||
|
||||
@DoNotImplement
|
||||
interface DriverDSL {
|
||||
/** Returns a list of [NotaryHandle]s matching the list of [NotarySpec]s passed into [driver]. */
|
||||
val notaryHandles: List<NotaryHandle>
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.corda.testing.node
|
||||
|
||||
import net.corda.core.DoNotImplement
|
||||
import net.corda.core.crypto.CompositeKey
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
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
|
||||
}
|
||||
|
||||
@ -181,6 +182,7 @@ class InMemoryMessagingNetwork internal constructor(
|
||||
/**
|
||||
* Mock service loadbalancing
|
||||
*/
|
||||
@DoNotImplement
|
||||
sealed class ServicePeerAllocationStrategy {
|
||||
abstract fun <A> pickNext(service: ServiceHandle, pickFrom: List<A>): A
|
||||
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.nhaarman.mockito_kotlin.doReturn
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import net.corda.core.DoNotImplement
|
||||
import net.corda.core.crypto.entropyToKeyPair
|
||||
import net.corda.core.crypto.random63BitValue
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
@ -492,6 +493,7 @@ fun StartedNode<MockNetwork.MockNode>.setMessagingServiceSpy(messagingServiceSpy
|
||||
}
|
||||
|
||||
private fun mockNodeConfiguration(): NodeConfiguration {
|
||||
@DoNotImplement
|
||||
abstract class AbstractNodeConfiguration : NodeConfiguration
|
||||
return rigorousMock<AbstractNodeConfiguration>().also {
|
||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||
|
@ -23,6 +23,7 @@ import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.node.VersionInfo
|
||||
import net.corda.node.internal.configureDatabase
|
||||
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.VaultServiceInternal
|
||||
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.transactions.InMemoryTransactionVerifierService
|
||||
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.DatabaseConfig
|
||||
import net.corda.nodeapi.internal.persistence.HibernateConfiguration
|
||||
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.DATA_SOURCE_CLASSNAME
|
||||
import net.corda.testing.database.DatabaseConstants.DATA_SOURCE_PASSWORD
|
||||
@ -59,7 +60,7 @@ import java.sql.Connection
|
||||
import java.time.Clock
|
||||
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
|
||||
* 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
|
||||
|
||||
val nextKeys = LinkedList<KeyPair>()
|
||||
private val nextKeys = LinkedList<KeyPair>()
|
||||
|
||||
override fun freshKey(): PublicKey {
|
||||
val k = nextKeys.poll() ?: generateKeyPair()
|
||||
@ -259,11 +260,10 @@ open class MockTransactionStorage : WritableTransactionStorage, SingletonSeriali
|
||||
}
|
||||
|
||||
fun <T : SerializeAsToken> createMockCordaService(serviceHub: MockServices, serviceConstructor: (AppServiceHub) -> T): T {
|
||||
class MockAppServiceHubImpl<T : SerializeAsToken>(val serviceHub: MockServices, serviceConstructor: (AppServiceHub) -> T) : AppServiceHub, ServiceHub by serviceHub {
|
||||
val serviceInstance: T
|
||||
class MockAppServiceHubImpl<out T : SerializeAsToken>(val serviceHub: MockServices, serviceConstructor: (AppServiceHub) -> T) : AppServiceHub, ServiceHub by serviceHub {
|
||||
val serviceInstance: T = serviceConstructor(this)
|
||||
|
||||
init {
|
||||
serviceInstance = serviceConstructor(this)
|
||||
serviceHub.cordappServices.putInstance(serviceInstance.javaClass, serviceInstance)
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.corda.testing.node
|
||||
|
||||
import net.corda.core.DoNotImplement
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.node.services.config.VerifierType
|
||||
import net.corda.nodeapi.internal.config.User
|
||||
@ -12,6 +13,7 @@ data class NotarySpec(
|
||||
val cluster: ClusterSpec? = null
|
||||
)
|
||||
|
||||
@DoNotImplement
|
||||
sealed class ClusterSpec {
|
||||
abstract val clusterSize: Int
|
||||
|
||||
|
@ -2,8 +2,6 @@ package net.corda.testing.node.internal.network
|
||||
|
||||
import net.corda.core.crypto.*
|
||||
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.serialization.deserialize
|
||||
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.NetworkParameters
|
||||
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.ServerConnector
|
||||
import org.eclipse.jetty.server.handler.HandlerCollection
|
||||
@ -29,6 +27,7 @@ import java.io.InputStream
|
||||
import java.net.InetSocketAddress
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
import javax.security.auth.x500.X500Principal
|
||||
import javax.ws.rs.*
|
||||
import javax.ws.rs.core.MediaType
|
||||
import javax.ws.rs.core.Response
|
||||
@ -36,7 +35,7 @@ import javax.ws.rs.core.Response.ok
|
||||
|
||||
class NetworkMapServer(cacheTimeout: Duration,
|
||||
hostAndPort: NetworkHostAndPort,
|
||||
rootCa: CertificateAndKeyPair = ROOT_CA, // Default to ROOT_CA for testing.
|
||||
rootCa: CertificateAndKeyPair = DEV_ROOT_CA,
|
||||
private val myHostNameValue: String = "test.host.name",
|
||||
vararg additionalServices: Any) : Closeable {
|
||||
companion object {
|
||||
@ -48,12 +47,12 @@ class NetworkMapServer(cacheTimeout: Duration,
|
||||
CertificateType.NETWORK_MAP,
|
||||
rootCAKeyAndCert.certificate,
|
||||
rootCAKeyAndCert.keyPair,
|
||||
CordaX500Name("Corda Network Map", "R3 Ltd", "London","GB"),
|
||||
networkMapKey.public).cert
|
||||
X500Principal("CN=Corda Network Map,O=R3 Ltd,L=London,C=GB"),
|
||||
networkMapKey.public)
|
||||
// Check that the certificate validates. Nodes will perform this check upon receiving a network map,
|
||||
// it's better to fail here than there.
|
||||
X509Utilities.validateCertificateChain(rootCAKeyAndCert.certificate.cert, networkMapCert)
|
||||
return CertificateAndKeyPair(networkMapCert.toX509CertHolder(), networkMapKey)
|
||||
X509Utilities.validateCertificateChain(rootCAKeyAndCert.certificate, networkMapCert)
|
||||
return CertificateAndKeyPair(networkMapCert, networkMapKey)
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,7 +129,7 @@ class NetworkMapServer(cacheTimeout: Duration,
|
||||
val networkMap = NetworkMap(nodeInfoMap.keys.toList(), parametersHash)
|
||||
val serializedNetworkMap = networkMap.serialize()
|
||||
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()
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
apply plugin: 'net.corda.plugins.publish-utils'
|
||||
apply plugin: 'net.corda.plugins.api-scanner'
|
||||
apply plugin: 'com.jfrog.artifactory'
|
||||
|
||||
dependencies {
|
||||
|
@ -2,6 +2,7 @@ apply plugin: 'kotlin'
|
||||
apply plugin: 'kotlin-jpa'
|
||||
apply plugin: 'net.corda.plugins.quasar-utils'
|
||||
apply plugin: 'net.corda.plugins.publish-utils'
|
||||
apply plugin: 'net.corda.plugins.api-scanner'
|
||||
apply plugin: 'com.jfrog.artifactory'
|
||||
|
||||
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.Party
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.unspecifiedCountry
|
||||
import net.corda.core.node.NodeInfo
|
||||
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.X509CertificateFactory
|
||||
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.security.KeyPair
|
||||
import java.security.PublicKey
|
||||
import java.security.cert.X509Certificate
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
/**
|
||||
@ -76,8 +78,8 @@ fun getFreeLocalPorts(hostName: String, numberToAlloc: Int): List<NetworkHostAnd
|
||||
}
|
||||
|
||||
fun getTestPartyAndCertificate(party: Party): PartyAndCertificate {
|
||||
val trustRoot: X509CertificateHolder = DEV_TRUST_ROOT
|
||||
val intermediate: CertificateAndKeyPair = DEV_CA
|
||||
val trustRoot: X509Certificate = DEV_ROOT_CA.certificate
|
||||
val intermediate: CertificateAndKeyPair = DEV_INTERMEDIATE_CA
|
||||
|
||||
val (nodeCaCert, nodeCaKeyPair) = createDevNodeCa(intermediate, party.name)
|
||||
|
||||
@ -85,11 +87,10 @@ fun getTestPartyAndCertificate(party: Party): PartyAndCertificate {
|
||||
CertificateType.LEGAL_IDENTITY,
|
||||
nodeCaCert,
|
||||
nodeCaKeyPair,
|
||||
party.name,
|
||||
party.name.x500Principal,
|
||||
party.owningKey)
|
||||
|
||||
val pathElements = listOf(identityCert, nodeCaCert, intermediate.certificate, trustRoot)
|
||||
val certPath = X509CertificateFactory().generateCertPath(pathElements.map(X509CertificateHolder::cert))
|
||||
val certPath = X509CertificateFactory().generateCertPath(identityCert, nodeCaCert, intermediate.certificate, trustRoot)
|
||||
return PartyAndCertificate(certPath)
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.corda.testing
|
||||
|
||||
import com.google.common.util.concurrent.SettableFuture
|
||||
import net.corda.core.DoNotImplement
|
||||
import net.corda.core.internal.uncheckedCast
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import org.slf4j.Logger
|
||||
@ -189,6 +190,7 @@ fun <S, E : Any> S.genericExpectEvents(
|
||||
finishFuture.getOrThrow()
|
||||
}
|
||||
|
||||
@DoNotImplement
|
||||
sealed class ExpectCompose<out 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>()
|
||||
|
@ -2,6 +2,7 @@ package net.corda.testing
|
||||
|
||||
import com.nhaarman.mockito_kotlin.*
|
||||
import net.corda.client.rpc.internal.KryoClientSerializationScheme
|
||||
import net.corda.core.DoNotImplement
|
||||
import net.corda.core.internal.staticField
|
||||
import net.corda.core.serialization.internal.*
|
||||
import net.corda.node.serialization.KryoServerSerializationScheme
|
||||
@ -62,6 +63,7 @@ class SerializationEnvironmentRule(private val inheritable: Boolean = false) : T
|
||||
}
|
||||
}
|
||||
|
||||
@DoNotImplement
|
||||
interface GlobalSerializationEnvironment : SerializationEnvironment {
|
||||
/** Unset this environment. */
|
||||
fun unset()
|
||||
|
@ -6,12 +6,10 @@ import net.corda.core.contracts.Command
|
||||
import net.corda.core.contracts.TypeOnlyCommandData
|
||||
import net.corda.core.crypto.generateKeyPair
|
||||
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.X509Utilities
|
||||
import net.corda.nodeapi.internal.crypto.getCertificateAndKeyPair
|
||||
import net.corda.nodeapi.internal.crypto.loadKeyStore
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import java.security.PublicKey
|
||||
import java.time.Instant
|
||||
|
||||
@ -34,22 +32,17 @@ val ALICE_NAME = CordaX500Name("Alice Corp", "Madrid", "ES")
|
||||
val BOB_NAME = CordaX500Name("Bob Plc", "Rome", "IT")
|
||||
@JvmField
|
||||
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
|
||||
val caKeyStore = loadKeyStore(ClassLoader.getSystemResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
|
||||
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
|
||||
val caKeyStore = loadKeyStore(ClassLoader.getSystemResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
|
||||
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())
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.corda.testing.contracts
|
||||
|
||||
import net.corda.core.DoNotImplement
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.identity.AbstractParty
|
||||
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"
|
||||
|
||||
@DoNotImplement // State is effectively a sealed class.
|
||||
interface State : ContractState {
|
||||
val magicNumber: Int
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.corda.testing.dsl
|
||||
|
||||
import net.corda.core.DoNotImplement
|
||||
import net.corda.core.contracts.ContractState
|
||||
import net.corda.core.contracts.StateAndRef
|
||||
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
|
||||
* be looked up both in ledger{..} and transaction{..} blocks.
|
||||
*/
|
||||
@DoNotImplement
|
||||
interface OutputStateLookup {
|
||||
/**
|
||||
* 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).
|
||||
*/
|
||||
@DoNotImplement
|
||||
interface Verifies {
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@DoNotImplement
|
||||
interface LedgerDSLInterpreter<out T : TransactionDSLInterpreter> : Verifies, OutputStateLookup {
|
||||
/**
|
||||
* Creates and adds a transaction to the ledger.
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.corda.testing.dsl
|
||||
|
||||
import net.corda.core.DoNotImplement
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.cordapp.CordappProvider
|
||||
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
|
||||
* the triggered diagnostic.
|
||||
*/
|
||||
@DoNotImplement
|
||||
sealed class EnforceVerifyOrFail {
|
||||
internal object Token : EnforceVerifyOrFail()
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.corda.testing.dsl
|
||||
|
||||
import net.corda.core.DoNotImplement
|
||||
import net.corda.core.contracts.AlwaysAcceptAttachmentConstraint
|
||||
import net.corda.core.contracts.Attachment
|
||||
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
|
||||
* we want to enforce users to call these methods (see [EnforceVerifyOrFail]) or not.
|
||||
*/
|
||||
@DoNotImplement
|
||||
interface TransactionDSLInterpreter : Verifies, OutputStateLookup {
|
||||
/**
|
||||
* 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.nio.file.Files
|
||||
import java.util.*
|
||||
import javax.security.auth.x500.X500Principal
|
||||
|
||||
@Suppress("unused")
|
||||
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 defaultIntermediateCaName = CordaX500Name("Corda Intermediate CA", "R3 Ltd", "London", "GB")
|
||||
private val defaultRootCaName = X500Principal("CN=Corda Root CA,O=R3 Ltd,L=London,C=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.
|
||||
@ -73,8 +74,8 @@ private val defaultIntermediateCaName = CordaX500Name("Corda Intermediate CA", "
|
||||
* @param intermediateCaName The subject name for the intermediate CA cert.
|
||||
*/
|
||||
fun createDevIntermediateCaCertPath(
|
||||
rootCaName: CordaX500Name = defaultRootCaName,
|
||||
intermediateCaName: CordaX500Name = defaultIntermediateCaName
|
||||
rootCaName: X500Principal = defaultRootCaName,
|
||||
intermediateCaName: X500Principal = defaultIntermediateCaName
|
||||
): Pair<CertificateAndKeyPair, CertificateAndKeyPair> {
|
||||
val rootKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val rootCert = X509Utilities.createSelfSignedCACertificate(rootCaName, rootKeyPair)
|
||||
@ -87,7 +88,10 @@ fun createDevIntermediateCaCertPath(
|
||||
intermediateCaName,
|
||||
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(
|
||||
legalName: CordaX500Name,
|
||||
rootCaName: CordaX500Name = defaultRootCaName,
|
||||
intermediateCaName: CordaX500Name = defaultIntermediateCaName
|
||||
rootCaName: X500Principal = defaultRootCaName,
|
||||
intermediateCaName: X500Principal = defaultIntermediateCaName
|
||||
): Triple<CertificateAndKeyPair, CertificateAndKeyPair, CertificateAndKeyPair> {
|
||||
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath(rootCaName, intermediateCaName)
|
||||
val nodeCa = createDevNodeCa(intermediateCa, legalName)
|
||||
|
Loading…
Reference in New Issue
Block a user