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:
Shams Asari 2018-01-04 10:58:44 +00:00
commit 13619df0b1
58 changed files with 825 additions and 509 deletions

View File

@ -3579,6 +3579,9 @@ public static final class net.corda.client.jackson.StringToMethodCallParser$Unpa
public <init>(String) public <init>(String)
@org.jetbrains.annotations.NotNull public final String getMethodName() @org.jetbrains.annotations.NotNull public final String getMethodName()
## ##
public static interface net.corda.testing.node.InMemoryMessagingNetwork$LatencyCalculator
@org.jetbrains.annotations.NotNull public abstract java.time.Duration between(net.corda.core.messaging.SingleMessageRecipient, net.corda.core.messaging.SingleMessageRecipient)
##
public final class net.corda.client.rpc.CordaRPCClient extends java.lang.Object public final class net.corda.client.rpc.CordaRPCClient extends java.lang.Object
public <init>(net.corda.core.utilities.NetworkHostAndPort) public <init>(net.corda.core.utilities.NetworkHostAndPort)
public <init>(net.corda.core.utilities.NetworkHostAndPort, net.corda.client.rpc.CordaRPCClientConfiguration) public <init>(net.corda.core.utilities.NetworkHostAndPort, net.corda.client.rpc.CordaRPCClientConfiguration)

View File

@ -10,7 +10,6 @@ import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate import net.corda.core.identity.PartyAndCertificate
import net.corda.core.internal.toX509CertHolder
import net.corda.core.node.services.IdentityService import net.corda.core.node.services.IdentityService
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.SerializedBytes
@ -18,14 +17,8 @@ import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.unwrap import net.corda.core.utilities.unwrap
import org.bouncycastle.asn1.DERSet
import org.bouncycastle.asn1.pkcs.CertificationRequestInfo
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
import java.io.ByteArrayOutputStream
import java.nio.charset.Charset
import java.security.PublicKey import java.security.PublicKey
import java.security.SignatureException import java.security.SignatureException
import java.security.cert.CertPath
import java.util.* import java.util.*
/** /**

View File

@ -80,8 +80,10 @@ data class CordaX500Name(val commonName: String?,
const val MAX_LENGTH_STATE = 64 const val MAX_LENGTH_STATE = 64
const val MAX_LENGTH_ORGANISATION_UNIT = 64 const val MAX_LENGTH_ORGANISATION_UNIT = 64
const val MAX_LENGTH_COMMON_NAME = 64 const val MAX_LENGTH_COMMON_NAME = 64
private val supportedAttributes = setOf(BCStyle.O, BCStyle.C, BCStyle.L, BCStyle.CN, BCStyle.ST, BCStyle.OU) private val supportedAttributes = setOf(BCStyle.O, BCStyle.C, BCStyle.L, BCStyle.CN, BCStyle.ST, BCStyle.OU)
private val countryCodes: Set<String> = ImmutableSet.copyOf(Locale.getISOCountries() + unspecifiedCountry) private val countryCodes: Set<String> = ImmutableSet.copyOf(Locale.getISOCountries() + unspecifiedCountry)
@JvmStatic @JvmStatic
fun build(principal: X500Principal): CordaX500Name { fun build(principal: X500Principal): CordaX500Name {
val x500Name = X500Name.getInstance(principal.encoded) val x500Name = X500Name.getInstance(principal.encoded)
@ -115,20 +117,12 @@ data class CordaX500Name(val commonName: String?,
} }
@Transient @Transient
private var x500Cache: X500Name? = null private var _x500Principal: X500Principal? = null
val x500Principal: X500Principal /** Return the [X500Principal] equivalent of this name. */
get() { val x500Principal: X500Principal get() {
if (x500Cache == null) { return _x500Principal ?: X500Principal(this.x500Name.encoded).also { _x500Principal = it }
x500Cache = this.x500Name
}
return X500Principal(x500Cache!!.encoded)
}
override fun toString(): String {
if (x500Cache == null) {
x500Cache = this.x500Name
}
return x500Cache.toString()
} }
override fun toString(): String = x500Principal.toString()
} }

View File

@ -10,8 +10,9 @@ import net.corda.core.node.ServicesForResolution
import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationContext
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
import org.bouncycastle.cert.X509CertificateHolder import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter import org.bouncycastle.asn1.x500.X500NameBuilder
import org.bouncycastle.asn1.x500.style.BCStyle
import org.slf4j.Logger import org.slf4j.Logger
import rx.Observable import rx.Observable
import rx.Observer import rx.Observer
@ -26,8 +27,6 @@ import java.nio.charset.Charset
import java.nio.charset.StandardCharsets.UTF_8 import java.nio.charset.StandardCharsets.UTF_8
import java.nio.file.* import java.nio.file.*
import java.nio.file.attribute.FileAttribute import java.nio.file.attribute.FileAttribute
import java.security.cert.Certificate
import java.security.cert.X509Certificate
import java.time.Duration import java.time.Duration
import java.time.temporal.Temporal import java.time.temporal.Temporal
import java.util.* import java.util.*
@ -186,9 +185,6 @@ fun <T> logElapsedTime(label: String, logger: Logger? = null, body: () -> T): T
} }
} }
fun Certificate.toX509CertHolder() = X509CertificateHolder(encoded)
val X509CertificateHolder.cert: X509Certificate get() = JcaX509CertificateConverter().getCertificate(this)
/** Convert a [ByteArrayOutputStream] to [InputStreamAndHash]. */ /** Convert a [ByteArrayOutputStream] to [InputStreamAndHash]. */
fun ByteArrayOutputStream.toInputStreamAndHash(): InputStreamAndHash { fun ByteArrayOutputStream.toInputStreamAndHash(): InputStreamAndHash {
val bytes = toByteArray() val bytes = toByteArray()
@ -320,6 +316,22 @@ fun ExecutorService.join() {
} }
} }
/**
* Return the underlying X.500 name from this Corda-safe X.500 name. These are guaranteed to have a consistent
* ordering, such that their `toString()` function returns the same value every time for the same [CordaX500Name].
*/
val CordaX500Name.x500Name: X500Name
get() {
return X500NameBuilder(BCStyle.INSTANCE).apply {
addRDN(BCStyle.C, country)
state?.let { addRDN(BCStyle.ST, it) }
addRDN(BCStyle.L, locality)
addRDN(BCStyle.O, organisation)
organisationUnit?.let { addRDN(BCStyle.OU, it) }
commonName?.let { addRDN(BCStyle.CN, it) }
}.build()
}
@Suppress("unused") @Suppress("unused")
@VisibleForTesting @VisibleForTesting
val CordaX500Name.Companion.unspecifiedCountry val CordaX500Name.Companion.unspecifiedCountry

View File

@ -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()
}

View File

@ -1,8 +1,6 @@
package net.corda.core.crypto package net.corda.core.crypto
import net.corda.core.crypto.CompositeKey.NodeAndWeight import net.corda.core.crypto.CompositeKey.NodeAndWeight
import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.cert
import net.corda.core.internal.declaredField import net.corda.core.internal.declaredField
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
@ -15,6 +13,7 @@ import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.TemporaryFolder import org.junit.rules.TemporaryFolder
import java.security.PublicKey import java.security.PublicKey
import javax.security.auth.x500.X500Principal
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
import kotlin.test.assertFalse import kotlin.test.assertFalse
@ -334,7 +333,7 @@ class CompositeKeyTests {
// Create self sign CA. // Create self sign CA.
val caKeyPair = Crypto.generateKeyPair() val caKeyPair = Crypto.generateKeyPair()
val caName = CordaX500Name(commonName = "Test CA", organisation = "R3 Ltd", locality = "London", country = "GB") val caName = X500Principal("CN=Test CA,O=R3 Ltd,L=London,C=GB")
val ca = X509Utilities.createSelfSignedCACertificate(caName, caKeyPair) val ca = X509Utilities.createSelfSignedCACertificate(caName, caKeyPair)
// Sign the composite key with the self sign CA. // Sign the composite key with the self sign CA.
@ -343,7 +342,7 @@ class CompositeKeyTests {
// Store certificate to keystore. // Store certificate to keystore.
val keystorePath = tempFolder.root.toPath() / "keystore.jks" val keystorePath = tempFolder.root.toPath() / "keystore.jks"
val keystore = loadOrCreateKeyStore(keystorePath, "password") val keystore = loadOrCreateKeyStore(keystorePath, "password")
keystore.setCertificateEntry("CompositeKey", compositeKeyCert.cert) keystore.setCertificateEntry("CompositeKey", compositeKeyCert)
keystore.save(keystorePath, "password") keystore.save(keystorePath, "password")
// Load keystore from disk. // Load keystore from disk.

View File

@ -1,7 +1,6 @@
package net.corda.core.crypto package net.corda.core.crypto
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.cert
import net.corda.nodeapi.internal.crypto.* import net.corda.nodeapi.internal.crypto.*
import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.testing.internal.createDevIntermediateCaCertPath
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
@ -14,6 +13,7 @@ import java.security.KeyStore
import java.security.cert.CertPathValidator import java.security.cert.CertPathValidator
import java.security.cert.CertPathValidatorException import java.security.cert.CertPathValidatorException
import java.security.cert.PKIXParameters import java.security.cert.PKIXParameters
import javax.security.auth.x500.X500Principal
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
import kotlin.test.assertTrue import kotlin.test.assertTrue
@ -26,17 +26,22 @@ class X509NameConstraintsTest {
CertificateType.NODE_CA, CertificateType.NODE_CA,
intermediateCa.certificate, intermediateCa.certificate,
intermediateCa.keyPair, intermediateCa.keyPair,
CordaX500Name("Corda Client CA", "R3 Ltd", "London", "GB"), CordaX500Name("Corda Client CA", "R3 Ltd", "London", "GB").x500Principal,
nodeCaKeyPair.public, nodeCaKeyPair.public,
nameConstraints = nameConstraints) nameConstraints = nameConstraints)
val keyPass = "password" val keyPass = "password"
val trustStore = KeyStore.getInstance(KEYSTORE_TYPE) val trustStore = KeyStore.getInstance(KEYSTORE_TYPE)
trustStore.load(null, keyPass.toCharArray()) trustStore.load(null, keyPass.toCharArray())
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCa.certificate.cert) trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCa.certificate)
val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, subjectName, tlsKeyPair.public) val tlsCert = X509Utilities.createCertificate(
CertificateType.TLS,
nodeCaCert,
nodeCaKeyPair,
X500Principal(subjectName.encoded),
tlsKeyPair.public)
val keyStore = KeyStore.getInstance(KEYSTORE_TYPE) val keyStore = KeyStore.getInstance(KEYSTORE_TYPE)
keyStore.load(null, keyPass.toCharArray()) keyStore.load(null, keyPass.toCharArray())

View File

@ -7,30 +7,36 @@ import kotlin.test.assertNull
class CordaX500NameTest { class CordaX500NameTest {
@Test @Test
fun `parse service name with organisational unit`() { fun `service name with organisational unit`() {
val name = CordaX500Name.parse("O=Bank A, L=New York, C=US, OU=Org Unit, CN=Service Name") val name = CordaX500Name.parse("O=Bank A, L=New York, C=US, OU=Org Unit, CN=Service Name")
assertEquals("Service Name", name.commonName) assertEquals("Service Name", name.commonName)
assertEquals("Org Unit", name.organisationUnit) assertEquals("Org Unit", name.organisationUnit)
assertEquals("Bank A", name.organisation) assertEquals("Bank A", name.organisation)
assertEquals("New York", name.locality) assertEquals("New York", name.locality)
assertEquals(CordaX500Name.parse(name.toString()), name)
assertEquals(CordaX500Name.build(name.x500Principal), name)
} }
@Test @Test
fun `parse service name`() { fun `service name`() {
val name = CordaX500Name.parse("O=Bank A, L=New York, C=US, CN=Service Name") val name = CordaX500Name.parse("O=Bank A, L=New York, C=US, CN=Service Name")
assertEquals("Service Name", name.commonName) assertEquals("Service Name", name.commonName)
assertNull(name.organisationUnit) assertNull(name.organisationUnit)
assertEquals("Bank A", name.organisation) assertEquals("Bank A", name.organisation)
assertEquals("New York", name.locality) assertEquals("New York", name.locality)
assertEquals(CordaX500Name.parse(name.toString()), name)
assertEquals(CordaX500Name.build(name.x500Principal), name)
} }
@Test @Test
fun `parse legal entity name`() { fun `legal entity name`() {
val name = CordaX500Name.parse("O=Bank A, L=New York, C=US") val name = CordaX500Name.parse("O=Bank A, L=New York, C=US")
assertNull(name.commonName) assertNull(name.commonName)
assertNull(name.organisationUnit) assertNull(name.organisationUnit)
assertEquals("Bank A", name.organisation) assertEquals("Bank A", name.organisation)
assertEquals("New York", name.locality) assertEquals("New York", name.locality)
assertEquals(CordaX500Name.parse(name.toString()), name)
assertEquals(CordaX500Name.build(name.x500Principal), name)
} }
@Test @Test

View File

@ -1,14 +1,13 @@
package net.corda.core.identity package net.corda.core.identity
import net.corda.core.crypto.entropyToKeyPair import net.corda.core.crypto.entropyToKeyPair
import net.corda.core.internal.cert
import net.corda.core.internal.read import net.corda.core.internal.read
import net.corda.core.serialization.deserialize import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.nodeapi.internal.crypto.KEYSTORE_TYPE import net.corda.nodeapi.internal.crypto.KEYSTORE_TYPE
import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509CertificateFactory
import net.corda.nodeapi.internal.crypto.save import net.corda.nodeapi.internal.crypto.save
import net.corda.testing.DEV_CA import net.corda.testing.DEV_ROOT_CA
import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.SerializationEnvironmentRule
import net.corda.testing.getTestPartyAndCertificate import net.corda.testing.getTestPartyAndCertificate
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
@ -25,8 +24,8 @@ class PartyAndCertificateTest {
val testSerialization = SerializationEnvironmentRule() val testSerialization = SerializationEnvironmentRule()
@Test @Test
fun `should reject a path with no roles`() { fun `reject a path with no roles`() {
val path = X509CertificateFactory().generateCertPath(DEV_CA.certificate.cert) val path = X509CertificateFactory().generateCertPath(DEV_ROOT_CA.certificate)
assertFailsWith<IllegalArgumentException> { PartyAndCertificate(path) } assertFailsWith<IllegalArgumentException> { PartyAndCertificate(path) }
} }

View File

@ -103,7 +103,7 @@ Go to the terminal window displaying the CRaSH shell of PartyA. Typing ``help``
commands. commands.
.. note:: Local terminal shell is available only in a development mode. In production environment SSH server can be enabled. .. note:: Local terminal shell is available only in a development mode. In production environment SSH server can be enabled.
More about SSH and how to connect can be found on :doc:`Shell` page. More about SSH and how to connect can be found on the :doc:`shell` page.
We want to create an IOU of 100 with PartyB. We start the ``IOUFlow`` by typing: We want to create an IOU of 100 with PartyB. We start the ``IOUFlow`` by typing:

View File

@ -192,9 +192,6 @@ The following 3rd party types are supported.
org.apache.activemq.artemis.api.core.SimpleString org.apache.activemq.artemis.api.core.SimpleString
org.bouncycastle.asn1.x500.X500Name
org.bouncycastle.cert.X509CertificateHolder
Corda Types Corda Types
``````````` ```````````

View File

@ -5,10 +5,8 @@ import net.corda.core.crypto.Crypto
import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.generateKeyPair
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.cert
import net.corda.core.internal.createDirectories import net.corda.core.internal.createDirectories
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.internal.toX509CertHolder
import net.corda.core.utilities.trace import net.corda.core.utilities.trace
import net.corda.nodeapi.internal.config.NodeSSLConfiguration import net.corda.nodeapi.internal.config.NodeSSLConfiguration
import net.corda.nodeapi.internal.crypto.* import net.corda.nodeapi.internal.crypto.*
@ -39,10 +37,10 @@ object DevIdentityGenerator {
// TODO The passwords for the dev key stores are spread everywhere and should be constants in a single location // TODO The passwords for the dev key stores are spread everywhere and should be constants in a single location
val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass") val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
val intermediateCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass") val intermediateCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
val rootCert = caKeyStore.getCertificate(X509Utilities.CORDA_ROOT_CA) val rootCert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA)
nodeSslConfig.certificatesDirectory.createDirectories() nodeSslConfig.certificatesDirectory.createDirectories()
nodeSslConfig.createDevKeyStores(rootCert.toX509CertHolder(), intermediateCa, legalName) nodeSslConfig.createDevKeyStores(rootCert, intermediateCa, legalName)
val keyStoreWrapper = KeyStoreWrapper(nodeSslConfig.nodeKeystore, nodeSslConfig.keyStorePassword) val keyStoreWrapper = KeyStoreWrapper(nodeSslConfig.nodeKeystore, nodeSslConfig.keyStorePassword)
val identity = keyStoreWrapper.storeLegalIdentity(legalName, "$NODE_IDENTITY_ALIAS_PREFIX-private-key", Crypto.generateKeyPair()) val identity = keyStoreWrapper.storeLegalIdentity(legalName, "$NODE_IDENTITY_ALIAS_PREFIX-private-key", Crypto.generateKeyPair())
@ -62,16 +60,21 @@ object DevIdentityGenerator {
keyPairs.zip(dirs) { keyPair, nodeDir -> keyPairs.zip(dirs) { keyPair, nodeDir ->
val (serviceKeyCert, compositeKeyCert) = listOf(keyPair.public, compositeKey).map { publicKey -> val (serviceKeyCert, compositeKeyCert) = listOf(keyPair.public, compositeKey).map { publicKey ->
X509Utilities.createCertificate(CertificateType.SERVICE_IDENTITY, intermediateCa.certificate, intermediateCa.keyPair, notaryName, publicKey) X509Utilities.createCertificate(
CertificateType.SERVICE_IDENTITY,
intermediateCa.certificate,
intermediateCa.keyPair,
notaryName.x500Principal,
publicKey)
} }
val distServKeyStoreFile = (nodeDir / "certificates").createDirectories() / "distributedService.jks" val distServKeyStoreFile = (nodeDir / "certificates").createDirectories() / "distributedService.jks"
val keystore = loadOrCreateKeyStore(distServKeyStoreFile, "cordacadevpass") val keystore = loadOrCreateKeyStore(distServKeyStoreFile, "cordacadevpass")
keystore.setCertificateEntry("$DISTRIBUTED_NOTARY_ALIAS_PREFIX-composite-key", compositeKeyCert.cert) keystore.setCertificateEntry("$DISTRIBUTED_NOTARY_ALIAS_PREFIX-composite-key", compositeKeyCert)
keystore.setKeyEntry( keystore.setKeyEntry(
"$DISTRIBUTED_NOTARY_ALIAS_PREFIX-private-key", "$DISTRIBUTED_NOTARY_ALIAS_PREFIX-private-key",
keyPair.private, keyPair.private,
"cordacadevkeypass".toCharArray(), "cordacadevkeypass".toCharArray(),
arrayOf(serviceKeyCert.cert, intermediateCa.certificate.cert, rootCert)) arrayOf(serviceKeyCert, intermediateCa.certificate, rootCert))
keystore.save(distServKeyStoreFile, "cordacadevpass") keystore.save(distServKeyStoreFile, "cordacadevpass")
} }

View File

@ -8,13 +8,13 @@ import net.corda.nodeapi.internal.crypto.*
import org.bouncycastle.asn1.x509.GeneralName import org.bouncycastle.asn1.x509.GeneralName
import org.bouncycastle.asn1.x509.GeneralSubtree import org.bouncycastle.asn1.x509.GeneralSubtree
import org.bouncycastle.asn1.x509.NameConstraints import org.bouncycastle.asn1.x509.NameConstraints
import org.bouncycastle.cert.X509CertificateHolder import java.security.cert.X509Certificate
/** /**
* Create the node and SSL key stores needed by a node. The node key store will be populated with a node CA cert (using * Create the node and SSL key stores needed by a node. The node key store will be populated with a node CA cert (using
* the given legal name), and the SSL key store will store the TLS cert which is a sub-cert of the node CA. * the given legal name), and the SSL key store will store the TLS cert which is a sub-cert of the node CA.
*/ */
fun SSLConfiguration.createDevKeyStores(rootCert: X509CertificateHolder, intermediateCa: CertificateAndKeyPair, legalName: CordaX500Name) { fun SSLConfiguration.createDevKeyStores(rootCert: X509Certificate, intermediateCa: CertificateAndKeyPair, legalName: CordaX500Name) {
val (nodeCaCert, nodeCaKeyPair) = createDevNodeCa(intermediateCa, legalName) val (nodeCaCert, nodeCaKeyPair) = createDevNodeCa(intermediateCa, legalName)
loadOrCreateKeyStore(nodeKeystore, keyStorePassword).apply { loadOrCreateKeyStore(nodeKeystore, keyStorePassword).apply {
@ -27,7 +27,7 @@ fun SSLConfiguration.createDevKeyStores(rootCert: X509CertificateHolder, interme
} }
val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, legalName, tlsKeyPair.public) val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, legalName.x500Principal, tlsKeyPair.public)
loadOrCreateKeyStore(sslKeystore, keyStorePassword).apply { loadOrCreateKeyStore(sslKeystore, keyStorePassword).apply {
addOrReplaceKey( addOrReplaceKey(
@ -50,7 +50,7 @@ fun createDevNodeCa(intermediateCa: CertificateAndKeyPair, legalName: CordaX500N
CertificateType.NODE_CA, CertificateType.NODE_CA,
intermediateCa.certificate, intermediateCa.certificate,
intermediateCa.keyPair, intermediateCa.keyPair,
legalName, legalName.x500Principal,
keyPair.public, keyPair.public,
nameConstraints = nameConstraints) nameConstraints = nameConstraints)
return CertificateAndKeyPair(cert, keyPair) return CertificateAndKeyPair(cert, keyPair)

View File

@ -3,8 +3,9 @@
package net.corda.nodeapi.internal.crypto package net.corda.nodeapi.internal.crypto
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.internal.* import net.corda.core.internal.exists
import org.bouncycastle.cert.X509CertificateHolder import net.corda.core.internal.read
import net.corda.core.internal.write
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.io.OutputStream import java.io.OutputStream
@ -67,18 +68,6 @@ fun loadKeyStore(input: InputStream, storePassword: String): KeyStore {
return keyStore return keyStore
} }
/**
* Helper extension method to add, or overwrite any key data in store.
* @param alias name to record the private key and certificate chain under.
* @param key cryptographic key to store.
* @param password password for unlocking the key entry in the future. This does not have to be the same password as any keys stored,
* but for SSL purposes this is recommended.
* @param chain the sequence of certificates starting with the public key certificate for this key and extending to the root CA cert.
*/
fun KeyStore.addOrReplaceKey(alias: String, key: Key, password: CharArray, chain: Array<out X509CertificateHolder>) {
addOrReplaceKey(alias, key, password, chain.map { it.cert }.toTypedArray<Certificate>())
}
/** /**
* Helper extension method to add, or overwrite any key data in store. * Helper extension method to add, or overwrite any key data in store.
* @param alias name to record the private key and certificate chain under. * @param alias name to record the private key and certificate chain under.
@ -132,9 +121,9 @@ fun KeyStore.getKeyPair(alias: String, keyPassword: String): KeyPair = getCertif
* @param keyPassword The password for the PrivateKey (not the store access password). * @param keyPassword The password for the PrivateKey (not the store access password).
*/ */
fun KeyStore.getCertificateAndKeyPair(alias: String, keyPassword: String): CertificateAndKeyPair { fun KeyStore.getCertificateAndKeyPair(alias: String, keyPassword: String): CertificateAndKeyPair {
val cert = getX509Certificate(alias).toX509CertHolder() val certificate = getX509Certificate(alias)
val publicKey = Crypto.toSupportedPublicKey(cert.subjectPublicKeyInfo) val publicKey = Crypto.toSupportedPublicKey(certificate.publicKey)
return CertificateAndKeyPair(cert, KeyPair(publicKey, getSupportedKey(alias, keyPassword))) return CertificateAndKeyPair(certificate, KeyPair(publicKey, getSupportedKey(alias, keyPassword)))
} }
/** /**

View File

@ -2,7 +2,6 @@ package net.corda.nodeapi.internal.crypto
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.PartyAndCertificate import net.corda.core.identity.PartyAndCertificate
import net.corda.core.internal.cert
import net.corda.core.internal.read import net.corda.core.internal.read
import java.nio.file.Path import java.nio.file.Path
import java.security.KeyPair import java.security.KeyPair
@ -15,8 +14,13 @@ class KeyStoreWrapper(private val storePath: Path, private val storePassword: St
fun storeLegalIdentity(legalName: CordaX500Name, alias: String, keyPair: KeyPair): PartyAndCertificate { fun storeLegalIdentity(legalName: CordaX500Name, alias: String, keyPair: KeyPair): PartyAndCertificate {
val nodeCaCertChain = keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA) val nodeCaCertChain = keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
val nodeCa = getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA) val nodeCa = getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)
val identityCert = X509Utilities.createCertificate(CertificateType.LEGAL_IDENTITY, nodeCa.certificate, nodeCa.keyPair, legalName, keyPair.public) val identityCert = X509Utilities.createCertificate(
val identityCertPath = X509CertificateFactory().generateCertPath(identityCert.cert, *nodeCaCertChain) CertificateType.LEGAL_IDENTITY,
nodeCa.certificate,
nodeCa.keyPair,
legalName.x500Principal,
keyPair.public)
val identityCertPath = X509CertificateFactory().generateCertPath(identityCert, *nodeCaCertChain)
// Assume key password = store password. // Assume key password = store password.
keyStore.addOrReplaceKey(alias, keyPair.private, storePassword.toCharArray(), identityCertPath.certificates.toTypedArray()) keyStore.addOrReplaceKey(alias, keyPair.private, storePassword.toCharArray(), identityCertPath.certificates.toTypedArray())
keyStore.save(storePath, storePassword) keyStore.save(storePath, storePassword)

View File

@ -4,12 +4,14 @@ import net.corda.core.CordaOID
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SignatureScheme import net.corda.core.crypto.SignatureScheme
import net.corda.core.crypto.random63BitValue import net.corda.core.crypto.random63BitValue
import net.corda.core.internal.CertRole
import net.corda.core.internal.reader
import net.corda.core.internal.writer
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.* import net.corda.core.internal.*
import net.corda.core.utilities.days import net.corda.core.utilities.days
import net.corda.core.utilities.millis import net.corda.core.utilities.millis
import org.bouncycastle.asn1.* import org.bouncycastle.asn1.*
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.asn1.x500.style.BCStyle import org.bouncycastle.asn1.x500.style.BCStyle
import org.bouncycastle.asn1.x509.* import org.bouncycastle.asn1.x509.*
import org.bouncycastle.asn1.x509.Extension import org.bouncycastle.asn1.x509.Extension
@ -34,6 +36,7 @@ import java.time.Duration
import java.time.Instant import java.time.Instant
import java.time.temporal.ChronoUnit import java.time.temporal.ChronoUnit
import java.util.* import java.util.*
import javax.security.auth.x500.X500Principal
object X509Utilities { object X509Utilities {
val DEFAULT_IDENTITY_SIGNATURE_SCHEME = Crypto.EDDSA_ED25519_SHA512 val DEFAULT_IDENTITY_SIGNATURE_SCHEME = Crypto.EDDSA_ED25519_SHA512
@ -74,7 +77,7 @@ object X509Utilities {
* @param after duration to roll forward returned end date relative to current date. * @param after duration to roll forward returned end date relative to current date.
* @param parent if provided certificate whose validity should bound the date interval returned. * @param parent if provided certificate whose validity should bound the date interval returned.
*/ */
fun getCertificateValidityWindow(before: Duration, after: Duration, parent: X509CertificateHolder? = null): Pair<Date, Date> { fun getCertificateValidityWindow(before: Duration, after: Duration, parent: X509Certificate? = null): Pair<Date, Date> {
val startOfDayUTC = Instant.now().truncatedTo(ChronoUnit.DAYS) val startOfDayUTC = Instant.now().truncatedTo(ChronoUnit.DAYS)
val notBefore = max(startOfDayUTC - before, parent?.notBefore) val notBefore = max(startOfDayUTC - before, parent?.notBefore)
val notAfter = min(startOfDayUTC + after, parent?.notAfter) val notAfter = min(startOfDayUTC + after, parent?.notAfter)
@ -85,60 +88,11 @@ object X509Utilities {
* Create a de novo root self-signed X509 v3 CA cert. * Create a de novo root self-signed X509 v3 CA cert.
*/ */
@JvmStatic @JvmStatic
fun createSelfSignedCACertificate(subject: CordaX500Name, fun createSelfSignedCACertificate(subject: X500Principal,
keyPair: KeyPair, keyPair: KeyPair,
validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW): X509CertificateHolder { validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW): X509Certificate {
val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second) val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second)
return createCertificate(CertificateType.ROOT_CA, subject.x500Name, keyPair, subject.x500Name, keyPair.public, window) return createCertificate(CertificateType.ROOT_CA, subject, keyPair, subject, keyPair.public, window)
}
/**
* Create a X509 v3 certificate for use as a CA or for TLS. This does not require a [CordaX500Name] because the
* constraints are inappropriate for TLS/CA usage, however as a result this is unsuitable for Corda identity
* certificate generation.
*
* @param issuerCertificate The Public certificate of the root CA above this used to sign it.
* @param issuerKeyPair The KeyPair of the root CA above this used to sign it.
* @param subject subject of the generated certificate.
* @param subjectPublicKey subject's public key.
* @param validityWindow The certificate's validity window. Default to [DEFAULT_VALIDITY_WINDOW] if not provided.
* @return A data class is returned containing the new intermediate CA Cert and its KeyPair for signing downstream certificates.
* Note the generated certificate tree is capped at max depth of 1 below this to be in line with commercially available certificates.
*/
@JvmStatic
fun createCertificate(certificateType: CertificateType,
issuerCertificate: X509CertificateHolder,
issuerKeyPair: KeyPair,
subject: CordaX500Name,
subjectPublicKey: PublicKey,
validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW,
nameConstraints: NameConstraints? = null): X509CertificateHolder {
return createCertificate(certificateType, issuerCertificate, issuerKeyPair, subject.x500Name, subjectPublicKey, validityWindow, nameConstraints)
}
/**
* Create a X509 v3 certificate for use as a CA or for TLS. This does not require a [CordaX500Name] because the
* constraints are inappropriate for TLS/CA usage, however as a result this is unsuitable for Corda identity
* certificate generation.
*
* @param issuerCertificate The Public certificate of the root CA above this used to sign it.
* @param issuerKeyPair The KeyPair of the root CA above this used to sign it.
* @param subject subject of the generated certificate.
* @param subjectPublicKey subject's public key.
* @param validityWindow The certificate's validity window. Default to [DEFAULT_VALIDITY_WINDOW] if not provided.
* @return A data class is returned containing the new intermediate CA Cert and its KeyPair for signing downstream certificates.
* Note the generated certificate tree is capped at max depth of 1 below this to be in line with commercially available certificates.
*/
@JvmStatic
fun createCertificate(certificateType: CertificateType,
issuerCertificate: X509CertificateHolder,
issuerKeyPair: KeyPair,
subject: X500Name,
subjectPublicKey: PublicKey,
validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW,
nameConstraints: NameConstraints? = null): X509CertificateHolder {
val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second, issuerCertificate)
return createCertificate(certificateType, issuerCertificate.subject, issuerKeyPair, subject, subjectPublicKey, window, nameConstraints)
} }
@Throws(CertPathValidatorException::class) @Throws(CertPathValidatorException::class)
@ -153,13 +107,13 @@ object X509Utilities {
/** /**
* Helper method to store a .pem/.cer format file copy of a certificate if required for import into a PC/Mac, or for inspection. * Helper method to store a .pem/.cer format file copy of a certificate if required for import into a PC/Mac, or for inspection.
* @param x509Certificate certificate to save. * @param certificate certificate to save.
* @param file Target file. * @param file Target file.
*/ */
@JvmStatic @JvmStatic
fun saveCertificateAsPEMFile(x509Certificate: X509Certificate, file: Path) { fun saveCertificateAsPEMFile(certificate: X509Certificate, file: Path) {
JcaPEMWriter(file.writer()).use { JcaPEMWriter(file.writer()).use {
it.writeObject(x509Certificate) it.writeObject(certificate)
} }
} }
@ -172,9 +126,10 @@ object X509Utilities {
fun loadCertificateFromPEMFile(file: Path): X509Certificate { fun loadCertificateFromPEMFile(file: Path): X509Certificate {
return file.reader().use { return file.reader().use {
val pemObject = PemReader(it).readPemObject() val pemObject = PemReader(it).readPemObject()
val certHolder = X509CertificateHolder(pemObject.content) X509CertificateHolder(pemObject.content).run {
certHolder.isValidOn(Date()) isValidOn(Date())
certHolder.cert toJca()
}
} }
} }
@ -187,38 +142,18 @@ object X509Utilities {
* @param validityWindow the time period the certificate is valid for. * @param validityWindow the time period the certificate is valid for.
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate. * @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
*/ */
fun createCertificate(certificateType: CertificateType, fun createPartialCertificate(certificateType: CertificateType,
issuer: CordaX500Name, issuer: X500Principal,
subject: CordaX500Name, subject: X500Principal,
subjectPublicKey: PublicKey, subjectPublicKey: PublicKey,
validityWindow: Pair<Date, Date>, validityWindow: Pair<Date, Date>,
nameConstraints: NameConstraints? = null): X509v3CertificateBuilder { nameConstraints: NameConstraints? = null): X509v3CertificateBuilder {
return createCertificate(certificateType, issuer.x500Name, subject.x500Name, subjectPublicKey, validityWindow, nameConstraints)
}
/**
* Build a partial X.509 certificate ready for signing.
*
* @param issuer name of the issuing entity.
* @param subject name of the certificate subject.
* @param subjectPublicKey public key of the certificate subject.
* @param validityWindow the time period the certificate is valid for.
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
*/
internal fun createCertificate(certificateType: CertificateType,
issuer: X500Name,
subject: X500Name,
subjectPublicKey: PublicKey,
validityWindow: Pair<Date, Date>,
nameConstraints: NameConstraints? = null): X509v3CertificateBuilder {
val serial = BigInteger.valueOf(random63BitValue()) val serial = BigInteger.valueOf(random63BitValue())
val keyPurposes = DERSequence(ASN1EncodableVector().apply { certificateType.purposes.forEach { add(it) } }) val keyPurposes = DERSequence(ASN1EncodableVector().apply { certificateType.purposes.forEach { add(it) } })
val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(subjectPublicKey.encoded)) val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(subjectPublicKey.encoded))
val role = certificateType.role val role = certificateType.role
val builder = JcaX509v3CertificateBuilder(issuer, serial, validityWindow.first, validityWindow.second, val builder = JcaX509v3CertificateBuilder(issuer, serial, validityWindow.first, validityWindow.second, subject, subjectPublicKey)
subject, subjectPublicKey)
.addExtension(Extension.subjectKeyIdentifier, false, BcX509ExtensionUtils().createSubjectKeyIdentifier(subjectPublicKeyInfo)) .addExtension(Extension.subjectKeyIdentifier, false, BcX509ExtensionUtils().createSubjectKeyIdentifier(subjectPublicKeyInfo))
.addExtension(Extension.basicConstraints, certificateType.isCA, BasicConstraints(certificateType.isCA)) .addExtension(Extension.basicConstraints, certificateType.isCA, BasicConstraints(certificateType.isCA))
.addExtension(Extension.keyUsage, false, certificateType.keyUsage) .addExtension(Extension.keyUsage, false, certificateType.keyUsage)
@ -234,6 +169,37 @@ object X509Utilities {
return builder return builder
} }
/**
* Create a X509 v3 certificate using the given issuer certificate and key pair.
*
* @param issuerCertificate The Public certificate of the root CA above this used to sign it.
* @param issuerKeyPair The KeyPair of the root CA above this used to sign it.
* @param subject subject of the generated certificate.
* @param subjectPublicKey subject's public key.
* @param validityWindow The certificate's validity window. Default to [DEFAULT_VALIDITY_WINDOW] if not provided.
* @return A data class is returned containing the new intermediate CA Cert and its KeyPair for signing downstream certificates.
* Note the generated certificate tree is capped at max depth of 1 below this to be in line with commercially available certificates.
*/
@JvmStatic
fun createCertificate(certificateType: CertificateType,
issuerCertificate: X509Certificate,
issuerKeyPair: KeyPair,
subject: X500Principal,
subjectPublicKey: PublicKey,
validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW,
nameConstraints: NameConstraints? = null): X509Certificate {
val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second, issuerCertificate)
return createCertificate(
certificateType,
issuerCertificate.subjectX500Principal,
issuerKeyPair,
subject,
subjectPublicKey,
window,
nameConstraints
)
}
/** /**
* Build and sign an X.509 certificate with the given signer. * Build and sign an X.509 certificate with the given signer.
* *
@ -245,15 +211,16 @@ object X509Utilities {
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate. * @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
*/ */
fun createCertificate(certificateType: CertificateType, fun createCertificate(certificateType: CertificateType,
issuer: X500Name, issuer: X500Principal,
issuerSigner: ContentSigner, issuerSigner: ContentSigner,
subject: CordaX500Name, subject: X500Principal,
subjectPublicKey: PublicKey, subjectPublicKey: PublicKey,
validityWindow: Pair<Date, Date>, validityWindow: Pair<Date, Date>,
nameConstraints: NameConstraints? = null): X509CertificateHolder { nameConstraints: NameConstraints? = null): X509Certificate {
val builder = createCertificate(certificateType, issuer, subject.x500Name, subjectPublicKey, validityWindow, nameConstraints) val builder = createPartialCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints)
return builder.build(issuerSigner).apply { return builder.build(issuerSigner).run {
require(isValidOn(Date())) require(isValidOn(Date()))
toJca()
} }
} }
@ -268,39 +235,47 @@ object X509Utilities {
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate. * @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
*/ */
fun createCertificate(certificateType: CertificateType, fun createCertificate(certificateType: CertificateType,
issuer: X500Name, issuer: X500Principal,
issuerKeyPair: KeyPair, issuerKeyPair: KeyPair,
subject: X500Name, subject: X500Principal,
subjectPublicKey: PublicKey, subjectPublicKey: PublicKey,
validityWindow: Pair<Date, Date>, validityWindow: Pair<Date, Date>,
nameConstraints: NameConstraints? = null): X509CertificateHolder { nameConstraints: NameConstraints? = null): X509Certificate {
val signatureScheme = Crypto.findSignatureScheme(issuerKeyPair.private) val signatureScheme = Crypto.findSignatureScheme(issuerKeyPair.private)
val provider = Crypto.findProvider(signatureScheme.providerName) val provider = Crypto.findProvider(signatureScheme.providerName)
val builder = createCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints)
val signer = ContentSignerBuilder.build(signatureScheme, issuerKeyPair.private, provider) val signer = ContentSignerBuilder.build(signatureScheme, issuerKeyPair.private, provider)
return builder.build(signer).apply { val builder = createPartialCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints)
return builder.build(signer).run {
require(isValidOn(Date())) require(isValidOn(Date()))
require(isSignatureValid(JcaContentVerifierProviderBuilder().build(issuerKeyPair.public))) require(isSignatureValid(JcaContentVerifierProviderBuilder().build(issuerKeyPair.public)))
toJca()
} }
} }
/** /**
* Create certificate signing request using provided information. * Create certificate signing request using provided information.
*/ */
private fun createCertificateSigningRequest(subject: CordaX500Name, private fun createCertificateSigningRequest(subject: X500Principal,
email: String, email: String,
keyPair: KeyPair, keyPair: KeyPair,
signatureScheme: SignatureScheme): PKCS10CertificationRequest { signatureScheme: SignatureScheme): PKCS10CertificationRequest {
val signer = ContentSignerBuilder.build(signatureScheme, keyPair.private, Crypto.findProvider(signatureScheme.providerName)) val signer = ContentSignerBuilder.build(signatureScheme, keyPair.private, Crypto.findProvider(signatureScheme.providerName))
return JcaPKCS10CertificationRequestBuilder(subject.x500Name, keyPair.public).addAttribute(BCStyle.E, DERUTF8String(email)).build(signer) return JcaPKCS10CertificationRequestBuilder(subject, keyPair.public).addAttribute(BCStyle.E, DERUTF8String(email)).build(signer)
} }
fun createCertificateSigningRequest(subject: CordaX500Name, email: String, keyPair: KeyPair): PKCS10CertificationRequest { fun createCertificateSigningRequest(subject: X500Principal, email: String, keyPair: KeyPair): PKCS10CertificationRequest {
return createCertificateSigningRequest(subject, email, keyPair, DEFAULT_TLS_SIGNATURE_SCHEME) return createCertificateSigningRequest(subject, email, keyPair, DEFAULT_TLS_SIGNATURE_SCHEME)
} }
} }
/**
* Convert a [X509Certificate] into Bouncycastle's [X509CertificateHolder].
*
* NOTE: To avoid unnecessary copying use [X509Certificate] where possible.
*/
fun X509Certificate.toBc() = X509CertificateHolder(encoded)
fun X509CertificateHolder.toJca(): X509Certificate = X509CertificateFactory().generateCertificate(encoded.inputStream())
/** /**
* Wraps a [CertificateFactory] to remove boilerplate. It's unclear whether [CertificateFactory] is threadsafe so best * Wraps a [CertificateFactory] to remove boilerplate. It's unclear whether [CertificateFactory] is threadsafe so best
* so assume this class is not. * so assume this class is not.
@ -396,4 +371,4 @@ enum class CertificateType(val keyUsage: KeyUsage, vararg val purposes: KeyPurpo
) )
} }
data class CertificateAndKeyPair(val certificate: X509CertificateHolder, val keyPair: KeyPair) data class CertificateAndKeyPair(val certificate: X509Certificate, val keyPair: KeyPair)

View File

@ -1,9 +1,9 @@
package net.corda.nodeapi.internal.serialization.amqp package net.corda.nodeapi.internal.serialization.amqp
import net.corda.core.serialization.ClassWhitelist
import net.corda.core.serialization.CordaSerializable
import com.google.common.primitives.Primitives import com.google.common.primitives.Primitives
import com.google.common.reflect.TypeToken import com.google.common.reflect.TypeToken
import net.corda.core.serialization.ClassWhitelist
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationContext
import org.apache.qpid.proton.codec.Data import org.apache.qpid.proton.codec.Data
import java.beans.IndexedPropertyDescriptor import java.beans.IndexedPropertyDescriptor
@ -81,10 +81,17 @@ private fun <T : Any> propertiesForSerializationFromConstructor(kotlinConstructo
val rc: MutableList<PropertySerializer> = ArrayList(kotlinConstructor.parameters.size) val rc: MutableList<PropertySerializer> = ArrayList(kotlinConstructor.parameters.size)
for (param in kotlinConstructor.parameters) { for (param in kotlinConstructor.parameters) {
val name = param.name ?: throw NotSerializableException("Constructor parameter of $clazz has no name.") val name = param.name ?: throw NotSerializableException("Constructor parameter of $clazz has no name.")
val matchingProperty = properties[name] ?: val matchingProperty = properties[name] ?:
throw NotSerializableException("No property matching constructor parameter named '$name' of '$clazz'. " + try {
"If using Java, check that you have the -parameters option specified in the Java compiler. " + clazz.getDeclaredField(param.name)
"Alternately, provide a proxy serializer (SerializationCustomSerializer) if recompiling isn't an option") throw NotSerializableException("Property '$name' or it's getter is non public, this renders class '$clazz' unserializable")
} catch (e: NoSuchFieldException) {
throw NotSerializableException("No property matching constructor parameter named '$name' of '$clazz'. " +
"If using Java, check that you have the -parameters option specified in the Java compiler. " +
"Alternately, provide a proxy serializer (SerializationCustomSerializer) if recompiling isn't an option")
}
// Check that the method has a getter in java. // Check that the method has a getter in java.
val getter = matchingProperty.readMethod ?: throw NotSerializableException("Property has no getter method for $name of $clazz. " + val getter = matchingProperty.readMethod ?: throw NotSerializableException("Property has no getter method for $name of $clazz. " +
"If using Java and the parameter name looks anonymous, check that you have the -parameters option specified in the Java compiler." + "If using Java and the parameter name looks anonymous, check that you have the -parameters option specified in the Java compiler." +

View File

@ -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()));
}
}

View File

@ -4,10 +4,7 @@ import net.corda.core.crypto.Crypto
import net.corda.core.crypto.Crypto.EDDSA_ED25519_SHA512 import net.corda.core.crypto.Crypto.EDDSA_ED25519_SHA512
import net.corda.core.crypto.Crypto.generateKeyPair import net.corda.core.crypto.Crypto.generateKeyPair
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.cert
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.internal.toTypedArray
import net.corda.core.internal.x500Name
import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.deserialize import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
@ -23,12 +20,9 @@ import net.corda.testing.BOB_NAME
import net.corda.testing.TestIdentity import net.corda.testing.TestIdentity
import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.testing.internal.createDevIntermediateCaCertPath
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.asn1.x509.BasicConstraints import org.bouncycastle.asn1.x509.BasicConstraints
import org.bouncycastle.asn1.x509.Extension import org.bouncycastle.asn1.x509.Extension
import org.bouncycastle.asn1.x509.KeyUsage import org.bouncycastle.asn1.x509.KeyUsage
import org.bouncycastle.cert.X509CertificateHolder
import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.TemporaryFolder import org.junit.rules.TemporaryFolder
@ -42,18 +36,16 @@ import java.security.SecureRandom
import java.security.cert.CertPath import java.security.cert.CertPath
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import java.util.* import java.util.*
import java.util.stream.Stream
import javax.net.ssl.* import javax.net.ssl.*
import javax.security.auth.x500.X500Principal
import kotlin.concurrent.thread import kotlin.concurrent.thread
import kotlin.test.* import kotlin.test.*
class X509UtilitiesTest { class X509UtilitiesTest {
private companion object { private companion object {
val ALICE = TestIdentity(ALICE_NAME, 70).party val ALICE = TestIdentity(ALICE_NAME, 70).party
val bob = TestIdentity(BOB_NAME, 80) val BOB = TestIdentity(BOB_NAME, 80)
val MEGA_CORP = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).party val MEGA_CORP = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).party
val BOB get() = bob.party
val BOB_PUBKEY get() = bob.publicKey
val CIPHER_SUITES = arrayOf( val CIPHER_SUITES = arrayOf(
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
@ -63,27 +55,30 @@ class X509UtilitiesTest {
@Rule @Rule
@JvmField @JvmField
val tempFolder: TemporaryFolder = TemporaryFolder() val tempFolder = TemporaryFolder()
@Test @Test
fun `create valid self-signed CA certificate`() { fun `create valid self-signed CA certificate`() {
val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val caCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Test Cert", organisation = "R3 Ltd", locality = "London", country = "GB"), caKey) val subject = X500Principal("CN=Test Cert,O=R3 Ltd,L=London,C=GB")
assertEquals(X500Name("CN=Test Cert,O=R3 Ltd,L=London,C=GB"), caCert.subject) // using our subject common name val caCert = X509Utilities.createSelfSignedCACertificate(subject, caKey)
assertEquals(caCert.issuer, caCert.subject) //self-signed assertEquals(subject, caCert.subjectX500Principal) // using our subject common name
caCert.isValidOn(Date()) // throws on verification problems assertEquals(caCert.issuerX500Principal, caCert.subjectX500Principal) //self-signed
caCert.isSignatureValid(JcaContentVerifierProviderBuilder().build(caKey.public)) // throws on verification problems caCert.checkValidity(Date()) // throws on verification problems
val basicConstraints = BasicConstraints.getInstance(caCert.getExtension(Extension.basicConstraints).parsedValue) caCert.verify(caKey.public) // throws on verification problems
val keyUsage = KeyUsage.getInstance(caCert.getExtension(Extension.keyUsage).parsedValue) caCert.toBc().run {
assertFalse { keyUsage.hasUsages(5) } // Bit 5 == keyCertSign according to ASN.1 spec (see full comment on KeyUsage property) val basicConstraints = BasicConstraints.getInstance(getExtension(Extension.basicConstraints).parsedValue)
assertNull(basicConstraints.pathLenConstraint) // No length constraint specified on this CA certificate val keyUsage = KeyUsage.getInstance(getExtension(Extension.keyUsage).parsedValue)
assertFalse { keyUsage.hasUsages(5) } // Bit 5 == keyCertSign according to ASN.1 spec (see full comment on KeyUsage property)
assertNull(basicConstraints.pathLenConstraint) // No length constraint specified on this CA certificate
}
} }
@Test @Test
fun `load and save a PEM file certificate`() { fun `load and save a PEM file certificate`() {
val tmpCertificateFile = tempFile("cacert.pem") val tmpCertificateFile = tempFile("cacert.pem")
val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val caCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Test Cert", organisation = "R3 Ltd", locality = "London", country = "GB"), caKey).cert val caCert = X509Utilities.createSelfSignedCACertificate(X500Principal("CN=Test Cert,O=R3 Ltd,L=London,C=GB"), caKey)
X509Utilities.saveCertificateAsPEMFile(caCert, tmpCertificateFile) X509Utilities.saveCertificateAsPEMFile(caCert, tmpCertificateFile)
val readCertificate = X509Utilities.loadCertificateFromPEMFile(tmpCertificateFile) val readCertificate = X509Utilities.loadCertificateFromPEMFile(tmpCertificateFile)
assertEquals(caCert, readCertificate) assertEquals(caCert, readCertificate)
@ -92,18 +87,20 @@ class X509UtilitiesTest {
@Test @Test
fun `create valid server certificate chain`() { fun `create valid server certificate chain`() {
val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val caCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Test CA Cert", organisation = "R3 Ltd", locality = "London", country = "GB"), caKey) val caCert = X509Utilities.createSelfSignedCACertificate(X500Principal("CN=Test CA Cert,O=R3 Ltd,L=London,C=GB"), caKey)
val subject = CordaX500Name(commonName = "Server Cert", organisation = "R3 Ltd", locality = "London", country = "GB") val subject = X500Principal("CN=Server Cert,O=R3 Ltd,L=London,C=GB")
val keyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val keyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val serverCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, caKey, subject, keyPair.public) val serverCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, caKey, subject, keyPair.public)
assertEquals(X500Name("C=GB,L=London,O=R3 Ltd,CN=Server Cert"), serverCert.subject) // using our subject common name assertEquals(subject, serverCert.subjectX500Principal) // using our subject common name
assertEquals(caCert.issuer, serverCert.issuer) // Issued by our CA cert assertEquals(caCert.issuerX500Principal, serverCert.issuerX500Principal) // Issued by our CA cert
serverCert.isValidOn(Date()) // throws on verification problems serverCert.checkValidity(Date()) // throws on verification problems
serverCert.isSignatureValid(JcaContentVerifierProviderBuilder().build(caKey.public)) // throws on verification problems serverCert.verify(caKey.public) // throws on verification problems
val basicConstraints = BasicConstraints.getInstance(serverCert.getExtension(Extension.basicConstraints).parsedValue) serverCert.toBc().run {
val keyUsage = KeyUsage.getInstance(serverCert.getExtension(Extension.keyUsage).parsedValue) val basicConstraints = BasicConstraints.getInstance(getExtension(Extension.basicConstraints).parsedValue)
assertFalse { keyUsage.hasUsages(5) } // Bit 5 == keyCertSign according to ASN.1 spec (see full comment on KeyUsage property) val keyUsage = KeyUsage.getInstance(getExtension(Extension.keyUsage).parsedValue)
assertNull(basicConstraints.pathLenConstraint) // Non-CA certificate assertFalse { keyUsage.hasUsages(5) } // Bit 5 == keyCertSign according to ASN.1 spec (see full comment on KeyUsage property)
assertNull(basicConstraints.pathLenConstraint) // Non-CA certificate
}
} }
@Test @Test
@ -111,15 +108,14 @@ class X509UtilitiesTest {
val tmpKeyStore = tempFile("keystore.jks") val tmpKeyStore = tempFile("keystore.jks")
val keyPair = generateKeyPair(EDDSA_ED25519_SHA512) val keyPair = generateKeyPair(EDDSA_ED25519_SHA512)
val testName = CordaX500Name(commonName = "Test", organisation = "R3 Ltd", locality = "London", country = "GB") val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB")
val selfSignCert = X509Utilities.createSelfSignedCACertificate(testName, keyPair) val selfSignCert = X509Utilities.createSelfSignedCACertificate(testName, keyPair)
assertTrue(Arrays.equals(selfSignCert.subjectPublicKeyInfo.encoded, keyPair.public.encoded)) assertTrue(Arrays.equals(selfSignCert.publicKey.encoded, keyPair.public.encoded))
// Save the EdDSA private key with self sign cert in the keystore. // Save the EdDSA private key with self sign cert in the keystore.
val keyStore = loadOrCreateKeyStore(tmpKeyStore, "keystorepass") val keyStore = loadOrCreateKeyStore(tmpKeyStore, "keystorepass")
keyStore.setKeyEntry("Key", keyPair.private, "password".toCharArray(), keyStore.setKeyEntry("Key", keyPair.private, "password".toCharArray(), arrayOf(selfSignCert))
Stream.of(selfSignCert).map { it.cert }.toTypedArray())
keyStore.save(tmpKeyStore, "keystorepass") keyStore.save(tmpKeyStore, "keystorepass")
// Load the keystore from file and make sure keys are intact. // Load the keystore from file and make sure keys are intact.
@ -137,15 +133,14 @@ class X509UtilitiesTest {
fun `signing EdDSA key with EcDSA certificate`() { fun `signing EdDSA key with EcDSA certificate`() {
val tmpKeyStore = tempFile("keystore.jks") val tmpKeyStore = tempFile("keystore.jks")
val ecDSAKey = generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256) val ecDSAKey = generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
val testName = CordaX500Name(commonName = "Test", organisation = "R3 Ltd", locality = "London", country = "GB") val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB")
val ecDSACert = X509Utilities.createSelfSignedCACertificate(testName, ecDSAKey) val ecDSACert = X509Utilities.createSelfSignedCACertificate(testName, ecDSAKey)
val edDSAKeypair = generateKeyPair(EDDSA_ED25519_SHA512) val edDSAKeypair = generateKeyPair(EDDSA_ED25519_SHA512)
val edDSACert = X509Utilities.createCertificate(CertificateType.TLS, ecDSACert, ecDSAKey, X500Name("CN=TestEdDSA"), edDSAKeypair.public) val edDSACert = X509Utilities.createCertificate(CertificateType.TLS, ecDSACert, ecDSAKey, BOB.name.x500Principal, edDSAKeypair.public)
// Save the EdDSA private key with cert chains. // Save the EdDSA private key with cert chains.
val keyStore = loadOrCreateKeyStore(tmpKeyStore, "keystorepass") val keyStore = loadOrCreateKeyStore(tmpKeyStore, "keystorepass")
keyStore.setKeyEntry("Key", edDSAKeypair.private, "password".toCharArray(), keyStore.setKeyEntry("Key", edDSAKeypair.private, "password".toCharArray(), arrayOf(ecDSACert, edDSACert))
Stream.of(ecDSACert, edDSACert).map { it.cert }.toTypedArray())
keyStore.save(tmpKeyStore, "keystorepass") keyStore.save(tmpKeyStore, "keystorepass")
// Load the keystore from file and make sure keys are intact. // Load the keystore from file and make sure keys are intact.
@ -179,23 +174,22 @@ class X509UtilitiesTest {
val serverKeyStore = loadKeyStore(sslConfig.nodeKeystore, sslConfig.keyStorePassword) val serverKeyStore = loadKeyStore(sslConfig.nodeKeystore, sslConfig.keyStorePassword)
val (serverCert, serverKeyPair) = serverKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, sslConfig.keyStorePassword) val (serverCert, serverKeyPair) = serverKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, sslConfig.keyStorePassword)
serverCert.cert.checkValidity() serverCert.checkValidity()
serverCert.cert.verify(intermediateCa.certificate.cert.publicKey) serverCert.verify(intermediateCa.certificate.publicKey)
assertThat(CordaX500Name.parse(serverCert.subject.toString())).isEqualTo(MEGA_CORP.name) assertThat(CordaX500Name.build(serverCert.subjectX500Principal)).isEqualTo(MEGA_CORP.name)
// Load back SSL certificate // Load back SSL certificate
val sslKeyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword) val sslKeyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword)
val (sslCert) = sslKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_TLS, sslConfig.keyStorePassword) val (sslCert) = sslKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_TLS, sslConfig.keyStorePassword)
sslCert.cert.checkValidity() sslCert.checkValidity()
sslCert.cert.verify(serverCert.cert.publicKey) sslCert.verify(serverCert.publicKey)
assertThat(CordaX500Name.parse(sslCert.subject.toString())).isEqualTo(MEGA_CORP.name) assertThat(CordaX500Name.build(sslCert.subjectX500Principal)).isEqualTo(MEGA_CORP.name)
// Now sign something with private key and verify against certificate public key // Now sign something with private key and verify against certificate public key
val testData = "123456".toByteArray() val testData = "123456".toByteArray()
val signature = Crypto.doSign(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME, serverKeyPair.private, testData) val signature = Crypto.doSign(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME, serverKeyPair.private, testData)
val publicKey = Crypto.toSupportedPublicKey(serverCert.subjectPublicKeyInfo) assertTrue { Crypto.isValid(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME, serverCert.publicKey, signature, testData) }
assertTrue { Crypto.isValid(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME, publicKey, signature, testData) }
} }
@Test @Test
@ -210,7 +204,7 @@ class X509UtilitiesTest {
// Generate server cert and private key and populate another keystore suitable for SSL // Generate server cert and private key and populate another keystore suitable for SSL
sslConfig.createDevKeyStores(rootCa.certificate, intermediateCa, MEGA_CORP.name) sslConfig.createDevKeyStores(rootCa.certificate, intermediateCa, MEGA_CORP.name)
sslConfig.createTrustStore(rootCa.certificate.cert) sslConfig.createTrustStore(rootCa.certificate)
val keyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword) val keyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword)
val trustStore = loadKeyStore(sslConfig.trustStoreFile, sslConfig.trustStorePassword) val trustStore = loadKeyStore(sslConfig.trustStoreFile, sslConfig.trustStorePassword)
@ -303,10 +297,10 @@ class X509UtilitiesTest {
@Test @Test
fun `get correct private key type from Keystore`() { fun `get correct private key type from Keystore`() {
val keyPair = generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256) val keyPair = generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
val testName = CordaX500Name(commonName = "Test", organisation = "R3 Ltd", locality = "London", country = "GB") val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB")
val selfSignCert = X509Utilities.createSelfSignedCACertificate(testName, keyPair) val selfSignCert = X509Utilities.createSelfSignedCACertificate(testName, keyPair)
val keyStore = loadOrCreateKeyStore(tempFile("testKeystore.jks"), "keystorepassword") val keyStore = loadOrCreateKeyStore(tempFile("testKeystore.jks"), "keystorepassword")
keyStore.setKeyEntry("Key", keyPair.private, "keypassword".toCharArray(), arrayOf(selfSignCert.cert)) keyStore.setKeyEntry("Key", keyPair.private, "keypassword".toCharArray(), arrayOf(selfSignCert))
val keyFromKeystore = keyStore.getKey("Key", "keypassword".toCharArray()) val keyFromKeystore = keyStore.getKey("Key", "keypassword".toCharArray())
val keyFromKeystoreCasted = keyStore.getSupportedKey("Key", "keypassword") val keyFromKeystoreCasted = keyStore.getSupportedKey("Key", "keypassword")
@ -316,7 +310,7 @@ class X509UtilitiesTest {
} }
@Test @Test
fun `serialize - deserialize X509CertififcateHolder`() { fun `serialize - deserialize X509Certififcate`() {
val factory = SerializationFactoryImpl().apply { registerScheme(KryoServerSerializationScheme()) } val factory = SerializationFactoryImpl().apply { registerScheme(KryoServerSerializationScheme()) }
val context = SerializationContextImpl(KryoHeaderV0_1, val context = SerializationContextImpl(KryoHeaderV0_1,
javaClass.classLoader, javaClass.classLoader,
@ -324,9 +318,9 @@ class X509UtilitiesTest {
emptyMap(), emptyMap(),
true, true,
SerializationContext.UseCase.P2P) SerializationContext.UseCase.P2P)
val expected: X509CertificateHolder = X509Utilities.createSelfSignedCACertificate(ALICE.name, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) val expected = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Principal, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
val serialized = expected.serialize(factory, context).bytes val serialized = expected.serialize(factory, context).bytes
val actual: X509CertificateHolder = serialized.deserialize(factory, context) val actual = serialized.deserialize<X509Certificate>(factory, context)
assertEquals(expected, actual) assertEquals(expected, actual)
} }
@ -340,9 +334,9 @@ class X509UtilitiesTest {
true, true,
SerializationContext.UseCase.P2P) SerializationContext.UseCase.P2P)
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val rootCACert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootCAKey) val rootCACert = X509Utilities.createSelfSignedCACertificate(ALICE_NAME.x500Principal, rootCAKey)
val certificate = X509Utilities.createCertificate(CertificateType.TLS, rootCACert, rootCAKey, BOB.name.x500Name, BOB_PUBKEY) val certificate = X509Utilities.createCertificate(CertificateType.TLS, rootCACert, rootCAKey, BOB_NAME.x500Principal, BOB.publicKey)
val expected = X509CertificateFactory().generateCertPath(certificate.cert, rootCACert.cert) val expected = X509CertificateFactory().generateCertPath(certificate, rootCACert)
val serialized = expected.serialize(factory, context).bytes val serialized = expected.serialize(factory, context).bytes
val actual: CertPath = serialized.deserialize(factory, context) val actual: CertPath = serialized.deserialize(factory, context)
assertEquals(expected, actual) assertEquals(expected, actual)

View File

@ -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))
}
}

View File

@ -1,8 +1,6 @@
package net.corda.node package net.corda.node
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.cert
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.node.services.config.configureDevKeyAndTrustStores import net.corda.node.services.config.configureDevKeyAndTrustStores
@ -17,6 +15,10 @@ import org.junit.ClassRule
import org.assertj.core.api.Assertions.assertThatThrownBy import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Test import org.junit.Test
import java.nio.file.Path import java.nio.file.Path
import javax.security.auth.x500.X500Principal
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertTrue
class NodeKeystoreCheckTest : IntegrationTest() { class NodeKeystoreCheckTest : IntegrationTest() {
companion object { companion object {
@ -59,10 +61,10 @@ class NodeKeystoreCheckTest : IntegrationTest() {
// Self signed root // Self signed root
val badRootKeyPair = Crypto.generateKeyPair() val badRootKeyPair = Crypto.generateKeyPair()
val badRoot = X509Utilities.createSelfSignedCACertificate(CordaX500Name("Bad Root", "Lodnon", "GB"), badRootKeyPair) val badRoot = X509Utilities.createSelfSignedCACertificate(X500Principal("O=Bad Root,L=Lodnon,C=GB"), badRootKeyPair)
val nodeCA = keystore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, config.keyStorePassword) val nodeCA = keystore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, config.keyStorePassword)
val badNodeCACert = X509Utilities.createCertificate(CertificateType.NODE_CA, badRoot, badRootKeyPair, ALICE_NAME, nodeCA.keyPair.public) val badNodeCACert = X509Utilities.createCertificate(CertificateType.NODE_CA, badRoot, badRootKeyPair, ALICE_NAME.x500Principal, nodeCA.keyPair.public)
keystore.setKeyEntry(X509Utilities.CORDA_CLIENT_CA, nodeCA.keyPair.private, config.keyStorePassword.toCharArray(), arrayOf(badNodeCACert.cert, badRoot.cert)) keystore.setKeyEntry(X509Utilities.CORDA_CLIENT_CA, nodeCA.keyPair.private, config.keyStorePassword.toCharArray(), arrayOf(badNodeCACert, badRoot))
keystore.save(config.nodeKeystore, config.keyStorePassword) keystore.save(config.nodeKeystore, config.keyStorePassword)
assertThatThrownBy { assertThatThrownBy {

View File

@ -59,10 +59,10 @@ class ProtonWrapperTests {
amqpClient.start() amqpClient.start()
val serverConnect = serverConnected.get() val serverConnect = serverConnected.get()
assertEquals(true, serverConnect.connected) assertEquals(true, serverConnect.connected)
assertEquals(BOB_NAME, CordaX500Name.parse(serverConnect.remoteCert!!.subject.toString())) assertEquals(BOB_NAME, CordaX500Name.build(serverConnect.remoteCert!!.subjectX500Principal))
val clientConnect = clientConnected.get() val clientConnect = clientConnected.get()
assertEquals(true, clientConnect.connected) assertEquals(true, clientConnect.connected)
assertEquals(ALICE_NAME, CordaX500Name.parse(clientConnect.remoteCert!!.subject.toString())) assertEquals(ALICE_NAME, CordaX500Name.build(clientConnect.remoteCert!!.subjectX500Principal))
val msg = amqpClient.createMessage("Test".toByteArray(), val msg = amqpClient.createMessage("Test".toByteArray(),
"p2p.inbound", "p2p.inbound",
ALICE_NAME.toString(), ALICE_NAME.toString(),
@ -102,10 +102,10 @@ class ProtonWrapperTests {
amqpClient.start() amqpClient.start()
val serverConn1 = serverConnected.get() val serverConn1 = serverConnected.get()
assertEquals(true, serverConn1.connected) assertEquals(true, serverConn1.connected)
assertEquals(BOB_NAME, CordaX500Name.parse(serverConn1.remoteCert!!.subject.toString())) assertEquals(BOB_NAME, CordaX500Name.build(serverConn1.remoteCert!!.subjectX500Principal))
val connState1 = clientConnected.next() val connState1 = clientConnected.next()
assertEquals(true, connState1.connected) assertEquals(true, connState1.connected)
assertEquals(ALICE_NAME, CordaX500Name.parse(connState1.remoteCert!!.subject.toString())) assertEquals(ALICE_NAME, CordaX500Name.build(connState1.remoteCert!!.subjectX500Principal))
assertEquals(serverPort, connState1.remoteAddress.port) assertEquals(serverPort, connState1.remoteAddress.port)
// Fail over // Fail over
@ -116,10 +116,10 @@ class ProtonWrapperTests {
assertEquals(serverPort, connState2.remoteAddress.port) assertEquals(serverPort, connState2.remoteAddress.port)
val serverConn2 = serverConnected2.get() val serverConn2 = serverConnected2.get()
assertEquals(true, serverConn2.connected) assertEquals(true, serverConn2.connected)
assertEquals(BOB_NAME, CordaX500Name.parse(serverConn2.remoteCert!!.subject.toString())) assertEquals(BOB_NAME, CordaX500Name.build(serverConn2.remoteCert!!.subjectX500Principal))
val connState3 = clientConnected.next() val connState3 = clientConnected.next()
assertEquals(true, connState3.connected) assertEquals(true, connState3.connected)
assertEquals(ALICE_NAME, CordaX500Name.parse(connState3.remoteCert!!.subject.toString())) assertEquals(ALICE_NAME, CordaX500Name.build(connState3.remoteCert!!.subjectX500Principal))
assertEquals(serverPort2, connState3.remoteAddress.port) assertEquals(serverPort2, connState3.remoteAddress.port)
// Fail back // Fail back
@ -130,10 +130,10 @@ class ProtonWrapperTests {
assertEquals(serverPort2, connState4.remoteAddress.port) assertEquals(serverPort2, connState4.remoteAddress.port)
val serverConn3 = serverConnected.get() val serverConn3 = serverConnected.get()
assertEquals(true, serverConn3.connected) assertEquals(true, serverConn3.connected)
assertEquals(BOB_NAME, CordaX500Name.parse(serverConn3.remoteCert!!.subject.toString())) assertEquals(BOB_NAME, CordaX500Name.build(serverConn3.remoteCert!!.subjectX500Principal))
val connState5 = clientConnected.next() val connState5 = clientConnected.next()
assertEquals(true, connState5.connected) assertEquals(true, connState5.connected)
assertEquals(ALICE_NAME, CordaX500Name.parse(connState5.remoteCert!!.subject.toString())) assertEquals(ALICE_NAME, CordaX500Name.build(connState5.remoteCert!!.subjectX500Principal))
assertEquals(serverPort, connState5.remoteAddress.port) assertEquals(serverPort, connState5.remoteAddress.port)
} finally { } finally {
amqpClient.close() amqpClient.close()
@ -149,7 +149,7 @@ class ProtonWrapperTests {
val clientConnected = amqpClient.onConnection.toFuture() val clientConnected = amqpClient.onConnection.toFuture()
amqpClient.start() amqpClient.start()
assertEquals(true, clientConnected.get().connected) assertEquals(true, clientConnected.get().connected)
assertEquals(CHARLIE_NAME, CordaX500Name.parse(clientConnected.get().remoteCert!!.subject.toString())) assertEquals(CHARLIE_NAME, CordaX500Name.build(clientConnected.get().remoteCert!!.subjectX500Principal))
val artemis = artemisClient.started!! val artemis = artemisClient.started!!
val sendAddress = "p2p.inbound" val sendAddress = "p2p.inbound"
artemis.session.createQueue(sendAddress, RoutingType.MULTICAST, "queue", true) artemis.session.createQueue(sendAddress, RoutingType.MULTICAST, "queue", true)
@ -180,13 +180,13 @@ class ProtonWrapperTests {
amqpClient1.start() amqpClient1.start()
val connection1 = connectionEvents.next() val connection1 = connectionEvents.next()
assertEquals(true, connection1.connected) assertEquals(true, connection1.connected)
val connection1ID = CordaX500Name.parse(connection1.remoteCert!!.subject.toString()) val connection1ID = CordaX500Name.build(connection1.remoteCert!!.subjectX500Principal)
assertEquals("client 0", connection1ID.organisationUnit) assertEquals("client 0", connection1ID.organisationUnit)
val source1 = connection1.remoteAddress val source1 = connection1.remoteAddress
amqpClient2.start() amqpClient2.start()
val connection2 = connectionEvents.next() val connection2 = connectionEvents.next()
assertEquals(true, connection2.connected) assertEquals(true, connection2.connected)
val connection2ID = CordaX500Name.parse(connection2.remoteCert!!.subject.toString()) val connection2ID = CordaX500Name.build(connection2.remoteCert!!.subjectX500Principal)
assertEquals("client 1", connection2ID.organisationUnit) assertEquals("client 1", connection2ID.organisationUnit)
val source2 = connection2.remoteAddress val source2 = connection2.remoteAddress
// Stopping one shouldn't disconnect the other // Stopping one shouldn't disconnect the other
@ -207,7 +207,7 @@ class ProtonWrapperTests {
amqpClient1.start() amqpClient1.start()
val connection5 = connectionEvents.next() val connection5 = connectionEvents.next()
assertEquals(true, connection5.connected) assertEquals(true, connection5.connected)
val connection5ID = CordaX500Name.parse(connection5.remoteCert!!.subject.toString()) val connection5ID = CordaX500Name.build(connection5.remoteCert!!.subjectX500Principal)
assertEquals("client 0", connection5ID.organisationUnit) assertEquals("client 0", connection5ID.organisationUnit)
assertEquals(true, amqpClient1.connected) assertEquals(true, amqpClient1.connected)
assertEquals(false, amqpClient2.connected) assertEquals(false, amqpClient2.connected)
@ -252,7 +252,8 @@ class ProtonWrapperTests {
val clientTruststore = loadKeyStore(clientConfig.trustStoreFile, clientConfig.trustStorePassword) val clientTruststore = loadKeyStore(clientConfig.trustStoreFile, clientConfig.trustStorePassword)
val clientKeystore = loadKeyStore(clientConfig.sslKeystore, clientConfig.keyStorePassword) val clientKeystore = loadKeyStore(clientConfig.sslKeystore, clientConfig.keyStorePassword)
val amqpClient = AMQPClient(listOf(NetworkHostAndPort("localhost", serverPort), return AMQPClient(
listOf(NetworkHostAndPort("localhost", serverPort),
NetworkHostAndPort("localhost", serverPort2), NetworkHostAndPort("localhost", serverPort2),
NetworkHostAndPort("localhost", artemisPort)), NetworkHostAndPort("localhost", artemisPort)),
setOf(ALICE_NAME, CHARLIE_NAME), setOf(ALICE_NAME, CHARLIE_NAME),
@ -261,7 +262,6 @@ class ProtonWrapperTests {
clientKeystore, clientKeystore,
clientConfig.keyStorePassword, clientConfig.keyStorePassword,
clientTruststore, true) clientTruststore, true)
return amqpClient
} }
private fun createSharedThreadsClient(sharedEventGroup: EventLoopGroup, id: Int): AMQPClient { private fun createSharedThreadsClient(sharedEventGroup: EventLoopGroup, id: Int): AMQPClient {
@ -275,14 +275,14 @@ class ProtonWrapperTests {
val clientTruststore = loadKeyStore(clientConfig.trustStoreFile, clientConfig.trustStorePassword) val clientTruststore = loadKeyStore(clientConfig.trustStoreFile, clientConfig.trustStorePassword)
val clientKeystore = loadKeyStore(clientConfig.sslKeystore, clientConfig.keyStorePassword) val clientKeystore = loadKeyStore(clientConfig.sslKeystore, clientConfig.keyStorePassword)
val amqpClient = AMQPClient(listOf(NetworkHostAndPort("localhost", serverPort)), return AMQPClient(
listOf(NetworkHostAndPort("localhost", serverPort)),
setOf(ALICE_NAME), setOf(ALICE_NAME),
PEER_USER, PEER_USER,
PEER_USER, PEER_USER,
clientKeystore, clientKeystore,
clientConfig.keyStorePassword, clientConfig.keyStorePassword,
clientTruststore, true, sharedEventGroup) clientTruststore, true, sharedEventGroup)
return amqpClient
} }
private fun createServer(port: Int, name: CordaX500Name = ALICE_NAME): AMQPServer { private fun createServer(port: Int, name: CordaX500Name = ALICE_NAME): AMQPServer {
@ -296,14 +296,13 @@ class ProtonWrapperTests {
val serverTruststore = loadKeyStore(serverConfig.trustStoreFile, serverConfig.trustStorePassword) val serverTruststore = loadKeyStore(serverConfig.trustStoreFile, serverConfig.trustStorePassword)
val serverKeystore = loadKeyStore(serverConfig.sslKeystore, serverConfig.keyStorePassword) val serverKeystore = loadKeyStore(serverConfig.sslKeystore, serverConfig.keyStorePassword)
val amqpServer = AMQPServer("0.0.0.0", return AMQPServer(
"0.0.0.0",
port, port,
PEER_USER, PEER_USER,
PEER_USER, PEER_USER,
serverKeystore, serverKeystore,
serverConfig.keyStorePassword, serverConfig.keyStorePassword,
serverTruststore) serverTruststore)
return amqpServer
} }
}
}

View File

@ -2,9 +2,7 @@ package net.corda.node.utilities.registration
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.cert
import net.corda.core.internal.concurrent.transpose import net.corda.core.internal.concurrent.transpose
import net.corda.core.internal.toX509CertHolder
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.OpaqueBytes
@ -38,8 +36,10 @@ import java.security.KeyPair
import java.security.cert.CertPath import java.security.cert.CertPath
import java.security.cert.CertPathValidatorException import java.security.cert.CertPathValidatorException
import java.security.cert.Certificate import java.security.cert.Certificate
import java.security.cert.X509Certificate
import java.util.zip.ZipEntry import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream import java.util.zip.ZipOutputStream
import javax.security.auth.x500.X500Principal
import javax.ws.rs.* import javax.ws.rs.*
import javax.ws.rs.core.MediaType import javax.ws.rs.core.MediaType
import javax.ws.rs.core.Response import javax.ws.rs.core.Response
@ -59,14 +59,13 @@ class NodeRegistrationTest : IntegrationTest() {
val testSerialization = SerializationEnvironmentRule(true) val testSerialization = SerializationEnvironmentRule(true)
private val portAllocation = PortAllocation.Incremental(13000) private val portAllocation = PortAllocation.Incremental(13000)
private val registrationHandler = RegistrationHandler(ROOT_CA) private val registrationHandler = RegistrationHandler(DEV_ROOT_CA)
private lateinit var server: NetworkMapServer private lateinit var server: NetworkMapServer
private lateinit var serverHostAndPort: NetworkHostAndPort private lateinit var serverHostAndPort: NetworkHostAndPort
@Before @Before
fun startServer() { fun startServer() {
server = NetworkMapServer(1.minutes, portAllocation.nextHostAndPort(), ROOT_CA, "localhost", registrationHandler) server = NetworkMapServer(1.minutes, portAllocation.nextHostAndPort(), DEV_ROOT_CA, "localhost", registrationHandler)
serverHostAndPort = server.start() serverHostAndPort = server.start()
} }
@ -80,7 +79,7 @@ class NodeRegistrationTest : IntegrationTest() {
val compatibilityZone = CompatibilityZoneParams( val compatibilityZone = CompatibilityZoneParams(
URL("http://$serverHostAndPort"), URL("http://$serverHostAndPort"),
publishNotaries = { server.networkParameters = testNetworkParameters(it) }, publishNotaries = { server.networkParameters = testNetworkParameters(it) },
rootCert = ROOT_CA.certificate.cert) rootCert = DEV_ROOT_CA.certificate)
internalDriver( internalDriver(
portAllocation = portAllocation, portAllocation = portAllocation,
compatibilityZone = compatibilityZone, compatibilityZone = compatibilityZone,
@ -116,12 +115,12 @@ class NodeRegistrationTest : IntegrationTest() {
@Test @Test
fun `node registration wrong root cert`() { fun `node registration wrong root cert`() {
val someRootCert = X509Utilities.createSelfSignedCACertificate( val someRootCert = X509Utilities.createSelfSignedCACertificate(
CordaX500Name("Integration Test Corda Node Root CA", "R3 Ltd", "London", "GB"), X500Principal("CN=Integration Test Corda Node Root CA,O=R3 Ltd,L=London,C=GB"),
Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
val compatibilityZone = CompatibilityZoneParams( val compatibilityZone = CompatibilityZoneParams(
URL("http://$serverHostAndPort"), URL("http://$serverHostAndPort"),
publishNotaries = { server.networkParameters = testNetworkParameters(it) }, publishNotaries = { server.networkParameters = testNetworkParameters(it) },
rootCert = someRootCert.cert) rootCert = someRootCert)
internalDriver( internalDriver(
portAllocation = portAllocation, portAllocation = portAllocation,
compatibilityZone = compatibilityZone, compatibilityZone = compatibilityZone,
@ -149,7 +148,7 @@ class RegistrationHandler(private val rootCertAndKeyPair: CertificateAndKeyPair)
val (certPath, name) = createSignedClientCertificate( val (certPath, name) = createSignedClientCertificate(
certificationRequest, certificationRequest,
rootCertAndKeyPair.keyPair, rootCertAndKeyPair.keyPair,
arrayOf(rootCertAndKeyPair.certificate.cert)) arrayOf(rootCertAndKeyPair.certificate))
require(!name.organisation.contains("\\s".toRegex())) { "Whitespace in the organisation name not supported" } require(!name.organisation.contains("\\s".toRegex())) { "Whitespace in the organisation name not supported" }
certPaths[name.organisation] = certPath certPaths[name.organisation] = certPath
return Response.ok(name.organisation).build() return Response.ok(name.organisation).build()
@ -183,12 +182,12 @@ class RegistrationHandler(private val rootCertAndKeyPair: CertificateAndKeyPair)
val name = CordaX500Name.parse(request.subject.toString()) val name = CordaX500Name.parse(request.subject.toString())
val nodeCaCert = X509Utilities.createCertificate( val nodeCaCert = X509Utilities.createCertificate(
CertificateType.NODE_CA, CertificateType.NODE_CA,
caCertPath.first().toX509CertHolder(), caCertPath[0] as X509Certificate ,
caKeyPair, caKeyPair,
name, name.x500Principal,
request.publicKey, request.publicKey,
nameConstraints = null) nameConstraints = null)
val certPath = X509CertificateFactory().generateCertPath(nodeCaCert.cert, *caCertPath) val certPath = X509CertificateFactory().generateCertPath(nodeCaCert, *caCertPath)
return Pair(certPath, name) return Pair(certPath, name)
} }
} }

View File

@ -94,22 +94,34 @@ class MQSecurityAsNodeTest : MQSecurityTest() {
javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"),
"cordacadevpass") "cordacadevpass")
val rootCACert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA).toX509CertHolder() val rootCACert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA)
val intermediateCA = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass") val intermediateCA = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
val clientKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val clientKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
// Set name constrain to the legal name. // Set name constrain to the legal name.
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.x500Name))), arrayOf()) val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.x500Name))), arrayOf())
val clientCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, intermediateCA.certificate, val clientCACert = X509Utilities.createCertificate(
intermediateCA.keyPair, legalName, clientKey.public, nameConstraints = nameConstraints) CertificateType.INTERMEDIATE_CA,
val tlsKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) intermediateCA.certificate,
intermediateCA.keyPair,
legalName.x500Principal,
clientKeyPair.public,
nameConstraints = nameConstraints)
val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
// Using different x500 name in the TLS cert which is not allowed in the name constraints. // Using different x500 name in the TLS cert which is not allowed in the name constraints.
val clientTLSCert = X509Utilities.createCertificate(CertificateType.TLS, clientCACert, clientKey, CordaX500Name("MiniCorp", "London", "GB"), tlsKey.public) val clientTLSCert = X509Utilities.createCertificate(
CertificateType.TLS,
clientCACert,
clientKeyPair,
CordaX500Name("MiniCorp", "London", "GB").x500Principal,
tlsKeyPair.public)
val keyPass = keyStorePassword.toCharArray() val keyPass = keyStorePassword.toCharArray()
val clientCAKeystore = loadOrCreateKeyStore(nodeKeystore, keyStorePassword) val clientCAKeystore = loadOrCreateKeyStore(nodeKeystore, keyStorePassword)
clientCAKeystore.addOrReplaceKey( clientCAKeystore.addOrReplaceKey(
X509Utilities.CORDA_CLIENT_CA, X509Utilities.CORDA_CLIENT_CA,
clientKey.private, clientKeyPair.private,
keyPass, keyPass,
arrayOf(clientCACert, intermediateCA.certificate, rootCACert)) arrayOf(clientCACert, intermediateCA.certificate, rootCACert))
clientCAKeystore.save(nodeKeystore, keyStorePassword) clientCAKeystore.save(nodeKeystore, keyStorePassword)
@ -117,7 +129,7 @@ class MQSecurityAsNodeTest : MQSecurityTest() {
val tlsKeystore = loadOrCreateKeyStore(sslKeystore, keyStorePassword) val tlsKeystore = loadOrCreateKeyStore(sslKeystore, keyStorePassword)
tlsKeystore.addOrReplaceKey( tlsKeystore.addOrReplaceKey(
X509Utilities.CORDA_CLIENT_TLS, X509Utilities.CORDA_CLIENT_TLS,
tlsKey.private, tlsKeyPair.private,
keyPass, keyPass,
arrayOf(clientTLSCert, clientCACert, intermediateCA.certificate, rootCACert)) arrayOf(clientTLSCert, clientCACert, intermediateCA.certificate, rootCACert))
tlsKeystore.save(sslKeystore, keyStorePassword) tlsKeystore.save(sslKeystore, keyStorePassword)

View File

@ -726,7 +726,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
val caKeyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword) val caKeyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword)
val trustRoot = trustStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA) val trustRoot = trustStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA)
val clientCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA) val clientCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)
val caCertificates = arrayOf(identityCert, clientCa.certificate.cert) val caCertificates = arrayOf(identityCert, clientCa.certificate)
return PersistentIdentityService(trustRoot, *caCertificates) return PersistentIdentityService(trustRoot, *caCertificates)
} }
@ -784,7 +784,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
listOf(certificate) + keyStore.getCertificateChain(privateKeyAlias).drop(1) listOf(certificate) + keyStore.getCertificateChain(privateKeyAlias).drop(1)
} else { } else {
keyStore.getCertificateChain(privateKeyAlias).let { keyStore.getCertificateChain(privateKeyAlias).let {
check(it[0].toX509CertHolder() == x509Cert) { "Certificates from key store do not line up!" } check(it[0] == x509Cert) { "Certificates from key store do not line up!" }
it.asList() it.asList()
} }
} }

View File

@ -9,7 +9,6 @@ import io.netty.handler.ssl.SslHandler
import io.netty.handler.ssl.SslHandshakeCompletionEvent import io.netty.handler.ssl.SslHandshakeCompletionEvent
import io.netty.util.ReferenceCountUtil import io.netty.util.ReferenceCountUtil
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.toX509CertHolder
import net.corda.core.utilities.debug import net.corda.core.utilities.debug
import net.corda.node.internal.protonwrapper.engine.EventProcessor import net.corda.node.internal.protonwrapper.engine.EventProcessor
import net.corda.node.internal.protonwrapper.messages.ReceivedMessage import net.corda.node.internal.protonwrapper.messages.ReceivedMessage
@ -19,9 +18,9 @@ import org.apache.qpid.proton.engine.ProtonJTransport
import org.apache.qpid.proton.engine.Transport import org.apache.qpid.proton.engine.Transport
import org.apache.qpid.proton.engine.impl.ProtocolTracer import org.apache.qpid.proton.engine.impl.ProtocolTracer
import org.apache.qpid.proton.framing.TransportFrame import org.apache.qpid.proton.framing.TransportFrame
import org.bouncycastle.cert.X509CertificateHolder
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.net.InetSocketAddress import java.net.InetSocketAddress
import java.security.cert.X509Certificate
/** /**
* An instance of AMQPChannelHandler sits inside the netty pipeline and controls the socket level lifecycle. * An instance of AMQPChannelHandler sits inside the netty pipeline and controls the socket level lifecycle.
@ -38,20 +37,20 @@ internal class AMQPChannelHandler(private val serverMode: Boolean,
private val onReceive: (ReceivedMessage) -> Unit) : ChannelDuplexHandler() { private val onReceive: (ReceivedMessage) -> Unit) : ChannelDuplexHandler() {
private val log = LoggerFactory.getLogger(allowedRemoteLegalNames?.firstOrNull()?.toString() ?: "AMQPChannelHandler") private val log = LoggerFactory.getLogger(allowedRemoteLegalNames?.firstOrNull()?.toString() ?: "AMQPChannelHandler")
private lateinit var remoteAddress: InetSocketAddress private lateinit var remoteAddress: InetSocketAddress
private lateinit var localCert: X509CertificateHolder private lateinit var localCert: X509Certificate
private lateinit var remoteCert: X509CertificateHolder private lateinit var remoteCert: X509Certificate
private var eventProcessor: EventProcessor? = null private var eventProcessor: EventProcessor? = null
override fun channelActive(ctx: ChannelHandlerContext) { override fun channelActive(ctx: ChannelHandlerContext) {
val ch = ctx.channel() val ch = ctx.channel()
remoteAddress = ch.remoteAddress() as InetSocketAddress remoteAddress = ch.remoteAddress() as InetSocketAddress
val localAddress = ch.localAddress() as InetSocketAddress val localAddress = ch.localAddress() as InetSocketAddress
log.info("New client connection ${ch.id()} from ${remoteAddress} to ${localAddress}") log.info("New client connection ${ch.id()} from $remoteAddress to $localAddress")
} }
private fun createAMQPEngine(ctx: ChannelHandlerContext) { private fun createAMQPEngine(ctx: ChannelHandlerContext) {
val ch = ctx.channel() val ch = ctx.channel()
eventProcessor = EventProcessor(ch, serverMode, localCert.subject.toString(), remoteCert.subject.toString(), userName, password) eventProcessor = EventProcessor(ch, serverMode, localCert.subjectX500Principal.toString(), remoteCert.subjectX500Principal.toString(), userName, password)
val connection = eventProcessor!!.connection val connection = eventProcessor!!.connection
val transport = connection.transport as ProtonJTransport val transport = connection.transport as ProtonJTransport
if (trace) { if (trace) {
@ -71,7 +70,7 @@ internal class AMQPChannelHandler(private val serverMode: Boolean,
override fun channelInactive(ctx: ChannelHandlerContext) { override fun channelInactive(ctx: ChannelHandlerContext) {
val ch = ctx.channel() val ch = ctx.channel()
log.info("Closed client connection ${ch.id()} from ${remoteAddress} to ${ch.localAddress()}") log.info("Closed client connection ${ch.id()} from $remoteAddress to ${ch.localAddress()}")
onClose(Pair(ch as SocketChannel, ConnectionChange(remoteAddress, null, false))) onClose(Pair(ch as SocketChannel, ConnectionChange(remoteAddress, null, false)))
eventProcessor?.close() eventProcessor?.close()
ctx.fireChannelInactive() ctx.fireChannelInactive()
@ -81,12 +80,12 @@ internal class AMQPChannelHandler(private val serverMode: Boolean,
if (evt is SslHandshakeCompletionEvent) { if (evt is SslHandshakeCompletionEvent) {
if (evt.isSuccess) { if (evt.isSuccess) {
val sslHandler = ctx.pipeline().get(SslHandler::class.java) val sslHandler = ctx.pipeline().get(SslHandler::class.java)
localCert = sslHandler.engine().session.localCertificates.first().toX509CertHolder() localCert = sslHandler.engine().session.localCertificates[0] as X509Certificate
remoteCert = sslHandler.engine().session.peerCertificates.first().toX509CertHolder() remoteCert = sslHandler.engine().session.peerCertificates[0] as X509Certificate
try { try {
val remoteX500Name = CordaX500Name.parse(remoteCert.subject.toString()) val remoteX500Name = CordaX500Name.build(remoteCert.subjectX500Principal)
require(allowedRemoteLegalNames == null || remoteX500Name in allowedRemoteLegalNames) require(allowedRemoteLegalNames == null || remoteX500Name in allowedRemoteLegalNames)
log.info("handshake completed subject: ${remoteX500Name}") log.info("handshake completed subject: $remoteX500Name")
} catch (ex: IllegalArgumentException) { } catch (ex: IllegalArgumentException) {
log.error("Invalid certificate subject", ex) log.error("Invalid certificate subject", ex)
ctx.close() ctx.close()
@ -124,7 +123,7 @@ internal class AMQPChannelHandler(private val serverMode: Boolean,
require(inetAddress == remoteAddress) { require(inetAddress == remoteAddress) {
"Message for incorrect endpoint" "Message for incorrect endpoint"
} }
require(CordaX500Name.parse(msg.destinationLegalName) == CordaX500Name.parse(remoteCert.subject.toString())) { require(CordaX500Name.parse(msg.destinationLegalName) == CordaX500Name.build(remoteCert.subjectX500Principal)) {
"Message for incorrect legal identity" "Message for incorrect legal identity"
} }
log.debug { "channel write ${msg.applicationProperties["_AMQ_DUPL_ID"]}" } log.debug { "channel write ${msg.applicationProperties["_AMQ_DUPL_ID"]}" }

View File

@ -1,6 +1,6 @@
package net.corda.node.internal.protonwrapper.netty package net.corda.node.internal.protonwrapper.netty
import org.bouncycastle.cert.X509CertificateHolder
import java.net.InetSocketAddress import java.net.InetSocketAddress
import java.security.cert.X509Certificate
data class ConnectionChange(val remoteAddress: InetSocketAddress, val remoteCert: X509CertificateHolder?, val connected: Boolean) data class ConnectionChange(val remoteAddress: InetSocketAddress, val remoteCert: X509Certificate?, val connected: Boolean)

View File

@ -11,7 +11,6 @@ import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.createDirectories import net.corda.core.internal.createDirectories
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.internal.exists import net.corda.core.internal.exists
import net.corda.core.internal.toX509CertHolder
import net.corda.nodeapi.internal.config.SSLConfiguration import net.corda.nodeapi.internal.config.SSLConfiguration
import net.corda.nodeapi.internal.config.toProperties import net.corda.nodeapi.internal.config.toProperties
import net.corda.nodeapi.internal.createDevKeyStores import net.corda.nodeapi.internal.createDevKeyStores
@ -73,7 +72,7 @@ fun SSLConfiguration.configureDevKeyAndTrustStores(myLegalName: CordaX500Name) {
} }
if (!sslKeystore.exists() || !nodeKeystore.exists()) { if (!sslKeystore.exists() || !nodeKeystore.exists()) {
val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass") val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
val rootCert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA).toX509CertHolder() val rootCert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA)
val intermediateCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass") val intermediateCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
createDevKeyStores(rootCert, intermediateCa, myLegalName) createDevKeyStores(rootCert, intermediateCa, myLegalName)

View File

@ -4,15 +4,12 @@ import net.corda.core.contracts.PartyAndReference
import net.corda.core.crypto.toStringShort import net.corda.core.crypto.toStringShort
import net.corda.core.identity.* import net.corda.core.identity.*
import net.corda.core.internal.CertRole import net.corda.core.internal.CertRole
import net.corda.core.internal.cert
import net.corda.core.internal.toX509CertHolder
import net.corda.core.node.services.UnknownAnonymousPartyException import net.corda.core.node.services.UnknownAnonymousPartyException
import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.utilities.contextLogger import net.corda.core.utilities.contextLogger
import net.corda.core.utilities.trace import net.corda.core.utilities.trace
import net.corda.node.services.api.IdentityServiceInternal import net.corda.node.services.api.IdentityServiceInternal
import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509CertificateFactory
import org.bouncycastle.cert.X509CertificateHolder
import java.security.InvalidAlgorithmParameterException import java.security.InvalidAlgorithmParameterException
import java.security.PublicKey import java.security.PublicKey
import java.security.cert.* import java.security.cert.*
@ -27,7 +24,7 @@ import javax.annotation.concurrent.ThreadSafe
// TODO There is duplicated logic between this and PersistentIdentityService // TODO There is duplicated logic between this and PersistentIdentityService
@ThreadSafe @ThreadSafe
class InMemoryIdentityService(identities: Array<out PartyAndCertificate>, class InMemoryIdentityService(identities: Array<out PartyAndCertificate>,
trustRoot: X509CertificateHolder) : SingletonSerializeAsToken(), IdentityServiceInternal { override val trustRoot: X509Certificate) : SingletonSerializeAsToken(), IdentityServiceInternal {
companion object { companion object {
private val log = contextLogger() private val log = contextLogger()
} }
@ -35,14 +32,12 @@ class InMemoryIdentityService(identities: Array<out PartyAndCertificate>,
/** /**
* Certificate store for certificate authority and intermediary certificates. * Certificate store for certificate authority and intermediary certificates.
*/ */
override val caCertStore: CertStore override val caCertStore: CertStore = CertStore.getInstance("Collection", CollectionCertStoreParameters(setOf(trustRoot)))
override val trustRoot = trustRoot.cert override val trustAnchor: TrustAnchor = TrustAnchor(trustRoot, null)
override val trustAnchor: TrustAnchor = TrustAnchor(this.trustRoot, null)
private val keyToParties = ConcurrentHashMap<PublicKey, PartyAndCertificate>() private val keyToParties = ConcurrentHashMap<PublicKey, PartyAndCertificate>()
private val principalToParties = ConcurrentHashMap<CordaX500Name, PartyAndCertificate>() private val principalToParties = ConcurrentHashMap<CordaX500Name, PartyAndCertificate>()
init { init {
caCertStore = CertStore.getInstance("Collection", CollectionCertStoreParameters(setOf(this.trustRoot)))
keyToParties.putAll(identities.associateBy { it.owningKey }) keyToParties.putAll(identities.associateBy { it.owningKey })
principalToParties.putAll(identities.associateBy { it.name }) principalToParties.putAll(identities.associateBy { it.name })
} }
@ -57,7 +52,7 @@ class InMemoryIdentityService(identities: Array<out PartyAndCertificate>,
log.warn("Certificate path :") log.warn("Certificate path :")
identity.certPath.certificates.reversed().forEachIndexed { index, certificate -> identity.certPath.certificates.reversed().forEachIndexed { index, certificate ->
val space = (0 until index).joinToString("") { " " } val space = (0 until index).joinToString("") { " " }
log.warn("$space${certificate.toX509CertHolder().subject}") log.warn("$space${(certificate as X509Certificate).subjectX500Principal}")
} }
throw e throw e
} }

View File

@ -5,8 +5,6 @@ import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.toStringShort import net.corda.core.crypto.toStringShort
import net.corda.core.identity.* import net.corda.core.identity.*
import net.corda.core.internal.CertRole import net.corda.core.internal.CertRole
import net.corda.core.internal.cert
import net.corda.core.internal.toX509CertHolder
import net.corda.core.node.services.UnknownAnonymousPartyException import net.corda.core.node.services.UnknownAnonymousPartyException
import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.utilities.MAX_HASH_HEX_SIZE import net.corda.core.utilities.MAX_HASH_HEX_SIZE
@ -16,7 +14,6 @@ import net.corda.node.services.api.IdentityServiceInternal
import net.corda.node.utilities.AppendOnlyPersistentMap import net.corda.node.utilities.AppendOnlyPersistentMap
import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509CertificateFactory
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
import org.bouncycastle.cert.X509CertificateHolder
import java.security.InvalidAlgorithmParameterException import java.security.InvalidAlgorithmParameterException
import java.security.PublicKey import java.security.PublicKey
import java.security.cert.* import java.security.cert.*
@ -30,7 +27,6 @@ import javax.persistence.Lob
@ThreadSafe @ThreadSafe
class PersistentIdentityService(override val trustRoot: X509Certificate, class PersistentIdentityService(override val trustRoot: X509Certificate,
vararg caCertificates: X509Certificate) : SingletonSerializeAsToken(), IdentityServiceInternal { vararg caCertificates: X509Certificate) : SingletonSerializeAsToken(), IdentityServiceInternal {
constructor(trustRoot: X509CertificateHolder) : this(trustRoot.cert)
companion object { companion object {
private val log = contextLogger() private val log = contextLogger()
@ -121,7 +117,7 @@ class PersistentIdentityService(override val trustRoot: X509Certificate,
log.warn(e.localizedMessage) log.warn(e.localizedMessage)
log.warn("Path = ") log.warn("Path = ")
identity.certPath.certificates.reversed().forEach { identity.certPath.certificates.reversed().forEach {
log.warn(it.toX509CertHolder().subject.toString()) log.warn((it as X509Certificate).subjectX500Principal.toString())
} }
throw e throw e
} }

View File

@ -3,8 +3,6 @@ package net.corda.node.services.keys
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.identity.PartyAndCertificate import net.corda.core.identity.PartyAndCertificate
import net.corda.core.internal.CertRole import net.corda.core.internal.CertRole
import net.corda.core.internal.cert
import net.corda.core.internal.toX509CertHolder
import net.corda.core.utilities.days import net.corda.core.utilities.days
import net.corda.node.services.api.IdentityServiceInternal import net.corda.node.services.api.IdentityServiceInternal
import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.CertificateType
@ -36,11 +34,16 @@ fun freshCertificate(identityService: IdentityServiceInternal,
revocationEnabled: Boolean = false): PartyAndCertificate { revocationEnabled: Boolean = false): PartyAndCertificate {
val issuerRole = CertRole.extract(issuer.certificate) val issuerRole = CertRole.extract(issuer.certificate)
require(issuerRole == CertRole.LEGAL_IDENTITY) { "Confidential identities can only be issued from well known identities, provided issuer ${issuer.name} has role $issuerRole" } require(issuerRole == CertRole.LEGAL_IDENTITY) { "Confidential identities can only be issued from well known identities, provided issuer ${issuer.name} has role $issuerRole" }
val issuerCert = issuer.certificate.toX509CertHolder() val issuerCert = issuer.certificate
val window = X509Utilities.getCertificateValidityWindow(Duration.ZERO, 3650.days, issuerCert) val window = X509Utilities.getCertificateValidityWindow(Duration.ZERO, 3650.days, issuerCert)
val ourCertificate = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuerCert.subject, val ourCertificate = X509Utilities.createCertificate(
issuerSigner, issuer.name, subjectPublicKey, window) CertificateType.CONFIDENTIAL_LEGAL_IDENTITY,
val ourCertPath = X509CertificateFactory().generateCertPath(listOf(ourCertificate.cert) + issuer.certPath.certificates) issuerCert.subjectX500Principal,
issuerSigner,
issuer.name.x500Principal,
subjectPublicKey,
window)
val ourCertPath = X509CertificateFactory().generateCertPath(listOf(ourCertificate) + issuer.certPath.certificates)
val anonymisedIdentity = PartyAndCertificate(ourCertPath) val anonymisedIdentity = PartyAndCertificate(ourCertPath)
identityService.justVerifyAndRegisterIdentity(anonymisedIdentity) identityService.justVerifyAndRegisterIdentity(anonymisedIdentity)
return anonymisedIdentity return anonymisedIdentity

View File

@ -22,9 +22,9 @@ import java.security.cert.X509Certificate
* needed. * needed.
*/ */
class NetworkRegistrationHelper(private val config: NodeConfiguration, private val certService: NetworkRegistrationService) { class NetworkRegistrationHelper(private val config: NodeConfiguration, private val certService: NetworkRegistrationService) {
companion object { private companion object {
val pollInterval = 10.seconds val pollInterval = 10.seconds
val SELF_SIGNED_PRIVATE_KEY = "Self Signed Private Key" const val SELF_SIGNED_PRIVATE_KEY = "Self Signed Private Key"
} }
private val requestIdStore = config.certificatesDirectory / "certificate-request-id.txt" private val requestIdStore = config.certificatesDirectory / "certificate-request-id.txt"
@ -62,54 +62,81 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v
*/ */
fun buildKeystore() { fun buildKeystore() {
config.certificatesDirectory.createDirectories() config.certificatesDirectory.createDirectories()
val caKeyStore = loadOrCreateKeyStore(config.nodeKeystore, keystorePassword) val nodeKeyStore = loadOrCreateKeyStore(config.nodeKeystore, keystorePassword)
if (!caKeyStore.containsAlias(CORDA_CLIENT_CA)) { if (nodeKeyStore.containsAlias(CORDA_CLIENT_CA)) {
// Create or load self signed keypair from the key store.
// We use the self sign certificate to store the key temporarily in the keystore while waiting for the request approval.
if (!caKeyStore.containsAlias(SELF_SIGNED_PRIVATE_KEY)) {
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val selfSignCert = X509Utilities.createSelfSignedCACertificate(config.myLegalName, keyPair)
// Save to the key store.
caKeyStore.addOrReplaceKey(SELF_SIGNED_PRIVATE_KEY, keyPair.private, privateKeyPassword.toCharArray(),
arrayOf(selfSignCert))
caKeyStore.save(config.nodeKeystore, keystorePassword)
}
val keyPair = caKeyStore.getKeyPair(SELF_SIGNED_PRIVATE_KEY, privateKeyPassword)
val requestId = submitOrResumeCertificateSigningRequest(keyPair)
val certificates = try {
pollServerForCertificates(requestId)
} catch (certificateRequestException: CertificateRequestException) {
System.err.println(certificateRequestException.message)
System.err.println("Please make sure the details in configuration file are correct and try again.")
System.err.println("Corda node will now terminate.")
requestIdStore.deleteIfExists()
throw certificateRequestException
}
println("Certificate signing request approved, storing private key with the certificate chain.")
// Save private key and certificate chain to the key store.
caKeyStore.addOrReplaceKey(CORDA_CLIENT_CA, keyPair.private, privateKeyPassword.toCharArray(), certificates)
caKeyStore.deleteEntry(SELF_SIGNED_PRIVATE_KEY)
caKeyStore.save(config.nodeKeystore, keystorePassword)
println("Node private key and certificate stored in ${config.nodeKeystore}.")
println("Checking root of the certificate path is what we expect.")
X509Utilities.validateCertificateChain(rootCert, *certificates)
println("Generating SSL certificate for node messaging service.")
val sslKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val caCert = caKeyStore.getX509Certificate(CORDA_CLIENT_CA).toX509CertHolder()
val sslCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, keyPair, CordaX500Name.build(caCert.cert.subjectX500Principal), sslKey.public)
val sslKeyStore = loadOrCreateKeyStore(config.sslKeystore, keystorePassword)
sslKeyStore.addOrReplaceKey(CORDA_CLIENT_TLS, sslKey.private, privateKeyPassword.toCharArray(), arrayOf(sslCert.cert, *certificates))
sslKeyStore.save(config.sslKeystore, config.keyStorePassword)
println("SSL private key and certificate stored in ${config.sslKeystore}.")
// All done, clean up temp files.
requestIdStore.deleteIfExists()
} else {
println("Certificate already exists, Corda node will now terminate...") println("Certificate already exists, Corda node will now terminate...")
return
} }
// Create or load self signed keypair from the key store.
// We use the self sign certificate to store the key temporarily in the keystore while waiting for the request approval.
if (!nodeKeyStore.containsAlias(SELF_SIGNED_PRIVATE_KEY)) {
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val selfSignCert = X509Utilities.createSelfSignedCACertificate(config.myLegalName.x500Principal, keyPair)
// Save to the key store.
nodeKeyStore.addOrReplaceKey(SELF_SIGNED_PRIVATE_KEY, keyPair.private, privateKeyPassword.toCharArray(),
arrayOf(selfSignCert))
nodeKeyStore.save(config.nodeKeystore, keystorePassword)
}
val keyPair = nodeKeyStore.getKeyPair(SELF_SIGNED_PRIVATE_KEY, privateKeyPassword)
val requestId = submitOrResumeCertificateSigningRequest(keyPair)
val certificates = try {
pollServerForCertificates(requestId)
} catch (certificateRequestException: CertificateRequestException) {
System.err.println(certificateRequestException.message)
System.err.println("Please make sure the details in configuration file are correct and try again.")
System.err.println("Corda node will now terminate.")
requestIdStore.deleteIfExists()
throw certificateRequestException
}
val nodeCaCert = certificates[0] as X509Certificate
val nodeCaSubject = try {
CordaX500Name.build(nodeCaCert.subjectX500Principal)
} catch (e: IllegalArgumentException) {
throw CertificateRequestException("Received node CA cert has invalid subject name: ${e.message}")
}
if (nodeCaSubject != config.myLegalName) {
throw CertificateRequestException("Subject of received node CA cert doesn't match with node legal name: $nodeCaSubject")
}
val nodeCaCertRole = try {
CertRole.extract(nodeCaCert)
} catch (e: IllegalArgumentException) {
throw CertificateRequestException("Unable to extract cert role from received node CA cert: ${e.message}")
}
if (nodeCaCertRole != CertRole.NODE_CA) {
throw CertificateRequestException("Received node CA cert has invalid role: $nodeCaCertRole")
}
println("Checking root of the certificate path is what we expect.")
X509Utilities.validateCertificateChain(rootCert, *certificates)
println("Certificate signing request approved, storing private key with the certificate chain.")
// Save private key and certificate chain to the key store.
nodeKeyStore.addOrReplaceKey(CORDA_CLIENT_CA, keyPair.private, privateKeyPassword.toCharArray(), certificates)
nodeKeyStore.deleteEntry(SELF_SIGNED_PRIVATE_KEY)
nodeKeyStore.save(config.nodeKeystore, keystorePassword)
println("Node private key and certificate stored in ${config.nodeKeystore}.")
println("Generating SSL certificate for node messaging service.")
val sslKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val sslCert = X509Utilities.createCertificate(
CertificateType.TLS,
nodeCaCert,
keyPair,
config.myLegalName.x500Principal,
sslKeyPair.public)
val sslKeyStore = loadOrCreateKeyStore(config.sslKeystore, keystorePassword)
sslKeyStore.addOrReplaceKey(CORDA_CLIENT_TLS, sslKeyPair.private, privateKeyPassword.toCharArray(), arrayOf(sslCert, *certificates))
sslKeyStore.save(config.sslKeystore, config.keyStorePassword)
println("SSL private key and certificate stored in ${config.sslKeystore}.")
// All done, clean up temp files.
requestIdStore.deleteIfExists()
} }
/** /**
@ -138,7 +165,7 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v
private fun submitOrResumeCertificateSigningRequest(keyPair: KeyPair): String { private fun submitOrResumeCertificateSigningRequest(keyPair: KeyPair): String {
// Retrieve request id from file if exists, else post a request to server. // Retrieve request id from file if exists, else post a request to server.
return if (!requestIdStore.exists()) { return if (!requestIdStore.exists()) {
val request = X509Utilities.createCertificateSigningRequest(config.myLegalName, config.emailAddress, keyPair) val request = X509Utilities.createCertificateSigningRequest(config.myLegalName.x500Principal, config.emailAddress, keyPair)
val writer = StringWriter() val writer = StringWriter()
JcaPEMWriter(writer).use { JcaPEMWriter(writer).use {
it.writeObject(PemObject("CERTIFICATE REQUEST", request.encoded)) it.writeObject(PemObject("CERTIFICATE REQUEST", request.encoded))

View File

@ -6,8 +6,6 @@ import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate import net.corda.core.identity.PartyAndCertificate
import net.corda.core.internal.cert
import net.corda.core.internal.toX509CertHolder
import net.corda.core.node.services.UnknownAnonymousPartyException import net.corda.core.node.services.UnknownAnonymousPartyException
import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.CertificateType
import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509CertificateFactory
@ -32,7 +30,7 @@ class InMemoryIdentityServiceTests {
val BOB get() = bob.party val BOB get() = bob.party
val BOB_IDENTITY get() = bob.identity val BOB_IDENTITY get() = bob.identity
val BOB_PUBKEY get() = bob.publicKey val BOB_PUBKEY get() = bob.publicKey
fun createService(vararg identities: PartyAndCertificate) = InMemoryIdentityService(identities, DEV_TRUST_ROOT) fun createService(vararg identities: PartyAndCertificate) = InMemoryIdentityService(identities, DEV_ROOT_CA.certificate)
} }
@Rule @Rule
@ -100,11 +98,11 @@ class InMemoryIdentityServiceTests {
@Test @Test
fun `assert unknown anonymous key is unrecognised`() { fun `assert unknown anonymous key is unrecognised`() {
val rootKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val rootKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootKey) val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Principal, rootKey)
val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val service = createService() val service = createService()
// TODO: Generate certificate with an EdDSA key rather than ECDSA // TODO: Generate certificate with an EdDSA key rather than ECDSA
val identity = Party(rootCert.cert) val identity = Party(rootCert)
val txIdentity = AnonymousParty(txKey.public) val txIdentity = AnonymousParty(txKey.public)
assertFailsWith<UnknownAnonymousPartyException> { assertFailsWith<UnknownAnonymousPartyException> {
@ -159,8 +157,8 @@ class InMemoryIdentityServiceTests {
} }
assertFailsWith<IllegalArgumentException> { assertFailsWith<IllegalArgumentException> {
val owningKey = Crypto.decodePublicKey(DEV_CA.certificate.subjectPublicKeyInfo.encoded) val owningKey = DEV_INTERMEDIATE_CA.certificate.publicKey
val subject = CordaX500Name.build(DEV_CA.certificate.cert.subjectX500Principal) val subject = CordaX500Name.build(DEV_INTERMEDIATE_CA.certificate.subjectX500Principal)
service.assertOwnership(Party(subject, owningKey), anonymousAlice.party.anonymise()) service.assertOwnership(Party(subject, owningKey), anonymousAlice.party.anonymise())
} }
} }
@ -168,9 +166,14 @@ class InMemoryIdentityServiceTests {
private fun createConfidentialIdentity(x500Name: CordaX500Name): Pair<PartyAndCertificate, PartyAndCertificate> { private fun createConfidentialIdentity(x500Name: CordaX500Name): Pair<PartyAndCertificate, PartyAndCertificate> {
val issuerKeyPair = generateKeyPair() val issuerKeyPair = generateKeyPair()
val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public) val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public)
val txKey = Crypto.generateKeyPair() val txKeyPair = Crypto.generateKeyPair()
val txCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuer.certificate.toX509CertHolder(), issuerKeyPair, x500Name, txKey.public) val txCert = X509Utilities.createCertificate(
val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates) CertificateType.CONFIDENTIAL_LEGAL_IDENTITY,
issuer.certificate,
issuerKeyPair,
x500Name.x500Principal,
txKeyPair.public)
val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert) + issuer.certPath.certificates)
return Pair(issuer, PartyAndCertificate(txCertPath)) return Pair(issuer, PartyAndCertificate(txCertPath))
} }

View File

@ -6,8 +6,6 @@ import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate import net.corda.core.identity.PartyAndCertificate
import net.corda.core.internal.cert
import net.corda.core.internal.toX509CertHolder
import net.corda.core.node.services.IdentityService import net.corda.core.node.services.IdentityService
import net.corda.core.node.services.UnknownAnonymousPartyException import net.corda.core.node.services.UnknownAnonymousPartyException
import net.corda.node.internal.configureDatabase import net.corda.node.internal.configureDatabase
@ -50,7 +48,7 @@ class PersistentIdentityServiceTests {
@Before @Before
fun setup() { fun setup() {
identityService = PersistentIdentityService(DEV_TRUST_ROOT) identityService = PersistentIdentityService(DEV_ROOT_CA.certificate)
database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), identityService) database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), identityService)
} }
@ -143,9 +141,9 @@ class PersistentIdentityServiceTests {
@Test @Test
fun `assert unknown anonymous key is unrecognised`() { fun `assert unknown anonymous key is unrecognised`() {
val rootKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val rootKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootKey) val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Principal, rootKey)
val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_IDENTITY_SIGNATURE_SCHEME) val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_IDENTITY_SIGNATURE_SCHEME)
val identity = Party(rootCert.cert) val identity = Party(rootCert)
val txIdentity = AnonymousParty(txKey.public) val txIdentity = AnonymousParty(txKey.public)
assertFailsWith<UnknownAnonymousPartyException> { assertFailsWith<UnknownAnonymousPartyException> {
@ -219,9 +217,9 @@ class PersistentIdentityServiceTests {
} }
assertFailsWith<IllegalArgumentException> { assertFailsWith<IllegalArgumentException> {
val owningKey = Crypto.decodePublicKey(DEV_CA.certificate.subjectPublicKeyInfo.encoded) val owningKey = DEV_INTERMEDIATE_CA.certificate.publicKey
database.transaction { database.transaction {
val subject = CordaX500Name.build(DEV_CA.certificate.cert.subjectX500Principal) val subject = CordaX500Name.build(DEV_INTERMEDIATE_CA.certificate.subjectX500Principal)
identityService.assertOwnership(Party(subject, owningKey), anonymousAlice.party.anonymise()) identityService.assertOwnership(Party(subject, owningKey), anonymousAlice.party.anonymise())
} }
} }
@ -243,7 +241,7 @@ class PersistentIdentityServiceTests {
// Create new identity service mounted onto same DB // Create new identity service mounted onto same DB
val newPersistentIdentityService = database.transaction { val newPersistentIdentityService = database.transaction {
PersistentIdentityService(DEV_TRUST_ROOT) PersistentIdentityService(DEV_ROOT_CA.certificate)
} }
database.transaction { database.transaction {
@ -266,8 +264,8 @@ class PersistentIdentityServiceTests {
val issuerKeyPair = generateKeyPair() val issuerKeyPair = generateKeyPair()
val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public) val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public)
val txKey = Crypto.generateKeyPair() val txKey = Crypto.generateKeyPair()
val txCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuer.certificate.toX509CertHolder(), issuerKeyPair, x500Name, txKey.public) val txCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuer.certificate, issuerKeyPair, x500Name.x500Principal, txKey.public)
val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates) val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert) + issuer.certPath.certificates)
return Pair(issuer, PartyAndCertificate(txCertPath)) return Pair(issuer, PartyAndCertificate(txCertPath))
} }

View File

@ -2,12 +2,11 @@ package net.corda.node.services.network
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.sha256 import net.corda.core.crypto.sha256
import net.corda.core.internal.cert
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.utilities.seconds import net.corda.core.utilities.seconds
import net.corda.testing.ALICE_NAME import net.corda.testing.ALICE_NAME
import net.corda.testing.BOB_NAME import net.corda.testing.BOB_NAME
import net.corda.testing.DEV_TRUST_ROOT import net.corda.testing.DEV_ROOT_CA
import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.SerializationEnvironmentRule
import net.corda.testing.driver.PortAllocation import net.corda.testing.driver.PortAllocation
import net.corda.testing.internal.createNodeInfoAndSigned import net.corda.testing.internal.createNodeInfoAndSigned
@ -35,7 +34,7 @@ class NetworkMapClientTest {
fun setUp() { fun setUp() {
server = NetworkMapServer(cacheTimeout, PortAllocation.Incremental(10000).nextHostAndPort()) server = NetworkMapServer(cacheTimeout, PortAllocation.Incremental(10000).nextHostAndPort())
val hostAndPort = server.start() val hostAndPort = server.start()
networkMapClient = NetworkMapClient(URL("http://${hostAndPort.host}:${hostAndPort.port}"), DEV_TRUST_ROOT.cert) networkMapClient = NetworkMapClient(URL("http://${hostAndPort.host}:${hostAndPort.port}"), DEV_ROOT_CA.certificate)
} }
@After @After

View File

@ -18,6 +18,7 @@ import java.net.ServerSocket
import java.nio.file.Path import java.nio.file.Path
import java.security.KeyStore import java.security.KeyStore
import javax.net.ssl.* import javax.net.ssl.*
import javax.security.auth.x500.X500Principal
import kotlin.concurrent.thread import kotlin.concurrent.thread
import kotlin.test.* import kotlin.test.*
@ -51,9 +52,9 @@ class TLSAuthenticationTests {
val tempFolder: TemporaryFolder = TemporaryFolder() val tempFolder: TemporaryFolder = TemporaryFolder()
// Root CA. // Root CA.
private val ROOT_X500 = CordaX500Name(commonName = "Root_CA_1", organisation = "R3CEV", locality = "London", country = "GB") private val ROOT_X500 = X500Principal("CN=Root_CA_1,O=R3CEV,L=London,C=GB")
// Intermediate CA. // Intermediate CA.
private val INTERMEDIATE_X500 = CordaX500Name(commonName = "Intermediate_CA_1", organisation = "R3CEV", locality = "London", country = "GB") private val INTERMEDIATE_X500 = X500Principal("CN=Intermediate_CA_1,O=R3CEV,L=London,C=GB")
// TLS server (client1). // TLS server (client1).
private val CLIENT_1_X500 = CordaX500Name(commonName = "Client_1", organisation = "R3CEV", locality = "London", country = "GB") private val CLIENT_1_X500 = CordaX500Name(commonName = "Client_1", organisation = "R3CEV", locality = "London", country = "GB")
// TLS client (client2). // TLS client (client2).
@ -274,7 +275,7 @@ class TLSAuthenticationTests {
CertificateType.NODE_CA, CertificateType.NODE_CA,
intermediateCACert, intermediateCACert,
intermediateCAKeyPair, intermediateCAKeyPair,
CLIENT_1_X500, CLIENT_1_X500.x500Principal,
client1CAKeyPair.public client1CAKeyPair.public
) )
@ -283,7 +284,7 @@ class TLSAuthenticationTests {
CertificateType.TLS, CertificateType.TLS,
client1CACert, client1CACert,
client1CAKeyPair, client1CAKeyPair,
CLIENT_1_X500, CLIENT_1_X500.x500Principal,
client1TLSKeyPair.public client1TLSKeyPair.public
) )
@ -301,7 +302,7 @@ class TLSAuthenticationTests {
CertificateType.NODE_CA, CertificateType.NODE_CA,
intermediateCACert, intermediateCACert,
intermediateCAKeyPair, intermediateCAKeyPair,
CLIENT_2_X500, CLIENT_2_X500.x500Principal,
client2CAKeyPair.public client2CAKeyPair.public
) )
@ -310,7 +311,7 @@ class TLSAuthenticationTests {
CertificateType.TLS, CertificateType.TLS,
client2CACert, client2CACert,
client2CAKeyPair, client2CAKeyPair,
CLIENT_2_X500, CLIENT_2_X500.x500Principal,
client2TLSKeyPair.public client2TLSKeyPair.public
) )
@ -323,8 +324,8 @@ class TLSAuthenticationTests {
// client2TLSKeyStore.save(client2TLSKeyStorePath, PASSWORD) // client2TLSKeyStore.save(client2TLSKeyStorePath, PASSWORD)
val trustStore = loadOrCreateKeyStore(trustStorePath, PASSWORD) val trustStore = loadOrCreateKeyStore(trustStorePath, PASSWORD)
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCACert.cert) trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCACert)
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_INTERMEDIATE_CA, intermediateCACert.cert) trustStore.addOrReplaceCertificate(X509Utilities.CORDA_INTERMEDIATE_CA, intermediateCACert)
// trustStore.save(trustStorePath, PASSWORD) // trustStore.save(trustStorePath, PASSWORD)
val client1SSLContext = sslContext(client1TLSKeyStore, PASSWORD, trustStore) val client1SSLContext = sslContext(client1TLSKeyStore, PASSWORD, trustStore)

View File

@ -9,21 +9,23 @@ import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.cert
import net.corda.core.internal.createDirectories import net.corda.core.internal.createDirectories
import net.corda.core.internal.x500Name
import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NodeConfiguration
import net.corda.nodeapi.internal.crypto.* import net.corda.nodeapi.internal.crypto.*
import net.corda.testing.ALICE_NAME import net.corda.testing.ALICE_NAME
import net.corda.testing.internal.createDevNodeCaCertPath import net.corda.testing.internal.createDevIntermediateCaCertPath
import net.corda.testing.internal.rigorousMock import net.corda.testing.internal.rigorousMock
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.*
import org.assertj.core.api.Assertions.assertThatThrownBy import org.bouncycastle.asn1.x509.GeneralName
import org.bouncycastle.asn1.x509.GeneralSubtree
import org.bouncycastle.asn1.x509.NameConstraints
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import java.security.cert.CertPathValidatorException import java.security.cert.CertPathValidatorException
import java.security.cert.Certificate
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import javax.security.auth.x500.X500Principal
import kotlin.test.assertFalse import kotlin.test.assertFalse
import kotlin.test.assertTrue import kotlin.test.assertTrue
@ -32,18 +34,10 @@ class NetworkRegistrationHelperTest {
private val requestId = SecureHash.randomSHA256().toString() private val requestId = SecureHash.randomSHA256().toString()
private val nodeLegalName = ALICE_NAME private val nodeLegalName = ALICE_NAME
private lateinit var rootCaCert: X509Certificate
private lateinit var intermediateCaCert: X509Certificate
private lateinit var nodeCaCert: X509Certificate
private lateinit var config: NodeConfiguration private lateinit var config: NodeConfiguration
@Before @Before
fun init() { fun init() {
val (rootCa, intermediateCa, nodeCa) = createDevNodeCaCertPath(nodeLegalName)
this.rootCaCert = rootCa.certificate.cert
this.intermediateCaCert = intermediateCa.certificate.cert
this.nodeCaCert = nodeCa.certificate.cert
val baseDirectory = fs.getPath("/baseDir").createDirectories() val baseDirectory = fs.getPath("/baseDir").createDirectories()
abstract class AbstractNodeConfiguration : NodeConfiguration abstract class AbstractNodeConfiguration : NodeConfiguration
config = rigorousMock<AbstractNodeConfiguration>().also { config = rigorousMock<AbstractNodeConfiguration>().also {
@ -66,9 +60,10 @@ class NetworkRegistrationHelperTest {
assertThat(config.sslKeystore).doesNotExist() assertThat(config.sslKeystore).doesNotExist()
assertThat(config.trustStoreFile).doesNotExist() assertThat(config.trustStoreFile).doesNotExist()
saveTrustStoreWithRootCa(rootCaCert) val nodeCaCertPath = createNodeCaCertPath()
createRegistrationHelper().buildKeystore() saveTrustStoreWithRootCa(nodeCaCertPath.last())
createRegistrationHelper(nodeCaCertPath).buildKeystore()
val nodeKeystore = loadKeyStore(config.nodeKeystore, config.keyStorePassword) val nodeKeystore = loadKeyStore(config.nodeKeystore, config.keyStorePassword)
val sslKeystore = loadKeyStore(config.sslKeystore, config.keyStorePassword) val sslKeystore = loadKeyStore(config.sslKeystore, config.keyStorePassword)
@ -79,8 +74,7 @@ class NetworkRegistrationHelperTest {
assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA)) assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA))
assertFalse(containsAlias(X509Utilities.CORDA_ROOT_CA)) assertFalse(containsAlias(X509Utilities.CORDA_ROOT_CA))
assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_TLS)) assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_TLS))
val nodeCaCertChain = getCertificateChain(X509Utilities.CORDA_CLIENT_CA) assertThat(getCertificateChain(X509Utilities.CORDA_CLIENT_CA)).containsExactly(*nodeCaCertPath)
assertThat(nodeCaCertChain).containsExactly(nodeCaCert, intermediateCaCert, rootCaCert)
} }
sslKeystore.run { sslKeystore.run {
@ -92,40 +86,77 @@ class NetworkRegistrationHelperTest {
assertThat(nodeTlsCertChain).hasSize(4) assertThat(nodeTlsCertChain).hasSize(4)
// The TLS cert has the same subject as the node CA cert // The TLS cert has the same subject as the node CA cert
assertThat(CordaX500Name.build((nodeTlsCertChain[0] as X509Certificate).subjectX500Principal)).isEqualTo(nodeLegalName) assertThat(CordaX500Name.build((nodeTlsCertChain[0] as X509Certificate).subjectX500Principal)).isEqualTo(nodeLegalName)
assertThat(nodeTlsCertChain.drop(1)).containsExactly(nodeCaCert, intermediateCaCert, rootCaCert) assertThat(nodeTlsCertChain.drop(1)).containsExactly(*nodeCaCertPath)
} }
trustStore.run { trustStore.run {
assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_CA)) assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_CA))
assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA)) assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA))
assertTrue(containsAlias(X509Utilities.CORDA_ROOT_CA)) assertTrue(containsAlias(X509Utilities.CORDA_ROOT_CA))
val trustStoreRootCaCert = getCertificate(X509Utilities.CORDA_ROOT_CA) assertThat(getCertificate(X509Utilities.CORDA_ROOT_CA)).isEqualTo(nodeCaCertPath.last())
assertThat(trustStoreRootCaCert).isEqualTo(rootCaCert)
} }
} }
@Test @Test
fun `missing truststore`() { fun `missing truststore`() {
val nodeCaCertPath = createNodeCaCertPath()
assertThatThrownBy { assertThatThrownBy {
createRegistrationHelper() createRegistrationHelper(nodeCaCertPath)
}.hasMessageContaining("This file must contain the root CA cert of your compatibility zone. Please contact your CZ operator.") }.hasMessageContaining("This file must contain the root CA cert of your compatibility zone. Please contact your CZ operator.")
} }
@Test
fun `node CA with incorrect cert role`() {
val nodeCaCertPath = createNodeCaCertPath(type = CertificateType.TLS)
saveTrustStoreWithRootCa(nodeCaCertPath.last())
val registrationHelper = createRegistrationHelper(nodeCaCertPath)
assertThatExceptionOfType(CertificateRequestException::class.java)
.isThrownBy { registrationHelper.buildKeystore() }
.withMessageContaining(CertificateType.TLS.toString())
}
@Test
fun `node CA with incorrect subject`() {
val invalidName = CordaX500Name("Foo", "MU", "GB")
val nodeCaCertPath = createNodeCaCertPath(legalName = invalidName)
saveTrustStoreWithRootCa(nodeCaCertPath.last())
val registrationHelper = createRegistrationHelper(nodeCaCertPath)
assertThatExceptionOfType(CertificateRequestException::class.java)
.isThrownBy { registrationHelper.buildKeystore() }
.withMessageContaining(invalidName.toString())
}
@Test @Test
fun `wrong root cert in truststore`() { fun `wrong root cert in truststore`() {
val rootKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val wrongRootCert = X509Utilities.createSelfSignedCACertificate(
val rootCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name("Foo", "MU", "GB"), rootKeyPair) X500Principal("O=Foo,L=MU,C=GB"),
saveTrustStoreWithRootCa(rootCert.cert) Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
val registrationHelper = createRegistrationHelper() saveTrustStoreWithRootCa(wrongRootCert)
val registrationHelper = createRegistrationHelper(createNodeCaCertPath())
assertThatThrownBy { assertThatThrownBy {
registrationHelper.buildKeystore() registrationHelper.buildKeystore()
}.isInstanceOf(CertPathValidatorException::class.java) }.isInstanceOf(CertPathValidatorException::class.java)
} }
private fun createRegistrationHelper(): NetworkRegistrationHelper { private fun createNodeCaCertPath(type: CertificateType = CertificateType.NODE_CA,
legalName: CordaX500Name = nodeLegalName): Array<X509Certificate> {
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.x500Name))), arrayOf())
val nodeCaCert = X509Utilities.createCertificate(
type,
intermediateCa.certificate,
intermediateCa.keyPair,
legalName.x500Principal,
keyPair.public,
nameConstraints = nameConstraints)
return arrayOf(nodeCaCert, intermediateCa.certificate, rootCa.certificate)
}
private fun createRegistrationHelper(response: Array<X509Certificate>): NetworkRegistrationHelper {
val certService = rigorousMock<NetworkRegistrationService>().also { val certService = rigorousMock<NetworkRegistrationService>().also {
doReturn(requestId).whenever(it).submitRequest(any()) doReturn(requestId).whenever(it).submitRequest(any())
doReturn(arrayOf<Certificate>(nodeCaCert, intermediateCaCert, rootCaCert)).whenever(it).retrieveCertificates(eq(requestId)) doReturn(response).whenever(it).retrieveCertificates(eq(requestId))
} }
return NetworkRegistrationHelper(config, certService) return NetworkRegistrationHelper(config, certService)
} }

View File

@ -9,11 +9,12 @@ webapp which provides REST API and web frontend. Application communicate using C
To run from the command line in Unix: To run from the command line in Unix:
1. Run ``./gradlew samples:irs-demo:cordapp:deployNodes`` to install configs and a command line tool under 1. Run ``./gradlew samples:irs-demo:cordapp:deployNodes`` to install configs and a command line tool under
``samples/irs-demo/build`` ``samples/irs-demo/cordapp/build``
2. Run ``./gradlew samples:irs-demo:web:deployWebapps`` to install configs and tools for running webservers 2. Run ``./gradlew samples:irs-demo:web:deployWebapps`` to install configs and tools for running webservers
3. Move to the ``samples/irs-demo/`` directory 3. Move to the ``samples/irs-demo/`` directory
4. Run ``./cordapp/build/nodes/runnodes`` to open up three new terminals with the three nodes (you may have to install xterm) 4. Run ``./cordapp/build/nodes/runnodes`` to open up three new terminals with the three nodes (you may have to install xterm)
5. Run ``./web/build/webapps/runwebapps`` to open three more terminals for associated webserver 5. On Linux, run ``./web/build/webapps/runwebapps`` to open three more terminals for associated webservers. On macOS,
use the following command instead: ``osascript ./web/build/webapps/runwebapps.scpt``
To run from the command line in Windows: To run from the command line in Windows:

View File

@ -1,8 +1,180 @@
# SIMM Valuation Demo # SIMM and Portfolio Demo - aka the Initial Margin Agreement Demo
See our [main documentation site](https://docs.corda.net/initial-margin-agreement.html) regarding the SIMM valuation and agreement on a distributed ledger. ## Background and SIMM Introduction
This app is a demonstration of how Corda can be used for the real world requirement of initial margin calculation and
agreement; featuring the integration of complex and industry proven third party libraries into Corda nodes.
SIMM is an acronym for "Standard Initial Margin Model". It is effectively the calculation of a "margin" that is paid
by one party to another when they agree a trade on certain types of transaction.
The SIMM was introduced to standardise the calculation of how much margin counterparties charge each other on their
bilateral transactions. Before SIMM, each counterparty computed margins according to its own model and it was made it very
difficult to agree the exact margin with the counterparty that faces the same trade on the other side.
To enact this, in September 2016, the ISDA committee - with full backing from various governing bodies -
[issued a ruling on what is known as the ISDA SIMM ™ model](http://www2.isda.org/news/isda-simm-deployed-today-new-industry-standard-for-calculating-initial-margin-widely-adopted-by-market-participants)
a way of fairly and consistently calculating this margin. Any parties wishing to trade a financial product that is
covered under this ruling would, independently, use this model and calculate their margin payment requirement,
agree it with their trading counterparty and then pay (or receive, depending on the results of this calculation)
this amount. In the case of disagreement that is not resolved in a timely fashion, this payment would increase
and so therefore it is in the parties' interest to reach agreement in as short as time frame as possible.
To be more accurate, the SIMM calculation is not performed on just one trade - it is calculated on an aggregate of
intermediary values (which in this model are sensitivities to risk factors) from a portfolio of trades; therefore
the input to a SIMM is actually this data, not the individual trades themselves.
Also note that implementations of the SIMM are actually protected and subject to license restrictions by ISDA
(this is due to the model itself being protected). We were fortunate enough to technically partner with
[OpenGamma](http://www.opengamma.com) who allowed us to demonstrate the SIMM process using their proprietary model.
In the source code released, we have replaced their analytics engine with very simple stub functions that allow
the process to run without actually calculating correct values, and can easily be swapped out in place for their real libraries.
## What happens in the demo (notionally)
Preliminaries
- Ensure that there are a number of live trades with another party based on financial products that are covered under the
ISDA SIMM agreement (if none, then use the demo to enter some simple trades as described below).
Initial Margin Agreement Process
- Agree that one will be performing the margining calculation against a portfolio of trades with another party, and agree the trades in that portfolio. In practice, one node will start the flow but it does not matter which node does.
- Individually (at the node level), identify the data (static, reference etc) one will need in order to be able to calculate the metrics on those trades
- Confirm with the other counterparty the dataset from the above set
- Calculate any intermediary steps and values needed for the margin calculation (ie sensitivities to risk factors)
- Agree on the results of these steps
- Calculate the initial margin
- Agree on the calculation of the above with the other party
- In practice, pay (or receive) this margin (omitted for the sake of complexity for this example)
## Demo execution (step by step)
**Setting up the Corda infrastructure**
To run from the command line in Unix:
1. Deploy the nodes using ``./gradlew samples:simm-valuation-demo:deployNodes``
2. Run the nodes using ``./samples/simm-valuation-demo/build/nodes/runnodes``
To run from the command line in Windows:
1. Deploy the nodes using ``gradlew samples:simm-valuation-demo:deployNodes``
2. Run the nodes using ``samples\simm-valuation-demo\build\nodes\runnodes``
**Getting Bank A's details**
From the command line run
curl http://localhost:10005/api/simmvaluationdemo/whoami
The response should be something like
{
"self" : {
"id" : "8Kqd4oWdx4KQGHGQW3FwXHQpjiv7cHaSsaAWMwRrK25bBJj792Z4rag7EtA",
"text" : "C=GB,L=London,O=Bank A"
},
"counterparties" : [
{
"id" : "8Kqd4oWdx4KQGHGL1DzULumUmZyyokeSGJDY1n5M6neUfAj2sjbf65wYwQM",
"text" : "C=JP,L=Tokyo,O=Bank C"
},
{
"id" : "8Kqd4oWdx4KQGHGTBm34eCM2nrpcWKeM1ZG3DUYat3JTFUQTwB3Lv2WbPM8",
"text" : "C=US,L=New York,O=Bank B"
}
]
}
Now, if we ask the same question of Bank C we will see that it's id matches the id for Bank C as a counter
party to Bank A and Bank A will appear as a counter party
curl -i -H "Content-Type: application/json" -X GET http://localhost:10011/api/simmvaluationdemo/whoami
**Creating a trade with Bank C**
In what follows, we assume we are Bank A (which is listening on port 10005)
Notice the id field in the output of the ``whoami`` command. We are going to use the id assocatied
with Bank C, one of our counter parties, to create a trade. The general command for this is:
curl -i -H "Content-Type: application/json" -X PUT -d <<<JSON representation of the trade>>> http://localhost:10005/api/simmvaluationdemo/<<<counter party id>>>/trades
where the representation of the trade is
{
"id" : "trade1",
"description" : "desc",
"tradeDate" : [ 2016, 6, 6 ],
"convention" : "EUR_FIXED_1Y_EURIBOR_3M",
"startDate" : [ 2016, 6, 6 ],
"endDate" : [ 2020, 1, 2 ],
"buySell" : "BUY",
"notional" : "1000",
"fixedRate" : "0.1"
}
Continuing our example, the specific command we would run is
curl -i -H "Content-Type: application/json" \
-X PUT \
-d '{"id":"trade1","description" : "desc","tradeDate" : [ 2016, 6, 6 ], "convention" : "EUR_FIXED_1Y_EURIBOR_3M", "startDate" : [ 2016, 6, 6 ], "endDate" : [ 2020, 1, 2 ], "buySell" : "BUY", "notional" : "1000", "fixedRate" : "0.1"}' \
http://localhost:10005/api/simmvaluationdemo/8Kqd4oWdx4KQGHGL1DzULumUmZyyokeSGJDY1n5M6neUfAj2sjbf65wYwQM/trades
With an expected response of
HTTP/1.1 202 Accepted
Date: Thu, 28 Sep 2017 17:19:39 GMT
Content-Type: text/plain
Access-Control-Allow-Origin: *
Content-Length: 2
Server: Jetty(9.3.9.v20160517)
**Verifying trade completion**
With the trade completed and stored by both parties, the complete list of trades with our couterparty can be seen with the following command
curl -X GET http://localhost:10005/api/simmvaluationdemo/<<<counter party id>>>/trades
The command for our example, using Bank A, would thus be
curl -X GET http://localhost:10005/api/simmvaluationdemo/8Kqd4oWdx4KQGHGL1DzULumUmZyyokeSGJDY1n5M6neUfAj2sjbf65wYwQM/trades
whilst a specific trade can be seen with
curl -X GET http://localhost:10005/api/simmvaluationdemo/<<<counter party id>>>/trades/<<<trade id>>>
If we look at the trade we created above, we assigned it the id "trade1", the complete command in this case would be
curl -X GET http://localhost:10005/api/simmvaluationdemo/8Kqd4oWdx4KQGHGL1DzULumUmZyyokeSGJDY1n5M6neUfAj2sjbf65wYwQM/trades/trade1
**Generating a valuation**
curl -i -H "Content-Type: application/json" \
-X POST \
-d <<<JSON representation>>>
http://localhost:10005/api/simmvaluationdemo/<<<counter party id>>>/portfolio/valuations/calculate
Again, the specific command to continue our example would be
curl -i -H "Content-Type: application/json" \
-X POST \
-d '{"valuationDate":[2016,6,6]}' \
http://localhost:10005/api/simmvaluationdemo/8Kqd4oWdx4KQGHGL1DzLumUmZyyokeSGJDY1n5M6neUfAj2sjbf65wYwQM/portfolio/valuations/calculate
**Viewing a valuation**
In the same way we can ask for specific instances of trades with a counter party, we can request details of valuations
curl -i -H "Content-Type: application/json" -X GET http://localhost:10005/api/simmvaluationdemo/<<<counter party id>>>/portfolio/valuations
The specific command for out Bank A example is
curl -i -H "Content-Type: application/json" \
-X GET http://localhost:10005/api/simmvaluationdemo/8Kqd4oWdx4KQGHGL1DzULumUmZyyokeSGJDY1n5M6neUfAj2sjbf65YwQM/portfolio/valuations
## SIMM Library Licensing ## SIMM Library Licensing

View File

@ -2,6 +2,7 @@ apply plugin: 'kotlin'
apply plugin: 'kotlin-jpa' apply plugin: 'kotlin-jpa'
apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.quasar-utils'
apply plugin: 'net.corda.plugins.publish-utils' apply plugin: 'net.corda.plugins.publish-utils'
apply plugin: 'net.corda.plugins.api-scanner'
apply plugin: 'com.jfrog.artifactory' apply plugin: 'com.jfrog.artifactory'
//noinspection GroovyAssignabilityCheck //noinspection GroovyAssignabilityCheck

View File

@ -3,6 +3,7 @@
package net.corda.testing.driver package net.corda.testing.driver
import net.corda.client.rpc.CordaRPCClient import net.corda.client.rpc.CordaRPCClient
import net.corda.core.DoNotImplement
import net.corda.core.concurrent.CordaFuture import net.corda.core.concurrent.CordaFuture
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party import net.corda.core.identity.Party
@ -30,6 +31,7 @@ import java.util.concurrent.atomic.AtomicInteger
*/ */
data class NotaryHandle(val identity: Party, val validating: Boolean, val nodeHandles: CordaFuture<List<NodeHandle>>) data class NotaryHandle(val identity: Party, val validating: Boolean, val nodeHandles: CordaFuture<List<NodeHandle>>)
@DoNotImplement
sealed class NodeHandle { sealed class NodeHandle {
abstract val nodeInfo: NodeInfo abstract val nodeInfo: NodeInfo
/** /**
@ -90,6 +92,7 @@ data class WebserverHandle(
val process: Process val process: Process
) )
@DoNotImplement
sealed class PortAllocation { sealed class PortAllocation {
abstract fun nextPort(): Int abstract fun nextPort(): Int
fun nextHostAndPort() = NetworkHostAndPort("localhost", nextPort()) fun nextHostAndPort() = NetworkHostAndPort("localhost", nextPort())

View File

@ -1,5 +1,6 @@
package net.corda.testing.driver package net.corda.testing.driver
import net.corda.core.DoNotImplement
import net.corda.core.concurrent.CordaFuture import net.corda.core.concurrent.CordaFuture
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party import net.corda.core.identity.Party
@ -10,6 +11,7 @@ import net.corda.nodeapi.internal.config.User
import net.corda.testing.node.NotarySpec import net.corda.testing.node.NotarySpec
import java.nio.file.Path import java.nio.file.Path
@DoNotImplement
interface DriverDSL { interface DriverDSL {
/** Returns a list of [NotaryHandle]s matching the list of [NotarySpec]s passed into [driver]. */ /** Returns a list of [NotaryHandle]s matching the list of [NotarySpec]s passed into [driver]. */
val notaryHandles: List<NotaryHandle> val notaryHandles: List<NotaryHandle>

View File

@ -1,5 +1,6 @@
package net.corda.testing.node package net.corda.testing.node
import net.corda.core.DoNotImplement
import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.CompositeKey
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party import net.corda.core.identity.Party
@ -123,7 +124,7 @@ class InMemoryMessagingNetwork internal constructor(
} }
} }
interface LatencyCalculator { interface LatencyCalculator { // XXX: Used?
fun between(sender: SingleMessageRecipient, receiver: SingleMessageRecipient): Duration fun between(sender: SingleMessageRecipient, receiver: SingleMessageRecipient): Duration
} }
@ -181,6 +182,7 @@ class InMemoryMessagingNetwork internal constructor(
/** /**
* Mock service loadbalancing * Mock service loadbalancing
*/ */
@DoNotImplement
sealed class ServicePeerAllocationStrategy { sealed class ServicePeerAllocationStrategy {
abstract fun <A> pickNext(service: ServiceHandle, pickFrom: List<A>): A abstract fun <A> pickNext(service: ServiceHandle, pickFrom: List<A>): A
class Random(val random: SplittableRandom = SplittableRandom()) : ServicePeerAllocationStrategy() { class Random(val random: SplittableRandom = SplittableRandom()) : ServicePeerAllocationStrategy() {

View File

@ -4,6 +4,7 @@ import com.google.common.jimfs.Configuration.unix
import com.google.common.jimfs.Jimfs import com.google.common.jimfs.Jimfs
import com.nhaarman.mockito_kotlin.doReturn import com.nhaarman.mockito_kotlin.doReturn
import com.nhaarman.mockito_kotlin.whenever import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.DoNotImplement
import net.corda.core.crypto.entropyToKeyPair import net.corda.core.crypto.entropyToKeyPair
import net.corda.core.crypto.random63BitValue import net.corda.core.crypto.random63BitValue
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
@ -492,6 +493,7 @@ fun StartedNode<MockNetwork.MockNode>.setMessagingServiceSpy(messagingServiceSpy
} }
private fun mockNodeConfiguration(): NodeConfiguration { private fun mockNodeConfiguration(): NodeConfiguration {
@DoNotImplement
abstract class AbstractNodeConfiguration : NodeConfiguration abstract class AbstractNodeConfiguration : NodeConfiguration
return rigorousMock<AbstractNodeConfiguration>().also { return rigorousMock<AbstractNodeConfiguration>().also {
doReturn("cordacadevpass").whenever(it).keyStorePassword doReturn("cordacadevpass").whenever(it).keyStorePassword

View File

@ -23,6 +23,7 @@ import net.corda.core.transactions.SignedTransaction
import net.corda.node.VersionInfo import net.corda.node.VersionInfo
import net.corda.node.internal.configureDatabase import net.corda.node.internal.configureDatabase
import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.internal.cordapp.CordappLoader
import net.corda.node.services.api.IdentityServiceInternal
import net.corda.node.services.api.SchemaService import net.corda.node.services.api.SchemaService
import net.corda.node.services.api.VaultServiceInternal import net.corda.node.services.api.VaultServiceInternal
import net.corda.node.services.api.WritableTransactionStorage import net.corda.node.services.api.WritableTransactionStorage
@ -34,12 +35,12 @@ import net.corda.node.services.schema.HibernateObserver
import net.corda.node.services.schema.NodeSchemaService import net.corda.node.services.schema.NodeSchemaService
import net.corda.node.services.transactions.InMemoryTransactionVerifierService import net.corda.node.services.transactions.InMemoryTransactionVerifierService
import net.corda.node.services.vault.NodeVaultService import net.corda.node.services.vault.NodeVaultService
import net.corda.node.services.api.IdentityServiceInternal
import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.nodeapi.internal.persistence.DatabaseConfig
import net.corda.nodeapi.internal.persistence.HibernateConfiguration import net.corda.nodeapi.internal.persistence.HibernateConfiguration
import net.corda.nodeapi.internal.persistence.TransactionIsolationLevel import net.corda.nodeapi.internal.persistence.TransactionIsolationLevel
import net.corda.testing.* import net.corda.testing.DEV_ROOT_CA
import net.corda.testing.TestIdentity
import net.corda.testing.database.DatabaseConstants import net.corda.testing.database.DatabaseConstants
import net.corda.testing.database.DatabaseConstants.DATA_SOURCE_CLASSNAME import net.corda.testing.database.DatabaseConstants.DATA_SOURCE_CLASSNAME
import net.corda.testing.database.DatabaseConstants.DATA_SOURCE_PASSWORD import net.corda.testing.database.DatabaseConstants.DATA_SOURCE_PASSWORD
@ -59,7 +60,7 @@ import java.sql.Connection
import java.time.Clock import java.time.Clock
import java.util.* import java.util.*
fun makeTestIdentityService(vararg identities: PartyAndCertificate) = InMemoryIdentityService(identities, DEV_TRUST_ROOT) fun makeTestIdentityService(vararg identities: PartyAndCertificate) = InMemoryIdentityService(identities, DEV_ROOT_CA.certificate)
/** /**
* A singleton utility that only provides a mock identity, key and storage service. However, this is sufficient for * A singleton utility that only provides a mock identity, key and storage service. However, this is sufficient for
* building chains of transactions and verifying them. It isn't sufficient for testing flows however. * building chains of transactions and verifying them. It isn't sufficient for testing flows however.
@ -197,7 +198,7 @@ class MockKeyManagementService(val identityService: IdentityServiceInternal,
override val keys: Set<PublicKey> get() = keyStore.keys override val keys: Set<PublicKey> get() = keyStore.keys
val nextKeys = LinkedList<KeyPair>() private val nextKeys = LinkedList<KeyPair>()
override fun freshKey(): PublicKey { override fun freshKey(): PublicKey {
val k = nextKeys.poll() ?: generateKeyPair() val k = nextKeys.poll() ?: generateKeyPair()
@ -259,11 +260,10 @@ open class MockTransactionStorage : WritableTransactionStorage, SingletonSeriali
} }
fun <T : SerializeAsToken> createMockCordaService(serviceHub: MockServices, serviceConstructor: (AppServiceHub) -> T): T { fun <T : SerializeAsToken> createMockCordaService(serviceHub: MockServices, serviceConstructor: (AppServiceHub) -> T): T {
class MockAppServiceHubImpl<T : SerializeAsToken>(val serviceHub: MockServices, serviceConstructor: (AppServiceHub) -> T) : AppServiceHub, ServiceHub by serviceHub { class MockAppServiceHubImpl<out T : SerializeAsToken>(val serviceHub: MockServices, serviceConstructor: (AppServiceHub) -> T) : AppServiceHub, ServiceHub by serviceHub {
val serviceInstance: T val serviceInstance: T = serviceConstructor(this)
init { init {
serviceInstance = serviceConstructor(this)
serviceHub.cordappServices.putInstance(serviceInstance.javaClass, serviceInstance) serviceHub.cordappServices.putInstance(serviceInstance.javaClass, serviceInstance)
} }

View File

@ -1,5 +1,6 @@
package net.corda.testing.node package net.corda.testing.node
import net.corda.core.DoNotImplement
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.node.services.config.VerifierType import net.corda.node.services.config.VerifierType
import net.corda.nodeapi.internal.config.User import net.corda.nodeapi.internal.config.User
@ -12,6 +13,7 @@ data class NotarySpec(
val cluster: ClusterSpec? = null val cluster: ClusterSpec? = null
) )
@DoNotImplement
sealed class ClusterSpec { sealed class ClusterSpec {
abstract val clusterSize: Int abstract val clusterSize: Int

View File

@ -2,8 +2,6 @@ package net.corda.testing.node.internal.network
import net.corda.core.crypto.* import net.corda.core.crypto.*
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.cert
import net.corda.core.internal.toX509CertHolder
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.serialization.deserialize import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
@ -16,7 +14,7 @@ import net.corda.nodeapi.internal.network.DigitalSignatureWithCert
import net.corda.nodeapi.internal.network.NetworkMap import net.corda.nodeapi.internal.network.NetworkMap
import net.corda.nodeapi.internal.network.NetworkParameters import net.corda.nodeapi.internal.network.NetworkParameters
import net.corda.nodeapi.internal.network.SignedNetworkMap import net.corda.nodeapi.internal.network.SignedNetworkMap
import net.corda.testing.ROOT_CA import net.corda.testing.DEV_ROOT_CA
import org.eclipse.jetty.server.Server import org.eclipse.jetty.server.Server
import org.eclipse.jetty.server.ServerConnector import org.eclipse.jetty.server.ServerConnector
import org.eclipse.jetty.server.handler.HandlerCollection import org.eclipse.jetty.server.handler.HandlerCollection
@ -29,6 +27,7 @@ import java.io.InputStream
import java.net.InetSocketAddress import java.net.InetSocketAddress
import java.time.Duration import java.time.Duration
import java.time.Instant import java.time.Instant
import javax.security.auth.x500.X500Principal
import javax.ws.rs.* import javax.ws.rs.*
import javax.ws.rs.core.MediaType import javax.ws.rs.core.MediaType
import javax.ws.rs.core.Response import javax.ws.rs.core.Response
@ -36,7 +35,7 @@ import javax.ws.rs.core.Response.ok
class NetworkMapServer(cacheTimeout: Duration, class NetworkMapServer(cacheTimeout: Duration,
hostAndPort: NetworkHostAndPort, hostAndPort: NetworkHostAndPort,
rootCa: CertificateAndKeyPair = ROOT_CA, // Default to ROOT_CA for testing. rootCa: CertificateAndKeyPair = DEV_ROOT_CA,
private val myHostNameValue: String = "test.host.name", private val myHostNameValue: String = "test.host.name",
vararg additionalServices: Any) : Closeable { vararg additionalServices: Any) : Closeable {
companion object { companion object {
@ -48,12 +47,12 @@ class NetworkMapServer(cacheTimeout: Duration,
CertificateType.NETWORK_MAP, CertificateType.NETWORK_MAP,
rootCAKeyAndCert.certificate, rootCAKeyAndCert.certificate,
rootCAKeyAndCert.keyPair, rootCAKeyAndCert.keyPair,
CordaX500Name("Corda Network Map", "R3 Ltd", "London","GB"), X500Principal("CN=Corda Network Map,O=R3 Ltd,L=London,C=GB"),
networkMapKey.public).cert networkMapKey.public)
// Check that the certificate validates. Nodes will perform this check upon receiving a network map, // Check that the certificate validates. Nodes will perform this check upon receiving a network map,
// it's better to fail here than there. // it's better to fail here than there.
X509Utilities.validateCertificateChain(rootCAKeyAndCert.certificate.cert, networkMapCert) X509Utilities.validateCertificateChain(rootCAKeyAndCert.certificate, networkMapCert)
return CertificateAndKeyPair(networkMapCert.toX509CertHolder(), networkMapKey) return CertificateAndKeyPair(networkMapCert, networkMapKey)
} }
} }
@ -130,7 +129,7 @@ class NetworkMapServer(cacheTimeout: Duration,
val networkMap = NetworkMap(nodeInfoMap.keys.toList(), parametersHash) val networkMap = NetworkMap(nodeInfoMap.keys.toList(), parametersHash)
val serializedNetworkMap = networkMap.serialize() val serializedNetworkMap = networkMap.serialize()
val signature = Crypto.doSign(networkMapKeyAndCert.keyPair.private, serializedNetworkMap.bytes) val signature = Crypto.doSign(networkMapKeyAndCert.keyPair.private, serializedNetworkMap.bytes)
val signedNetworkMap = SignedNetworkMap(networkMap.serialize(), DigitalSignatureWithCert(networkMapKeyAndCert.certificate.cert, signature)) val signedNetworkMap = SignedNetworkMap(networkMap.serialize(), DigitalSignatureWithCert(networkMapKeyAndCert.certificate, signature))
return Response.ok(signedNetworkMap.serialize().bytes).header("Cache-Control", "max-age=${cacheTimeout.seconds}").build() return Response.ok(signedNetworkMap.serialize().bytes).header("Cache-Control", "max-age=${cacheTimeout.seconds}").build()
} }

View File

@ -1,4 +1,5 @@
apply plugin: 'net.corda.plugins.publish-utils' apply plugin: 'net.corda.plugins.publish-utils'
apply plugin: 'net.corda.plugins.api-scanner'
apply plugin: 'com.jfrog.artifactory' apply plugin: 'com.jfrog.artifactory'
dependencies { dependencies {

View File

@ -2,6 +2,7 @@ apply plugin: 'kotlin'
apply plugin: 'kotlin-jpa' apply plugin: 'kotlin-jpa'
apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.quasar-utils'
apply plugin: 'net.corda.plugins.publish-utils' apply plugin: 'net.corda.plugins.publish-utils'
apply plugin: 'net.corda.plugins.api-scanner'
apply plugin: 'com.jfrog.artifactory' apply plugin: 'com.jfrog.artifactory'
description 'Testing utilities for Corda' description 'Testing utilities for Corda'

View File

@ -12,7 +12,6 @@ import net.corda.core.crypto.toStringShort
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate import net.corda.core.identity.PartyAndCertificate
import net.corda.core.internal.cert
import net.corda.core.internal.unspecifiedCountry import net.corda.core.internal.unspecifiedCountry
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
@ -21,10 +20,13 @@ import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.CertificateType
import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509CertificateFactory
import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities
import org.bouncycastle.cert.X509CertificateHolder import org.bouncycastle.asn1.x509.GeneralName
import org.bouncycastle.asn1.x509.GeneralSubtree
import org.bouncycastle.asn1.x509.NameConstraints
import java.math.BigInteger import java.math.BigInteger
import java.security.KeyPair import java.security.KeyPair
import java.security.PublicKey import java.security.PublicKey
import java.security.cert.X509Certificate
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
/** /**
@ -76,8 +78,8 @@ fun getFreeLocalPorts(hostName: String, numberToAlloc: Int): List<NetworkHostAnd
} }
fun getTestPartyAndCertificate(party: Party): PartyAndCertificate { fun getTestPartyAndCertificate(party: Party): PartyAndCertificate {
val trustRoot: X509CertificateHolder = DEV_TRUST_ROOT val trustRoot: X509Certificate = DEV_ROOT_CA.certificate
val intermediate: CertificateAndKeyPair = DEV_CA val intermediate: CertificateAndKeyPair = DEV_INTERMEDIATE_CA
val (nodeCaCert, nodeCaKeyPair) = createDevNodeCa(intermediate, party.name) val (nodeCaCert, nodeCaKeyPair) = createDevNodeCa(intermediate, party.name)
@ -85,11 +87,10 @@ fun getTestPartyAndCertificate(party: Party): PartyAndCertificate {
CertificateType.LEGAL_IDENTITY, CertificateType.LEGAL_IDENTITY,
nodeCaCert, nodeCaCert,
nodeCaKeyPair, nodeCaKeyPair,
party.name, party.name.x500Principal,
party.owningKey) party.owningKey)
val pathElements = listOf(identityCert, nodeCaCert, intermediate.certificate, trustRoot) val certPath = X509CertificateFactory().generateCertPath(identityCert, nodeCaCert, intermediate.certificate, trustRoot)
val certPath = X509CertificateFactory().generateCertPath(pathElements.map(X509CertificateHolder::cert))
return PartyAndCertificate(certPath) return PartyAndCertificate(certPath)
} }

View File

@ -1,6 +1,7 @@
package net.corda.testing package net.corda.testing
import com.google.common.util.concurrent.SettableFuture import com.google.common.util.concurrent.SettableFuture
import net.corda.core.DoNotImplement
import net.corda.core.internal.uncheckedCast import net.corda.core.internal.uncheckedCast
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import org.slf4j.Logger import org.slf4j.Logger
@ -189,6 +190,7 @@ fun <S, E : Any> S.genericExpectEvents(
finishFuture.getOrThrow() finishFuture.getOrThrow()
} }
@DoNotImplement
sealed class ExpectCompose<out E> { sealed class ExpectCompose<out E> {
internal class Single<out E, T : E>(val expect: Expect<E, T>) : ExpectCompose<E>() internal class Single<out E, T : E>(val expect: Expect<E, T>) : ExpectCompose<E>()
internal class Sequential<out E>(val sequence: List<ExpectCompose<E>>) : ExpectCompose<E>() internal class Sequential<out E>(val sequence: List<ExpectCompose<E>>) : ExpectCompose<E>()

View File

@ -2,6 +2,7 @@ package net.corda.testing
import com.nhaarman.mockito_kotlin.* import com.nhaarman.mockito_kotlin.*
import net.corda.client.rpc.internal.KryoClientSerializationScheme import net.corda.client.rpc.internal.KryoClientSerializationScheme
import net.corda.core.DoNotImplement
import net.corda.core.internal.staticField import net.corda.core.internal.staticField
import net.corda.core.serialization.internal.* import net.corda.core.serialization.internal.*
import net.corda.node.serialization.KryoServerSerializationScheme import net.corda.node.serialization.KryoServerSerializationScheme
@ -62,6 +63,7 @@ class SerializationEnvironmentRule(private val inheritable: Boolean = false) : T
} }
} }
@DoNotImplement
interface GlobalSerializationEnvironment : SerializationEnvironment { interface GlobalSerializationEnvironment : SerializationEnvironment {
/** Unset this environment. */ /** Unset this environment. */
fun unset() fun unset()

View File

@ -6,12 +6,10 @@ import net.corda.core.contracts.Command
import net.corda.core.contracts.TypeOnlyCommandData import net.corda.core.contracts.TypeOnlyCommandData
import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.generateKeyPair
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.toX509CertHolder
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.nodeapi.internal.crypto.getCertificateAndKeyPair import net.corda.nodeapi.internal.crypto.getCertificateAndKeyPair
import net.corda.nodeapi.internal.crypto.loadKeyStore import net.corda.nodeapi.internal.crypto.loadKeyStore
import org.bouncycastle.cert.X509CertificateHolder
import java.security.PublicKey import java.security.PublicKey
import java.time.Instant import java.time.Instant
@ -34,22 +32,17 @@ val ALICE_NAME = CordaX500Name("Alice Corp", "Madrid", "ES")
val BOB_NAME = CordaX500Name("Bob Plc", "Rome", "IT") val BOB_NAME = CordaX500Name("Bob Plc", "Rome", "IT")
@JvmField @JvmField
val CHARLIE_NAME = CordaX500Name("Charlie Ltd", "Athens", "GR") val CHARLIE_NAME = CordaX500Name("Charlie Ltd", "Athens", "GR")
val DEV_CA: CertificateAndKeyPair by lazy { val DEV_INTERMEDIATE_CA: CertificateAndKeyPair by lazy {
// TODO: Should be identity scheme // TODO: Should be identity scheme
val caKeyStore = loadKeyStore(ClassLoader.getSystemResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass") val caKeyStore = loadKeyStore(ClassLoader.getSystemResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass") caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
} }
val ROOT_CA: CertificateAndKeyPair by lazy { val DEV_ROOT_CA: CertificateAndKeyPair by lazy {
// TODO: Should be identity scheme // TODO: Should be identity scheme
val caKeyStore = loadKeyStore(ClassLoader.getSystemResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass") val caKeyStore = loadKeyStore(ClassLoader.getSystemResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_ROOT_CA, "cordacadevkeypass") caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_ROOT_CA, "cordacadevkeypass")
} }
val DEV_TRUST_ROOT: X509CertificateHolder by lazy {
// TODO: Should be identity scheme
val caKeyStore = loadKeyStore(ClassLoader.getSystemResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
caKeyStore.getCertificateChain(X509Utilities.CORDA_INTERMEDIATE_CA).last().toX509CertHolder()
}
fun dummyCommand(vararg signers: PublicKey = arrayOf(generateKeyPair().public)) = Command<TypeOnlyCommandData>(DummyCommandData, signers.toList()) fun dummyCommand(vararg signers: PublicKey = arrayOf(generateKeyPair().public)) = Command<TypeOnlyCommandData>(DummyCommandData, signers.toList())

View File

@ -1,5 +1,6 @@
package net.corda.testing.contracts package net.corda.testing.contracts
import net.corda.core.DoNotImplement
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
@ -12,6 +13,7 @@ data class DummyContract(val blank: Any? = null) : Contract {
val PROGRAM_ID = "net.corda.testing.contracts.DummyContract" val PROGRAM_ID = "net.corda.testing.contracts.DummyContract"
@DoNotImplement // State is effectively a sealed class.
interface State : ContractState { interface State : ContractState {
val magicNumber: Int val magicNumber: Int
} }

View File

@ -1,5 +1,6 @@
package net.corda.testing.dsl package net.corda.testing.dsl
import net.corda.core.DoNotImplement
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.TransactionState import net.corda.core.contracts.TransactionState
@ -14,6 +15,7 @@ import java.io.InputStream
* This interface defines output state lookup by label. It is split from the interpreter interfaces so that outputs may * This interface defines output state lookup by label. It is split from the interpreter interfaces so that outputs may
* be looked up both in ledger{..} and transaction{..} blocks. * be looked up both in ledger{..} and transaction{..} blocks.
*/ */
@DoNotImplement
interface OutputStateLookup { interface OutputStateLookup {
/** /**
* Retrieves an output previously defined by [TransactionDSLInterpreter.output] with a label passed in. * Retrieves an output previously defined by [TransactionDSLInterpreter.output] with a label passed in.
@ -27,6 +29,7 @@ interface OutputStateLookup {
/** /**
* This interface asserts that the DSL at hand is capable of verifying its underlying construct(ledger/transaction). * This interface asserts that the DSL at hand is capable of verifying its underlying construct(ledger/transaction).
*/ */
@DoNotImplement
interface Verifies { interface Verifies {
/** /**
* Verifies the ledger/transaction, throws if the verification fails. * Verifies the ledger/transaction, throws if the verification fails.
@ -83,6 +86,7 @@ interface Verifies {
* *
* TODO (Kotlin 1.1): Use type synonyms to make the type params less unwieldy * TODO (Kotlin 1.1): Use type synonyms to make the type params less unwieldy
*/ */
@DoNotImplement
interface LedgerDSLInterpreter<out T : TransactionDSLInterpreter> : Verifies, OutputStateLookup { interface LedgerDSLInterpreter<out T : TransactionDSLInterpreter> : Verifies, OutputStateLookup {
/** /**
* Creates and adds a transaction to the ledger. * Creates and adds a transaction to the ledger.

View File

@ -1,5 +1,6 @@
package net.corda.testing.dsl package net.corda.testing.dsl
import net.corda.core.DoNotImplement
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.cordapp.CordappProvider import net.corda.core.cordapp.CordappProvider
import net.corda.core.crypto.* import net.corda.core.crypto.*
@ -50,6 +51,7 @@ import kotlin.collections.set
* will have as the last line either an accept or a failure test. The name is deliberately long to help make sense of * will have as the last line either an accept or a failure test. The name is deliberately long to help make sense of
* the triggered diagnostic. * the triggered diagnostic.
*/ */
@DoNotImplement
sealed class EnforceVerifyOrFail { sealed class EnforceVerifyOrFail {
internal object Token : EnforceVerifyOrFail() internal object Token : EnforceVerifyOrFail()
} }

View File

@ -1,5 +1,6 @@
package net.corda.testing.dsl package net.corda.testing.dsl
import net.corda.core.DoNotImplement
import net.corda.core.contracts.AlwaysAcceptAttachmentConstraint import net.corda.core.contracts.AlwaysAcceptAttachmentConstraint
import net.corda.core.contracts.Attachment import net.corda.core.contracts.Attachment
import net.corda.core.contracts.AttachmentConstraint import net.corda.core.contracts.AttachmentConstraint
@ -22,6 +23,7 @@ import java.time.Instant
* @param <R> The return type of [verifies]/[failsWith] and the like. It is generic so that we have control over whether * @param <R> The return type of [verifies]/[failsWith] and the like. It is generic so that we have control over whether
* we want to enforce users to call these methods (see [EnforceVerifyOrFail]) or not. * we want to enforce users to call these methods (see [EnforceVerifyOrFail]) or not.
*/ */
@DoNotImplement
interface TransactionDSLInterpreter : Verifies, OutputStateLookup { interface TransactionDSLInterpreter : Verifies, OutputStateLookup {
/** /**
* A reference to the enclosing ledger{..}'s interpreter. * A reference to the enclosing ledger{..}'s interpreter.

View File

@ -15,6 +15,7 @@ import org.mockito.internal.stubbing.answers.ThrowsException
import java.lang.reflect.Modifier import java.lang.reflect.Modifier
import java.nio.file.Files import java.nio.file.Files
import java.util.* import java.util.*
import javax.security.auth.x500.X500Principal
@Suppress("unused") @Suppress("unused")
inline fun <reified T : Any> T.kryoSpecific(reason: String, function: () -> Unit) = if (!AMQP_ENABLED) { inline fun <reified T : Any> T.kryoSpecific(reason: String, function: () -> Unit) = if (!AMQP_ENABLED) {
@ -64,8 +65,8 @@ fun configureTestSSL(legalName: CordaX500Name): SSLConfiguration {
} }
} }
private val defaultRootCaName = CordaX500Name("Corda Root CA", "R3 Ltd", "London", "GB") private val defaultRootCaName = X500Principal("CN=Corda Root CA,O=R3 Ltd,L=London,C=GB")
private val defaultIntermediateCaName = CordaX500Name("Corda Intermediate CA", "R3 Ltd", "London", "GB") private val defaultIntermediateCaName = X500Principal("CN=Corda Intermediate CA,O=R3 Ltd,L=London,C=GB")
/** /**
* Returns a pair of [CertificateAndKeyPair]s, the first being the root CA and the second the intermediate CA. * Returns a pair of [CertificateAndKeyPair]s, the first being the root CA and the second the intermediate CA.
@ -73,8 +74,8 @@ private val defaultIntermediateCaName = CordaX500Name("Corda Intermediate CA", "
* @param intermediateCaName The subject name for the intermediate CA cert. * @param intermediateCaName The subject name for the intermediate CA cert.
*/ */
fun createDevIntermediateCaCertPath( fun createDevIntermediateCaCertPath(
rootCaName: CordaX500Name = defaultRootCaName, rootCaName: X500Principal = defaultRootCaName,
intermediateCaName: CordaX500Name = defaultIntermediateCaName intermediateCaName: X500Principal = defaultIntermediateCaName
): Pair<CertificateAndKeyPair, CertificateAndKeyPair> { ): Pair<CertificateAndKeyPair, CertificateAndKeyPair> {
val rootKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val rootKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val rootCert = X509Utilities.createSelfSignedCACertificate(rootCaName, rootKeyPair) val rootCert = X509Utilities.createSelfSignedCACertificate(rootCaName, rootKeyPair)
@ -87,7 +88,10 @@ fun createDevIntermediateCaCertPath(
intermediateCaName, intermediateCaName,
intermediateCaKeyPair.public) intermediateCaKeyPair.public)
return Pair(CertificateAndKeyPair(rootCert, rootKeyPair), CertificateAndKeyPair(intermediateCaCert, intermediateCaKeyPair)) return Pair(
CertificateAndKeyPair(rootCert, rootKeyPair),
CertificateAndKeyPair(intermediateCaCert, intermediateCaKeyPair)
)
} }
/** /**
@ -97,8 +101,8 @@ fun createDevIntermediateCaCertPath(
*/ */
fun createDevNodeCaCertPath( fun createDevNodeCaCertPath(
legalName: CordaX500Name, legalName: CordaX500Name,
rootCaName: CordaX500Name = defaultRootCaName, rootCaName: X500Principal = defaultRootCaName,
intermediateCaName: CordaX500Name = defaultIntermediateCaName intermediateCaName: X500Principal = defaultIntermediateCaName
): Triple<CertificateAndKeyPair, CertificateAndKeyPair, CertificateAndKeyPair> { ): Triple<CertificateAndKeyPair, CertificateAndKeyPair, CertificateAndKeyPair> {
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath(rootCaName, intermediateCaName) val (rootCa, intermediateCa) = createDevIntermediateCaCertPath(rootCaName, intermediateCaName)
val nodeCa = createDevNodeCa(intermediateCa, legalName) val nodeCa = createDevNodeCa(intermediateCa, legalName)