diff --git a/.ci/api-current.txt b/.ci/api-current.txt index 8770bf5465..8a98f07116 100644 --- a/.ci/api-current.txt +++ b/.ci/api-current.txt @@ -3579,6 +3579,9 @@ public static final class net.corda.client.jackson.StringToMethodCallParser$Unpa public (String) @org.jetbrains.annotations.NotNull public final String getMethodName() ## +public static interface net.corda.testing.node.InMemoryMessagingNetwork$LatencyCalculator + @org.jetbrains.annotations.NotNull public abstract java.time.Duration between(net.corda.core.messaging.SingleMessageRecipient, net.corda.core.messaging.SingleMessageRecipient) +## public final class net.corda.client.rpc.CordaRPCClient extends java.lang.Object public (net.corda.core.utilities.NetworkHostAndPort) public (net.corda.core.utilities.NetworkHostAndPort, net.corda.client.rpc.CordaRPCClientConfiguration) diff --git a/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt b/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt index 4a22ee467d..be24fe66ad 100644 --- a/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt +++ b/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt @@ -31,6 +31,9 @@ import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.internal.config.User import net.corda.testing.* import net.corda.testing.driver.driver +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaNames import org.junit.ClassRule import org.junit.Test import rx.Observable @@ -54,7 +57,7 @@ class NodeMonitorModelTest : IntegrationTest() { companion object { @ClassRule @JvmField val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, CHARLIE_NAME, DUMMY_NOTARY_NAME) - .map { it.toDatabaseSchemaNames("","_10000","_10003") }.flatten().toTypedArray()) + .map { it.toDatabaseSchemaNames("", "_10000", "_10003") }.flatten().toTypedArray()) } private fun setup(runTest: () -> Unit) { diff --git a/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java b/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java index b0bce0c0b4..0b3646493b 100644 --- a/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java +++ b/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java @@ -12,8 +12,8 @@ import net.corda.node.internal.Node; import net.corda.node.internal.StartedNode; import net.corda.nodeapi.internal.config.User; import net.corda.testing.CoreTestUtils; -import net.corda.testing.IntegrationTestKt; -import net.corda.testing.IntegrationTestSchemas; +import net.corda.testing.internal.IntegrationTestKt; +import net.corda.testing.internal.IntegrationTestSchemas; import net.corda.testing.node.internal.NodeBasedTest; import org.junit.After; import org.junit.Before; @@ -32,7 +32,6 @@ import static net.corda.finance.contracts.GetBalances.getCashBalance; import static net.corda.node.services.Permissions.invokeRpc; import static net.corda.node.services.Permissions.startFlow; import static net.corda.testing.TestConstants.ALICE_NAME; -import static net.corda.testing.TestConstants.DUMMY_NOTARY_NAME; public class CordaRPCJavaClientTest extends NodeBasedTest { public CordaRPCJavaClientTest() { diff --git a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/BlacklistKotlinClosureTest.kt b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/BlacklistKotlinClosureTest.kt index 12372dbf5f..6afba56f62 100644 --- a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/BlacklistKotlinClosureTest.kt +++ b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/BlacklistKotlinClosureTest.kt @@ -9,6 +9,9 @@ import net.corda.core.serialization.CordaSerializable import net.corda.core.utilities.getOrThrow import net.corda.testing.* import net.corda.testing.driver.driver +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import org.assertj.core.api.Assertions.assertThatExceptionOfType import org.junit.ClassRule import org.junit.Test diff --git a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt index bef0cde9c4..a5a3b81c40 100644 --- a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt +++ b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt @@ -22,6 +22,8 @@ import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.internal.config.User import net.corda.testing.* +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import net.corda.testing.node.internal.NodeBasedTest import org.apache.activemq.artemis.api.core.ActiveMQSecurityException import org.assertj.core.api.Assertions.assertThat diff --git a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/RPCStabilityTests.kt b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/RPCStabilityTests.kt index dfc2950471..a3f6b9f9b7 100644 --- a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/RPCStabilityTests.kt +++ b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/RPCStabilityTests.kt @@ -13,7 +13,10 @@ import net.corda.core.utilities.* import net.corda.node.services.messaging.RPCServerConfiguration import net.corda.nodeapi.RPCApi import net.corda.testing.* +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.internal.testThreadFactory +import net.corda.testing.internal.toDatabaseSchemaNames import net.corda.testing.node.internal.* import org.apache.activemq.artemis.api.core.SimpleString import org.junit.After @@ -45,7 +48,7 @@ class RPCStabilityTests : IntegrationTest() { companion object { @ClassRule @JvmField val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, DUMMY_BANK_A_NAME) - .map { it.toDatabaseSchemaNames("","_10000","_10003","_10012") }.flatten().toTypedArray()) + .map { it.toDatabaseSchemaNames("", "_10000", "_10003", "_10012") }.flatten().toTypedArray()) } object DummyOps : RPCOps { diff --git a/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesFlow.kt b/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesFlow.kt index 575c1f8d52..f98970c322 100644 --- a/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesFlow.kt +++ b/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesFlow.kt @@ -10,7 +10,6 @@ import net.corda.core.identity.AnonymousParty import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate -import net.corda.core.internal.toX509CertHolder import net.corda.core.node.services.IdentityService import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.SerializedBytes @@ -18,14 +17,8 @@ import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.unwrap -import org.bouncycastle.asn1.DERSet -import org.bouncycastle.asn1.pkcs.CertificationRequestInfo -import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo -import java.io.ByteArrayOutputStream -import java.nio.charset.Charset import java.security.PublicKey import java.security.SignatureException -import java.security.cert.CertPath import java.util.* /** diff --git a/constants.properties b/constants.properties index 935fb23f75..ea74e20f17 100644 --- a/constants.properties +++ b/constants.properties @@ -1,4 +1,4 @@ -gradlePluginsVersion=3.0.2 +gradlePluginsVersion=3.0.3 kotlinVersion=1.1.60 platformVersion=1 guavaVersion=21.0 diff --git a/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt b/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt index e736628215..1657eb2d4a 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt @@ -30,6 +30,7 @@ import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPublicKey import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter import org.bouncycastle.jce.ECNamedCurveTable import org.bouncycastle.jce.provider.BouncyCastleProvider +import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec import org.bouncycastle.jce.spec.ECParameterSpec import org.bouncycastle.jce.spec.ECPrivateKeySpec import org.bouncycastle.jce.spec.ECPublicKeySpec @@ -808,7 +809,7 @@ object Crypto { /** * Returns a key pair derived from the given [BigInteger] entropy. This is useful for unit tests * and other cases where you want hard-coded private keys. - * Currently, [EDDSA_ED25519_SHA512] is the sole scheme supported for this operation. + * Currently, the following schemes are supported: [EDDSA_ED25519_SHA512], [ECDSA_SECP256R1_SHA256] and [ECDSA_SECP256K1_SHA256]. * @param signatureScheme a supported [SignatureScheme], see [Crypto]. * @param entropy a [BigInteger] value. * @return a new [KeyPair] from an entropy input. @@ -818,6 +819,7 @@ object Crypto { fun deriveKeyPairFromEntropy(signatureScheme: SignatureScheme, entropy: BigInteger): KeyPair { return when (signatureScheme) { EDDSA_ED25519_SHA512 -> deriveEdDSAKeyPairFromEntropy(entropy) + ECDSA_SECP256R1_SHA256, ECDSA_SECP256K1_SHA256 -> deriveECDSAKeyPairFromEntropy(signatureScheme, entropy) else -> throw IllegalArgumentException("Unsupported signature scheme for fixed entropy-based key pair " + "generation: ${signatureScheme.schemeCodeName}") } @@ -832,6 +834,9 @@ object Crypto { fun deriveKeyPairFromEntropy(entropy: BigInteger): KeyPair = deriveKeyPairFromEntropy(DEFAULT_SIGNATURE_SCHEME, entropy) // Custom key pair generator from entropy. + // The BigIntenger.toByteArray() uses the two's-complement representation. + // The entropy is transformed to a byte array in big-endian byte-order and + // only the first ed25519.field.getb() / 8 bytes are used. private fun deriveEdDSAKeyPairFromEntropy(entropy: BigInteger): KeyPair { val params = EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec val bytes = entropy.toByteArray().copyOf(params.curve.field.getb() / 8) // Need to pad the entropy to the valid seed length. @@ -840,6 +845,32 @@ object Crypto { return KeyPair(EdDSAPublicKey(pub), EdDSAPrivateKey(priv)) } + // Custom key pair generator from an entropy required for various tests. It is similar to deriveKeyPairECDSA, + // but the accepted range of the input entropy is more relaxed: + // 2 <= entropy < N, where N is the order of base-point G. + private fun deriveECDSAKeyPairFromEntropy(signatureScheme: SignatureScheme, entropy: BigInteger): KeyPair { + val parameterSpec = signatureScheme.algSpec as ECNamedCurveParameterSpec + + // The entropy might be a negative number and/or out of range (e.g. PRNG output). + // In such cases we retry with hash(currentEntropy). + while (entropy < ECConstants.TWO || entropy >= parameterSpec.n) { + return deriveECDSAKeyPairFromEntropy(signatureScheme, BigInteger(1, entropy.toByteArray().sha256().bytes)) + } + + val privateKeySpec = ECPrivateKeySpec(entropy, parameterSpec) + val priv = BCECPrivateKey("EC", privateKeySpec, BouncyCastleProvider.CONFIGURATION) + + val pointQ = FixedPointCombMultiplier().multiply(parameterSpec.g, entropy) + while (pointQ.isInfinity) { + // Instead of throwing an exception, we retry with hash(entropy). + return deriveECDSAKeyPairFromEntropy(signatureScheme, BigInteger(1, entropy.toByteArray().sha256().bytes)) + } + val publicKeySpec = ECPublicKeySpec(pointQ, parameterSpec) + val pub = BCECPublicKey("EC", publicKeySpec, BouncyCastleProvider.CONFIGURATION) + + return KeyPair(pub, priv) + } + // Compute the HMAC-SHA512 using a privateKey as the MAC_key and a seed ByteArray. private fun deriveHMAC(privateKey: PrivateKey, seed: ByteArray): ByteArray { // Compute hmac(privateKey, seed). diff --git a/core/src/main/kotlin/net/corda/core/crypto/CryptoUtils.kt b/core/src/main/kotlin/net/corda/core/crypto/CryptoUtils.kt index 8549908a25..764a115854 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/CryptoUtils.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/CryptoUtils.kt @@ -118,18 +118,19 @@ fun Iterable.byKeys() = map { it.by }.toSet() // Allow Kotlin destructuring: // val (private, public) = keyPair -/* The [PrivateKey] of this [KeyPair] .*/ +/* The [PrivateKey] of this [KeyPair]. */ operator fun KeyPair.component1(): PrivateKey = this.private -/* The [PublicKey] of this [KeyPair] .*/ +/* The [PublicKey] of this [KeyPair]. */ operator fun KeyPair.component2(): PublicKey = this.public -/** A simple wrapper that will make it easier to swap out the EC algorithm we use in future. */ +/** A simple wrapper that will make it easier to swap out the signature algorithm we use in future. */ fun generateKeyPair(): KeyPair = Crypto.generateKeyPair() /** * Returns a key pair derived from the given private key entropy. This is useful for unit tests and other cases where * you want hard-coded private keys. - * This currently works for the default signature scheme EdDSA ed25519 only. + * @param entropy a [BigInteger] value. + * @return a deterministically generated [KeyPair] for the [Crypto.DEFAULT_SIGNATURE_SCHEME]. */ fun entropyToKeyPair(entropy: BigInteger): KeyPair = Crypto.deriveKeyPairFromEntropy(entropy) diff --git a/core/src/main/kotlin/net/corda/core/flows/FlowLogicRef.kt b/core/src/main/kotlin/net/corda/core/flows/FlowLogicRef.kt index 4768ff9259..36b0971cb0 100644 --- a/core/src/main/kotlin/net/corda/core/flows/FlowLogicRef.kt +++ b/core/src/main/kotlin/net/corda/core/flows/FlowLogicRef.kt @@ -10,7 +10,17 @@ import net.corda.core.serialization.CordaSerializable */ @DoNotImplement interface FlowLogicRefFactory { + /** + * Construct a FlowLogicRef. This is intended for cases where the calling code has the relevant class already + * and can provide it directly. + */ + @Deprecated("This should be avoided, and the version which takes a class name used instead to avoid requiring the class on the classpath to deserialize calling code") fun create(flowClass: Class>, vararg args: Any?): FlowLogicRef + /** + * Construct a FlowLogicRef. This is intended for cases where the calling code does not want to require the flow + * class on the classpath for all cases where the calling code is loaded. + */ + fun create(flowClassName: String, vararg args: Any?): FlowLogicRef fun createForRPC(flowClass: Class>, vararg args: Any?): FlowLogicRef fun toFlowLogic(ref: FlowLogicRef): FlowLogic<*> } diff --git a/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt b/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt index ad315c065e..abe3d21fd4 100644 --- a/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt +++ b/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt @@ -80,8 +80,10 @@ data class CordaX500Name(val commonName: String?, const val MAX_LENGTH_STATE = 64 const val MAX_LENGTH_ORGANISATION_UNIT = 64 const val MAX_LENGTH_COMMON_NAME = 64 + private val supportedAttributes = setOf(BCStyle.O, BCStyle.C, BCStyle.L, BCStyle.CN, BCStyle.ST, BCStyle.OU) private val countryCodes: Set = ImmutableSet.copyOf(Locale.getISOCountries() + unspecifiedCountry) + @JvmStatic fun build(principal: X500Principal): CordaX500Name { val x500Name = X500Name.getInstance(principal.encoded) @@ -115,20 +117,12 @@ data class CordaX500Name(val commonName: String?, } @Transient - private var x500Cache: X500Name? = null + private var _x500Principal: X500Principal? = null - val x500Principal: X500Principal - get() { - if (x500Cache == null) { - x500Cache = this.x500Name - } - return X500Principal(x500Cache!!.encoded) - } - - override fun toString(): String { - if (x500Cache == null) { - x500Cache = this.x500Name - } - return x500Cache.toString() + /** Return the [X500Principal] equivalent of this name. */ + val x500Principal: X500Principal get() { + return _x500Principal ?: X500Principal(this.x500Name.encoded).also { _x500Principal = it } } + + override fun toString(): String = x500Principal.toString() } \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt index 42145a2f26..1ad9e16630 100644 --- a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt @@ -10,8 +10,9 @@ import net.corda.core.node.ServicesForResolution import net.corda.core.serialization.SerializationContext import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.WireTransaction -import org.bouncycastle.cert.X509CertificateHolder -import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter +import org.bouncycastle.asn1.x500.X500Name +import org.bouncycastle.asn1.x500.X500NameBuilder +import org.bouncycastle.asn1.x500.style.BCStyle import org.slf4j.Logger import rx.Observable import rx.Observer @@ -26,8 +27,6 @@ import java.nio.charset.Charset import java.nio.charset.StandardCharsets.UTF_8 import java.nio.file.* import java.nio.file.attribute.FileAttribute -import java.security.cert.Certificate -import java.security.cert.X509Certificate import java.time.Duration import java.time.temporal.Temporal import java.util.* @@ -186,9 +185,6 @@ fun logElapsedTime(label: String, logger: Logger? = null, body: () -> T): T } } -fun Certificate.toX509CertHolder() = X509CertificateHolder(encoded) -val X509CertificateHolder.cert: X509Certificate get() = JcaX509CertificateConverter().getCertificate(this) - /** Convert a [ByteArrayOutputStream] to [InputStreamAndHash]. */ fun ByteArrayOutputStream.toInputStreamAndHash(): InputStreamAndHash { val bytes = toByteArray() @@ -320,6 +316,22 @@ fun ExecutorService.join() { } } +/** + * Return the underlying X.500 name from this Corda-safe X.500 name. These are guaranteed to have a consistent + * ordering, such that their `toString()` function returns the same value every time for the same [CordaX500Name]. + */ +val CordaX500Name.x500Name: X500Name + get() { + return X500NameBuilder(BCStyle.INSTANCE).apply { + addRDN(BCStyle.C, country) + state?.let { addRDN(BCStyle.ST, it) } + addRDN(BCStyle.L, locality) + addRDN(BCStyle.O, organisation) + organisationUnit?.let { addRDN(BCStyle.OU, it) } + commonName?.let { addRDN(BCStyle.CN, it) } + }.build() + } + @Suppress("unused") @VisibleForTesting val CordaX500Name.Companion.unspecifiedCountry diff --git a/core/src/main/kotlin/net/corda/core/internal/X500NameUtils.kt b/core/src/main/kotlin/net/corda/core/internal/X500NameUtils.kt deleted file mode 100644 index 7d57d7d522..0000000000 --- a/core/src/main/kotlin/net/corda/core/internal/X500NameUtils.kt +++ /dev/null @@ -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() - } \ No newline at end of file diff --git a/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt b/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt index 751037b1ee..61d0c3d2f6 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt @@ -1,8 +1,6 @@ package net.corda.core.crypto import net.corda.core.crypto.CompositeKey.NodeAndWeight -import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.cert import net.corda.core.internal.declaredField import net.corda.core.internal.div import net.corda.core.serialization.serialize @@ -15,6 +13,7 @@ import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder import java.security.PublicKey +import javax.security.auth.x500.X500Principal import kotlin.test.assertEquals import kotlin.test.assertFailsWith import kotlin.test.assertFalse @@ -334,7 +333,7 @@ class CompositeKeyTests { // Create self sign CA. val caKeyPair = Crypto.generateKeyPair() - val caName = CordaX500Name(commonName = "Test CA", organisation = "R3 Ltd", locality = "London", country = "GB") + val caName = X500Principal("CN=Test CA,O=R3 Ltd,L=London,C=GB") val ca = X509Utilities.createSelfSignedCACertificate(caName, caKeyPair) // Sign the composite key with the self sign CA. @@ -343,7 +342,7 @@ class CompositeKeyTests { // Store certificate to keystore. val keystorePath = tempFolder.root.toPath() / "keystore.jks" val keystore = loadOrCreateKeyStore(keystorePath, "password") - keystore.setCertificateEntry("CompositeKey", compositeKeyCert.cert) + keystore.setCertificateEntry("CompositeKey", compositeKeyCert) keystore.save(keystorePath, "password") // Load keystore from disk. diff --git a/core/src/test/kotlin/net/corda/core/crypto/CryptoUtilsTest.kt b/core/src/test/kotlin/net/corda/core/crypto/CryptoUtilsTest.kt index d10944bdb8..587700fc32 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/CryptoUtilsTest.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/CryptoUtilsTest.kt @@ -14,21 +14,22 @@ import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey import org.bouncycastle.jce.ECNamedCurveTable import org.bouncycastle.jce.interfaces.ECKey +import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PrivateKey import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PublicKey import org.junit.Assert.assertNotEquals import org.junit.Test +import java.math.BigInteger import java.security.KeyPairGenerator import java.util.* import kotlin.test.* /** - * Run tests for cryptographic algorithms + * Run tests for cryptographic algorithms. */ class CryptoUtilsTest { - private val testString = "Hello World" - private val testBytes = testString.toByteArray() + private val testBytes = "Hello World".toByteArray() // key generation test @Test @@ -781,4 +782,113 @@ class CryptoUtilsTest { assertEquals(dpriv2, dpriv_2) assertEquals(dpub2, dpub_2) } + + @Test + fun `EdDSA ed25519 keyPair from entropy`() { + val keyPairPositive = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("10")) + assertEquals("DLBL3iHCp9uRReWhhCGfCsrxZZpfAm9h9GLbfN8ijqXTq", keyPairPositive.public.toStringShort()) + + val keyPairNegative = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("-10")) + assertEquals("DLC5HXnYsJAFqmM9hgPj5G8whQ4TpyE9WMBssqCayLBwA2", keyPairNegative.public.toStringShort()) + + val keyPairZero = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("0")) + assertEquals("DL4UVhGh4tqu1G86UVoGNaDDNCMsBtNHzE6BSZuNNJN7W2", keyPairZero.public.toStringShort()) + + val keyPairOne = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("1")) + assertEquals("DL8EZUdHixovcCynKMQzrMWBnXQAcbVDHi6ArPphqwJVzq", keyPairOne.public.toStringShort()) + + val keyPairBiggerThan256bits = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("2").pow(258).minus(BigInteger.TEN)) + assertEquals("DLB9K1UiBrWonn481z6NzkqoWHjMBXpfDeaet3wiwRNWSU", keyPairBiggerThan256bits.public.toStringShort()) + // The underlying implementation uses the first 256 bytes of the entropy. Thus, 2^258-10 and 2^258-50 and 2^514-10 have the same impact. + val keyPairBiggerThan256bitsV2 = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("2").pow(258).minus(BigInteger("50"))) + assertEquals("DLB9K1UiBrWonn481z6NzkqoWHjMBXpfDeaet3wiwRNWSU", keyPairBiggerThan256bitsV2.public.toStringShort()) + val keyPairBiggerThan512bits = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("2").pow(514).minus(BigInteger.TEN)) + assertEquals("DLB9K1UiBrWonn481z6NzkqoWHjMBXpfDeaet3wiwRNWSU", keyPairBiggerThan512bits.public.toStringShort()) + + // Try another big number. + val keyPairBiggerThan258bits = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("2").pow(259).plus(BigInteger.ONE)) + assertEquals("DL5tEFVMXMGrzwjfCAW34JjkhsRkPfFyJ38iEnmpB6L2Z9", keyPairBiggerThan258bits.public.toStringShort()) + } + + @Test + fun `ECDSA R1 keyPair from entropy`() { + val keyPairPositive = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("10")) + assertEquals("DLHDcxuSt9J3cbjd2Dsx4rAgYYA7BAP7A8VLrFiq1tH9yy", keyPairPositive.public.toStringShort()) + // The underlying implementation uses the hash of entropy if it is out of range 2 < entropy < N, where N the order of the group. + val keyPairNegative = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("-10")) + assertEquals("DLBASmjiMZuu1g3EtdHJxfSueXE8PRoUWbkdU61Qcnpamt", keyPairNegative.public.toStringShort()) + + val keyPairZero = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("0")) + assertEquals("DLH2FEHEnsT3MpCJt2gfyNjpqRqcBxeupK4YRPXvDsVEkb", keyPairZero.public.toStringShort()) + // BigIntenger.Zero is out or range, so 1 and hash(1.toByteArray) would have the same impact. + val zeroHashed = BigInteger(1, BigInteger("0").toByteArray().sha256().bytes) + // Check oneHashed < N (order of the group), otherwise we would need an extra hash. + assertEquals(-1, zeroHashed.compareTo((Crypto.ECDSA_SECP256R1_SHA256.algSpec as ECNamedCurveParameterSpec).n)) + val keyPairZeroHashed = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, zeroHashed) + assertEquals("DLH2FEHEnsT3MpCJt2gfyNjpqRqcBxeupK4YRPXvDsVEkb", keyPairZeroHashed.public.toStringShort()) + + val keyPairOne = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("1")) + assertEquals("DLHrtKwjv6onq9HcrQDJPs8Cgtai5mZU5ZU6sb1ivJjx3z", keyPairOne.public.toStringShort()) + // BigIntenger.ONE is out or range, so 1 and hash(1.toByteArray) would have the same impact. + val oneHashed = BigInteger(1, BigInteger("1").toByteArray().sha256().bytes) + // Check oneHashed < N (order of the group), otherwise we would need an extra hash. + assertEquals(-1, oneHashed.compareTo((Crypto.ECDSA_SECP256R1_SHA256.algSpec as ECNamedCurveParameterSpec).n)) + val keyPairOneHashed = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, oneHashed) + assertEquals("DLHrtKwjv6onq9HcrQDJPs8Cgtai5mZU5ZU6sb1ivJjx3z", keyPairOneHashed.public.toStringShort()) + + // 2 is in the range. + val keyPairTwo = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2")) + assertEquals("DLFoz6txJ3vHcKNSM1vFxHJUoEQ69PorBwW64dHsAnEoZB", keyPairTwo.public.toStringShort()) + + // Try big numbers that are out of range. + val keyPairBiggerThan256bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2").pow(258).minus(BigInteger.TEN)) + assertEquals("DLBv6fZqaCTbE4L7sgjbt19biXHMgU9CzR5s8g8XBJjZ11", keyPairBiggerThan256bits.public.toStringShort()) + val keyPairBiggerThan256bitsV2 = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2").pow(258).minus(BigInteger("50"))) + assertEquals("DLANmjhGSVdLyghxcPHrn3KuGatscf6LtvqifUDxw7SGU8", keyPairBiggerThan256bitsV2.public.toStringShort()) + val keyPairBiggerThan512bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2").pow(514).minus(BigInteger.TEN)) + assertEquals("DL9sKwMExBTD3MnJN6LWGqo496Erkebs9fxZtXLVJUBY9Z", keyPairBiggerThan512bits.public.toStringShort()) + val keyPairBiggerThan258bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2").pow(259).plus(BigInteger.ONE)) + assertEquals("DLBwjWwPJSF9E7b1NWaSbEJ4oK8CF7RDGWd648TiBhZoL1", keyPairBiggerThan258bits.public.toStringShort()) + } + + @Test + fun `ECDSA K1 keyPair from entropy`() { + val keyPairPositive = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("10")) + assertEquals("DL6pYKUgH17az8MLdonvvUtUPN8TqwpCGcdgLr7vg3skCU", keyPairPositive.public.toStringShort()) + // The underlying implementation uses the hash of entropy if it is out of range 2 <= entropy < N, where N the order of the group. + val keyPairNegative = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("-10")) + assertEquals("DLnpXhxece69Nyqgm3pPt3yV7ESQYDJKoYxs1hKgfBAEu", keyPairNegative.public.toStringShort()) + + val keyPairZero = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("0")) + assertEquals("DLBC28e18T6KsYwjTFfUWJfhvHjvYVapyVf6antnqUkbgd", keyPairZero.public.toStringShort()) + // BigIntenger.Zero is out or range, so 1 and hash(1.toByteArray) would have the same impact. + val zeroHashed = BigInteger(1, BigInteger("0").toByteArray().sha256().bytes) + // Check oneHashed < N (order of the group), otherwise we would need an extra hash. + assertEquals(-1, zeroHashed.compareTo((Crypto.ECDSA_SECP256K1_SHA256.algSpec as ECNamedCurveParameterSpec).n)) + val keyPairZeroHashed = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, zeroHashed) + assertEquals("DLBC28e18T6KsYwjTFfUWJfhvHjvYVapyVf6antnqUkbgd", keyPairZeroHashed.public.toStringShort()) + + val keyPairOne = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("1")) + assertEquals("DLBimRXdEQhJUTpL6f9ri9woNdsze6mwkRrhsML13Eh7ET", keyPairOne.public.toStringShort()) + // BigIntenger.ONE is out or range, so 1 and hash(1.toByteArray) would have the same impact. + val oneHashed = BigInteger(1, BigInteger("1").toByteArray().sha256().bytes) + // Check oneHashed < N (order of the group), otherwise we would need an extra hash. + assertEquals(-1, oneHashed.compareTo((Crypto.ECDSA_SECP256K1_SHA256.algSpec as ECNamedCurveParameterSpec).n)) + val keyPairOneHashed = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, oneHashed) + assertEquals("DLBimRXdEQhJUTpL6f9ri9woNdsze6mwkRrhsML13Eh7ET", keyPairOneHashed.public.toStringShort()) + + // 2 is in the range. + val keyPairTwo = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2")) + assertEquals("DLG32UWaevGw9YY7w1Rf9mmK88biavgpDnJA9bG4GapVPs", keyPairTwo.public.toStringShort()) + + // Try big numbers that are out of range. + val keyPairBiggerThan256bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2").pow(258).minus(BigInteger.TEN)) + assertEquals("DLGHsdv2xeAuM7n3sBc6mFfiphXe6VSf3YxqvviKDU6Vbd", keyPairBiggerThan256bits.public.toStringShort()) + val keyPairBiggerThan256bitsV2 = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2").pow(258).minus(BigInteger("50"))) + assertEquals("DL9yJfiNGqteRrKPjGUkRQkeqzuQ4kwcYQWMCi5YKuUHrk", keyPairBiggerThan256bitsV2.public.toStringShort()) + val keyPairBiggerThan512bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2").pow(514).minus(BigInteger.TEN)) + assertEquals("DL3Wr5EQGrMTaKBy5XMvG8rvSfKX1AYZLCRU8kixGbxt1E", keyPairBiggerThan512bits.public.toStringShort()) + val keyPairBiggerThan258bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2").pow(259).plus(BigInteger.ONE)) + assertEquals("DL7NbssqvuuJ4cqFkkaVYu9j1MsVswESGgCfbqBS9ULwuM", keyPairBiggerThan258bits.public.toStringShort()) + } } diff --git a/core/src/test/kotlin/net/corda/core/crypto/X509NameConstraintsTest.kt b/core/src/test/kotlin/net/corda/core/crypto/X509NameConstraintsTest.kt index 707a32bba4..3e51ad7b9f 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/X509NameConstraintsTest.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/X509NameConstraintsTest.kt @@ -1,7 +1,6 @@ package net.corda.core.crypto import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.cert import net.corda.nodeapi.internal.crypto.* import net.corda.testing.internal.createDevIntermediateCaCertPath import org.bouncycastle.asn1.x500.X500Name @@ -14,6 +13,7 @@ import java.security.KeyStore import java.security.cert.CertPathValidator import java.security.cert.CertPathValidatorException import java.security.cert.PKIXParameters +import javax.security.auth.x500.X500Principal import kotlin.test.assertFailsWith import kotlin.test.assertTrue @@ -26,17 +26,22 @@ class X509NameConstraintsTest { CertificateType.NODE_CA, intermediateCa.certificate, intermediateCa.keyPair, - CordaX500Name("Corda Client CA", "R3 Ltd", "London", "GB"), + CordaX500Name("Corda Client CA", "R3 Ltd", "London", "GB").x500Principal, nodeCaKeyPair.public, nameConstraints = nameConstraints) val keyPass = "password" val trustStore = KeyStore.getInstance(KEYSTORE_TYPE) trustStore.load(null, keyPass.toCharArray()) - trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCa.certificate.cert) + trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCa.certificate) val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, subjectName, tlsKeyPair.public) + val tlsCert = X509Utilities.createCertificate( + CertificateType.TLS, + nodeCaCert, + nodeCaKeyPair, + X500Principal(subjectName.encoded), + tlsKeyPair.public) val keyStore = KeyStore.getInstance(KEYSTORE_TYPE) keyStore.load(null, keyPass.toCharArray()) diff --git a/core/src/test/kotlin/net/corda/core/identity/CordaX500NameTest.kt b/core/src/test/kotlin/net/corda/core/identity/CordaX500NameTest.kt index 766fa202a4..3cfc7c089b 100644 --- a/core/src/test/kotlin/net/corda/core/identity/CordaX500NameTest.kt +++ b/core/src/test/kotlin/net/corda/core/identity/CordaX500NameTest.kt @@ -7,30 +7,36 @@ import kotlin.test.assertNull class CordaX500NameTest { @Test - fun `parse service name with organisational unit`() { + fun `service name with organisational unit`() { val name = CordaX500Name.parse("O=Bank A, L=New York, C=US, OU=Org Unit, CN=Service Name") assertEquals("Service Name", name.commonName) assertEquals("Org Unit", name.organisationUnit) assertEquals("Bank A", name.organisation) assertEquals("New York", name.locality) + assertEquals(CordaX500Name.parse(name.toString()), name) + assertEquals(CordaX500Name.build(name.x500Principal), name) } @Test - fun `parse service name`() { + fun `service name`() { val name = CordaX500Name.parse("O=Bank A, L=New York, C=US, CN=Service Name") assertEquals("Service Name", name.commonName) assertNull(name.organisationUnit) assertEquals("Bank A", name.organisation) assertEquals("New York", name.locality) + assertEquals(CordaX500Name.parse(name.toString()), name) + assertEquals(CordaX500Name.build(name.x500Principal), name) } @Test - fun `parse legal entity name`() { + fun `legal entity name`() { val name = CordaX500Name.parse("O=Bank A, L=New York, C=US") assertNull(name.commonName) assertNull(name.organisationUnit) assertEquals("Bank A", name.organisation) assertEquals("New York", name.locality) + assertEquals(CordaX500Name.parse(name.toString()), name) + assertEquals(CordaX500Name.build(name.x500Principal), name) } @Test diff --git a/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt b/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt index f64ac5440a..ff8207db8d 100644 --- a/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt +++ b/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt @@ -1,14 +1,13 @@ package net.corda.core.identity import net.corda.core.crypto.entropyToKeyPair -import net.corda.core.internal.cert import net.corda.core.internal.read import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.nodeapi.internal.crypto.KEYSTORE_TYPE import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.save -import net.corda.testing.DEV_CA +import net.corda.testing.DEV_ROOT_CA import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.getTestPartyAndCertificate import org.assertj.core.api.Assertions.assertThat @@ -25,8 +24,8 @@ class PartyAndCertificateTest { val testSerialization = SerializationEnvironmentRule() @Test - fun `should reject a path with no roles`() { - val path = X509CertificateFactory().generateCertPath(DEV_CA.certificate.cert) + fun `reject a path with no roles`() { + val path = X509CertificateFactory().generateCertPath(DEV_ROOT_CA.certificate) assertFailsWith { PartyAndCertificate(path) } } diff --git a/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt b/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt index 745acb6605..377b1858c4 100644 --- a/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt +++ b/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt @@ -16,6 +16,9 @@ import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.internal.config.User import net.corda.testing.* import net.corda.testing.driver.driver +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import org.junit.ClassRule import org.junit.Test import kotlin.test.assertEquals diff --git a/docs/source/hello-world-running.rst b/docs/source/hello-world-running.rst index 4ac394d26c..58e3271c71 100644 --- a/docs/source/hello-world-running.rst +++ b/docs/source/hello-world-running.rst @@ -103,7 +103,7 @@ Go to the terminal window displaying the CRaSH shell of PartyA. Typing ``help`` commands. .. note:: Local terminal shell is available only in a development mode. In production environment SSH server can be enabled. - More about SSH and how to connect can be found on :doc:`Shell` page. + More about SSH and how to connect can be found on the :doc:`shell` page. We want to create an IOU of 100 with PartyB. We start the ``IOUFlow`` by typing: diff --git a/docs/source/serialization.rst b/docs/source/serialization.rst index 70dcb44dee..b4f87b912a 100644 --- a/docs/source/serialization.rst +++ b/docs/source/serialization.rst @@ -192,9 +192,6 @@ The following 3rd party types are supported. org.apache.activemq.artemis.api.core.SimpleString - org.bouncycastle.asn1.x500.X500Name - org.bouncycastle.cert.X509CertificateHolder - Corda Types ``````````` diff --git a/finance/src/integration-test/kotlin/net/corda/finance/flows/CashConfigDataFlowTest.kt b/finance/src/integration-test/kotlin/net/corda/finance/flows/CashConfigDataFlowTest.kt index ca3f20e6a8..1fa2459d8d 100644 --- a/finance/src/integration-test/kotlin/net/corda/finance/flows/CashConfigDataFlowTest.kt +++ b/finance/src/integration-test/kotlin/net/corda/finance/flows/CashConfigDataFlowTest.kt @@ -6,6 +6,9 @@ import net.corda.finance.EUR import net.corda.finance.USD import net.corda.testing.* import net.corda.testing.driver.driver +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaNames import org.assertj.core.api.Assertions.assertThat import org.junit.ClassRule import org.junit.Test @@ -14,7 +17,7 @@ class CashConfigDataFlowTest : IntegrationTest() { companion object { @ClassRule @JvmField val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, DUMMY_BANK_A_NAME) - .map { it.toDatabaseSchemaNames("","_10000","_10003") }.flatten().toTypedArray()) + .map { it.toDatabaseSchemaNames("", "_10000", "_10003") }.flatten().toTypedArray()) } @Test fun `issuable currencies are read in from node config`() { diff --git a/finance/src/main/kotlin/net/corda/finance/flows/CashExitFlow.kt b/finance/src/main/kotlin/net/corda/finance/flows/CashExitFlow.kt index 23b05a1e8f..adc6f1b55a 100644 --- a/finance/src/main/kotlin/net/corda/finance/flows/CashExitFlow.kt +++ b/finance/src/main/kotlin/net/corda/finance/flows/CashExitFlow.kt @@ -29,8 +29,8 @@ import java.util.* class CashExitFlow(private val amount: Amount, private val issuerRef: OpaqueBytes, progressTracker: ProgressTracker) : AbstractCashFlow(progressTracker) { - constructor(amount: Amount, issueRef: OpaqueBytes) : this(amount, issueRef, tracker()) - constructor(request: ExitRequest) : this(request.amount, request.issueRef, tracker()) + constructor(amount: Amount, issuerRef: OpaqueBytes) : this(amount, issuerRef, tracker()) + constructor(request: ExitRequest) : this(request.amount, request.issuerRef, tracker()) companion object { fun tracker() = ProgressTracker(GENERATING_TX, SIGNING_TX, FINALISING_TX) @@ -78,5 +78,5 @@ class CashExitFlow(private val amount: Amount, } @CordaSerializable - class ExitRequest(amount: Amount, val issueRef: OpaqueBytes) : AbstractRequest(amount) + class ExitRequest(amount: Amount, val issuerRef: OpaqueBytes) : AbstractRequest(amount) } diff --git a/gradle-plugins/api-scanner/README.md b/gradle-plugins/api-scanner/README.md index 9f8fc4f674..a0256d480b 100644 --- a/gradle-plugins/api-scanner/README.md +++ b/gradle-plugins/api-scanner/README.md @@ -6,7 +6,7 @@ Generates a text summary of Corda's public API that we can check for API-breakin $ gradlew generateApi ``` -See [here](../../docs/source/api-index.rst) for Corda's public API strategy. We will need to +See [here](../../docs/source/corda-api.rst) for Corda's public API strategy. We will need to apply this plugin to other modules in future Corda releases as those modules' APIs stabilise. Basically, this plugin will document a module's `public` and `protected` classes/methods/fields, diff --git a/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Cordform.kt b/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Cordform.kt index dc131cc8b5..1d44484eb2 100644 --- a/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Cordform.kt +++ b/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Cordform.kt @@ -22,6 +22,7 @@ import java.util.jar.JarInputStream @Suppress("unused") open class Cordform : DefaultTask() { private companion object { + val nodeJarName = "corda.jar" private val defaultDirectory: Path = Paths.get("build", "nodes") } @@ -132,9 +133,26 @@ open class Cordform : DefaultTask() { fun build() { project.logger.info("Running Cordform task") initializeConfiguration() + nodes.forEach(Node::installConfig) + installCordaJar() installRunScript() - nodes.forEach(Node::build) bootstrapNetwork() + nodes.forEach(Node::build) + } + + /** + * Installs the corda fat JAR to the root directory, for the network bootstrapper to use. + */ + private fun installCordaJar() { + val cordaJar = Cordformation.verifyAndGetRuntimeJar(project, "corda") + project.copy { + it.apply { + from(cordaJar) + into(directory) + rename(cordaJar.name, nodeJarName) + fileMode = Cordformation.executableFileMode + } + } } private fun initializeConfiguration() { @@ -150,8 +168,8 @@ open class Cordform : DefaultTask() { cd.nodeConfigurers.forEach { val node = node { } it.accept(node) + node.additionalCordapps.addAll(cordapps) node.rootDir(directory) - node.installCordapps(cordapps) } cd.setup { nodeName -> project.projectDir.toPath().resolve(getNodeByName(nodeName)!!.nodeDir.toPath()) } } else { diff --git a/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Cordformation.kt b/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Cordformation.kt index 4b722a7f03..26878fa357 100644 --- a/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Cordformation.kt +++ b/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Cordformation.kt @@ -20,14 +20,35 @@ class Cordformation : Plugin { * @return A file handle to the file in the JAR. */ fun getPluginFile(project: Project, filePathInJar: String): File { - val archive: File? = project.rootProject.buildscript.configurations + val archive = project.rootProject.buildscript.configurations .single { it.name == "classpath" } - .find { it.name.contains("cordformation") } + .first { it.name.contains("cordformation") } return project.rootProject.resources.text .fromArchiveEntry(archive, filePathInJar) .asFile() } + /** + * Gets a current built corda jar file + * + * @param project The project environment this plugin executes in. + * @param jarName The name of the JAR you wish to access. + * @return A file handle to the file in the JAR. + */ + fun verifyAndGetRuntimeJar(project: Project, jarName: String): File { + val releaseVersion = project.rootProject.ext("corda_release_version") + val maybeJar = project.configuration("runtime").filter { + "$jarName-$releaseVersion.jar" in it.toString() || "$jarName-enterprise-$releaseVersion.jar" in it.toString() + } + if (maybeJar.isEmpty) { + throw IllegalStateException("No $jarName JAR found. Have you deployed the Corda project to Maven? Looked for \"$jarName-$releaseVersion.jar\"") + } else { + val jar = maybeJar.singleFile + require(jar.isFile) + return jar + } + } + val executableFileMode = "0755".toInt(8) } diff --git a/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Node.kt b/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Node.kt index ffaab5c5b1..7424f41e69 100644 --- a/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Node.kt +++ b/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Node.kt @@ -15,8 +15,6 @@ import java.nio.file.Path */ class Node(private val project: Project) : CordformNode() { companion object { - @JvmStatic - val nodeJarName = "corda.jar" @JvmStatic val webJarName = "corda-webserver.jar" private val configFileProperty = "configFile" @@ -30,10 +28,11 @@ class Node(private val project: Project) : CordformNode() { * @note Type is any due to gradle's use of "GStrings" - each value will have "toString" called on it */ var cordapps = mutableListOf() - - private val releaseVersion = project.rootProject.ext("corda_release_version") + var additionalCordapps = mutableListOf() internal lateinit var nodeDir: File private set + internal lateinit var rootDir: File + private set /** * Sets whether this node will use HTTPS communication. @@ -65,16 +64,12 @@ class Node(private val project: Project) : CordformNode() { } internal fun build() { - configureProperties() - installCordaJar() if (config.hasPath("webAddress")) { installWebserverJar() } installAgentJar() installBuiltCordapp() installCordapps() - installConfig() - appendOptionalConfig() } internal fun rootDir(rootDir: Path) { @@ -86,7 +81,9 @@ class Node(private val project: Project) : CordformNode() { // with loading our custom X509EdDSAEngine. val organizationName = name.trim().split(",").firstOrNull { it.startsWith("O=") }?.substringAfter("=") val dirName = organizationName ?: name - nodeDir = File(rootDir.toFile(), dirName.replace("\\s", "")) + this.rootDir = rootDir.toFile() + nodeDir = File(this.rootDir, dirName.replace("\\s", "")) + Files.createDirectories(nodeDir.toPath()) } private fun configureProperties() { @@ -99,26 +96,11 @@ class Node(private val project: Project) : CordformNode() { } } - /** - * Installs the corda fat JAR to the node directory. - */ - private fun installCordaJar() { - val cordaJar = verifyAndGetRuntimeJar("corda") - project.copy { - it.apply { - from(cordaJar) - into(nodeDir) - rename(cordaJar.name, nodeJarName) - fileMode = Cordformation.executableFileMode - } - } - } - /** * Installs the corda webserver JAR to the node directory */ private fun installWebserverJar() { - val webJar = verifyAndGetRuntimeJar("corda-webserver") + val webJar = Cordformation.verifyAndGetRuntimeJar(project, "corda-webserver") project.copy { it.apply { from(webJar) @@ -141,19 +123,6 @@ class Node(private val project: Project) : CordformNode() { } } - /** - * Installs other cordapps to this node's cordapps directory. - */ - internal fun installCordapps(cordapps: Collection = getCordappList()) { - val cordappsDir = File(nodeDir, "cordapps") - project.copy { - it.apply { - from(cordapps) - into(cordappsDir) - } - } - } - /** * Installs the jolokia monitoring agent JAR to the node/drivers directory */ @@ -161,8 +130,8 @@ class Node(private val project: Project) : CordformNode() { val jolokiaVersion = project.rootProject.ext("jolokia_version") val agentJar = project.configuration("runtime").files { (it.group == "org.jolokia") && - (it.name == "jolokia-jvm") && - (it.version == jolokiaVersion) + (it.name == "jolokia-jvm") && + (it.version == jolokiaVersion) // TODO: revisit when classifier attribute is added. eg && (it.classifier = "agent") }.first() // should always be the jolokia agent fat jar: eg. jolokia-jvm-1.3.7-agent.jar project.logger.info("Jolokia agent jar: $agentJar") @@ -177,10 +146,7 @@ class Node(private val project: Project) : CordformNode() { } } - /** - * Installs the configuration file to this node's directory and detokenises it. - */ - private fun installConfig() { + private fun createTempConfigFile(): File { val options = ConfigRenderOptions .defaults() .setOriginComments(false) @@ -188,16 +154,26 @@ class Node(private val project: Project) : CordformNode() { .setFormatted(true) .setJson(false) val configFileText = config.root().render(options).split("\n").toList() - // Need to write a temporary file first to use the project.copy, which resolves directories correctly. val tmpDir = File(project.buildDir, "tmp") - val tmpConfFile = File(tmpDir, "node.conf") + Files.createDirectories(tmpDir.toPath()) + var fileName = "${nodeDir.getName()}.conf" + val tmpConfFile = File(tmpDir, fileName) Files.write(tmpConfFile.toPath(), configFileText, StandardCharsets.UTF_8) + return tmpConfFile + } + /** + * Installs the configuration file to the root directory and detokenises it. + */ + internal fun installConfig() { + configureProperties() + val tmpConfFile = createTempConfigFile() + appendOptionalConfig(tmpConfFile) project.copy { it.apply { from(tmpConfFile) - into(nodeDir) + into(rootDir) } } } @@ -205,7 +181,7 @@ class Node(private val project: Project) : CordformNode() { /** * Appends installed config file with properties from an optional file. */ - private fun appendOptionalConfig() { + private fun appendOptionalConfig(confFile: File) { val optionalConfig: File? = when { project.findProperty(configFileProperty) != null -> //provided by -PconfigFile command line property when running Gradle task File(project.findProperty(configFileProperty) as String) @@ -217,28 +193,22 @@ class Node(private val project: Project) : CordformNode() { if (!optionalConfig.exists()) { project.logger.error("$configFileProperty '$optionalConfig' not found") } else { - val confFile = File(project.buildDir.path + "/../" + nodeDir, "node.conf") confFile.appendBytes(optionalConfig.readBytes()) } } } /** - * Find the given JAR amongst the dependencies - * @param jarName JAR name without the version part, for example for corda-2.0-SNAPSHOT.jar provide only "corda" as jarName - * - * @return A file representing found JAR + * Installs other cordapps to this node's cordapps directory. */ - private fun verifyAndGetRuntimeJar(jarName: String): File { - val maybeJar = project.configuration("runtime").filter { - "$jarName-$releaseVersion.jar" in it.toString() || "$jarName-enterprise-$releaseVersion.jar" in it.toString() - } - if (maybeJar.isEmpty) { - throw IllegalStateException("No $jarName JAR found. Have you deployed the Corda project to Maven? Looked for \"$jarName-$releaseVersion.jar\"") - } else { - val jar = maybeJar.singleFile - require(jar.isFile) - return jar + internal fun installCordapps() { + additionalCordapps.addAll(getCordappList()) + val cordappsDir = File(nodeDir, "cordapps") + project.copy { + it.apply { + from(additionalCordapps) + into(cordappsDir) + } } } diff --git a/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/doorman/DoormanIntegrationTest.kt b/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/doorman/DoormanIntegrationTest.kt index 5993cef210..207a524aba 100644 --- a/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/doorman/DoormanIntegrationTest.kt +++ b/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/doorman/DoormanIntegrationTest.kt @@ -3,14 +3,11 @@ package com.r3.corda.networkmanage.doorman import com.nhaarman.mockito_kotlin.doReturn import com.nhaarman.mockito_kotlin.whenever import com.r3.corda.networkmanage.common.persistence.configureDatabase -import com.r3.corda.networkmanage.common.utils.toX509Certificate import com.r3.corda.networkmanage.doorman.signer.LocalSigner import net.corda.core.crypto.Crypto import net.corda.core.crypto.SecureHash import net.corda.core.crypto.sign -import net.corda.core.identity.CordaX500Name import net.corda.core.identity.PartyAndCertificate -import net.corda.core.internal.cert import net.corda.core.internal.createDirectories import net.corda.core.internal.div import net.corda.core.node.NodeInfo @@ -28,12 +25,13 @@ import net.corda.testing.ALICE_NAME import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.internal.rigorousMock -import org.bouncycastle.cert.X509CertificateHolder import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder import java.net.URL +import java.security.cert.X509Certificate import java.util.* +import javax.security.auth.x500.X500Principal import kotlin.test.assertEquals import kotlin.test.assertNotNull @@ -60,7 +58,7 @@ class DoormanIntegrationTest { } config.trustStoreFile.parent.createDirectories() loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also { - it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCertAndKey.certificate.cert) + it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCertAndKey.certificate) it.save(config.trustStoreFile, config.trustStorePassword) } @@ -77,18 +75,18 @@ class DoormanIntegrationTest { loadKeyStore(config.nodeKeystore, config.keyStorePassword).apply { assert(containsAlias(X509Utilities.CORDA_CLIENT_CA)) assertEquals(ALICE_NAME.x500Principal, getX509Certificate(X509Utilities.CORDA_CLIENT_CA).subjectX500Principal) - assertEquals(listOf(intermediateCACert.cert, rootCACert.cert), getCertificateChain(X509Utilities.CORDA_CLIENT_CA).drop(1).toList()) + assertEquals(listOf(intermediateCACert, rootCACert), getCertificateChain(X509Utilities.CORDA_CLIENT_CA).drop(1).toList()) } loadKeyStore(config.sslKeystore, config.keyStorePassword).apply { assert(containsAlias(X509Utilities.CORDA_CLIENT_TLS)) assertEquals(ALICE_NAME.x500Principal, getX509Certificate(X509Utilities.CORDA_CLIENT_TLS).subjectX500Principal) - assertEquals(listOf(intermediateCACert.cert, rootCACert.cert), getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).drop(2).toList()) + assertEquals(listOf(intermediateCACert, rootCACert), getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).drop(2).toList()) } loadKeyStore(config.trustStoreFile, config.trustStorePassword).apply { assert(containsAlias(X509Utilities.CORDA_ROOT_CA)) - assertEquals(rootCACert.cert.subjectX500Principal, getX509Certificate(X509Utilities.CORDA_ROOT_CA).subjectX500Principal) + assertEquals(rootCACert.subjectX500Principal, getX509Certificate(X509Utilities.CORDA_ROOT_CA).subjectX500Principal) } doorman.close() @@ -110,21 +108,26 @@ class DoormanIntegrationTest { } config.trustStoreFile.parent.createDirectories() loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also { - it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCertAndKey.certificate.cert) + it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCertAndKey.certificate) it.save(config.trustStoreFile, config.trustStorePassword) } NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.compatibilityZoneURL!!)).buildKeystore() // Publish NodeInfo - val networkMapClient = NetworkMapClient(config.compatibilityZoneURL!!, rootCertAndKey.certificate.cert) + val networkMapClient = NetworkMapClient(config.compatibilityZoneURL!!, rootCertAndKey.certificate) val keyStore = loadKeyStore(config.nodeKeystore, config.keyStorePassword) val clientCertPath = keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA) val clientCA = keyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, config.keyStorePassword) val identityKeyPair = Crypto.generateKeyPair() - val identityCert = X509Utilities.createCertificate(CertificateType.LEGAL_IDENTITY, clientCA.certificate, clientCA.keyPair, ALICE_NAME, identityKeyPair.public) - val certPath = X509CertificateFactory().generateCertPath(identityCert.cert, *clientCertPath) + val identityCert = X509Utilities.createCertificate( + CertificateType.LEGAL_IDENTITY, + clientCA.certificate, + clientCA.keyPair, + ALICE_NAME.x500Principal, + identityKeyPair.public) + val certPath = X509CertificateFactory().generateCertPath(identityCert, *clientCertPath) val nodeInfo = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(certPath)), 1, serial = 1L) val nodeInfoBytes = nodeInfo.serialize() @@ -156,23 +159,23 @@ class DoormanIntegrationTest { } -fun createDoormanIntermediateCertificateAndKeyPair(rootCertificateAndKeyPair: CertificateAndKeyPair): CertificateAndKeyPair { - val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCertificateAndKeyPair.certificate, rootCertificateAndKeyPair.keyPair, - CordaX500Name(commonName = "Integration Test Corda Node Intermediate CA", - locality = "London", - country = "GB", - organisation = "R3 Ltd"), intermediateCAKey.public) - return CertificateAndKeyPair(intermediateCACert, intermediateCAKey) +fun createDoormanIntermediateCertificateAndKeyPair(rootCa: CertificateAndKeyPair): CertificateAndKeyPair { + val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) + val intermediateCACert = X509Utilities.createCertificate( + CertificateType.INTERMEDIATE_CA, + rootCa.certificate, + rootCa.keyPair, + X500Principal("CN=Integration Test Corda Node Intermediate CA,O=R3 Ltd,L=London,C=GB"), + keyPair.public) + return CertificateAndKeyPair(intermediateCACert, keyPair) } fun createDoormanRootCertificateAndKeyPair(): CertificateAndKeyPair { - val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val rootCACert = X509Utilities.createSelfSignedCACertificate( - CordaX500Name(commonName = "Integration Test Corda Node Root CA", - organisation = "R3 Ltd", locality = "London", - country = "GB"), rootCAKey) - return CertificateAndKeyPair(rootCACert, rootCAKey) + val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) + val rootCaCert = X509Utilities.createSelfSignedCACertificate( + X500Principal("CN=Integration Test Corda Node Root CA,O=R3 Ltd,L=London,C=GB"), + keyPair) + return CertificateAndKeyPair(rootCaCert, keyPair) } fun makeTestDataSourceProperties(nodeName: String = SecureHash.randomSHA256().toString()): Properties { @@ -184,9 +187,8 @@ fun makeTestDataSourceProperties(nodeName: String = SecureHash.randomSHA256().to return props } -fun startDoorman(intermediateCACertAndKey: CertificateAndKeyPair, rootCACert: X509CertificateHolder): NetworkManagementServer { - val signer = LocalSigner(intermediateCACertAndKey.keyPair, - arrayOf(intermediateCACertAndKey.certificate.toX509Certificate(), rootCACert.toX509Certificate())) +fun startDoorman(intermediateCACertAndKey: CertificateAndKeyPair, rootCACert: X509Certificate): NetworkManagementServer { + val signer = LocalSigner(intermediateCACertAndKey.keyPair, arrayOf(intermediateCACertAndKey.certificate, rootCACert)) //Start doorman server return startDoorman(signer) } diff --git a/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/hsm/SigningServiceIntegrationTest.kt b/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/hsm/SigningServiceIntegrationTest.kt index 1c0bef94f2..b2e7298de1 100644 --- a/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/hsm/SigningServiceIntegrationTest.kt +++ b/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/hsm/SigningServiceIntegrationTest.kt @@ -10,7 +10,6 @@ import com.r3.corda.networkmanage.hsm.persistence.DBSignedCertificateRequestStor import com.r3.corda.networkmanage.hsm.persistence.SignedCertificateRequestStorage import com.r3.corda.networkmanage.hsm.signer.HsmCsrSigner import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.cert import net.corda.core.internal.createDirectories import net.corda.core.internal.div import net.corda.core.internal.uncheckedCast @@ -28,12 +27,12 @@ import net.corda.testing.CHARLIE_NAME import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.testing.internal.rigorousMock -import org.bouncycastle.cert.X509CertificateHolder import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest import org.h2.tools.Server import org.junit.* import org.junit.rules.TemporaryFolder import java.net.URL +import java.security.cert.X509Certificate import java.util.* import javax.persistence.PersistenceException import kotlin.concurrent.scheduleAtFixedRate @@ -55,7 +54,7 @@ class SigningServiceIntegrationTest { val testSerialization = SerializationEnvironmentRule(true) private lateinit var timer: Timer - private lateinit var rootCaCert: X509CertificateHolder + private lateinit var rootCaCert: X509Certificate private lateinit var intermediateCa: CertificateAndKeyPair @Before @@ -79,7 +78,7 @@ class SigningServiceIntegrationTest { for (approvedRequest in approvedRequests) { JcaPKCS10CertificationRequest(approvedRequest.request).run { val nodeCa = createDevNodeCa(intermediateCa, CordaX500Name.parse(subject.toString())) - approvedRequest.certPath = buildCertPath(nodeCa.certificate.cert, intermediateCa.certificate.cert, rootCaCert.cert) + approvedRequest.certPath = buildCertPath(nodeCa.certificate, intermediateCa.certificate, rootCaCert) } } storage.store(approvedRequests, listOf("TEST")) @@ -124,7 +123,7 @@ class SigningServiceIntegrationTest { } config.certificatesDirectory.createDirectories() loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also { - it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCaCert.cert) + it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCaCert) it.save(config.trustStoreFile, config.trustStorePassword) } NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.compatibilityZoneURL!!)).buildKeystore() @@ -168,7 +167,7 @@ class SigningServiceIntegrationTest { } config.certificatesDirectory.createDirectories() loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also { - it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCaCert.cert) + it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCaCert) it.save(config.trustStoreFile, config.trustStorePassword) } NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.compatibilityZoneURL!!)).buildKeystore() diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentCertificateRequestStorage.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentCertificateRequestStorage.kt index f134727fac..566729a239 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentCertificateRequestStorage.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentCertificateRequestStorage.kt @@ -5,15 +5,14 @@ import com.r3.corda.networkmanage.common.persistence.entity.CertificateSigningRe import com.r3.corda.networkmanage.common.utils.hashString import net.corda.core.crypto.SecureHash import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.x500Name import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseTransaction import net.corda.nodeapi.internal.persistence.TransactionIsolationLevel -import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.pkcs.PKCS10CertificationRequest import org.hibernate.Session import java.security.cert.CertPath import java.time.Instant +import javax.security.auth.x500.X500Principal /** * Database implementation of the [CertificationRequestStorage] interface. @@ -48,7 +47,7 @@ class PersistentCertificateRequestStorage(private val database: CordaPersistence val (legalName, rejectReason) = parseAndValidateLegalName(request, session) session.save(CertificateSigningRequestEntity( requestId = requestId, - legalName = legalName.toString(), + legalName = legalName, requestBytes = request.encoded, remark = rejectReason, modifiedBy = emptyList(), @@ -126,25 +125,27 @@ class PersistentCertificateRequestStorage(private val database: CordaPersistence } } - private fun parseAndValidateLegalName(request: PKCS10CertificationRequest, session: Session): Pair { + private fun parseAndValidateLegalName(request: PKCS10CertificationRequest, session: Session): Pair { + // It's important that we always use the toString() output of CordaX500Name as it standardises the string format + // to make querying possible. val legalName = try { - CordaX500Name.parse(request.subject.toString()) + CordaX500Name.build(X500Principal(request.subject.encoded)).toString() } catch (e: IllegalArgumentException) { - return Pair(request.subject, "Name validation failed with exception : ${e.message}") + return Pair(request.subject.toString(), "Name validation failed: ${e.message}") } + val query = session.criteriaBuilder.run { val criteriaQuery = createQuery(CertificateSigningRequestEntity::class.java) criteriaQuery.from(CertificateSigningRequestEntity::class.java).run { - criteriaQuery.where(equal(get(CertificateSigningRequestEntity::legalName.name), legalName.toString())) + criteriaQuery.where(equal(get(CertificateSigningRequestEntity::legalName.name), legalName)) } } + val duplicates = session.createQuery(query).resultList.filter { - it.status in setOf(RequestStatus.NEW, RequestStatus.TICKET_CREATED, RequestStatus.APPROVED) || it.certificateData?.certificateStatus == CertificateStatus.VALID - } - return if (duplicates.isEmpty()) { - Pair(legalName.x500Name, null) - } else { - Pair(legalName.x500Name, "Duplicate legal name") + it.status in setOf(RequestStatus.NEW, RequestStatus.TICKET_CREATED, RequestStatus.APPROVED) || + it.certificateData?.certificateStatus == CertificateStatus.VALID } + + return Pair(legalName, if (duplicates.isEmpty()) null else "Duplicate legal name") } } \ No newline at end of file diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/JiraCient.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/JiraCient.kt index e53a927b14..d7ebf4abc0 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/JiraCient.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/JiraCient.kt @@ -6,9 +6,7 @@ import com.atlassian.jira.rest.client.api.domain.Issue import com.atlassian.jira.rest.client.api.domain.IssueType import com.atlassian.jira.rest.client.api.domain.input.IssueInputBuilder import com.atlassian.jira.rest.client.api.domain.input.TransitionInput -import net.corda.core.internal.country -import net.corda.core.internal.locality -import net.corda.core.internal.organisation +import net.corda.core.identity.CordaX500Name import net.corda.core.utilities.loggerFor import net.corda.nodeapi.internal.crypto.X509Utilities import org.bouncycastle.asn1.x500.style.BCStyle @@ -17,6 +15,7 @@ import org.bouncycastle.pkcs.PKCS10CertificationRequest import org.bouncycastle.util.io.pem.PemObject import java.io.StringWriter import java.security.cert.CertPath +import javax.security.auth.x500.X500Principal class JiraClient(private val restClient: JiraRestClient, private val projectCode: String, private val doneTransitionCode: Int) { companion object { @@ -39,16 +38,17 @@ class JiraClient(private val restClient: JiraRestClient, private val projectCode JcaPEMWriter(request).use { it.writeObject(PemObject("CERTIFICATE REQUEST", signingRequest.encoded)) } - val organisation = signingRequest.subject.organisation - val nearestCity = signingRequest.subject.locality - val country = signingRequest.subject.country + + // TODO The subject of the signing request has already been validated and parsed into a CordaX500Name. We shouldn't + // have to do it again here. + val subject = CordaX500Name.build(X500Principal(signingRequest.subject.encoded)) val email = signingRequest.getAttributes(BCStyle.E).firstOrNull()?.attrValues?.firstOrNull()?.toString() val issue = IssueInputBuilder().setIssueTypeId(taskIssueType.id) .setProjectKey(projectCode) - .setDescription("Organisation: $organisation\nNearest City: $nearestCity\nCountry: $country\nEmail: $email\n\n{code}$request{code}") - .setSummary(organisation) + .setDescription("Organisation: ${subject.organisation}\nNearest City: ${subject.locality}\nCountry: ${subject.country}\nEmail: $email\n\n{code}$request{code}") + .setSummary(subject.organisation) .setFieldValue(requestIdField.id, requestId) // This will block until the issue is created. restClient.issueClient.createIssue(issue.build()).fail { logger.error("Exception when creating JIRA issue.", it) }.claim() diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/Main.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/Main.kt index 2a4db95100..6b5f3636f3 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/Main.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/Main.kt @@ -13,7 +13,6 @@ import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService import com.r3.corda.networkmanage.doorman.webservice.RegistrationWebService import net.corda.core.crypto.Crypto import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.cert import net.corda.core.internal.createDirectories import net.corda.core.internal.div import net.corda.core.serialization.internal.SerializationEnvironmentImpl @@ -188,8 +187,8 @@ fun generateRootKeyPair(rootStoreFile: Path, rootKeystorePass: String?, rootPriv val selfSignKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) // TODO Make the cert subject configurable val selfSignCert = X509Utilities.createSelfSignedCACertificate( - CordaX500Name(commonName = "Corda Root CA", organisation = "R3 Ltd", locality = "London", country = "GB", organisationUnit = "Corda", state = null), - selfSignKey).cert + CordaX500Name(commonName = "Corda Root CA", organisation = "R3 Ltd", locality = "London", country = "GB", organisationUnit = "Corda", state = null).x500Principal, + selfSignKey) rootStore.addOrReplaceKey(X509Utilities.CORDA_ROOT_CA, selfSignKey.private, rootPrivateKeyPassword.toCharArray(), arrayOf(selfSignCert)) rootStore.save(rootStoreFile, rootKeystorePassword) @@ -226,11 +225,20 @@ fun generateCAKeyPair(keystoreFile: Path, rootStoreFile: Path, rootKeystorePass: exitProcess(1) } - val intermediateKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val intermediateCert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootKeyAndCert.certificate, rootKeyAndCert.keyPair, - CordaX500Name(commonName = "Corda Intermediate CA", organisation = "R3 Ltd", organisationUnit = "Corda", locality = "London", country = "GB", state = null), intermediateKey.public) - keyStore.addOrReplaceKey(X509Utilities.CORDA_INTERMEDIATE_CA, intermediateKey.private, - caPrivateKeyPassword.toCharArray(), arrayOf(intermediateCert, rootKeyAndCert.certificate)) + val intermediateKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) + val intermediateCert = X509Utilities.createCertificate( + CertificateType.INTERMEDIATE_CA, + rootKeyAndCert.certificate, + rootKeyAndCert.keyPair, + CordaX500Name(commonName = "Corda Intermediate CA", organisation = "R3 Ltd", organisationUnit = "Corda", locality = "London", country = "GB", state = null).x500Principal, + intermediateKeyPair.public + ) + keyStore.addOrReplaceKey( + X509Utilities.CORDA_INTERMEDIATE_CA, + intermediateKeyPair.private, + caPrivateKeyPassword.toCharArray(), + arrayOf(intermediateCert, rootKeyAndCert.certificate) + ) keyStore.save(keystoreFile, keystorePassword) println("Intermediate CA keypair and certificate stored in $keystoreFile.") println(loadKeyStore(keystoreFile, keystorePassword).getCertificate(X509Utilities.CORDA_INTERMEDIATE_CA).publicKey) diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/signer/LocalSigner.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/signer/LocalSigner.kt index 50a7b7f971..aa7ceaa742 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/signer/LocalSigner.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/signer/LocalSigner.kt @@ -4,9 +4,6 @@ import com.r3.corda.networkmanage.common.signer.Signer import com.r3.corda.networkmanage.common.utils.buildCertPath import com.r3.corda.networkmanage.common.utils.withCert import net.corda.core.crypto.sign -import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.cert -import net.corda.core.internal.toX509CertHolder import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.network.DigitalSignatureWithCert @@ -18,6 +15,7 @@ import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest import java.security.KeyPair import java.security.cert.CertPath import java.security.cert.X509Certificate +import javax.security.auth.x500.X500Principal /** * The [LocalSigner] class signs [PKCS10CertificationRequest] using provided CA key pair and certificate path. @@ -35,12 +33,12 @@ class LocalSigner(private val caKeyPair: KeyPair, private val caCertPath: Array< arrayOf()) val nodeCaCert = X509Utilities.createCertificate( CertificateType.NODE_CA, - caCertPath.first().toX509CertHolder(), + caCertPath[0], caKeyPair, - CordaX500Name.parse(request.subject.toString()), + X500Principal(request.subject.encoded), request.publicKey, nameConstraints = nameConstraints) - return buildCertPath(nodeCaCert.cert, *caCertPath) + return buildCertPath(nodeCaCert, *caCertPath) } override fun sign(data: ByteArray): DigitalSignatureWithCert { diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/signer/HsmNetworkMapSigner.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/signer/HsmNetworkMapSigner.kt index 2fec2a234c..0465a3621a 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/signer/HsmNetworkMapSigner.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/signer/HsmNetworkMapSigner.kt @@ -9,13 +9,12 @@ import com.r3.corda.networkmanage.hsm.authentication.Authenticator import com.r3.corda.networkmanage.hsm.utils.X509Utilities.getAndInitializeKeyStore import com.r3.corda.networkmanage.hsm.utils.X509Utilities.signData import com.r3.corda.networkmanage.hsm.utils.X509Utilities.verify -import net.corda.core.internal.cert -import net.corda.core.internal.toX509CertHolder import net.corda.core.utilities.loggerFor import net.corda.core.utilities.minutes import net.corda.nodeapi.internal.network.DigitalSignatureWithCert import java.security.KeyPair import java.security.PrivateKey +import java.security.cert.X509Certificate import java.time.Duration import java.util.concurrent.Executors import java.util.concurrent.ScheduledExecutorService @@ -68,7 +67,7 @@ class HsmNetworkMapSigner(networkMapStorage: NetworkMapStorage, val caKey = keyStore.getKey(caCertificateKeyName, caPrivateKeyPass.toCharArray()) as PrivateKey val signature = signData(data, KeyPair(caCertificateChain.first().publicKey, caKey), provider) verify(data, signature, caCertificateChain.first().publicKey) - signature.withCert(caCertificateChain.first().toX509CertHolder().cert) + signature.withCert(caCertificateChain[0] as X509Certificate) } } } diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/utils/X509Utils.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/utils/X509Utils.kt index 22e94ec1b5..dc4354fe0b 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/utils/X509Utils.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/utils/X509Utils.kt @@ -3,7 +3,6 @@ package com.r3.corda.networkmanage.hsm.utils import CryptoServerJCE.CryptoServerProvider import net.corda.core.crypto.DigitalSignature import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.toX509CertHolder import net.corda.core.internal.x500Name import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.crypto.CertificateType @@ -81,7 +80,7 @@ object X509Utilities { cert.checkValidity(Date()) cert.verify(pubKey) - return CertificateAndKeyPair(cert.toX509CertHolder(), KeyPair(pubKey, keyPair.private)) + return CertificateAndKeyPair(cert, KeyPair(pubKey, keyPair.private)) } /** @@ -109,7 +108,7 @@ object X509Utilities { fun retrieveCertificateAndKeys(certificateKeyName: String, privateKeyPassword: String, keyStore: KeyStore): CertificateAndKeyPair { val privateKey = keyStore.getKey(certificateKeyName, privateKeyPassword.toCharArray()) as PrivateKey val publicKey = keyStore.getCertificate(certificateKeyName).publicKey - val certificate = keyStore.getX509Certificate(certificateKeyName).toX509CertHolder() + val certificate = keyStore.getX509Certificate(certificateKeyName) return CertificateAndKeyPair(certificate, getCleanEcdsaKeyPair(publicKey, privateKey)) } @@ -160,7 +159,7 @@ object X509Utilities { cert.checkValidity(Date()) cert.verify(certificateAuthority.keyPair.public) - return CertificateAndKeyPair(cert.toX509CertHolder(), KeyPair(pubKey, keyPair.private)) + return CertificateAndKeyPair(cert, KeyPair(pubKey, keyPair.private)) } /** @@ -186,7 +185,7 @@ object X509Utilities { val subject = CordaX500Name.parse(jcaRequest.subject.toString()).x500Name val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(jcaRequest.publicKey.encoded)) val keyPurposes = DERSequence(ASN1EncodableVector().apply { certificateType.purposes.forEach { add(it) } }) - val builder = JcaX509v3CertificateBuilder(issuerCertificate.subject, serial, validityWindow.first, validityWindow.second, subject, jcaRequest.publicKey) + val builder = JcaX509v3CertificateBuilder(issuerCertificate, serial, validityWindow.first, validityWindow.second, subject, jcaRequest.publicKey) .addExtension(Extension.subjectKeyIdentifier, false, BcX509ExtensionUtils().createSubjectKeyIdentifier(subjectPublicKeyInfo)) .addExtension(Extension.basicConstraints, certificateType.isCA, BasicConstraints(certificateType.isCA)) .addExtension(Extension.keyUsage, false, certificateType.keyUsage) diff --git a/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/DBCertificateRequestStorageTest.kt b/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentCertificateRequestStorageTest.kt similarity index 71% rename from network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/DBCertificateRequestStorageTest.kt rename to network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentCertificateRequestStorageTest.kt index 56ee6f2d26..df80ebdd6e 100644 --- a/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/DBCertificateRequestStorageTest.kt +++ b/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentCertificateRequestStorageTest.kt @@ -4,15 +4,13 @@ import com.r3.corda.networkmanage.TestBase import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage.Companion.DOORMAN_SIGNATURE import com.r3.corda.networkmanage.common.persistence.entity.CertificateSigningRequestEntity import com.r3.corda.networkmanage.common.utils.buildCertPath -import com.r3.corda.networkmanage.common.utils.toX509Certificate import net.corda.core.crypto.Crypto import net.corda.core.crypto.SecureHash import net.corda.core.identity.CordaX500Name -import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.persistence.CordaPersistence +import net.corda.testing.internal.createDevNodeCaCertPath import org.assertj.core.api.Assertions.assertThat -import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.pkcs.PKCS10CertificationRequest import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest import org.hibernate.envers.AuditReaderFactory @@ -21,9 +19,10 @@ import org.junit.Before import org.junit.Test import java.security.KeyPair import java.util.* +import javax.security.auth.x500.X500Principal import kotlin.test.* -class DBCertificateRequestStorageTest : TestBase() { +class PersistentCertificateRequestStorageTest : TestBase() { private lateinit var storage: PersistentCertificateRequestStorage private lateinit var persistence: CordaPersistence @@ -84,14 +83,15 @@ class DBCertificateRequestStorageTest : TestBase() { // New request should be empty. assertTrue(storage.getRequests(RequestStatus.NEW).isEmpty()) // Sign certificate - storage.putCertificatePath(requestId, JcaPKCS10CertificationRequest(csr).run { - val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 LTD", country = "GB"), rootCAKey) - val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, X500Name("CN=Corda Node Intermediate CA,L=London"), intermediateCAKey.public) - val ourCertificate = X509Utilities.createCertificate(CertificateType.TLS, intermediateCACert, intermediateCAKey, subject, publicKey).toX509Certificate() - buildCertPath(ourCertificate, intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate()) - }, listOf(DOORMAN_SIGNATURE)) + storage.putCertificatePath( + requestId, + JcaPKCS10CertificationRequest(csr).run { + // TODO We need a utility in InternalUtils for converting X500Name -> CordaX500Name + val (rootCa, intermediateCa, nodeCa) = createDevNodeCaCertPath(CordaX500Name.build(X500Principal(subject.encoded))) + buildCertPath(nodeCa.certificate, intermediateCa.certificate, rootCa.certificate) + }, + listOf(DOORMAN_SIGNATURE) + ) // Check request is ready assertNotNull(storage.getRequest(requestId)!!.certData) } @@ -104,25 +104,24 @@ class DBCertificateRequestStorageTest : TestBase() { // Store certificate to DB. storage.markRequestTicketCreated(requestId) storage.approveRequest(requestId, DOORMAN_SIGNATURE) - storage.putCertificatePath(requestId, JcaPKCS10CertificationRequest(csr).run { - val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 LTD", country = "GB"), rootCAKey) - val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, X500Name("CN=Corda Node Intermediate CA,L=London"), intermediateCAKey.public) - val ourCertificate = X509Utilities.createCertificate(CertificateType.TLS, intermediateCACert, intermediateCAKey, subject, publicKey).toX509Certificate() - buildCertPath(ourCertificate, intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate()) - }, listOf(DOORMAN_SIGNATURE)) + storage.putCertificatePath( + requestId, + JcaPKCS10CertificationRequest(csr).run { + val (rootCa, intermediateCa, nodeCa) = createDevNodeCaCertPath(CordaX500Name.build(X500Principal(subject.encoded))) + buildCertPath(nodeCa.certificate, intermediateCa.certificate, rootCa.certificate) + }, + listOf(DOORMAN_SIGNATURE) + ) // Sign certificate // When subsequent signature requested assertFailsWith(IllegalArgumentException::class) { - storage.putCertificatePath(requestId, JcaPKCS10CertificationRequest(csr).run { - val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 LTD", country = "GB"), rootCAKey) - val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, X500Name("CN=Corda Node Intermediate CA,L=London"), intermediateCAKey.public) - val ourCertificate = X509Utilities.createCertificate(CertificateType.TLS, intermediateCACert, intermediateCAKey, subject, publicKey).toX509Certificate() - buildCertPath(ourCertificate, intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate()) - }, listOf(DOORMAN_SIGNATURE)) + storage.putCertificatePath( + requestId, + JcaPKCS10CertificationRequest(csr).run { + val (rootCa, intermediateCa, nodeCa) = createDevNodeCaCertPath(CordaX500Name.build(X500Principal(subject.encoded))) + buildCertPath(nodeCa.certificate, intermediateCa.certificate, rootCa.certificate) + }, + listOf(DOORMAN_SIGNATURE)) } } @@ -207,6 +206,6 @@ class DBCertificateRequestStorageTest : TestBase() { internal fun createRequest(organisation: String): Pair { val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val request = X509Utilities.createCertificateSigningRequest(CordaX500Name(organisation = organisation, locality = "London", country = "GB"), "my@mail.com", keyPair) + val request = X509Utilities.createCertificateSigningRequest(X500Principal("O=$organisation,L=London,C=GB"), "my@mail.com", keyPair) return Pair(request, keyPair) } diff --git a/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNetworkMapStorageTest.kt b/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNetworkMapStorageTest.kt index 832c124d79..de5ef7e27b 100644 --- a/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNetworkMapStorageTest.kt +++ b/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNetworkMapStorageTest.kt @@ -3,25 +3,24 @@ package com.r3.corda.networkmanage.common.persistence import com.r3.corda.networkmanage.TestBase import com.r3.corda.networkmanage.common.utils.withCert import com.r3.corda.networkmanage.doorman.signer.LocalSigner -import net.corda.core.crypto.Crypto import net.corda.core.crypto.sign import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.cert import net.corda.core.serialization.serialize import net.corda.nodeapi.internal.SignedNodeInfo -import net.corda.nodeapi.internal.crypto.CertificateType +import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.crypto.X509CertificateFactory -import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.network.NetworkMap import net.corda.nodeapi.internal.network.SignedNetworkMap import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.internal.TestNodeInfoBuilder +import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import org.assertj.core.api.Assertions.assertThat import org.junit.After import org.junit.Before import org.junit.Test +import java.security.cert.X509Certificate import kotlin.test.assertEquals class PersistentNetworkMapStorageTest : TestBase() { @@ -30,15 +29,16 @@ class PersistentNetworkMapStorageTest : TestBase() { private lateinit var nodeInfoStorage: PersistentNodeInfoStorage private lateinit var requestStorage: PersistentCertificateRequestStorage - private val rootCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - private val rootCaCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name("Corda Node Root CA", "R3 LTD", "London", "GB"), rootCaKeyPair) - private val intermediateCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - private val intermediateCaCert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCaCert, rootCaKeyPair, CordaX500Name("Corda Node Intermediate CA", "R3 LTD", "London", "GB"), intermediateCaKeyPair.public) + private lateinit var rootCaCert: X509Certificate + private lateinit var intermediateCa: CertificateAndKeyPair @Before fun startDb() { + val (rootCa, intermediateCa) = createDevIntermediateCaCertPath() + rootCaCert = rootCa.certificate + this.intermediateCa = intermediateCa persistence = configureDatabase(makeTestDataSourceProperties()) - networkMapStorage = PersistentNetworkMapStorage(persistence, LocalSigner(intermediateCaKeyPair, arrayOf(intermediateCaCert.cert, rootCaCert.cert))) + networkMapStorage = PersistentNetworkMapStorage(persistence, LocalSigner(intermediateCa.keyPair, arrayOf(intermediateCa.certificate, rootCaCert))) nodeInfoStorage = PersistentNodeInfoStorage(persistence) requestStorage = PersistentCertificateRequestStorage(persistence) } @@ -60,7 +60,7 @@ class PersistentNetworkMapStorageTest : TestBase() { val networkMap = NetworkMap(listOf(nodeInfoHash), networkParametersHash) val serializedNetworkMap = networkMap.serialize() - val signatureData = intermediateCaKeyPair.sign(serializedNetworkMap).withCert(intermediateCaCert.cert) + val signatureData = intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate) val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, signatureData) // when @@ -70,7 +70,7 @@ class PersistentNetworkMapStorageTest : TestBase() { val persistedSignedNetworkMap = networkMapStorage.getCurrentNetworkMap() assertEquals(signedNetworkMap.signature, persistedSignedNetworkMap?.signature) - assertEquals(signedNetworkMap.verified(rootCaCert.cert), persistedSignedNetworkMap?.verified(rootCaCert.cert)) + assertEquals(signedNetworkMap.verified(rootCaCert), persistedSignedNetworkMap?.verified(rootCaCert)) } @Test @@ -96,7 +96,7 @@ class PersistentNetworkMapStorageTest : TestBase() { // Sign network map making it current network map val networkMap = NetworkMap(emptyList(), networkParametersHash) val serializedNetworkMap = networkMap.serialize() - val signatureData = intermediateCaKeyPair.sign(serializedNetworkMap).withCert(intermediateCaCert.cert) + val signatureData = intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate) val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, signatureData) networkMapStorage.saveNetworkMap(signedNetworkMap) @@ -117,7 +117,7 @@ class PersistentNetworkMapStorageTest : TestBase() { val netParamsHash = networkMapStorage.saveNetworkParameters(netParams) val signedNetParams = networkMapStorage.getSignedNetworkParameters(netParamsHash) assertThat(signedNetParams?.verified()).isEqualTo(netParams) - assertThat(signedNetParams?.sig?.by).isEqualTo(intermediateCaKeyPair.public) + assertThat(signedNetParams?.sig?.by).isEqualTo(intermediateCa.keyPair.public) } @Test @@ -135,7 +135,7 @@ class PersistentNetworkMapStorageTest : TestBase() { val networkParametersHash = networkMapStorage.saveNetworkParameters(testNetworkParameters(emptyList())) val networkMap = NetworkMap(listOf(nodeInfoHashA), networkParametersHash) val serializedNetworkMap = networkMap.serialize() - val signatureData = intermediateCaKeyPair.sign(serializedNetworkMap).withCert(intermediateCaCert.cert) + val signatureData = intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate) val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, signatureData) // Sign network map diff --git a/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersitenceNodeInfoStorageTest.kt b/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersitenceNodeInfoStorageTest.kt index e5b2e37699..c8df5a3a3d 100644 --- a/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersitenceNodeInfoStorageTest.kt +++ b/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersitenceNodeInfoStorageTest.kt @@ -6,15 +6,16 @@ import com.r3.corda.networkmanage.common.utils.hashString import net.corda.core.crypto.Crypto import net.corda.core.crypto.SecureHash import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.cert import net.corda.core.node.NodeInfo import net.corda.core.serialization.serialize import net.corda.nodeapi.internal.SignedNodeInfo +import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.testing.internal.TestNodeInfoBuilder +import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.testing.internal.signWith import net.corda.testing.node.MockServices import org.assertj.core.api.Assertions.assertThat @@ -22,6 +23,7 @@ import org.junit.After import org.junit.Before import org.junit.Test import java.security.PrivateKey +import java.security.cert.X509Certificate import kotlin.test.assertEquals import kotlin.test.assertNotNull import kotlin.test.assertNull @@ -31,13 +33,14 @@ class PersitenceNodeInfoStorageTest : TestBase() { private lateinit var nodeInfoStorage: PersistentNodeInfoStorage private lateinit var persistence: CordaPersistence - private val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - private val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 LTD", country = "GB"), rootCAKey) - private val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - private val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, CordaX500Name(commonName = "Corda Node Intermediate CA", locality = "London", organisation = "R3 LTD", country = "GB"), intermediateCAKey.public) + private lateinit var rootCaCert: X509Certificate + private lateinit var intermediateCa: CertificateAndKeyPair @Before fun startDb() { + val (rootCa, intermediateCa) = createDevIntermediateCaCertPath() + rootCaCert = rootCa.certificate + this.intermediateCa = intermediateCa persistence = configureDatabase(MockServices.makeTestDataSourceProperties()) nodeInfoStorage = PersistentNodeInfoStorage(persistence) requestStorage = PersistentCertificateRequestStorage(persistence) @@ -53,9 +56,14 @@ class PersitenceNodeInfoStorageTest : TestBase() { // Create node info. val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val name = CordaX500Name(organisation = "Test", locality = "London", country = "GB") - val nodeCaCert = X509Utilities.createCertificate(CertificateType.NODE_CA, intermediateCACert, intermediateCAKey, name, keyPair.public) + val nodeCaCert = X509Utilities.createCertificate( + CertificateType.NODE_CA, + intermediateCa.certificate, + intermediateCa.keyPair, + name.x500Principal, + keyPair.public) - val request = X509Utilities.createCertificateSigningRequest(name, "my@mail.com", keyPair) + val request = X509Utilities.createCertificateSigningRequest(name.x500Principal, "my@mail.com", keyPair) val requestId = requestStorage.saveRequest(request) requestStorage.markRequestTicketCreated(requestId) @@ -63,12 +71,15 @@ class PersitenceNodeInfoStorageTest : TestBase() { assertNull(nodeInfoStorage.getCertificatePath(SecureHash.parse(keyPair.public.hashString()))) - requestStorage.putCertificatePath(requestId, buildCertPath(nodeCaCert.cert, intermediateCACert.cert, rootCACert.cert), listOf(CertificationRequestStorage.DOORMAN_SIGNATURE)) + requestStorage.putCertificatePath( + requestId, + buildCertPath(nodeCaCert, intermediateCa.certificate, rootCaCert), + listOf(CertificationRequestStorage.DOORMAN_SIGNATURE)) val storedCertPath = nodeInfoStorage.getCertificatePath(SecureHash.parse(keyPair.public.hashString())) assertNotNull(storedCertPath) - assertEquals(nodeCaCert.cert, storedCertPath!!.certificates.first()) + assertEquals(nodeCaCert, storedCertPath!!.certificates.first()) } @Test diff --git a/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/signer/NetworkMapSignerTest.kt b/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/signer/NetworkMapSignerTest.kt index 92b17f107f..71926c7fac 100644 --- a/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/signer/NetworkMapSignerTest.kt +++ b/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/signer/NetworkMapSignerTest.kt @@ -4,20 +4,18 @@ import com.nhaarman.mockito_kotlin.* import com.r3.corda.networkmanage.TestBase import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage import com.r3.corda.networkmanage.common.utils.withCert -import net.corda.core.crypto.Crypto import net.corda.core.crypto.SecureHash import net.corda.core.crypto.sha256 import net.corda.core.crypto.sign -import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.cert import net.corda.core.serialization.serialize -import net.corda.nodeapi.internal.crypto.CertificateType -import net.corda.nodeapi.internal.crypto.X509Utilities +import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.network.NetworkMap import net.corda.nodeapi.internal.network.SignedNetworkMap import net.corda.testing.common.internal.testNetworkParameters +import net.corda.testing.internal.createDevIntermediateCaCertPath import org.junit.Before import org.junit.Test +import java.security.cert.X509Certificate import kotlin.test.assertEquals import kotlin.test.assertTrue @@ -25,12 +23,15 @@ class NetworkMapSignerTest : TestBase() { private lateinit var signer: Signer private lateinit var networkMapStorage: NetworkMapStorage private lateinit var networkMapSigner: NetworkMapSigner - private val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - private val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 LTD", country = "GB"), rootCAKey) - private val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - private val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, CordaX500Name(commonName = "Corda Node Intermediate CA", locality = "London", organisation = "R3 LTD", country = "GB"), intermediateCAKey.public) + + private lateinit var rootCaCert: X509Certificate + private lateinit var intermediateCa: CertificateAndKeyPair + @Before fun setUp() { + val (rootCa, intermediateCa) = createDevIntermediateCaCertPath() + rootCaCert = rootCa.certificate + this.intermediateCa = intermediateCa signer = mock() networkMapStorage = mock() networkMapSigner = NetworkMapSigner(networkMapStorage, signer) @@ -43,11 +44,11 @@ class NetworkMapSignerTest : TestBase() { val networkParameters = testNetworkParameters(emptyList()) val serializedNetworkMap = NetworkMap(signedNodeInfoHashes, SecureHash.randomSHA256()).serialize() whenever(networkMapStorage.getCurrentNetworkMap()) - .thenReturn(SignedNetworkMap(serializedNetworkMap, intermediateCAKey.sign(serializedNetworkMap).withCert(intermediateCACert.cert))) + .thenReturn(SignedNetworkMap(serializedNetworkMap, intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate))) whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(signedNodeInfoHashes) whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters) whenever(signer.sign(any())).then { - intermediateCAKey.sign(it.arguments.first() as ByteArray).withCert(intermediateCACert.cert) + intermediateCa.keyPair.sign(it.arguments[0] as ByteArray).withCert(intermediateCa.certificate) } // when @@ -59,7 +60,7 @@ class NetworkMapSignerTest : TestBase() { verify(networkMapStorage).getLatestNetworkParameters() argumentCaptor().apply { verify(networkMapStorage).saveNetworkMap(capture()) - val networkMap = firstValue.verified(rootCACert.cert) + val networkMap = firstValue.verified(rootCaCert) assertEquals(networkParameters.serialize().hash, networkMap.networkParameterHash) assertEquals(signedNodeInfoHashes.size, networkMap.nodeInfoHashes.size) assertTrue(networkMap.nodeInfoHashes.containsAll(signedNodeInfoHashes)) @@ -73,7 +74,7 @@ class NetworkMapSignerTest : TestBase() { val networkMapParametersHash = networkParameters.serialize().bytes.sha256() val networkMap = NetworkMap(emptyList(), networkMapParametersHash) val serializedNetworkMap = networkMap.serialize() - val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, intermediateCAKey.sign(serializedNetworkMap).withCert(intermediateCACert.cert)) + val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate)) whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(signedNetworkMap) whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList()) whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters) @@ -94,7 +95,7 @@ class NetworkMapSignerTest : TestBase() { whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList()) whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters) whenever(signer.sign(any())).then { - intermediateCAKey.sign(it.arguments.first() as ByteArray).withCert(intermediateCACert.cert) + intermediateCa.keyPair.sign(it.arguments[0] as ByteArray).withCert(intermediateCa.certificate) } // when networkMapSigner.signNetworkMap() @@ -105,7 +106,7 @@ class NetworkMapSignerTest : TestBase() { verify(networkMapStorage).getLatestNetworkParameters() argumentCaptor().apply { verify(networkMapStorage).saveNetworkMap(capture()) - val networkMap = firstValue.verified(rootCACert.cert) + val networkMap = firstValue.verified(rootCaCert) assertEquals(networkParameters.serialize().hash, networkMap.networkParameterHash) } } diff --git a/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/DefaultRequestProcessorTest.kt b/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/DefaultRequestProcessorTest.kt index 3029723e79..b0091a4cf6 100644 --- a/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/DefaultRequestProcessorTest.kt +++ b/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/DefaultRequestProcessorTest.kt @@ -10,24 +10,23 @@ import com.r3.corda.networkmanage.common.persistence.CertificateStatus import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage import com.r3.corda.networkmanage.common.persistence.RequestStatus import com.r3.corda.networkmanage.common.utils.buildCertPath -import com.r3.corda.networkmanage.common.utils.toX509Certificate import com.r3.corda.networkmanage.doorman.signer.DefaultCsrHandler import com.r3.corda.networkmanage.doorman.signer.LocalSigner import net.corda.core.crypto.Crypto -import net.corda.core.identity.CordaX500Name import net.corda.nodeapi.internal.crypto.X509Utilities import org.junit.Test +import javax.security.auth.x500.X500Principal import kotlin.test.assertEquals class DefaultRequestProcessorTest : TestBase() { @Test fun `get response`() { val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val cert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(locality = "London", organisation = "Test", country = "GB"), keyPair) + val cert = X509Utilities.createSelfSignedCACertificate(X500Principal("O=Test,L=London,C=GB"), keyPair) val requestStorage: CertificationRequestStorage = mock { on { getRequest("New") }.thenReturn(certificateSigningRequest()) - on { getRequest("Signed") }.thenReturn(certificateSigningRequest(status = RequestStatus.SIGNED, certData = certificateData("", CertificateStatus.VALID, buildCertPath(cert.toX509Certificate())))) + on { getRequest("Signed") }.thenReturn(certificateSigningRequest(status = RequestStatus.SIGNED, certData = certificateData("", CertificateStatus.VALID, buildCertPath(cert)))) on { getRequest("Rejected") }.thenReturn(certificateSigningRequest(status = RequestStatus.REJECTED, remark = "Random reason")) } val signer: LocalSigner = mock() @@ -35,15 +34,15 @@ class DefaultRequestProcessorTest : TestBase() { assertEquals(CertificateResponse.NotReady, requestProcessor.getResponse("random")) assertEquals(CertificateResponse.NotReady, requestProcessor.getResponse("New")) - assertEquals(CertificateResponse.Ready(buildCertPath(cert.toX509Certificate())), requestProcessor.getResponse("Signed")) + assertEquals(CertificateResponse.Ready(buildCertPath(cert)), requestProcessor.getResponse("Signed")) assertEquals(CertificateResponse.Unauthorised("Random reason"), requestProcessor.getResponse("Rejected")) } @Test fun `process request`() { - val request1 = X509Utilities.createCertificateSigningRequest(CordaX500Name(locality = "London", organisation = "Test1", country = "GB"), "my@email.com", Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) - val request2 = X509Utilities.createCertificateSigningRequest(CordaX500Name(locality = "London", organisation = "Test2", country = "GB"), "my@email.com", Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) - val request3 = X509Utilities.createCertificateSigningRequest(CordaX500Name(locality = "London", organisation = "Test3", country = "GB"), "my@email.com", Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) + val (request1, request2, request3) = (1..3).map { + X509Utilities.createCertificateSigningRequest(X500Principal("O=Test1,L=London,C=GB"), "my@email.com", Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) + } val requestStorage: CertificationRequestStorage = mock { on { getRequests(RequestStatus.APPROVED) }.thenReturn(listOf( diff --git a/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/NodeInfoWebServiceTest.kt b/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/NodeInfoWebServiceTest.kt index 939c901c65..b376833aa1 100644 --- a/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/NodeInfoWebServiceTest.kt +++ b/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/NodeInfoWebServiceTest.kt @@ -7,33 +7,32 @@ import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage import com.r3.corda.networkmanage.common.persistence.NodeInfoStorage import com.r3.corda.networkmanage.common.utils.withCert import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService -import net.corda.core.crypto.Crypto import net.corda.core.crypto.SecureHash.Companion.randomSHA256 import net.corda.core.crypto.SignedData import net.corda.core.crypto.sign import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.cert import net.corda.core.internal.openHttpConnection import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.seconds import net.corda.nodeapi.internal.SignedNodeInfo -import net.corda.nodeapi.internal.crypto.CertificateType -import net.corda.nodeapi.internal.crypto.X509Utilities +import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.network.NetworkMap import net.corda.nodeapi.internal.network.NetworkParameters import net.corda.nodeapi.internal.network.SignedNetworkMap import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.common.internal.testNetworkParameters +import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.testing.internal.createNodeInfoAndSigned import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatExceptionOfType -import org.bouncycastle.asn1.x500.X500Name +import org.junit.Before import org.junit.Rule import org.junit.Test import java.io.FileNotFoundException import java.net.URL +import java.security.cert.X509Certificate import javax.ws.rs.core.MediaType import kotlin.test.assertEquals @@ -42,12 +41,18 @@ class NodeInfoWebServiceTest { @JvmField val testSerialization = SerializationEnvironmentRule(true) - private val rootCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - private val rootCaCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name("Corda Node Root CA", "R3 LTD", "London", "GB"), rootCaKeyPair) - private val intermediateCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - private val intermediateCaCert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCaCert, rootCaKeyPair, X500Name("CN=Corda Node Intermediate CA,L=London"), intermediateCaKeyPair.public) + private lateinit var rootCaCert: X509Certificate + private lateinit var intermediateCa: CertificateAndKeyPair + private val testNetworkMapConfig = NetworkMapConfig(10.seconds.toMillis(), 10.seconds.toMillis()) + @Before + fun init() { + val (rootCa, intermediateCa) = createDevIntermediateCaCertPath() + rootCaCert = rootCa.certificate + this.intermediateCa = intermediateCa + } + @Test fun `submit nodeInfo`() { // Create node info. @@ -65,7 +70,7 @@ class NodeInfoWebServiceTest { fun `get network map`() { val networkMap = NetworkMap(listOf(randomSHA256(), randomSHA256()), randomSHA256()) val serializedNetworkMap = networkMap.serialize() - val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, intermediateCaKeyPair.sign(serializedNetworkMap).withCert(intermediateCaCert.cert)) + val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate)) val networkMapStorage: NetworkMapStorage = mock { on { getCurrentNetworkMap() }.thenReturn(signedNetworkMap) @@ -75,7 +80,7 @@ class NodeInfoWebServiceTest { it.start() val signedNetworkMapResponse = it.doGet("") verify(networkMapStorage, times(1)).getCurrentNetworkMap() - assertEquals(signedNetworkMapResponse.verified(rootCaCert.cert), networkMap) + assertEquals(signedNetworkMapResponse.verified(rootCaCert), networkMap) } } @@ -104,7 +109,7 @@ class NodeInfoWebServiceTest { fun `get network parameters`() { val netParams = testNetworkParameters(emptyList()) val serializedNetParams = netParams.serialize() - val signedNetParams = SignedData(serializedNetParams, intermediateCaKeyPair.sign(serializedNetParams)) + val signedNetParams = SignedData(serializedNetParams, intermediateCa.keyPair.sign(serializedNetParams)) val netParamsHash = serializedNetParams.hash val networkMapStorage: NetworkMapStorage = mock { @@ -116,7 +121,7 @@ class NodeInfoWebServiceTest { val netParamsResponse = it.doGet>("network-parameter/$netParamsHash") verify(networkMapStorage, times(1)).getSignedNetworkParameters(netParamsHash) assertThat(netParamsResponse.verified()).isEqualTo(netParams) - assertThat(netParamsResponse.sig.by).isEqualTo(intermediateCaKeyPair.public) + assertThat(netParamsResponse.sig.by).isEqualTo(intermediateCa.keyPair.public) assertThatExceptionOfType(FileNotFoundException::class.java).isThrownBy { it.doGet>("network-parameter/${randomSHA256()}") diff --git a/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/RegistrationWebServiceTest.kt b/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/RegistrationWebServiceTest.kt index 0f5bdec26b..39a6d58630 100644 --- a/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/RegistrationWebServiceTest.kt +++ b/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/RegistrationWebServiceTest.kt @@ -4,46 +4,54 @@ import com.nhaarman.mockito_kotlin.* import com.r3.corda.networkmanage.TestBase import com.r3.corda.networkmanage.common.persistence.CertificateResponse import com.r3.corda.networkmanage.common.utils.buildCertPath -import com.r3.corda.networkmanage.common.utils.toX509Certificate import com.r3.corda.networkmanage.doorman.signer.CsrHandler import com.r3.corda.networkmanage.doorman.webservice.RegistrationWebService import net.corda.core.crypto.Crypto import net.corda.core.crypto.SecureHash import net.corda.core.identity.CordaX500Name import net.corda.core.utilities.NetworkHostAndPort +import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME +import net.corda.testing.internal.createDevIntermediateCaCertPath import org.apache.commons.io.IOUtils import org.assertj.core.api.Assertions.assertThat import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x509.GeneralName import org.bouncycastle.asn1.x509.GeneralSubtree import org.bouncycastle.asn1.x509.NameConstraints -import org.bouncycastle.cert.X509CertificateHolder import org.bouncycastle.pkcs.PKCS10CertificationRequest import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest import org.junit.After +import org.junit.Before import org.junit.Test import java.io.IOException import java.net.HttpURLConnection import java.net.HttpURLConnection.* import java.net.URL +import java.nio.charset.StandardCharsets.UTF_8 import java.security.cert.CertPath import java.security.cert.X509Certificate import java.util.* import java.util.zip.ZipInputStream +import javax.security.auth.x500.X500Principal import javax.ws.rs.core.MediaType import kotlin.test.assertEquals class RegistrationWebServiceTest : TestBase() { - private val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - private val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 Ltd", country = "GB"), rootCAKey) - private val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - private val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, X500Name("CN=Corda Node Intermediate CA,L=London"), intermediateCAKey.public) + private lateinit var rootCaCert: X509Certificate + private lateinit var intermediateCa: CertificateAndKeyPair private lateinit var webServer: NetworkManagementWebServer + @Before + fun init() { + val (rootCa, intermediateCa) = createDevIntermediateCaCertPath() + rootCaCert = rootCa.certificate + this.intermediateCa = intermediateCa + } + private fun startSigningServer(csrHandler: CsrHandler) { webServer = NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), RegistrationWebService(csrHandler)) webServer.start() @@ -65,7 +73,10 @@ class RegistrationWebServiceTest : TestBase() { startSigningServer(requestProcessor) val keyPair = Crypto.generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME) - val request = X509Utilities.createCertificateSigningRequest(CordaX500Name(locality = "London", organisation = "Legal Name", country = "GB"), "my@mail.com", keyPair) + val request = X509Utilities.createCertificateSigningRequest( + CordaX500Name(locality = "London", organisation = "Legal Name", country = "GB").x500Principal, + "my@mail.com", + keyPair) // Post request to signing server via http. assertEquals(id, submitRequest(request)) @@ -79,6 +90,8 @@ class RegistrationWebServiceTest : TestBase() { val keyPair = Crypto.generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME) val id = SecureHash.randomSHA256().toString() + val subject = CordaX500Name(locality = "London", organisation = "LegalName", country = "GB").x500Principal + // Mock Storage behaviour. val certificateStore = mutableMapOf() val requestProcessor = mock { @@ -88,10 +101,15 @@ class RegistrationWebServiceTest : TestBase() { } ?: CertificateResponse.NotReady } on { processApprovedRequests() }.then { - val request = X509Utilities.createCertificateSigningRequest(CordaX500Name(locality = "London", organisation = "LegalName", country = "GB"), "my@mail.com", keyPair) + val request = X509Utilities.createCertificateSigningRequest(subject, "my@mail.com", keyPair) certificateStore[id] = JcaPKCS10CertificationRequest(request).run { - val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, intermediateCACert, intermediateCAKey, subject, publicKey).toX509Certificate() - buildCertPath(tlsCert, intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate()) + val tlsCert = X509Utilities.createCertificate( + CertificateType.TLS, + intermediateCa.certificate, + intermediateCa.keyPair, + X500Principal(subject.encoded), + publicKey) + buildCertPath(tlsCert, intermediateCa.certificate, rootCaCert) } null } @@ -104,22 +122,15 @@ class RegistrationWebServiceTest : TestBase() { val certificates = (pollForResponse(id) as PollResponse.Ready).certChain verify(requestProcessor, times(2)).getResponse(any()) - assertEquals(3, certificates.size) - certificates.first().run { - assertThat(subjectDN.name).contains("O=LegalName") - assertThat(subjectDN.name).contains("L=London") - } - - certificates.last().run { - assertThat(subjectDN.name).contains("CN=Corda Node Root CA") - assertThat(subjectDN.name).contains("L=London") - } + assertThat(certificates).hasSize(3) + assertThat(certificates[0].subjectX500Principal).isEqualTo(subject) + assertThat(certificates).endsWith(intermediateCa.certificate, rootCaCert) } @Test fun `retrieve certificate and create valid TLS certificate`() { - val keyPair = Crypto.generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME) + val nodeCaKeyPair = Crypto.generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME) val id = SecureHash.randomSHA256().toString() // Mock Storage behaviour. @@ -131,11 +142,22 @@ class RegistrationWebServiceTest : TestBase() { } ?: CertificateResponse.NotReady } on { processApprovedRequests() }.then { - val request = X509Utilities.createCertificateSigningRequest(CordaX500Name(locality = "London", organisation = "Legal Name", country = "GB"), "my@mail.com", keyPair) + val request = X509Utilities.createCertificateSigningRequest( + CordaX500Name(locality = "London", organisation = "Legal Name", country = "GB").x500Principal, + "my@mail.com", + nodeCaKeyPair) certificateStore[id] = JcaPKCS10CertificationRequest(request).run { - val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, X500Name("CN=LegalName, L=London")))), arrayOf()) - val clientCert = X509Utilities.createCertificate(CertificateType.NODE_CA, intermediateCACert, intermediateCAKey, subject, publicKey, nameConstraints = nameConstraints).toX509Certificate() - buildCertPath(clientCert, intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate()) + val nameConstraints = NameConstraints( + arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, X500Name("CN=LegalName, L=London")))), + arrayOf()) + val clientCert = X509Utilities.createCertificate( + CertificateType.NODE_CA, + intermediateCa.certificate, + intermediateCa.keyPair, + X500Principal(subject.encoded), + publicKey, + nameConstraints = nameConstraints) + buildCertPath(clientCert, intermediateCa.certificate, rootCaCert) } true } @@ -149,11 +171,17 @@ class RegistrationWebServiceTest : TestBase() { verify(storage, times(2)).getResponse(any()) assertEquals(3, certificates.size) - val sslKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val sslCert = X509Utilities.createCertificate(CertificateType.TLS, X509CertificateHolder(certificates.first().encoded), keyPair, X500Name("CN=LegalName,L=London"), sslKey.public).toX509Certificate() + val sslKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) + val sslCert = X509Utilities.createCertificate( + CertificateType.TLS, + certificates[0], + nodeCaKeyPair, + // TODO Investigate why X500Principal("CN=LegalName, L=London") results in a name constraints violation + X500Principal(X500Name("CN=LegalName, L=London").encoded), + sslKeyPair.public) // TODO: This is temporary solution, remove all certificate re-shaping after identity refactoring is done. - X509Utilities.validateCertificateChain(certificates.last(), sslCert, *certificates.toTypedArray()) + X509Utilities.validateCertificateChain(rootCaCert, sslCert, *certificates.toTypedArray()) } @Test @@ -192,7 +220,7 @@ class RegistrationWebServiceTest : TestBase() { PollResponse.Ready(certificates) } HTTP_NO_CONTENT -> PollResponse.NotReady - HTTP_UNAUTHORIZED -> PollResponse.Unauthorised(IOUtils.toString(conn.errorStream)) + HTTP_UNAUTHORIZED -> PollResponse.Unauthorised(IOUtils.toString(conn.errorStream, UTF_8)) else -> throw IOException("Cannot connect to Certificate Signing Server, HTTP response code : ${conn.responseCode}") } } diff --git a/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/signer/CsrHandlerTest.kt b/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/signer/CsrHandlerTest.kt index 21d3ed1375..42314f4b38 100644 --- a/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/signer/CsrHandlerTest.kt +++ b/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/signer/CsrHandlerTest.kt @@ -39,7 +39,10 @@ class JiraCsrHandlerTest { private lateinit var certificateResponse: CertificateResponse.Ready private val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - private val pkcS10CertificationRequest = X509Utilities.createCertificateSigningRequest(CordaX500Name(locality = "London", organisation = "LegalName", country = "GB"), "my@mail.com", keyPair) + private val pkcS10CertificationRequest = X509Utilities.createCertificateSigningRequest( + CordaX500Name(locality = "London", organisation = "LegalName", country = "GB").x500Principal, + "my@mail.com", + keyPair) @Before fun setup() { diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/DevIdentityGenerator.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/DevIdentityGenerator.kt index ef58a930b2..0cb8472f89 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/DevIdentityGenerator.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/DevIdentityGenerator.kt @@ -5,10 +5,8 @@ import net.corda.core.crypto.Crypto import net.corda.core.crypto.generateKeyPair import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party -import net.corda.core.internal.cert import net.corda.core.internal.createDirectories import net.corda.core.internal.div -import net.corda.core.internal.toX509CertHolder import net.corda.core.utilities.trace import net.corda.nodeapi.internal.config.NodeSSLConfiguration import net.corda.nodeapi.internal.crypto.* @@ -39,10 +37,10 @@ object DevIdentityGenerator { // TODO The passwords for the dev key stores are spread everywhere and should be constants in a single location val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass") val intermediateCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass") - val rootCert = caKeyStore.getCertificate(X509Utilities.CORDA_ROOT_CA) + val rootCert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA) nodeSslConfig.certificatesDirectory.createDirectories() - nodeSslConfig.createDevKeyStores(rootCert.toX509CertHolder(), intermediateCa, legalName) + nodeSslConfig.createDevKeyStores(rootCert, intermediateCa, legalName) val keyStoreWrapper = KeyStoreWrapper(nodeSslConfig.nodeKeystore, nodeSslConfig.keyStorePassword) val identity = keyStoreWrapper.storeLegalIdentity(legalName, "$NODE_IDENTITY_ALIAS_PREFIX-private-key", Crypto.generateKeyPair()) @@ -62,16 +60,21 @@ object DevIdentityGenerator { keyPairs.zip(dirs) { keyPair, nodeDir -> val (serviceKeyCert, compositeKeyCert) = listOf(keyPair.public, compositeKey).map { publicKey -> - X509Utilities.createCertificate(CertificateType.SERVICE_IDENTITY, intermediateCa.certificate, intermediateCa.keyPair, notaryName, publicKey) + X509Utilities.createCertificate( + CertificateType.SERVICE_IDENTITY, + intermediateCa.certificate, + intermediateCa.keyPair, + notaryName.x500Principal, + publicKey) } val distServKeyStoreFile = (nodeDir / "certificates").createDirectories() / "distributedService.jks" val keystore = loadOrCreateKeyStore(distServKeyStoreFile, "cordacadevpass") - keystore.setCertificateEntry("$DISTRIBUTED_NOTARY_ALIAS_PREFIX-composite-key", compositeKeyCert.cert) + keystore.setCertificateEntry("$DISTRIBUTED_NOTARY_ALIAS_PREFIX-composite-key", compositeKeyCert) keystore.setKeyEntry( "$DISTRIBUTED_NOTARY_ALIAS_PREFIX-private-key", keyPair.private, "cordacadevkeypass".toCharArray(), - arrayOf(serviceKeyCert.cert, intermediateCa.certificate.cert, rootCert)) + arrayOf(serviceKeyCert, intermediateCa.certificate, rootCert)) keystore.save(distServKeyStoreFile, "cordacadevpass") } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt index 03bd39e635..374a4cdf24 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt @@ -8,13 +8,13 @@ import net.corda.nodeapi.internal.crypto.* import org.bouncycastle.asn1.x509.GeneralName import org.bouncycastle.asn1.x509.GeneralSubtree import org.bouncycastle.asn1.x509.NameConstraints -import org.bouncycastle.cert.X509CertificateHolder +import java.security.cert.X509Certificate /** * Create the node and SSL key stores needed by a node. The node key store will be populated with a node CA cert (using * the given legal name), and the SSL key store will store the TLS cert which is a sub-cert of the node CA. */ -fun SSLConfiguration.createDevKeyStores(rootCert: X509CertificateHolder, intermediateCa: CertificateAndKeyPair, legalName: CordaX500Name) { +fun SSLConfiguration.createDevKeyStores(rootCert: X509Certificate, intermediateCa: CertificateAndKeyPair, legalName: CordaX500Name) { val (nodeCaCert, nodeCaKeyPair) = createDevNodeCa(intermediateCa, legalName) loadOrCreateKeyStore(nodeKeystore, keyStorePassword).apply { @@ -27,7 +27,7 @@ fun SSLConfiguration.createDevKeyStores(rootCert: X509CertificateHolder, interme } val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, legalName, tlsKeyPair.public) + val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, legalName.x500Principal, tlsKeyPair.public) loadOrCreateKeyStore(sslKeystore, keyStorePassword).apply { addOrReplaceKey( @@ -50,7 +50,7 @@ fun createDevNodeCa(intermediateCa: CertificateAndKeyPair, legalName: CordaX500N CertificateType.NODE_CA, intermediateCa.certificate, intermediateCa.keyPair, - legalName, + legalName.x500Principal, keyPair.public, nameConstraints = nameConstraints) return CertificateAndKeyPair(cert, keyPair) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreUtilities.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreUtilities.kt index b49493ded2..d060c756b0 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreUtilities.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreUtilities.kt @@ -3,8 +3,9 @@ package net.corda.nodeapi.internal.crypto import net.corda.core.crypto.Crypto -import net.corda.core.internal.* -import org.bouncycastle.cert.X509CertificateHolder +import net.corda.core.internal.exists +import net.corda.core.internal.read +import net.corda.core.internal.write import java.io.IOException import java.io.InputStream import java.io.OutputStream @@ -67,18 +68,6 @@ fun loadKeyStore(input: InputStream, storePassword: String): KeyStore { return keyStore } -/** - * Helper extension method to add, or overwrite any key data in store. - * @param alias name to record the private key and certificate chain under. - * @param key cryptographic key to store. - * @param password password for unlocking the key entry in the future. This does not have to be the same password as any keys stored, - * but for SSL purposes this is recommended. - * @param chain the sequence of certificates starting with the public key certificate for this key and extending to the root CA cert. - */ -fun KeyStore.addOrReplaceKey(alias: String, key: Key, password: CharArray, chain: Array) { - addOrReplaceKey(alias, key, password, chain.map { it.cert }.toTypedArray()) -} - /** * Helper extension method to add, or overwrite any key data in store. * @param alias name to record the private key and certificate chain under. @@ -132,9 +121,9 @@ fun KeyStore.getKeyPair(alias: String, keyPassword: String): KeyPair = getCertif * @param keyPassword The password for the PrivateKey (not the store access password). */ fun KeyStore.getCertificateAndKeyPair(alias: String, keyPassword: String): CertificateAndKeyPair { - val cert = getX509Certificate(alias).toX509CertHolder() - val publicKey = Crypto.toSupportedPublicKey(cert.subjectPublicKeyInfo) - return CertificateAndKeyPair(cert, KeyPair(publicKey, getSupportedKey(alias, keyPassword))) + val certificate = getX509Certificate(alias) + val publicKey = Crypto.toSupportedPublicKey(certificate.publicKey) + return CertificateAndKeyPair(certificate, KeyPair(publicKey, getSupportedKey(alias, keyPassword))) } /** diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreWrapper.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreWrapper.kt index 2504ce221b..f7cae5b441 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreWrapper.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreWrapper.kt @@ -2,7 +2,6 @@ package net.corda.nodeapi.internal.crypto import net.corda.core.identity.CordaX500Name import net.corda.core.identity.PartyAndCertificate -import net.corda.core.internal.cert import net.corda.core.internal.read import java.nio.file.Path import java.security.KeyPair @@ -15,8 +14,13 @@ class KeyStoreWrapper(private val storePath: Path, private val storePassword: St fun storeLegalIdentity(legalName: CordaX500Name, alias: String, keyPair: KeyPair): PartyAndCertificate { val nodeCaCertChain = keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA) val nodeCa = getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA) - val identityCert = X509Utilities.createCertificate(CertificateType.LEGAL_IDENTITY, nodeCa.certificate, nodeCa.keyPair, legalName, keyPair.public) - val identityCertPath = X509CertificateFactory().generateCertPath(identityCert.cert, *nodeCaCertChain) + val identityCert = X509Utilities.createCertificate( + CertificateType.LEGAL_IDENTITY, + nodeCa.certificate, + nodeCa.keyPair, + legalName.x500Principal, + keyPair.public) + val identityCertPath = X509CertificateFactory().generateCertPath(identityCert, *nodeCaCertChain) // Assume key password = store password. keyStore.addOrReplaceKey(alias, keyPair.private, storePassword.toCharArray(), identityCertPath.certificates.toTypedArray()) keyStore.save(storePath, storePassword) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt index 78b44a8786..fb2e512d38 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt @@ -4,12 +4,14 @@ import net.corda.core.CordaOID import net.corda.core.crypto.Crypto import net.corda.core.crypto.SignatureScheme import net.corda.core.crypto.random63BitValue +import net.corda.core.internal.CertRole +import net.corda.core.internal.reader +import net.corda.core.internal.writer import net.corda.core.identity.CordaX500Name import net.corda.core.internal.* import net.corda.core.utilities.days import net.corda.core.utilities.millis import org.bouncycastle.asn1.* -import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.style.BCStyle import org.bouncycastle.asn1.x509.* import org.bouncycastle.asn1.x509.Extension @@ -34,6 +36,7 @@ import java.time.Duration import java.time.Instant import java.time.temporal.ChronoUnit import java.util.* +import javax.security.auth.x500.X500Principal object X509Utilities { val DEFAULT_IDENTITY_SIGNATURE_SCHEME = Crypto.EDDSA_ED25519_SHA512 @@ -74,7 +77,7 @@ object X509Utilities { * @param after duration to roll forward returned end date relative to current date. * @param parent if provided certificate whose validity should bound the date interval returned. */ - fun getCertificateValidityWindow(before: Duration, after: Duration, parent: X509CertificateHolder? = null): Pair { + fun getCertificateValidityWindow(before: Duration, after: Duration, parent: X509Certificate? = null): Pair { val startOfDayUTC = Instant.now().truncatedTo(ChronoUnit.DAYS) val notBefore = max(startOfDayUTC - before, parent?.notBefore) val notAfter = min(startOfDayUTC + after, parent?.notAfter) @@ -85,60 +88,11 @@ object X509Utilities { * Create a de novo root self-signed X509 v3 CA cert. */ @JvmStatic - fun createSelfSignedCACertificate(subject: CordaX500Name, + fun createSelfSignedCACertificate(subject: X500Principal, keyPair: KeyPair, - validityWindow: Pair = DEFAULT_VALIDITY_WINDOW): X509CertificateHolder { + validityWindow: Pair = DEFAULT_VALIDITY_WINDOW): X509Certificate { val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second) - return createCertificate(CertificateType.ROOT_CA, subject.x500Name, keyPair, subject.x500Name, keyPair.public, window) - } - - /** - * Create a X509 v3 certificate for use as a CA or for TLS. This does not require a [CordaX500Name] because the - * constraints are inappropriate for TLS/CA usage, however as a result this is unsuitable for Corda identity - * certificate generation. - * - * @param issuerCertificate The Public certificate of the root CA above this used to sign it. - * @param issuerKeyPair The KeyPair of the root CA above this used to sign it. - * @param subject subject of the generated certificate. - * @param subjectPublicKey subject's public key. - * @param validityWindow The certificate's validity window. Default to [DEFAULT_VALIDITY_WINDOW] if not provided. - * @return A data class is returned containing the new intermediate CA Cert and its KeyPair for signing downstream certificates. - * Note the generated certificate tree is capped at max depth of 1 below this to be in line with commercially available certificates. - */ - @JvmStatic - fun createCertificate(certificateType: CertificateType, - issuerCertificate: X509CertificateHolder, - issuerKeyPair: KeyPair, - subject: CordaX500Name, - subjectPublicKey: PublicKey, - validityWindow: Pair = 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 = DEFAULT_VALIDITY_WINDOW, - nameConstraints: NameConstraints? = null): X509CertificateHolder { - val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second, issuerCertificate) - return createCertificate(certificateType, issuerCertificate.subject, issuerKeyPair, subject, subjectPublicKey, window, nameConstraints) + return createCertificate(CertificateType.ROOT_CA, subject, keyPair, subject, keyPair.public, window) } @Throws(CertPathValidatorException::class) @@ -153,13 +107,13 @@ object X509Utilities { /** * Helper method to store a .pem/.cer format file copy of a certificate if required for import into a PC/Mac, or for inspection. - * @param x509Certificate certificate to save. + * @param certificate certificate to save. * @param file Target file. */ @JvmStatic - fun saveCertificateAsPEMFile(x509Certificate: X509Certificate, file: Path) { + fun saveCertificateAsPEMFile(certificate: X509Certificate, file: Path) { JcaPEMWriter(file.writer()).use { - it.writeObject(x509Certificate) + it.writeObject(certificate) } } @@ -172,9 +126,10 @@ object X509Utilities { fun loadCertificateFromPEMFile(file: Path): X509Certificate { return file.reader().use { val pemObject = PemReader(it).readPemObject() - val certHolder = X509CertificateHolder(pemObject.content) - certHolder.isValidOn(Date()) - certHolder.cert + X509CertificateHolder(pemObject.content).run { + isValidOn(Date()) + toJca() + } } } @@ -187,38 +142,18 @@ object X509Utilities { * @param validityWindow the time period the certificate is valid for. * @param nameConstraints any name constraints to impose on certificates signed by the generated certificate. */ - fun createCertificate(certificateType: CertificateType, - issuer: CordaX500Name, - subject: CordaX500Name, - subjectPublicKey: PublicKey, - validityWindow: Pair, - 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, - nameConstraints: NameConstraints? = null): X509v3CertificateBuilder { - + fun createPartialCertificate(certificateType: CertificateType, + issuer: X500Principal, + subject: X500Principal, + subjectPublicKey: PublicKey, + validityWindow: Pair, + nameConstraints: NameConstraints? = null): X509v3CertificateBuilder { val serial = BigInteger.valueOf(random63BitValue()) val keyPurposes = DERSequence(ASN1EncodableVector().apply { certificateType.purposes.forEach { add(it) } }) val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(subjectPublicKey.encoded)) val role = certificateType.role - val builder = JcaX509v3CertificateBuilder(issuer, serial, validityWindow.first, validityWindow.second, - subject, subjectPublicKey) + val builder = JcaX509v3CertificateBuilder(issuer, serial, validityWindow.first, validityWindow.second, subject, subjectPublicKey) .addExtension(Extension.subjectKeyIdentifier, false, BcX509ExtensionUtils().createSubjectKeyIdentifier(subjectPublicKeyInfo)) .addExtension(Extension.basicConstraints, certificateType.isCA, BasicConstraints(certificateType.isCA)) .addExtension(Extension.keyUsage, false, certificateType.keyUsage) @@ -234,6 +169,37 @@ object X509Utilities { return builder } + /** + * Create a X509 v3 certificate using the given issuer certificate and key pair. + * + * @param issuerCertificate The Public certificate of the root CA above this used to sign it. + * @param issuerKeyPair The KeyPair of the root CA above this used to sign it. + * @param subject subject of the generated certificate. + * @param subjectPublicKey subject's public key. + * @param validityWindow The certificate's validity window. Default to [DEFAULT_VALIDITY_WINDOW] if not provided. + * @return A data class is returned containing the new intermediate CA Cert and its KeyPair for signing downstream certificates. + * Note the generated certificate tree is capped at max depth of 1 below this to be in line with commercially available certificates. + */ + @JvmStatic + fun createCertificate(certificateType: CertificateType, + issuerCertificate: X509Certificate, + issuerKeyPair: KeyPair, + subject: X500Principal, + subjectPublicKey: PublicKey, + validityWindow: Pair = DEFAULT_VALIDITY_WINDOW, + nameConstraints: NameConstraints? = null): X509Certificate { + val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second, issuerCertificate) + return createCertificate( + certificateType, + issuerCertificate.subjectX500Principal, + issuerKeyPair, + subject, + subjectPublicKey, + window, + nameConstraints + ) + } + /** * Build and sign an X.509 certificate with the given signer. * @@ -245,15 +211,16 @@ object X509Utilities { * @param nameConstraints any name constraints to impose on certificates signed by the generated certificate. */ fun createCertificate(certificateType: CertificateType, - issuer: X500Name, + issuer: X500Principal, issuerSigner: ContentSigner, - subject: CordaX500Name, + subject: X500Principal, subjectPublicKey: PublicKey, validityWindow: Pair, - nameConstraints: NameConstraints? = null): X509CertificateHolder { - val builder = createCertificate(certificateType, issuer, subject.x500Name, subjectPublicKey, validityWindow, nameConstraints) - return builder.build(issuerSigner).apply { + nameConstraints: NameConstraints? = null): X509Certificate { + val builder = createPartialCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints) + return builder.build(issuerSigner).run { require(isValidOn(Date())) + toJca() } } @@ -268,39 +235,47 @@ object X509Utilities { * @param nameConstraints any name constraints to impose on certificates signed by the generated certificate. */ fun createCertificate(certificateType: CertificateType, - issuer: X500Name, + issuer: X500Principal, issuerKeyPair: KeyPair, - subject: X500Name, + subject: X500Principal, subjectPublicKey: PublicKey, validityWindow: Pair, - nameConstraints: NameConstraints? = null): X509CertificateHolder { + nameConstraints: NameConstraints? = null): X509Certificate { val signatureScheme = Crypto.findSignatureScheme(issuerKeyPair.private) val provider = Crypto.findProvider(signatureScheme.providerName) - val builder = createCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints) - val signer = ContentSignerBuilder.build(signatureScheme, issuerKeyPair.private, provider) - return builder.build(signer).apply { + val builder = createPartialCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints) + return builder.build(signer).run { require(isValidOn(Date())) require(isSignatureValid(JcaContentVerifierProviderBuilder().build(issuerKeyPair.public))) + toJca() } } /** * Create certificate signing request using provided information. */ - private fun createCertificateSigningRequest(subject: CordaX500Name, + private fun createCertificateSigningRequest(subject: X500Principal, email: String, keyPair: KeyPair, signatureScheme: SignatureScheme): PKCS10CertificationRequest { val signer = ContentSignerBuilder.build(signatureScheme, keyPair.private, Crypto.findProvider(signatureScheme.providerName)) - return JcaPKCS10CertificationRequestBuilder(subject.x500Name, keyPair.public).addAttribute(BCStyle.E, DERUTF8String(email)).build(signer) + return JcaPKCS10CertificationRequestBuilder(subject, keyPair.public).addAttribute(BCStyle.E, DERUTF8String(email)).build(signer) } - fun createCertificateSigningRequest(subject: CordaX500Name, email: String, keyPair: KeyPair): PKCS10CertificationRequest { + fun createCertificateSigningRequest(subject: X500Principal, email: String, keyPair: KeyPair): PKCS10CertificationRequest { return createCertificateSigningRequest(subject, email, keyPair, DEFAULT_TLS_SIGNATURE_SCHEME) } } +/** + * Convert a [X509Certificate] into Bouncycastle's [X509CertificateHolder]. + * + * NOTE: To avoid unnecessary copying use [X509Certificate] where possible. + */ +fun X509Certificate.toBc() = X509CertificateHolder(encoded) +fun X509CertificateHolder.toJca(): X509Certificate = X509CertificateFactory().generateCertificate(encoded.inputStream()) + /** * Wraps a [CertificateFactory] to remove boilerplate. It's unclear whether [CertificateFactory] is threadsafe so best * so assume this class is not. @@ -396,4 +371,4 @@ enum class CertificateType(val keyUsage: KeyUsage, vararg val purposes: KeyPurpo ) } -data class CertificateAndKeyPair(val certificate: X509CertificateHolder, val keyPair: KeyPair) +data class CertificateAndKeyPair(val certificate: X509Certificate, val keyPair: KeyPair) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt index af625adae5..a9b16f61e9 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt @@ -83,16 +83,20 @@ class NetworkBootstrapper { for (confFile in confFiles) { val nodeName = confFile.fileName.toString().removeSuffix(".conf") println("Generating directory for $nodeName") - val nodeDir = (directory / nodeName).createDirectory() - confFile.moveTo(nodeDir / "node.conf") - Files.copy(cordaJar, (nodeDir / "corda.jar")) + val nodeDir = (directory / nodeName) + if (!nodeDir.exists()) { nodeDir.createDirectory() } + confFile.moveTo(nodeDir / "node.conf", StandardCopyOption.REPLACE_EXISTING) + Files.copy(cordaJar, (nodeDir / "corda.jar"), StandardCopyOption.REPLACE_EXISTING) } Files.delete(cordaJar) } private fun extractCordaJarTo(directory: Path): Path { val cordaJarPath = (directory / "corda.jar") - Thread.currentThread().contextClassLoader.getResourceAsStream("corda.jar").copyTo(cordaJarPath) + if (!cordaJarPath.exists()) { + println("No corda jar found in root directory. Extracting from jar") + Thread.currentThread().contextClassLoader.getResourceAsStream("corda.jar").copyTo(cordaJarPath) + } return cordaJarPath } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationHelper.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationHelper.kt index 8b677630d2..b3f6d5ce53 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationHelper.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationHelper.kt @@ -1,9 +1,9 @@ package net.corda.nodeapi.internal.serialization.amqp -import net.corda.core.serialization.ClassWhitelist -import net.corda.core.serialization.CordaSerializable import com.google.common.primitives.Primitives import com.google.common.reflect.TypeToken +import net.corda.core.serialization.ClassWhitelist +import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.SerializationContext import org.apache.qpid.proton.codec.Data import java.beans.IndexedPropertyDescriptor @@ -81,10 +81,17 @@ private fun propertiesForSerializationFromConstructor(kotlinConstructo val rc: MutableList = ArrayList(kotlinConstructor.parameters.size) for (param in kotlinConstructor.parameters) { val name = param.name ?: throw NotSerializableException("Constructor parameter of $clazz has no name.") + val matchingProperty = properties[name] ?: - throw NotSerializableException("No property matching constructor parameter named '$name' of '$clazz'. " + - "If using Java, check that you have the -parameters option specified in the Java compiler. " + - "Alternately, provide a proxy serializer (SerializationCustomSerializer) if recompiling isn't an option") + try { + clazz.getDeclaredField(param.name) + throw NotSerializableException("Property '$name' or its getter is non public, this renders class '$clazz' unserializable") + } catch (e: NoSuchFieldException) { + throw NotSerializableException("No property matching constructor parameter named '$name' of '$clazz'. " + + "If using Java, check that you have the -parameters option specified in the Java compiler. " + + "Alternately, provide a proxy serializer (SerializationCustomSerializer) if recompiling isn't an option") + } + // Check that the method has a getter in java. val getter = matchingProperty.readMethod ?: throw NotSerializableException("Property has no getter method for $name of $clazz. " + "If using Java and the parameter name looks anonymous, check that you have the -parameters option specified in the Java compiler." + diff --git a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/ErrorMessageTests.java b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/ErrorMessageTests.java new file mode 100644 index 0000000000..5e98cf9b82 --- /dev/null +++ b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/ErrorMessageTests.java @@ -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 its 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())); + } + +} diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt index 47c2c8b9ef..af38597bfe 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt @@ -4,10 +4,7 @@ import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto.EDDSA_ED25519_SHA512 import net.corda.core.crypto.Crypto.generateKeyPair import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.cert import net.corda.core.internal.div -import net.corda.core.internal.toTypedArray -import net.corda.core.internal.x500Name import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize @@ -23,12 +20,9 @@ import net.corda.testing.BOB_NAME import net.corda.testing.TestIdentity import net.corda.testing.internal.createDevIntermediateCaCertPath import org.assertj.core.api.Assertions.assertThat -import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x509.BasicConstraints import org.bouncycastle.asn1.x509.Extension import org.bouncycastle.asn1.x509.KeyUsage -import org.bouncycastle.cert.X509CertificateHolder -import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder @@ -42,18 +36,16 @@ import java.security.SecureRandom import java.security.cert.CertPath import java.security.cert.X509Certificate import java.util.* -import java.util.stream.Stream import javax.net.ssl.* +import javax.security.auth.x500.X500Principal import kotlin.concurrent.thread import kotlin.test.* class X509UtilitiesTest { private companion object { val ALICE = TestIdentity(ALICE_NAME, 70).party - val bob = TestIdentity(BOB_NAME, 80) + val BOB = TestIdentity(BOB_NAME, 80) val MEGA_CORP = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).party - val BOB get() = bob.party - val BOB_PUBKEY get() = bob.publicKey val CIPHER_SUITES = arrayOf( "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", @@ -63,27 +55,30 @@ class X509UtilitiesTest { @Rule @JvmField - val tempFolder: TemporaryFolder = TemporaryFolder() + val tempFolder = TemporaryFolder() @Test fun `create valid self-signed CA certificate`() { val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val caCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Test Cert", organisation = "R3 Ltd", locality = "London", country = "GB"), caKey) - assertEquals(X500Name("CN=Test Cert,O=R3 Ltd,L=London,C=GB"), caCert.subject) // using our subject common name - assertEquals(caCert.issuer, caCert.subject) //self-signed - caCert.isValidOn(Date()) // throws on verification problems - caCert.isSignatureValid(JcaContentVerifierProviderBuilder().build(caKey.public)) // throws on verification problems - val basicConstraints = BasicConstraints.getInstance(caCert.getExtension(Extension.basicConstraints).parsedValue) - val keyUsage = KeyUsage.getInstance(caCert.getExtension(Extension.keyUsage).parsedValue) - assertFalse { keyUsage.hasUsages(5) } // Bit 5 == keyCertSign according to ASN.1 spec (see full comment on KeyUsage property) - assertNull(basicConstraints.pathLenConstraint) // No length constraint specified on this CA certificate + val subject = X500Principal("CN=Test Cert,O=R3 Ltd,L=London,C=GB") + val caCert = X509Utilities.createSelfSignedCACertificate(subject, caKey) + assertEquals(subject, caCert.subjectX500Principal) // using our subject common name + assertEquals(caCert.issuerX500Principal, caCert.subjectX500Principal) //self-signed + caCert.checkValidity(Date()) // throws on verification problems + caCert.verify(caKey.public) // throws on verification problems + caCert.toBc().run { + val basicConstraints = BasicConstraints.getInstance(getExtension(Extension.basicConstraints).parsedValue) + val keyUsage = KeyUsage.getInstance(getExtension(Extension.keyUsage).parsedValue) + assertFalse { keyUsage.hasUsages(5) } // Bit 5 == keyCertSign according to ASN.1 spec (see full comment on KeyUsage property) + assertNull(basicConstraints.pathLenConstraint) // No length constraint specified on this CA certificate + } } @Test fun `load and save a PEM file certificate`() { val tmpCertificateFile = tempFile("cacert.pem") val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val caCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Test Cert", organisation = "R3 Ltd", locality = "London", country = "GB"), caKey).cert + val caCert = X509Utilities.createSelfSignedCACertificate(X500Principal("CN=Test Cert,O=R3 Ltd,L=London,C=GB"), caKey) X509Utilities.saveCertificateAsPEMFile(caCert, tmpCertificateFile) val readCertificate = X509Utilities.loadCertificateFromPEMFile(tmpCertificateFile) assertEquals(caCert, readCertificate) @@ -92,18 +87,20 @@ class X509UtilitiesTest { @Test fun `create valid server certificate chain`() { val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val caCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Test CA Cert", organisation = "R3 Ltd", locality = "London", country = "GB"), caKey) - val subject = CordaX500Name(commonName = "Server Cert", organisation = "R3 Ltd", locality = "London", country = "GB") + val caCert = X509Utilities.createSelfSignedCACertificate(X500Principal("CN=Test CA Cert,O=R3 Ltd,L=London,C=GB"), caKey) + val subject = X500Principal("CN=Server Cert,O=R3 Ltd,L=London,C=GB") val keyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val serverCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, caKey, subject, keyPair.public) - assertEquals(X500Name("C=GB,L=London,O=R3 Ltd,CN=Server Cert"), serverCert.subject) // using our subject common name - assertEquals(caCert.issuer, serverCert.issuer) // Issued by our CA cert - serverCert.isValidOn(Date()) // throws on verification problems - serverCert.isSignatureValid(JcaContentVerifierProviderBuilder().build(caKey.public)) // throws on verification problems - val basicConstraints = BasicConstraints.getInstance(serverCert.getExtension(Extension.basicConstraints).parsedValue) - val keyUsage = KeyUsage.getInstance(serverCert.getExtension(Extension.keyUsage).parsedValue) - assertFalse { keyUsage.hasUsages(5) } // Bit 5 == keyCertSign according to ASN.1 spec (see full comment on KeyUsage property) - assertNull(basicConstraints.pathLenConstraint) // Non-CA certificate + assertEquals(subject, serverCert.subjectX500Principal) // using our subject common name + assertEquals(caCert.issuerX500Principal, serverCert.issuerX500Principal) // Issued by our CA cert + serverCert.checkValidity(Date()) // throws on verification problems + serverCert.verify(caKey.public) // throws on verification problems + serverCert.toBc().run { + val basicConstraints = BasicConstraints.getInstance(getExtension(Extension.basicConstraints).parsedValue) + val keyUsage = KeyUsage.getInstance(getExtension(Extension.keyUsage).parsedValue) + assertFalse { keyUsage.hasUsages(5) } // Bit 5 == keyCertSign according to ASN.1 spec (see full comment on KeyUsage property) + assertNull(basicConstraints.pathLenConstraint) // Non-CA certificate + } } @Test @@ -111,15 +108,14 @@ class X509UtilitiesTest { val tmpKeyStore = tempFile("keystore.jks") val keyPair = generateKeyPair(EDDSA_ED25519_SHA512) - val testName = CordaX500Name(commonName = "Test", organisation = "R3 Ltd", locality = "London", country = "GB") + val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB") val selfSignCert = X509Utilities.createSelfSignedCACertificate(testName, keyPair) - assertTrue(Arrays.equals(selfSignCert.subjectPublicKeyInfo.encoded, keyPair.public.encoded)) + assertTrue(Arrays.equals(selfSignCert.publicKey.encoded, keyPair.public.encoded)) // Save the EdDSA private key with self sign cert in the keystore. val keyStore = loadOrCreateKeyStore(tmpKeyStore, "keystorepass") - keyStore.setKeyEntry("Key", keyPair.private, "password".toCharArray(), - Stream.of(selfSignCert).map { it.cert }.toTypedArray()) + keyStore.setKeyEntry("Key", keyPair.private, "password".toCharArray(), arrayOf(selfSignCert)) keyStore.save(tmpKeyStore, "keystorepass") // Load the keystore from file and make sure keys are intact. @@ -137,15 +133,14 @@ class X509UtilitiesTest { fun `signing EdDSA key with EcDSA certificate`() { val tmpKeyStore = tempFile("keystore.jks") val ecDSAKey = generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256) - val testName = CordaX500Name(commonName = "Test", organisation = "R3 Ltd", locality = "London", country = "GB") + val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB") val ecDSACert = X509Utilities.createSelfSignedCACertificate(testName, ecDSAKey) val edDSAKeypair = generateKeyPair(EDDSA_ED25519_SHA512) - val edDSACert = X509Utilities.createCertificate(CertificateType.TLS, ecDSACert, ecDSAKey, X500Name("CN=TestEdDSA"), edDSAKeypair.public) + val edDSACert = X509Utilities.createCertificate(CertificateType.TLS, ecDSACert, ecDSAKey, BOB.name.x500Principal, edDSAKeypair.public) // Save the EdDSA private key with cert chains. val keyStore = loadOrCreateKeyStore(tmpKeyStore, "keystorepass") - keyStore.setKeyEntry("Key", edDSAKeypair.private, "password".toCharArray(), - Stream.of(ecDSACert, edDSACert).map { it.cert }.toTypedArray()) + keyStore.setKeyEntry("Key", edDSAKeypair.private, "password".toCharArray(), arrayOf(ecDSACert, edDSACert)) keyStore.save(tmpKeyStore, "keystorepass") // Load the keystore from file and make sure keys are intact. @@ -179,23 +174,22 @@ class X509UtilitiesTest { val serverKeyStore = loadKeyStore(sslConfig.nodeKeystore, sslConfig.keyStorePassword) val (serverCert, serverKeyPair) = serverKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, sslConfig.keyStorePassword) - serverCert.cert.checkValidity() - serverCert.cert.verify(intermediateCa.certificate.cert.publicKey) - assertThat(CordaX500Name.parse(serverCert.subject.toString())).isEqualTo(MEGA_CORP.name) + serverCert.checkValidity() + serverCert.verify(intermediateCa.certificate.publicKey) + assertThat(CordaX500Name.build(serverCert.subjectX500Principal)).isEqualTo(MEGA_CORP.name) // Load back SSL certificate val sslKeyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword) val (sslCert) = sslKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_TLS, sslConfig.keyStorePassword) - sslCert.cert.checkValidity() - sslCert.cert.verify(serverCert.cert.publicKey) - assertThat(CordaX500Name.parse(sslCert.subject.toString())).isEqualTo(MEGA_CORP.name) + sslCert.checkValidity() + sslCert.verify(serverCert.publicKey) + assertThat(CordaX500Name.build(sslCert.subjectX500Principal)).isEqualTo(MEGA_CORP.name) // Now sign something with private key and verify against certificate public key val testData = "123456".toByteArray() val signature = Crypto.doSign(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME, serverKeyPair.private, testData) - val publicKey = Crypto.toSupportedPublicKey(serverCert.subjectPublicKeyInfo) - assertTrue { Crypto.isValid(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME, publicKey, signature, testData) } + assertTrue { Crypto.isValid(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME, serverCert.publicKey, signature, testData) } } @Test @@ -210,7 +204,7 @@ class X509UtilitiesTest { // Generate server cert and private key and populate another keystore suitable for SSL sslConfig.createDevKeyStores(rootCa.certificate, intermediateCa, MEGA_CORP.name) - sslConfig.createTrustStore(rootCa.certificate.cert) + sslConfig.createTrustStore(rootCa.certificate) val keyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword) val trustStore = loadKeyStore(sslConfig.trustStoreFile, sslConfig.trustStorePassword) @@ -303,10 +297,10 @@ class X509UtilitiesTest { @Test fun `get correct private key type from Keystore`() { val keyPair = generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256) - val testName = CordaX500Name(commonName = "Test", organisation = "R3 Ltd", locality = "London", country = "GB") + val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB") val selfSignCert = X509Utilities.createSelfSignedCACertificate(testName, keyPair) val keyStore = loadOrCreateKeyStore(tempFile("testKeystore.jks"), "keystorepassword") - keyStore.setKeyEntry("Key", keyPair.private, "keypassword".toCharArray(), arrayOf(selfSignCert.cert)) + keyStore.setKeyEntry("Key", keyPair.private, "keypassword".toCharArray(), arrayOf(selfSignCert)) val keyFromKeystore = keyStore.getKey("Key", "keypassword".toCharArray()) val keyFromKeystoreCasted = keyStore.getSupportedKey("Key", "keypassword") @@ -316,7 +310,7 @@ class X509UtilitiesTest { } @Test - fun `serialize - deserialize X509CertififcateHolder`() { + fun `serialize - deserialize X509Certififcate`() { val factory = SerializationFactoryImpl().apply { registerScheme(KryoServerSerializationScheme()) } val context = SerializationContextImpl(KryoHeaderV0_1, javaClass.classLoader, @@ -324,9 +318,9 @@ class X509UtilitiesTest { emptyMap(), true, SerializationContext.UseCase.P2P) - val expected: X509CertificateHolder = X509Utilities.createSelfSignedCACertificate(ALICE.name, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) + val expected = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Principal, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) val serialized = expected.serialize(factory, context).bytes - val actual: X509CertificateHolder = serialized.deserialize(factory, context) + val actual = serialized.deserialize(factory, context) assertEquals(expected, actual) } @@ -340,9 +334,9 @@ class X509UtilitiesTest { true, SerializationContext.UseCase.P2P) val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val rootCACert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootCAKey) - val certificate = X509Utilities.createCertificate(CertificateType.TLS, rootCACert, rootCAKey, BOB.name.x500Name, BOB_PUBKEY) - val expected = X509CertificateFactory().generateCertPath(certificate.cert, rootCACert.cert) + val rootCACert = X509Utilities.createSelfSignedCACertificate(ALICE_NAME.x500Principal, rootCAKey) + val certificate = X509Utilities.createCertificate(CertificateType.TLS, rootCACert, rootCAKey, BOB_NAME.x500Principal, BOB.publicKey) + val expected = X509CertificateFactory().generateCertPath(certificate, rootCACert) val serialized = expected.serialize(factory, context).bytes val actual: CertPath = serialized.deserialize(factory, context) assertEquals(expected, actual) diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/ErrorMessagesTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/ErrorMessagesTests.kt new file mode 100644 index 0000000000..15f543da0d --- /dev/null +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/ErrorMessagesTests.kt @@ -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 its 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)) + } +} \ No newline at end of file diff --git a/node/src/integration-test/kotlin/net/corda/node/services/UserAuthServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt similarity index 61% rename from node/src/integration-test/kotlin/net/corda/node/services/UserAuthServiceTests.kt rename to node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt index de631de7a4..c05824f972 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/UserAuthServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt @@ -1,4 +1,4 @@ -package net.corda.node.services +package net.corda.node import co.paralleluniverse.fibers.Suspendable import net.corda.client.rpc.CordaRPCClient @@ -11,33 +11,94 @@ import net.corda.core.messaging.startFlow import net.corda.finance.flows.CashIssueFlow import net.corda.node.internal.Node import net.corda.node.internal.StartedNode -import net.corda.node.services.config.AuthDataSourceType +import net.corda.node.services.Permissions import net.corda.node.services.config.PasswordEncryption -import net.corda.node.services.config.SecurityConfiguration -import net.corda.nodeapi.internal.config.User -import net.corda.nodeapi.internal.config.toConfig import net.corda.testing.ALICE_NAME -import net.corda.testing.IntegrationTestSchemas +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import net.corda.testing.node.internal.NodeBasedTest -import net.corda.testing.toDatabaseSchemaName import org.apache.activemq.artemis.api.core.ActiveMQSecurityException +import org.apache.shiro.authc.credential.DefaultPasswordService import org.junit.After import org.junit.Before import org.junit.ClassRule import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized import java.sql.DriverManager import java.sql.Statement -import java.util.* import kotlin.test.assertFailsWith -abstract class UserAuthServiceTest : NodeBasedTest() { +/* + * Starts Node's instance configured to load clients credentials and permissions from an external DB, then + * check authentication/authorization of RPC connections. + */ +@RunWith(Parameterized::class) +class AuthDBTests : NodeBasedTest() { companion object { @ClassRule @JvmField val databaseSchemas = IntegrationTestSchemas(ALICE_NAME.toDatabaseSchemaName()) + + private val cacheExpireAfterSecs: Long = 1 + + @JvmStatic + @Parameterized.Parameters(name = "password encryption format = {0}") + fun encFormats() = arrayOf(PasswordEncryption.NONE, PasswordEncryption.SHIRO_1_CRYPT) } - protected lateinit var node: StartedNode - protected lateinit var client: CordaRPCClient + private lateinit var node: StartedNode + private lateinit var client: CordaRPCClient + private lateinit var db: UsersDB + + @Parameterized.Parameter + lateinit var passwordEncryption: PasswordEncryption + + @Before + fun setup() { + db = UsersDB( + name = "SecurityDataSourceTestDB", + users = listOf(UserAndRoles(username = "user", + password = encodePassword("foo", passwordEncryption), + roles = listOf("default"))), + roleAndPermissions = listOf( + RoleAndPermissions( + role = "default", + permissions = listOf( + Permissions.startFlow(), + Permissions.invokeRpc("vaultQueryBy"), + Permissions.invokeRpc(CordaRPCOps::stateMachinesFeed), + Permissions.invokeRpc("vaultQueryByCriteria"))), + RoleAndPermissions( + role = "admin", + permissions = listOf("ALL") + ))) + + val securityConfig = mapOf( + "security" to mapOf( + "authService" to mapOf( + "dataSource" to mapOf( + "type" to "DB", + "passwordEncryption" to passwordEncryption.toString(), + "connection" to mapOf( + "jdbcUrl" to db.jdbcUrl, + "username" to "", + "password" to "", + "driverClassName" to "org.h2.Driver" + ) + ) + ), + "options" to mapOf( + "cache" to mapOf( + "expireAfterSecs" to cacheExpireAfterSecs, + "maxEntries" to 50 + ) + ) + ) + ) + + node = startNode(ALICE_NAME, rpcUsers = emptyList(), configOverrides = securityConfig) + client = CordaRPCClient(node.internals.configuration.rpcAddress!!) + } @Test fun `login with correct credentials`() { @@ -65,7 +126,7 @@ abstract class UserAuthServiceTest : NodeBasedTest() { val proxy = it.proxy proxy.startFlowDynamic(DummyFlow::class.java) proxy.startTrackedFlowDynamic(DummyFlow::class.java) - proxy.startFlow(::DummyFlow) + proxy.startFlow(AuthDBTests::DummyFlow) assertFailsWith( PermissionException::class, "This user should not be authorized to start flow `CashIssueFlow`") { @@ -92,77 +153,8 @@ abstract class UserAuthServiceTest : NodeBasedTest() { } } - @StartableByRPC - @InitiatingFlow - class DummyFlow : FlowLogic() { - @Suspendable - override fun call() = Unit - } -} - -class UserAuthServiceEmbedded : UserAuthServiceTest() { - - private val rpcUser = User("user", "foo", permissions = setOf( - Permissions.startFlow(), - Permissions.invokeRpc("vaultQueryBy"), - Permissions.invokeRpc(CordaRPCOps::stateMachinesFeed), - Permissions.invokeRpc("vaultQueryByCriteria"))) - - @Before - fun setup() { - val securityConfig = SecurityConfiguration( - authService = SecurityConfiguration.AuthService.fromUsers(listOf(rpcUser))) - - val configOverrides = mapOf("security" to securityConfig.toConfig().root().unwrapped()) - node = startNode(ALICE_NAME, rpcUsers = emptyList(), configOverrides = configOverrides) - client = CordaRPCClient(node.internals.configuration.rpcAddress!!) - } -} - -class UserAuthServiceTestsJDBC : UserAuthServiceTest() { - - private val db = UsersDB( - name = "SecurityDataSourceTestDB", - users = listOf(UserAndRoles(username = "user", - password = "foo", - roles = listOf("default"))), - roleAndPermissions = listOf( - RoleAndPermissions( - role = "default", - permissions = listOf( - Permissions.startFlow(), - Permissions.invokeRpc("vaultQueryBy"), - Permissions.invokeRpc(CordaRPCOps::stateMachinesFeed), - Permissions.invokeRpc("vaultQueryByCriteria"))), - RoleAndPermissions( - role = "admin", - permissions = listOf("ALL") - ))) - - @Before - fun setup() { - val securityConfig = SecurityConfiguration( - authService = SecurityConfiguration.AuthService( - dataSource = SecurityConfiguration.AuthService.DataSource( - type = AuthDataSourceType.DB, - passwordEncryption = PasswordEncryption.NONE, - connection = Properties().apply { - setProperty("jdbcUrl", db.jdbcUrl) - setProperty("username", "") - setProperty("password", "") - setProperty("driverClassName", "org.h2.Driver") - } - ) - ) - ) - - val configOverrides = mapOf("security" to securityConfig.toConfig().root().unwrapped()) - node = startNode(ALICE_NAME, rpcUsers = emptyList(), configOverrides = configOverrides) - client = CordaRPCClient(node.internals.configuration.rpcAddress!!) - } - @Test - fun `Add new users on-the-fly`() { + fun `Add new users dynamically`() { assertFailsWith( ActiveMQSecurityException::class, "Login with incorrect password should fail") { @@ -171,7 +163,7 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() { db.insert(UserAndRoles( username = "user2", - password = "bar", + password = encodePassword("bar"), roles = listOf("default"))) client.start("user2", "bar") @@ -181,10 +173,9 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() { fun `Modify user permissions during RPC session`() { db.insert(UserAndRoles( username = "user3", - password = "bar", + password = encodePassword("bar"), roles = emptyList())) - client.start("user3", "bar").use { val proxy = it.proxy assertFailsWith( @@ -193,6 +184,7 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() { proxy.stateMachinesFeed() } db.addRoleToUser("user3", "default") + Thread.sleep(1500) proxy.stateMachinesFeed() } } @@ -201,13 +193,14 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() { fun `Revoke user permissions during RPC session`() { db.insert(UserAndRoles( username = "user4", - password = "test", + password = encodePassword("test"), roles = listOf("default"))) client.start("user4", "test").use { val proxy = it.proxy proxy.stateMachinesFeed() db.deleteUser("user4") + Thread.sleep(1500) assertFailsWith( PermissionException::class, "This user should not be authorized to call 'nodeInfo'") { @@ -216,15 +209,27 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() { } } + @StartableByRPC + @InitiatingFlow + class DummyFlow : FlowLogic() { + @Suspendable + override fun call() = Unit + } + @After override fun tearDown() { - db.close() + db.close() } + + private fun encodePassword(s: String) = encodePassword(s, passwordEncryption) } private data class UserAndRoles(val username: String, val password: String, val roles: List) private data class RoleAndPermissions(val role: String, val permissions: List) +/* + * Manage in-memory DB mocking a users database with the schema expected by Node's security manager + */ private class UsersDB : AutoCloseable { val jdbcUrl: String @@ -261,12 +266,6 @@ private class UsersDB : AutoCloseable { } } - fun deleteRole(role: String) { - session { - it.execute("DELETE FROM role_permissions WHERE role_name = '$role'") - } - } - fun deleteUser(username: String) { session { it.execute("DELETE FROM users WHERE username = '$username'") @@ -307,4 +306,22 @@ private class UsersDB : AutoCloseable { } } } -} \ No newline at end of file +} + +/* + * Sample of hardcoded hashes to watch for format backward compatibility + */ +private val hashedPasswords = mapOf( + PasswordEncryption.SHIRO_1_CRYPT to mapOf( + "foo" to "\$shiro1\$SHA-256$500000\$WSiEVj6q8d02sFcCk1dkoA==\$MBkU/ghdD9ovoDerdzNfkXdP9Bdhmok7tidvVIqGzcA=", + "bar" to "\$shiro1\$SHA-256$500000\$Q6dmdY1uVMm0LYAWaOHtCA==\$u7NbFaj9tHf2RTW54jedLPiOiGjJv0RVEPIjVquJuYY=", + "test" to "\$shiro1\$SHA-256$500000\$F6CWSFDDxGTlzvREwih8Gw==\$DQhyAPoUw3RdvNYJ1aubCnzEIXm+szGQ3HplaG+euz8=")) + +/* + * A functional object for producing password encoded according to the given scheme. + */ +private fun encodePassword(s: String, format: PasswordEncryption) = when (format) { + PasswordEncryption.NONE -> s + PasswordEncryption.SHIRO_1_CRYPT -> hashedPasswords[format]!![s] ?: + DefaultPasswordService().encryptPassword(s.toCharArray()) + } \ No newline at end of file diff --git a/node/src/integration-test/kotlin/net/corda/node/BootTests.kt b/node/src/integration-test/kotlin/net/corda/node/BootTests.kt index 6ad9235002..f32b573259 100644 --- a/node/src/integration-test/kotlin/net/corda/node/BootTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/BootTests.kt @@ -12,6 +12,9 @@ import net.corda.nodeapi.internal.config.User import net.corda.testing.* import net.corda.testing.common.internal.ProjectStructure.projectRootDir import net.corda.testing.driver.driver +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaNames import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.ClassRule @@ -24,7 +27,7 @@ class BootTests : IntegrationTest() { companion object { @ClassRule @JvmField val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, DUMMY_BANK_A_NAME) - .map { it.toDatabaseSchemaNames("", "_10000","_10003") }.flatten().toTypedArray()) + .map { it.toDatabaseSchemaNames("", "_10000", "_10003") }.flatten().toTypedArray()) } @Test diff --git a/node/src/integration-test/kotlin/net/corda/node/CordappScanningDriverTest.kt b/node/src/integration-test/kotlin/net/corda/node/CordappScanningDriverTest.kt index 99e08cea01..de34d975e2 100644 --- a/node/src/integration-test/kotlin/net/corda/node/CordappScanningDriverTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/CordappScanningDriverTest.kt @@ -11,6 +11,9 @@ import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.internal.config.User import net.corda.testing.* import net.corda.testing.driver.driver +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import org.assertj.core.api.Assertions.assertThat import org.junit.ClassRule import org.junit.Test diff --git a/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt b/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt index c8bd587e37..25e41028ad 100644 --- a/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt @@ -1,22 +1,21 @@ package net.corda.node import net.corda.core.crypto.Crypto -import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.cert import net.corda.core.internal.div import net.corda.core.utilities.getOrThrow import net.corda.node.services.config.configureDevKeyAndTrustStores import net.corda.nodeapi.internal.config.SSLConfiguration import net.corda.nodeapi.internal.crypto.* import net.corda.testing.ALICE_NAME -import net.corda.testing.IntegrationTest -import net.corda.testing.IntegrationTestSchemas +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.driver.driver -import net.corda.testing.toDatabaseSchemaName +import net.corda.testing.internal.toDatabaseSchemaName import org.junit.ClassRule import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Test import java.nio.file.Path +import javax.security.auth.x500.X500Principal class NodeKeystoreCheckTest : IntegrationTest() { companion object { @@ -59,10 +58,10 @@ class NodeKeystoreCheckTest : IntegrationTest() { // Self signed root val badRootKeyPair = Crypto.generateKeyPair() - val badRoot = X509Utilities.createSelfSignedCACertificate(CordaX500Name("Bad Root", "Lodnon", "GB"), badRootKeyPair) + val badRoot = X509Utilities.createSelfSignedCACertificate(X500Principal("O=Bad Root,L=Lodnon,C=GB"), badRootKeyPair) val nodeCA = keystore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, config.keyStorePassword) - val badNodeCACert = X509Utilities.createCertificate(CertificateType.NODE_CA, badRoot, badRootKeyPair, ALICE_NAME, nodeCA.keyPair.public) - keystore.setKeyEntry(X509Utilities.CORDA_CLIENT_CA, nodeCA.keyPair.private, config.keyStorePassword.toCharArray(), arrayOf(badNodeCACert.cert, badRoot.cert)) + val badNodeCACert = X509Utilities.createCertificate(CertificateType.NODE_CA, badRoot, badRootKeyPair, ALICE_NAME.x500Principal, nodeCA.keyPair.public) + keystore.setKeyEntry(X509Utilities.CORDA_CLIENT_CA, nodeCA.keyPair.private, config.keyStorePassword.toCharArray(), arrayOf(badNodeCACert, badRoot)) keystore.save(config.nodeKeystore, config.keyStorePassword) assertThatThrownBy { diff --git a/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt b/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt index 628bea91ed..8e7e8f547f 100644 --- a/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt @@ -18,7 +18,11 @@ import net.corda.testing.* import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.PortAllocation import net.corda.testing.driver.driver +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.internal.performance.div +import net.corda.testing.internal.toDatabaseSchemaName +import net.corda.testing.internal.toDatabaseSchemaNames import net.corda.testing.node.NotarySpec import net.corda.testing.node.internal.InternalDriverDSL import net.corda.testing.node.internal.performance.startPublishingFixedRateInjector diff --git a/node/src/integration-test/kotlin/net/corda/node/NodeStartupPerformanceTests.kt b/node/src/integration-test/kotlin/net/corda/node/NodeStartupPerformanceTests.kt index bcd5321713..3020c7f731 100644 --- a/node/src/integration-test/kotlin/net/corda/node/NodeStartupPerformanceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/NodeStartupPerformanceTests.kt @@ -3,6 +3,9 @@ package net.corda.node import com.google.common.base.Stopwatch import net.corda.testing.* import net.corda.testing.driver.driver +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import org.junit.ClassRule import org.junit.Ignore import org.junit.Test diff --git a/node/src/integration-test/kotlin/net/corda/node/SSHServerTest.kt b/node/src/integration-test/kotlin/net/corda/node/SSHServerTest.kt index bbfe09b159..3ec641e49f 100644 --- a/node/src/integration-test/kotlin/net/corda/node/SSHServerTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/SSHServerTest.kt @@ -14,13 +14,14 @@ import net.corda.core.utilities.unwrap import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.internal.config.User import net.corda.testing.ALICE_NAME -import net.corda.testing.IntegrationTest -import net.corda.testing.IntegrationTestSchemas import net.corda.testing.driver.driver -import net.corda.testing.toDatabaseSchemaName +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import org.assertj.core.api.Assertions.assertThat import org.bouncycastle.util.io.Streams import org.junit.ClassRule +import org.junit.Ignore import org.junit.Test import java.net.ConnectException import java.util.regex.Pattern @@ -33,6 +34,7 @@ class SSHServerTest : IntegrationTest() { val databaseSchemas = IntegrationTestSchemas(ALICE_NAME.toDatabaseSchemaName()) } + @Ignore("Test has undeterministic capacity to hang, ignore till fixed") @Test() fun `ssh server does not start be default`() { val user = User("u", "p", setOf()) @@ -54,6 +56,7 @@ class SSHServerTest : IntegrationTest() { } } + @Ignore("Test has undeterministic capacity to hang, ignore till fixed") @Test fun `ssh server starts when configured`() { val user = User("u", "p", setOf()) @@ -74,6 +77,7 @@ class SSHServerTest : IntegrationTest() { } + @Ignore("Test has undeterministic capacity to hang, ignore till fixed") @Test fun `ssh server verify credentials`() { val user = User("u", "p", setOf()) @@ -97,6 +101,7 @@ class SSHServerTest : IntegrationTest() { } } + @Ignore("Test has undeterministic capacity to hang, ignore till fixed") @Test fun `ssh respects permissions`() { val user = User("u", "p", setOf(startFlow())) @@ -127,6 +132,7 @@ class SSHServerTest : IntegrationTest() { } } + @Ignore("Test has undeterministic capacity to hang, ignore till fixed") @Test fun `ssh runs flows`() { val user = User("u", "p", setOf(startFlow())) diff --git a/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt b/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt index 871c7de400..88c299dd41 100644 --- a/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt @@ -59,10 +59,10 @@ class ProtonWrapperTests { amqpClient.start() val serverConnect = serverConnected.get() assertEquals(true, serverConnect.connected) - assertEquals(BOB_NAME, CordaX500Name.parse(serverConnect.remoteCert!!.subject.toString())) + assertEquals(BOB_NAME, CordaX500Name.build(serverConnect.remoteCert!!.subjectX500Principal)) val clientConnect = clientConnected.get() assertEquals(true, clientConnect.connected) - assertEquals(ALICE_NAME, CordaX500Name.parse(clientConnect.remoteCert!!.subject.toString())) + assertEquals(ALICE_NAME, CordaX500Name.build(clientConnect.remoteCert!!.subjectX500Principal)) val msg = amqpClient.createMessage("Test".toByteArray(), "p2p.inbound", ALICE_NAME.toString(), @@ -102,10 +102,10 @@ class ProtonWrapperTests { amqpClient.start() val serverConn1 = serverConnected.get() assertEquals(true, serverConn1.connected) - assertEquals(BOB_NAME, CordaX500Name.parse(serverConn1.remoteCert!!.subject.toString())) + assertEquals(BOB_NAME, CordaX500Name.build(serverConn1.remoteCert!!.subjectX500Principal)) val connState1 = clientConnected.next() assertEquals(true, connState1.connected) - assertEquals(ALICE_NAME, CordaX500Name.parse(connState1.remoteCert!!.subject.toString())) + assertEquals(ALICE_NAME, CordaX500Name.build(connState1.remoteCert!!.subjectX500Principal)) assertEquals(serverPort, connState1.remoteAddress.port) // Fail over @@ -116,10 +116,10 @@ class ProtonWrapperTests { assertEquals(serverPort, connState2.remoteAddress.port) val serverConn2 = serverConnected2.get() assertEquals(true, serverConn2.connected) - assertEquals(BOB_NAME, CordaX500Name.parse(serverConn2.remoteCert!!.subject.toString())) + assertEquals(BOB_NAME, CordaX500Name.build(serverConn2.remoteCert!!.subjectX500Principal)) val connState3 = clientConnected.next() assertEquals(true, connState3.connected) - assertEquals(ALICE_NAME, CordaX500Name.parse(connState3.remoteCert!!.subject.toString())) + assertEquals(ALICE_NAME, CordaX500Name.build(connState3.remoteCert!!.subjectX500Principal)) assertEquals(serverPort2, connState3.remoteAddress.port) // Fail back @@ -130,10 +130,10 @@ class ProtonWrapperTests { assertEquals(serverPort2, connState4.remoteAddress.port) val serverConn3 = serverConnected.get() assertEquals(true, serverConn3.connected) - assertEquals(BOB_NAME, CordaX500Name.parse(serverConn3.remoteCert!!.subject.toString())) + assertEquals(BOB_NAME, CordaX500Name.build(serverConn3.remoteCert!!.subjectX500Principal)) val connState5 = clientConnected.next() assertEquals(true, connState5.connected) - assertEquals(ALICE_NAME, CordaX500Name.parse(connState5.remoteCert!!.subject.toString())) + assertEquals(ALICE_NAME, CordaX500Name.build(connState5.remoteCert!!.subjectX500Principal)) assertEquals(serverPort, connState5.remoteAddress.port) } finally { amqpClient.close() @@ -149,7 +149,7 @@ class ProtonWrapperTests { val clientConnected = amqpClient.onConnection.toFuture() amqpClient.start() assertEquals(true, clientConnected.get().connected) - assertEquals(CHARLIE_NAME, CordaX500Name.parse(clientConnected.get().remoteCert!!.subject.toString())) + assertEquals(CHARLIE_NAME, CordaX500Name.build(clientConnected.get().remoteCert!!.subjectX500Principal)) val artemis = artemisClient.started!! val sendAddress = "p2p.inbound" artemis.session.createQueue(sendAddress, RoutingType.MULTICAST, "queue", true) @@ -180,13 +180,13 @@ class ProtonWrapperTests { amqpClient1.start() val connection1 = connectionEvents.next() assertEquals(true, connection1.connected) - val connection1ID = CordaX500Name.parse(connection1.remoteCert!!.subject.toString()) + val connection1ID = CordaX500Name.build(connection1.remoteCert!!.subjectX500Principal) assertEquals("client 0", connection1ID.organisationUnit) val source1 = connection1.remoteAddress amqpClient2.start() val connection2 = connectionEvents.next() assertEquals(true, connection2.connected) - val connection2ID = CordaX500Name.parse(connection2.remoteCert!!.subject.toString()) + val connection2ID = CordaX500Name.build(connection2.remoteCert!!.subjectX500Principal) assertEquals("client 1", connection2ID.organisationUnit) val source2 = connection2.remoteAddress // Stopping one shouldn't disconnect the other @@ -207,7 +207,7 @@ class ProtonWrapperTests { amqpClient1.start() val connection5 = connectionEvents.next() assertEquals(true, connection5.connected) - val connection5ID = CordaX500Name.parse(connection5.remoteCert!!.subject.toString()) + val connection5ID = CordaX500Name.build(connection5.remoteCert!!.subjectX500Principal) assertEquals("client 0", connection5ID.organisationUnit) assertEquals(true, amqpClient1.connected) assertEquals(false, amqpClient2.connected) @@ -252,7 +252,8 @@ class ProtonWrapperTests { val clientTruststore = loadKeyStore(clientConfig.trustStoreFile, clientConfig.trustStorePassword) val clientKeystore = loadKeyStore(clientConfig.sslKeystore, clientConfig.keyStorePassword) - val amqpClient = AMQPClient(listOf(NetworkHostAndPort("localhost", serverPort), + return AMQPClient( + listOf(NetworkHostAndPort("localhost", serverPort), NetworkHostAndPort("localhost", serverPort2), NetworkHostAndPort("localhost", artemisPort)), setOf(ALICE_NAME, CHARLIE_NAME), @@ -261,7 +262,6 @@ class ProtonWrapperTests { clientKeystore, clientConfig.keyStorePassword, clientTruststore, true) - return amqpClient } private fun createSharedThreadsClient(sharedEventGroup: EventLoopGroup, id: Int): AMQPClient { @@ -275,14 +275,14 @@ class ProtonWrapperTests { val clientTruststore = loadKeyStore(clientConfig.trustStoreFile, clientConfig.trustStorePassword) val clientKeystore = loadKeyStore(clientConfig.sslKeystore, clientConfig.keyStorePassword) - val amqpClient = AMQPClient(listOf(NetworkHostAndPort("localhost", serverPort)), + return AMQPClient( + listOf(NetworkHostAndPort("localhost", serverPort)), setOf(ALICE_NAME), PEER_USER, PEER_USER, clientKeystore, clientConfig.keyStorePassword, clientTruststore, true, sharedEventGroup) - return amqpClient } private fun createServer(port: Int, name: CordaX500Name = ALICE_NAME): AMQPServer { @@ -296,14 +296,13 @@ class ProtonWrapperTests { val serverTruststore = loadKeyStore(serverConfig.trustStoreFile, serverConfig.trustStorePassword) val serverKeystore = loadKeyStore(serverConfig.sslKeystore, serverConfig.keyStorePassword) - val amqpServer = AMQPServer("0.0.0.0", + return AMQPServer( + "0.0.0.0", port, PEER_USER, PEER_USER, serverKeystore, serverConfig.keyStorePassword, serverTruststore) - return amqpServer } - -} \ No newline at end of file +} diff --git a/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt index cd6c10c3eb..a9d190355a 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt @@ -24,8 +24,7 @@ import net.corda.testing.* import net.corda.testing.driver.DriverDSL import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver -import net.corda.testing.internal.rigorousMock -import net.corda.testing.internal.withoutTestSerialization +import net.corda.testing.internal.* import net.corda.testing.services.MockAttachmentStorage import org.junit.Assert.assertEquals import org.junit.ClassRule diff --git a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt index ea05e0c223..00842db789 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt @@ -26,8 +26,8 @@ import net.corda.node.services.transactions.minCorrectReplicas import net.corda.nodeapi.internal.DevIdentityGenerator import net.corda.nodeapi.internal.network.NetworkParametersCopier import net.corda.nodeapi.internal.network.NotaryInfo -import net.corda.testing.IntegrationTest -import net.corda.testing.IntegrationTestSchemas +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.chooseIdentity import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.contracts.DummyContract diff --git a/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt index aa62f67cfe..478fcfc96d 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt @@ -11,13 +11,16 @@ import net.corda.core.utilities.getOrThrow import net.corda.finance.POUNDS import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow -import net.corda.finance.schemas.CashSchemaV1 import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.internal.config.User import net.corda.testing.* import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName +import net.corda.testing.internal.toDatabaseSchemaNames import net.corda.testing.node.ClusterSpec import net.corda.testing.node.NotarySpec import org.assertj.core.api.Assertions.assertThat diff --git a/node/src/integration-test/kotlin/net/corda/node/services/MySQLNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/MySQLNotaryServiceTests.kt index 832514e770..a7fd1c2ca5 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/MySQLNotaryServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/MySQLNotaryServiceTests.kt @@ -20,6 +20,8 @@ import net.corda.nodeapi.internal.network.NotaryInfo import net.corda.testing.* import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.contracts.DummyContract +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNodeParameters import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties diff --git a/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt index 8110118362..ecd44ade27 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt @@ -15,6 +15,9 @@ import net.corda.testing.* import net.corda.testing.contracts.DummyContract import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import net.corda.testing.node.ClusterSpec import net.corda.testing.node.NotarySpec import net.corda.testing.node.startFlow @@ -27,7 +30,7 @@ import kotlin.test.assertFailsWith class RaftNotaryServiceTests : IntegrationTest() { companion object { @ClassRule @JvmField - val databaseSchemas = IntegrationTestSchemas( "RAFTNotaryService_0", "RAFTNotaryService_1", "RAFTNotaryService_2", + val databaseSchemas = IntegrationTestSchemas("RAFTNotaryService_0", "RAFTNotaryService_1", "RAFTNotaryService_2", DUMMY_BANK_A_NAME.toDatabaseSchemaName()) } private val notaryName = CordaX500Name("RAFT Notary Service", "London", "GB") diff --git a/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt index 09dff74761..93d7b363eb 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt @@ -21,6 +21,9 @@ import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.PortAllocation +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import net.corda.testing.node.internal.CompatibilityZoneParams import net.corda.testing.node.internal.internalDriver import net.corda.testing.node.internal.network.NetworkMapServer diff --git a/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt index bf44322ee0..7b82b44b22 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt @@ -7,6 +7,8 @@ import net.corda.core.utilities.NetworkHostAndPort import net.corda.node.internal.Node import net.corda.node.internal.StartedNode import net.corda.testing.* +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import net.corda.testing.node.internal.NodeBasedTest import org.junit.Before import org.junit.ClassRule diff --git a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt index bcb2aefb9b..934645ba84 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt @@ -8,6 +8,8 @@ import net.corda.core.identity.Party import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.unwrap import net.corda.testing.* +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import net.corda.testing.node.internal.NodeBasedTest import net.corda.testing.node.startFlow import org.assertj.core.api.Assertions.assertThat diff --git a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt index 4ebed07cee..156fd71f0d 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt @@ -13,6 +13,9 @@ import net.corda.testing.* import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyState import net.corda.testing.driver.driver +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import org.junit.ClassRule import org.junit.Test import kotlin.test.assertEquals diff --git a/node/src/integration-test/kotlin/net/corda/node/services/vault/VaultQueryIntegrationTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/vault/VaultQueryIntegrationTests.kt index 0543085978..641c49a5af 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/vault/VaultQueryIntegrationTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/vault/VaultQueryIntegrationTests.kt @@ -3,6 +3,9 @@ package net.corda.node.services.vault import net.corda.core.identity.CordaX500Name import net.corda.testing.* +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import org.junit.* class VaultQueryIntegrationTests : VaultQueryTests() { diff --git a/node/src/integration-test/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt b/node/src/integration-test/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt index c3af62c480..7ac559b8d3 100644 --- a/node/src/integration-test/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt @@ -2,9 +2,7 @@ package net.corda.node.utilities.registration import net.corda.core.crypto.Crypto import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.cert import net.corda.core.internal.concurrent.transpose -import net.corda.core.internal.toX509CertHolder import net.corda.core.messaging.startFlow import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.OpaqueBytes @@ -22,6 +20,8 @@ import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA import net.corda.testing.* import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.driver.PortAllocation +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.node.NotarySpec import net.corda.testing.node.internal.CompatibilityZoneParams import net.corda.testing.node.internal.internalDriver @@ -38,8 +38,10 @@ import java.security.KeyPair import java.security.cert.CertPath import java.security.cert.CertPathValidatorException import java.security.cert.Certificate +import java.security.cert.X509Certificate import java.util.zip.ZipEntry import java.util.zip.ZipOutputStream +import javax.security.auth.x500.X500Principal import javax.ws.rs.* import javax.ws.rs.core.MediaType import javax.ws.rs.core.Response @@ -59,14 +61,13 @@ class NodeRegistrationTest : IntegrationTest() { val testSerialization = SerializationEnvironmentRule(true) private val portAllocation = PortAllocation.Incremental(13000) - private val registrationHandler = RegistrationHandler(ROOT_CA) - + private val registrationHandler = RegistrationHandler(DEV_ROOT_CA) private lateinit var server: NetworkMapServer private lateinit var serverHostAndPort: NetworkHostAndPort @Before fun startServer() { - server = NetworkMapServer(1.minutes, portAllocation.nextHostAndPort(), ROOT_CA, "localhost", registrationHandler) + server = NetworkMapServer(1.minutes, portAllocation.nextHostAndPort(), DEV_ROOT_CA, "localhost", registrationHandler) serverHostAndPort = server.start() } @@ -80,7 +81,7 @@ class NodeRegistrationTest : IntegrationTest() { val compatibilityZone = CompatibilityZoneParams( URL("http://$serverHostAndPort"), publishNotaries = { server.networkParameters = testNetworkParameters(it) }, - rootCert = ROOT_CA.certificate.cert) + rootCert = DEV_ROOT_CA.certificate) internalDriver( portAllocation = portAllocation, compatibilityZone = compatibilityZone, @@ -116,12 +117,12 @@ class NodeRegistrationTest : IntegrationTest() { @Test fun `node registration wrong root cert`() { val someRootCert = X509Utilities.createSelfSignedCACertificate( - CordaX500Name("Integration Test Corda Node Root CA", "R3 Ltd", "London", "GB"), + X500Principal("CN=Integration Test Corda Node Root CA,O=R3 Ltd,L=London,C=GB"), Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) val compatibilityZone = CompatibilityZoneParams( URL("http://$serverHostAndPort"), publishNotaries = { server.networkParameters = testNetworkParameters(it) }, - rootCert = someRootCert.cert) + rootCert = someRootCert) internalDriver( portAllocation = portAllocation, compatibilityZone = compatibilityZone, @@ -149,7 +150,7 @@ class RegistrationHandler(private val rootCertAndKeyPair: CertificateAndKeyPair) val (certPath, name) = createSignedClientCertificate( certificationRequest, rootCertAndKeyPair.keyPair, - arrayOf(rootCertAndKeyPair.certificate.cert)) + arrayOf(rootCertAndKeyPair.certificate)) require(!name.organisation.contains("\\s".toRegex())) { "Whitespace in the organisation name not supported" } certPaths[name.organisation] = certPath return Response.ok(name.organisation).build() @@ -183,12 +184,12 @@ class RegistrationHandler(private val rootCertAndKeyPair: CertificateAndKeyPair) val name = CordaX500Name.parse(request.subject.toString()) val nodeCaCert = X509Utilities.createCertificate( CertificateType.NODE_CA, - caCertPath.first().toX509CertHolder(), + caCertPath[0] as X509Certificate , caKeyPair, - name, + name.x500Principal, request.publicKey, nameConstraints = null) - val certPath = X509CertificateFactory().generateCertPath(nodeCaCert.cert, *caCertPath) + val certPath = X509CertificateFactory().generateCertPath(nodeCaCert, *caCertPath) return Pair(certPath, name) } } diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt index f8da5302a4..8252173ca0 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt @@ -94,22 +94,34 @@ class MQSecurityAsNodeTest : MQSecurityTest() { javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass") - val rootCACert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA).toX509CertHolder() + val rootCACert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA) val intermediateCA = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass") - val clientKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) + val clientKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) // Set name constrain to the legal name. val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.x500Name))), arrayOf()) - val clientCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, intermediateCA.certificate, - intermediateCA.keyPair, legalName, clientKey.public, nameConstraints = nameConstraints) - val tlsKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) + val clientCACert = X509Utilities.createCertificate( + CertificateType.INTERMEDIATE_CA, + intermediateCA.certificate, + intermediateCA.keyPair, + legalName.x500Principal, + clientKeyPair.public, + nameConstraints = nameConstraints) + + val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) // Using different x500 name in the TLS cert which is not allowed in the name constraints. - val clientTLSCert = X509Utilities.createCertificate(CertificateType.TLS, clientCACert, clientKey, CordaX500Name("MiniCorp", "London", "GB"), tlsKey.public) + val clientTLSCert = X509Utilities.createCertificate( + CertificateType.TLS, + clientCACert, + clientKeyPair, + CordaX500Name("MiniCorp", "London", "GB").x500Principal, + tlsKeyPair.public) + val keyPass = keyStorePassword.toCharArray() val clientCAKeystore = loadOrCreateKeyStore(nodeKeystore, keyStorePassword) clientCAKeystore.addOrReplaceKey( X509Utilities.CORDA_CLIENT_CA, - clientKey.private, + clientKeyPair.private, keyPass, arrayOf(clientCACert, intermediateCA.certificate, rootCACert)) clientCAKeystore.save(nodeKeystore, keyStorePassword) @@ -117,7 +129,7 @@ class MQSecurityAsNodeTest : MQSecurityTest() { val tlsKeystore = loadOrCreateKeyStore(sslKeystore, keyStorePassword) tlsKeystore.addOrReplaceKey( X509Utilities.CORDA_CLIENT_TLS, - tlsKey.private, + tlsKeyPair.private, keyPass, arrayOf(clientTLSCert, clientCACert, intermediateCA.certificate, rootCACert)) tlsKeystore.save(sslKeystore, keyStorePassword) diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt index 5f6d32e69d..201b9fa506 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt @@ -26,7 +26,9 @@ import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEERS_PREF import net.corda.nodeapi.internal.config.SSLConfiguration import net.corda.nodeapi.internal.config.User import net.corda.testing.* +import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.internal.configureTestSSL +import net.corda.testing.internal.toDatabaseSchemaName import net.corda.testing.node.internal.NodeBasedTest import net.corda.testing.node.startFlow import org.apache.activemq.artemis.api.core.ActiveMQNonExistentQueueException diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt index 189506d02b..b6bf5c02b7 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt @@ -17,14 +17,14 @@ import net.corda.node.internal.StartedNode import net.corda.node.services.messaging.MessagingService import net.corda.node.services.messaging.ReceivedMessage import net.corda.node.services.messaging.send -import net.corda.node.services.transactions.RaftValidatingNotaryService -import net.corda.testing.* -import net.corda.node.services.messaging.* import net.corda.testing.ALICE_NAME import net.corda.testing.chooseIdentity import net.corda.testing.driver.DriverDSL import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import net.corda.testing.node.ClusterSpec import net.corda.testing.node.NotarySpec import org.assertj.core.api.Assertions.assertThat diff --git a/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt b/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt index 3d072aaad3..3021f4a699 100644 --- a/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt @@ -22,6 +22,10 @@ import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.internal.config.User import net.corda.testing.* import net.corda.testing.driver.driver +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName +import net.corda.testing.internal.toDatabaseSchemaNames import org.junit.Assume.assumeFalse import org.junit.ClassRule import org.junit.Test @@ -36,7 +40,7 @@ class NodeStatePersistenceTests : IntegrationTest() { companion object { @ClassRule @JvmField val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, DUMMY_BANK_A_NAME) - .map { it.toDatabaseSchemaNames("", "_10000","_10003","_10006") }.flatten().toTypedArray(), + .map { it.toDatabaseSchemaNames("", "_10000", "_10003", "_10006") }.flatten().toTypedArray(), DUMMY_NOTARY_NAME.toDatabaseSchemaName()) } @Test diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index aaffb40be2..4ab8030d09 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -726,7 +726,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, val caKeyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword) val trustRoot = trustStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA) val clientCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA) - val caCertificates = arrayOf(identityCert, clientCa.certificate.cert) + val caCertificates = arrayOf(identityCert, clientCa.certificate) return PersistentIdentityService(trustRoot, *caCertificates) } @@ -784,7 +784,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, listOf(certificate) + keyStore.getCertificateChain(privateKeyAlias).drop(1) } else { keyStore.getCertificateChain(privateKeyAlias).let { - check(it[0].toX509CertHolder() == x509Cert) { "Certificates from key store do not line up!" } + check(it[0] == x509Cert) { "Certificates from key store do not line up!" } it.asList() } } diff --git a/node/src/main/kotlin/net/corda/node/internal/protonwrapper/netty/AMQPChannelHandler.kt b/node/src/main/kotlin/net/corda/node/internal/protonwrapper/netty/AMQPChannelHandler.kt index 0f9e6cd607..f31f9fbe88 100644 --- a/node/src/main/kotlin/net/corda/node/internal/protonwrapper/netty/AMQPChannelHandler.kt +++ b/node/src/main/kotlin/net/corda/node/internal/protonwrapper/netty/AMQPChannelHandler.kt @@ -9,7 +9,6 @@ import io.netty.handler.ssl.SslHandler import io.netty.handler.ssl.SslHandshakeCompletionEvent import io.netty.util.ReferenceCountUtil import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.toX509CertHolder import net.corda.core.utilities.debug import net.corda.node.internal.protonwrapper.engine.EventProcessor import net.corda.node.internal.protonwrapper.messages.ReceivedMessage @@ -19,9 +18,9 @@ import org.apache.qpid.proton.engine.ProtonJTransport import org.apache.qpid.proton.engine.Transport import org.apache.qpid.proton.engine.impl.ProtocolTracer import org.apache.qpid.proton.framing.TransportFrame -import org.bouncycastle.cert.X509CertificateHolder import org.slf4j.LoggerFactory import java.net.InetSocketAddress +import java.security.cert.X509Certificate /** * An instance of AMQPChannelHandler sits inside the netty pipeline and controls the socket level lifecycle. @@ -38,20 +37,20 @@ internal class AMQPChannelHandler(private val serverMode: Boolean, private val onReceive: (ReceivedMessage) -> Unit) : ChannelDuplexHandler() { private val log = LoggerFactory.getLogger(allowedRemoteLegalNames?.firstOrNull()?.toString() ?: "AMQPChannelHandler") private lateinit var remoteAddress: InetSocketAddress - private lateinit var localCert: X509CertificateHolder - private lateinit var remoteCert: X509CertificateHolder + private lateinit var localCert: X509Certificate + private lateinit var remoteCert: X509Certificate private var eventProcessor: EventProcessor? = null override fun channelActive(ctx: ChannelHandlerContext) { val ch = ctx.channel() remoteAddress = ch.remoteAddress() as InetSocketAddress val localAddress = ch.localAddress() as InetSocketAddress - log.info("New client connection ${ch.id()} from ${remoteAddress} to ${localAddress}") + log.info("New client connection ${ch.id()} from $remoteAddress to $localAddress") } private fun createAMQPEngine(ctx: ChannelHandlerContext) { val ch = ctx.channel() - eventProcessor = EventProcessor(ch, serverMode, localCert.subject.toString(), remoteCert.subject.toString(), userName, password) + eventProcessor = EventProcessor(ch, serverMode, localCert.subjectX500Principal.toString(), remoteCert.subjectX500Principal.toString(), userName, password) val connection = eventProcessor!!.connection val transport = connection.transport as ProtonJTransport if (trace) { @@ -71,7 +70,7 @@ internal class AMQPChannelHandler(private val serverMode: Boolean, override fun channelInactive(ctx: ChannelHandlerContext) { val ch = ctx.channel() - log.info("Closed client connection ${ch.id()} from ${remoteAddress} to ${ch.localAddress()}") + log.info("Closed client connection ${ch.id()} from $remoteAddress to ${ch.localAddress()}") onClose(Pair(ch as SocketChannel, ConnectionChange(remoteAddress, null, false))) eventProcessor?.close() ctx.fireChannelInactive() @@ -81,12 +80,12 @@ internal class AMQPChannelHandler(private val serverMode: Boolean, if (evt is SslHandshakeCompletionEvent) { if (evt.isSuccess) { val sslHandler = ctx.pipeline().get(SslHandler::class.java) - localCert = sslHandler.engine().session.localCertificates.first().toX509CertHolder() - remoteCert = sslHandler.engine().session.peerCertificates.first().toX509CertHolder() + localCert = sslHandler.engine().session.localCertificates[0] as X509Certificate + remoteCert = sslHandler.engine().session.peerCertificates[0] as X509Certificate try { - val remoteX500Name = CordaX500Name.parse(remoteCert.subject.toString()) + val remoteX500Name = CordaX500Name.build(remoteCert.subjectX500Principal) require(allowedRemoteLegalNames == null || remoteX500Name in allowedRemoteLegalNames) - log.info("handshake completed subject: ${remoteX500Name}") + log.info("handshake completed subject: $remoteX500Name") } catch (ex: IllegalArgumentException) { log.error("Invalid certificate subject", ex) ctx.close() @@ -124,7 +123,7 @@ internal class AMQPChannelHandler(private val serverMode: Boolean, require(inetAddress == remoteAddress) { "Message for incorrect endpoint" } - require(CordaX500Name.parse(msg.destinationLegalName) == CordaX500Name.parse(remoteCert.subject.toString())) { + require(CordaX500Name.parse(msg.destinationLegalName) == CordaX500Name.build(remoteCert.subjectX500Principal)) { "Message for incorrect legal identity" } log.debug { "channel write ${msg.applicationProperties["_AMQ_DUPL_ID"]}" } diff --git a/node/src/main/kotlin/net/corda/node/internal/protonwrapper/netty/ConnectionChange.kt b/node/src/main/kotlin/net/corda/node/internal/protonwrapper/netty/ConnectionChange.kt index a576a25d2b..f0d83a8cf5 100644 --- a/node/src/main/kotlin/net/corda/node/internal/protonwrapper/netty/ConnectionChange.kt +++ b/node/src/main/kotlin/net/corda/node/internal/protonwrapper/netty/ConnectionChange.kt @@ -1,6 +1,6 @@ package net.corda.node.internal.protonwrapper.netty -import org.bouncycastle.cert.X509CertificateHolder import java.net.InetSocketAddress +import java.security.cert.X509Certificate -data class ConnectionChange(val remoteAddress: InetSocketAddress, val remoteCert: X509CertificateHolder?, val connected: Boolean) \ No newline at end of file +data class ConnectionChange(val remoteAddress: InetSocketAddress, val remoteCert: X509Certificate?, val connected: Boolean) \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt index 3e31a977ae..c3942b3fc9 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt @@ -11,7 +11,6 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.internal.createDirectories import net.corda.core.internal.div import net.corda.core.internal.exists -import net.corda.core.internal.toX509CertHolder import net.corda.nodeapi.internal.config.SSLConfiguration import net.corda.nodeapi.internal.config.toProperties import net.corda.nodeapi.internal.createDevKeyStores @@ -73,7 +72,7 @@ fun SSLConfiguration.configureDevKeyAndTrustStores(myLegalName: CordaX500Name) { } if (!sslKeystore.exists() || !nodeKeystore.exists()) { val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass") - val rootCert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA).toX509CertHolder() + val rootCert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA) val intermediateCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass") createDevKeyStores(rootCert, intermediateCa, myLegalName) diff --git a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt index 315598c00b..d843193f5c 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt @@ -203,8 +203,16 @@ data class SecurityConfiguration(val authService: SecurityConfiguration.AuthServ data class Options(val cache: Options.Cache?) { // Cache parameters - data class Cache(val expireAfterSecs: Long, val maxEntries: Long) - + data class Cache(val expireAfterSecs: Long, val maxEntries: Long) { + init { + require(expireAfterSecs >= 0) { + "Expected positive value for 'cache.expireAfterSecs'" + } + require(maxEntries > 0) { + "Expected positive value for 'cache.maxEntries'" + } + } + } } // Provider of users credentials and permissions data @@ -228,12 +236,13 @@ data class SecurityConfiguration(val authService: SecurityConfiguration.AuthServ AuthDataSourceType.DB -> AuthServiceId("REMOTE_DATABASE") } - fun fromUsers(users: List) = AuthService( - dataSource = DataSource( - type = AuthDataSourceType.INMEMORY, - users = users, - passwordEncryption = PasswordEncryption.NONE), - id = AuthServiceId("NODE_CONFIG")) + fun fromUsers(users: List, encryption: PasswordEncryption = PasswordEncryption.NONE) = + AuthService( + dataSource = DataSource( + type = AuthDataSourceType.INMEMORY, + users = users, + passwordEncryption = encryption), + id = AuthServiceId("NODE_CONFIG")) } } } diff --git a/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt b/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt index 4654473ef1..4876533003 100644 --- a/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt +++ b/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt @@ -4,15 +4,12 @@ import net.corda.core.contracts.PartyAndReference import net.corda.core.crypto.toStringShort import net.corda.core.identity.* import net.corda.core.internal.CertRole -import net.corda.core.internal.cert -import net.corda.core.internal.toX509CertHolder import net.corda.core.node.services.UnknownAnonymousPartyException import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.utilities.contextLogger import net.corda.core.utilities.trace import net.corda.node.services.api.IdentityServiceInternal import net.corda.nodeapi.internal.crypto.X509CertificateFactory -import org.bouncycastle.cert.X509CertificateHolder import java.security.InvalidAlgorithmParameterException import java.security.PublicKey import java.security.cert.* @@ -27,7 +24,7 @@ import javax.annotation.concurrent.ThreadSafe // TODO There is duplicated logic between this and PersistentIdentityService @ThreadSafe class InMemoryIdentityService(identities: Array, - trustRoot: X509CertificateHolder) : SingletonSerializeAsToken(), IdentityServiceInternal { + override val trustRoot: X509Certificate) : SingletonSerializeAsToken(), IdentityServiceInternal { companion object { private val log = contextLogger() } @@ -35,14 +32,12 @@ class InMemoryIdentityService(identities: Array, /** * Certificate store for certificate authority and intermediary certificates. */ - override val caCertStore: CertStore - override val trustRoot = trustRoot.cert - override val trustAnchor: TrustAnchor = TrustAnchor(this.trustRoot, null) + override val caCertStore: CertStore = CertStore.getInstance("Collection", CollectionCertStoreParameters(setOf(trustRoot))) + override val trustAnchor: TrustAnchor = TrustAnchor(trustRoot, null) private val keyToParties = ConcurrentHashMap() private val principalToParties = ConcurrentHashMap() init { - caCertStore = CertStore.getInstance("Collection", CollectionCertStoreParameters(setOf(this.trustRoot))) keyToParties.putAll(identities.associateBy { it.owningKey }) principalToParties.putAll(identities.associateBy { it.name }) } @@ -57,7 +52,7 @@ class InMemoryIdentityService(identities: Array, log.warn("Certificate path :") identity.certPath.certificates.reversed().forEachIndexed { index, certificate -> val space = (0 until index).joinToString("") { " " } - log.warn("$space${certificate.toX509CertHolder().subject}") + log.warn("$space${(certificate as X509Certificate).subjectX500Principal}") } throw e } diff --git a/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt b/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt index 83e7b0b267..b36072fd81 100644 --- a/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt +++ b/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt @@ -5,8 +5,6 @@ import net.corda.core.crypto.SecureHash import net.corda.core.crypto.toStringShort import net.corda.core.identity.* import net.corda.core.internal.CertRole -import net.corda.core.internal.cert -import net.corda.core.internal.toX509CertHolder import net.corda.core.node.services.UnknownAnonymousPartyException import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.utilities.MAX_HASH_HEX_SIZE @@ -16,7 +14,6 @@ import net.corda.node.services.api.IdentityServiceInternal import net.corda.node.utilities.AppendOnlyPersistentMap import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX -import org.bouncycastle.cert.X509CertificateHolder import java.security.InvalidAlgorithmParameterException import java.security.PublicKey import java.security.cert.* @@ -30,7 +27,6 @@ import javax.persistence.Lob @ThreadSafe class PersistentIdentityService(override val trustRoot: X509Certificate, vararg caCertificates: X509Certificate) : SingletonSerializeAsToken(), IdentityServiceInternal { - constructor(trustRoot: X509CertificateHolder) : this(trustRoot.cert) companion object { private val log = contextLogger() @@ -121,7 +117,7 @@ class PersistentIdentityService(override val trustRoot: X509Certificate, log.warn(e.localizedMessage) log.warn("Path = ") identity.certPath.certificates.reversed().forEach { - log.warn(it.toX509CertHolder().subject.toString()) + log.warn((it as X509Certificate).subjectX500Principal.toString()) } throw e } diff --git a/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt b/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt index df99435dd3..dad6a7b2a7 100644 --- a/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt +++ b/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt @@ -3,8 +3,6 @@ package net.corda.node.services.keys import net.corda.core.crypto.Crypto import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.CertRole -import net.corda.core.internal.cert -import net.corda.core.internal.toX509CertHolder import net.corda.core.utilities.days import net.corda.node.services.api.IdentityServiceInternal import net.corda.nodeapi.internal.crypto.CertificateType @@ -36,11 +34,16 @@ fun freshCertificate(identityService: IdentityServiceInternal, revocationEnabled: Boolean = false): PartyAndCertificate { val issuerRole = CertRole.extract(issuer.certificate) require(issuerRole == CertRole.LEGAL_IDENTITY) { "Confidential identities can only be issued from well known identities, provided issuer ${issuer.name} has role $issuerRole" } - val issuerCert = issuer.certificate.toX509CertHolder() + val issuerCert = issuer.certificate val window = X509Utilities.getCertificateValidityWindow(Duration.ZERO, 3650.days, issuerCert) - val ourCertificate = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuerCert.subject, - issuerSigner, issuer.name, subjectPublicKey, window) - val ourCertPath = X509CertificateFactory().generateCertPath(listOf(ourCertificate.cert) + issuer.certPath.certificates) + val ourCertificate = X509Utilities.createCertificate( + CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, + issuerCert.subjectX500Principal, + issuerSigner, + issuer.name.x500Principal, + subjectPublicKey, + window) + val ourCertPath = X509CertificateFactory().generateCertPath(listOf(ourCertificate) + issuer.certPath.certificates) val anonymisedIdentity = PartyAndCertificate(ourCertPath) identityService.justVerifyAndRegisterIdentity(anonymisedIdentity) return anonymisedIdentity diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowLogicRefFactoryImpl.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowLogicRefFactoryImpl.kt index 1b20e75c5b..8d1aba86c6 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowLogicRefFactoryImpl.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowLogicRefFactoryImpl.kt @@ -40,6 +40,18 @@ class FlowLogicRefFactoryImpl(private val classloader: ClassLoader) : SingletonS return createForRPC(flowClass, *args) } + override fun create(flowClassName: String, vararg args: Any?): FlowLogicRef { + val flowClass = Class.forName(flowClassName, true, classloader).asSubclass(FlowLogic::class.java) + if (flowClass == null) { + throw IllegalArgumentException("The class $flowClassName is not a subclass of FlowLogic.") + } else { + if (!flowClass.isAnnotationPresent(SchedulableFlow::class.java)) { + throw IllegalFlowLogicException(flowClass, "because it's not a schedulable flow") + } + return createForRPC(flowClass, *args) + } + } + override fun createForRPC(flowClass: Class>, vararg args: Any?): FlowLogicRef { // TODO: This is used via RPC but it's probably better if we pass in argument names and values explicitly // to avoid requiring only a single constructor. diff --git a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt index af9735b89a..090eef335c 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt @@ -22,9 +22,9 @@ import java.security.cert.X509Certificate * needed. */ class NetworkRegistrationHelper(private val config: NodeConfiguration, private val certService: NetworkRegistrationService) { - companion object { + private companion object { val pollInterval = 10.seconds - val SELF_SIGNED_PRIVATE_KEY = "Self Signed Private Key" + const val SELF_SIGNED_PRIVATE_KEY = "Self Signed Private Key" } private val requestIdStore = config.certificatesDirectory / "certificate-request-id.txt" @@ -62,54 +62,81 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v */ fun buildKeystore() { config.certificatesDirectory.createDirectories() - val caKeyStore = loadOrCreateKeyStore(config.nodeKeystore, keystorePassword) - if (!caKeyStore.containsAlias(CORDA_CLIENT_CA)) { - // Create or load self signed keypair from the key store. - // We use the self sign certificate to store the key temporarily in the keystore while waiting for the request approval. - if (!caKeyStore.containsAlias(SELF_SIGNED_PRIVATE_KEY)) { - val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val selfSignCert = X509Utilities.createSelfSignedCACertificate(config.myLegalName, keyPair) - // Save to the key store. - caKeyStore.addOrReplaceKey(SELF_SIGNED_PRIVATE_KEY, keyPair.private, privateKeyPassword.toCharArray(), - arrayOf(selfSignCert)) - caKeyStore.save(config.nodeKeystore, keystorePassword) - } - val keyPair = caKeyStore.getKeyPair(SELF_SIGNED_PRIVATE_KEY, privateKeyPassword) - val requestId = submitOrResumeCertificateSigningRequest(keyPair) - - val certificates = try { - pollServerForCertificates(requestId) - } catch (certificateRequestException: CertificateRequestException) { - System.err.println(certificateRequestException.message) - System.err.println("Please make sure the details in configuration file are correct and try again.") - System.err.println("Corda node will now terminate.") - requestIdStore.deleteIfExists() - throw certificateRequestException - } - - println("Certificate signing request approved, storing private key with the certificate chain.") - // Save private key and certificate chain to the key store. - caKeyStore.addOrReplaceKey(CORDA_CLIENT_CA, keyPair.private, privateKeyPassword.toCharArray(), certificates) - caKeyStore.deleteEntry(SELF_SIGNED_PRIVATE_KEY) - caKeyStore.save(config.nodeKeystore, keystorePassword) - println("Node private key and certificate stored in ${config.nodeKeystore}.") - - println("Checking root of the certificate path is what we expect.") - X509Utilities.validateCertificateChain(rootCert, *certificates) - - println("Generating SSL certificate for node messaging service.") - val sslKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val caCert = caKeyStore.getX509Certificate(CORDA_CLIENT_CA).toX509CertHolder() - val sslCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, keyPair, CordaX500Name.build(caCert.cert.subjectX500Principal), sslKey.public) - val sslKeyStore = loadOrCreateKeyStore(config.sslKeystore, keystorePassword) - sslKeyStore.addOrReplaceKey(CORDA_CLIENT_TLS, sslKey.private, privateKeyPassword.toCharArray(), arrayOf(sslCert.cert, *certificates)) - sslKeyStore.save(config.sslKeystore, config.keyStorePassword) - println("SSL private key and certificate stored in ${config.sslKeystore}.") - // All done, clean up temp files. - requestIdStore.deleteIfExists() - } else { + val nodeKeyStore = loadOrCreateKeyStore(config.nodeKeystore, keystorePassword) + if (nodeKeyStore.containsAlias(CORDA_CLIENT_CA)) { println("Certificate already exists, Corda node will now terminate...") + return } + + // Create or load self signed keypair from the key store. + // We use the self sign certificate to store the key temporarily in the keystore while waiting for the request approval. + if (!nodeKeyStore.containsAlias(SELF_SIGNED_PRIVATE_KEY)) { + val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) + val selfSignCert = X509Utilities.createSelfSignedCACertificate(config.myLegalName.x500Principal, keyPair) + // Save to the key store. + nodeKeyStore.addOrReplaceKey(SELF_SIGNED_PRIVATE_KEY, keyPair.private, privateKeyPassword.toCharArray(), + arrayOf(selfSignCert)) + nodeKeyStore.save(config.nodeKeystore, keystorePassword) + } + + val keyPair = nodeKeyStore.getKeyPair(SELF_SIGNED_PRIVATE_KEY, privateKeyPassword) + val requestId = submitOrResumeCertificateSigningRequest(keyPair) + + val certificates = try { + pollServerForCertificates(requestId) + } catch (certificateRequestException: CertificateRequestException) { + System.err.println(certificateRequestException.message) + System.err.println("Please make sure the details in configuration file are correct and try again.") + System.err.println("Corda node will now terminate.") + requestIdStore.deleteIfExists() + throw certificateRequestException + } + + val nodeCaCert = certificates[0] as X509Certificate + + val nodeCaSubject = try { + CordaX500Name.build(nodeCaCert.subjectX500Principal) + } catch (e: IllegalArgumentException) { + throw CertificateRequestException("Received node CA cert has invalid subject name: ${e.message}") + } + if (nodeCaSubject != config.myLegalName) { + throw CertificateRequestException("Subject of received node CA cert doesn't match with node legal name: $nodeCaSubject") + } + + val nodeCaCertRole = try { + CertRole.extract(nodeCaCert) + } catch (e: IllegalArgumentException) { + throw CertificateRequestException("Unable to extract cert role from received node CA cert: ${e.message}") + } + if (nodeCaCertRole != CertRole.NODE_CA) { + throw CertificateRequestException("Received node CA cert has invalid role: $nodeCaCertRole") + } + + println("Checking root of the certificate path is what we expect.") + X509Utilities.validateCertificateChain(rootCert, *certificates) + + println("Certificate signing request approved, storing private key with the certificate chain.") + // Save private key and certificate chain to the key store. + nodeKeyStore.addOrReplaceKey(CORDA_CLIENT_CA, keyPair.private, privateKeyPassword.toCharArray(), certificates) + nodeKeyStore.deleteEntry(SELF_SIGNED_PRIVATE_KEY) + nodeKeyStore.save(config.nodeKeystore, keystorePassword) + println("Node private key and certificate stored in ${config.nodeKeystore}.") + + println("Generating SSL certificate for node messaging service.") + val sslKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) + val sslCert = X509Utilities.createCertificate( + CertificateType.TLS, + nodeCaCert, + keyPair, + config.myLegalName.x500Principal, + sslKeyPair.public) + val sslKeyStore = loadOrCreateKeyStore(config.sslKeystore, keystorePassword) + sslKeyStore.addOrReplaceKey(CORDA_CLIENT_TLS, sslKeyPair.private, privateKeyPassword.toCharArray(), arrayOf(sslCert, *certificates)) + sslKeyStore.save(config.sslKeystore, config.keyStorePassword) + println("SSL private key and certificate stored in ${config.sslKeystore}.") + + // All done, clean up temp files. + requestIdStore.deleteIfExists() } /** @@ -138,7 +165,7 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v private fun submitOrResumeCertificateSigningRequest(keyPair: KeyPair): String { // Retrieve request id from file if exists, else post a request to server. return if (!requestIdStore.exists()) { - val request = X509Utilities.createCertificateSigningRequest(config.myLegalName, config.emailAddress, keyPair) + val request = X509Utilities.createCertificateSigningRequest(config.myLegalName.x500Principal, config.emailAddress, keyPair) val writer = StringWriter() JcaPEMWriter(writer).use { it.writeObject(PemObject("CERTIFICATE REQUEST", request.encoded)) diff --git a/node/src/test/kotlin/net/corda/node/services/RPCSecurityManagerTest.kt b/node/src/test/kotlin/net/corda/node/services/RPCSecurityManagerTest.kt index be0d40ae8e..230170f0a7 100644 --- a/node/src/test/kotlin/net/corda/node/services/RPCSecurityManagerTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/RPCSecurityManagerTest.kt @@ -7,6 +7,7 @@ import net.corda.node.internal.security.Password import net.corda.node.internal.security.RPCSecurityManagerImpl import net.corda.node.internal.security.tryAuthenticate import net.corda.nodeapi.internal.config.User +import net.corda.node.services.config.SecurityConfiguration import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Test import javax.security.auth.login.FailedLoginException @@ -26,7 +27,7 @@ class RPCSecurityManagerTest { @Test fun `Generic RPC call authorization`() { - checkUserPermissions( + checkUserActions( permitted = setOf(arrayListOf("nodeInfo"), arrayListOf("notaryIdentities")), permissions = setOf( Permissions.invokeRpc(CordaRPCOps::nodeInfo), @@ -35,7 +36,7 @@ class RPCSecurityManagerTest { @Test fun `Flow invocation authorization`() { - checkUserPermissions( + checkUserActions( permissions = setOf(Permissions.startFlow()), permitted = setOf( arrayListOf("startTrackedFlowDynamic", "net.corda.node.services.RPCSecurityManagerTest\$DummyFlow"), @@ -44,21 +45,21 @@ class RPCSecurityManagerTest { @Test fun `Check startFlow RPC permission implies startFlowDynamic`() { - checkUserPermissions( + checkUserActions( permissions = setOf(Permissions.invokeRpc("startFlow")), permitted = setOf(arrayListOf("startFlow"), arrayListOf("startFlowDynamic"))) } @Test fun `Check startTrackedFlow RPC permission implies startTrackedFlowDynamic`() { - checkUserPermissions( + checkUserActions( permitted = setOf(arrayListOf("startTrackedFlow"), arrayListOf("startTrackedFlowDynamic")), permissions = setOf(Permissions.invokeRpc("startTrackedFlow"))) } @Test fun `Admin authorization`() { - checkUserPermissions( + checkUserActions( permissions = setOf("all"), permitted = allActions.map { arrayListOf(it) }.toSet()) } @@ -118,9 +119,9 @@ class RPCSecurityManagerTest { users = listOf(User(username, "password", setOf())), id = AuthServiceId("TEST")) } - private fun checkUserPermissions(permissions: Set, permitted: Set>) { + private fun checkUserActions(permissions: Set, permitted: Set>) { val user = User(username = "user", password = "password", permissions = permissions) - val userRealms = RPCSecurityManagerImpl.fromUserList(users = listOf(user), id = AuthServiceId("TEST")) + val userRealms = RPCSecurityManagerImpl(SecurityConfiguration.AuthService.fromUsers(listOf(user))) val disabled = allActions.filter { !permitted.contains(listOf(it)) } for (subject in listOf( userRealms.authenticate("user", Password("password")), diff --git a/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt b/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt index d963030134..600d99e50c 100644 --- a/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt @@ -30,6 +30,7 @@ import org.junit.Assert.* import org.junit.Before import org.junit.Test import java.time.Instant +import kotlin.reflect.jvm.jvmName import kotlin.test.assertEquals class ScheduledFlowTests { @@ -52,7 +53,7 @@ class ScheduledFlowTests { override val linearId: UniqueIdentifier = UniqueIdentifier()) : SchedulableState, LinearState { override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? { return if (!processed) { - val logicRef = flowLogicRefFactory.create(ScheduledFlow::class.java, thisStateRef) + val logicRef = flowLogicRefFactory.create(ScheduledFlow::class.jvmName, thisStateRef) ScheduledActivity(logicRef, creationTime) } else { null diff --git a/node/src/test/kotlin/net/corda/node/services/identity/InMemoryIdentityServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/identity/InMemoryIdentityServiceTests.kt index 30717cdad4..9131143e9f 100644 --- a/node/src/test/kotlin/net/corda/node/services/identity/InMemoryIdentityServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/identity/InMemoryIdentityServiceTests.kt @@ -6,8 +6,6 @@ import net.corda.core.identity.AnonymousParty import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate -import net.corda.core.internal.cert -import net.corda.core.internal.toX509CertHolder import net.corda.core.node.services.UnknownAnonymousPartyException import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509CertificateFactory @@ -32,7 +30,7 @@ class InMemoryIdentityServiceTests { val BOB get() = bob.party val BOB_IDENTITY get() = bob.identity val BOB_PUBKEY get() = bob.publicKey - fun createService(vararg identities: PartyAndCertificate) = InMemoryIdentityService(identities, DEV_TRUST_ROOT) + fun createService(vararg identities: PartyAndCertificate) = InMemoryIdentityService(identities, DEV_ROOT_CA.certificate) } @Rule @@ -100,11 +98,11 @@ class InMemoryIdentityServiceTests { @Test fun `assert unknown anonymous key is unrecognised`() { val rootKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootKey) + val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Principal, rootKey) val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val service = createService() // TODO: Generate certificate with an EdDSA key rather than ECDSA - val identity = Party(rootCert.cert) + val identity = Party(rootCert) val txIdentity = AnonymousParty(txKey.public) assertFailsWith { @@ -159,8 +157,8 @@ class InMemoryIdentityServiceTests { } assertFailsWith { - val owningKey = Crypto.decodePublicKey(DEV_CA.certificate.subjectPublicKeyInfo.encoded) - val subject = CordaX500Name.build(DEV_CA.certificate.cert.subjectX500Principal) + val owningKey = DEV_INTERMEDIATE_CA.certificate.publicKey + val subject = CordaX500Name.build(DEV_INTERMEDIATE_CA.certificate.subjectX500Principal) service.assertOwnership(Party(subject, owningKey), anonymousAlice.party.anonymise()) } } @@ -168,9 +166,14 @@ class InMemoryIdentityServiceTests { private fun createConfidentialIdentity(x500Name: CordaX500Name): Pair { val issuerKeyPair = generateKeyPair() val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public) - val txKey = Crypto.generateKeyPair() - val txCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuer.certificate.toX509CertHolder(), issuerKeyPair, x500Name, txKey.public) - val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates) + val txKeyPair = Crypto.generateKeyPair() + val txCert = X509Utilities.createCertificate( + CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, + issuer.certificate, + issuerKeyPair, + x500Name.x500Principal, + txKeyPair.public) + val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert) + issuer.certPath.certificates) return Pair(issuer, PartyAndCertificate(txCertPath)) } diff --git a/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt index 2553d30ef5..5d6cc41ce1 100644 --- a/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt @@ -6,8 +6,6 @@ import net.corda.core.identity.AnonymousParty import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate -import net.corda.core.internal.cert -import net.corda.core.internal.toX509CertHolder import net.corda.core.node.services.IdentityService import net.corda.core.node.services.UnknownAnonymousPartyException import net.corda.node.internal.configureDatabase @@ -50,7 +48,7 @@ class PersistentIdentityServiceTests { @Before fun setup() { - identityService = PersistentIdentityService(DEV_TRUST_ROOT) + identityService = PersistentIdentityService(DEV_ROOT_CA.certificate) database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), identityService) } @@ -143,9 +141,9 @@ class PersistentIdentityServiceTests { @Test fun `assert unknown anonymous key is unrecognised`() { val rootKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootKey) + val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Principal, rootKey) val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_IDENTITY_SIGNATURE_SCHEME) - val identity = Party(rootCert.cert) + val identity = Party(rootCert) val txIdentity = AnonymousParty(txKey.public) assertFailsWith { @@ -219,9 +217,9 @@ class PersistentIdentityServiceTests { } assertFailsWith { - val owningKey = Crypto.decodePublicKey(DEV_CA.certificate.subjectPublicKeyInfo.encoded) + val owningKey = DEV_INTERMEDIATE_CA.certificate.publicKey database.transaction { - val subject = CordaX500Name.build(DEV_CA.certificate.cert.subjectX500Principal) + val subject = CordaX500Name.build(DEV_INTERMEDIATE_CA.certificate.subjectX500Principal) identityService.assertOwnership(Party(subject, owningKey), anonymousAlice.party.anonymise()) } } @@ -243,7 +241,7 @@ class PersistentIdentityServiceTests { // Create new identity service mounted onto same DB val newPersistentIdentityService = database.transaction { - PersistentIdentityService(DEV_TRUST_ROOT) + PersistentIdentityService(DEV_ROOT_CA.certificate) } database.transaction { @@ -266,8 +264,8 @@ class PersistentIdentityServiceTests { val issuerKeyPair = generateKeyPair() val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public) val txKey = Crypto.generateKeyPair() - val txCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuer.certificate.toX509CertHolder(), issuerKeyPair, x500Name, txKey.public) - val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates) + val txCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuer.certificate, issuerKeyPair, x500Name.x500Principal, txKey.public) + val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert) + issuer.certPath.certificates) return Pair(issuer, PartyAndCertificate(txCertPath)) } diff --git a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapClientTest.kt b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapClientTest.kt index c609d59f2c..9f342b2e0b 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapClientTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapClientTest.kt @@ -2,12 +2,11 @@ package net.corda.node.services.network import net.corda.core.crypto.SecureHash import net.corda.core.crypto.sha256 -import net.corda.core.internal.cert import net.corda.core.serialization.serialize import net.corda.core.utilities.seconds import net.corda.testing.ALICE_NAME import net.corda.testing.BOB_NAME -import net.corda.testing.DEV_TRUST_ROOT +import net.corda.testing.DEV_ROOT_CA import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.driver.PortAllocation import net.corda.testing.internal.createNodeInfoAndSigned @@ -35,7 +34,7 @@ class NetworkMapClientTest { fun setUp() { server = NetworkMapServer(cacheTimeout, PortAllocation.Incremental(10000).nextHostAndPort()) val hostAndPort = server.start() - networkMapClient = NetworkMapClient(URL("http://${hostAndPort.host}:${hostAndPort.port}"), DEV_TRUST_ROOT.cert) + networkMapClient = NetworkMapClient(URL("http://${hostAndPort.host}:${hostAndPort.port}"), DEV_ROOT_CA.certificate) } @After diff --git a/node/src/test/kotlin/net/corda/node/services/events/FlowLogicRefTest.kt b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowLogicRefFactoryImplTest.kt similarity index 81% rename from node/src/test/kotlin/net/corda/node/services/events/FlowLogicRefTest.kt rename to node/src/test/kotlin/net/corda/node/services/statemachine/FlowLogicRefFactoryImplTest.kt index 3794a39ebf..b0953afdc3 100644 --- a/node/src/test/kotlin/net/corda/node/services/events/FlowLogicRefTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowLogicRefFactoryImplTest.kt @@ -1,17 +1,20 @@ -package net.corda.node.services.events +package net.corda.node.services.statemachine import net.corda.core.flows.FlowLogic import net.corda.core.flows.IllegalFlowLogicException -import net.corda.node.services.statemachine.FlowLogicRefFactoryImpl +import net.corda.core.flows.SchedulableFlow import org.junit.Test import java.time.Duration +import kotlin.reflect.jvm.jvmName +import kotlin.test.assertEquals -class FlowLogicRefTest { +class FlowLogicRefFactoryImplTest { data class ParamType1(val value: Int) data class ParamType2(val value: String) @Suppress("UNUSED_PARAMETER", "unused") // Things are used via reflection. + @SchedulableFlow class KotlinFlowLogic(A: ParamType1, b: ParamType2) : FlowLogic() { constructor() : this(ParamType1(1), ParamType2("2")) @@ -26,6 +29,7 @@ class FlowLogicRefTest { override fun call() = Unit } + @SchedulableFlow class KotlinNoArgFlowLogic : FlowLogic() { override fun call() = Unit } @@ -37,18 +41,18 @@ class FlowLogicRefTest { private val flowLogicRefFactory = FlowLogicRefFactoryImpl(FlowLogicRefFactoryImpl::class.java.classLoader) @Test fun `create kotlin no arg`() { - flowLogicRefFactory.createForRPC(KotlinNoArgFlowLogic::class.java) + flowLogicRefFactory.create(KotlinNoArgFlowLogic::class.jvmName) } @Test - fun `create kotlin`() { + fun `should create kotlin types`() { val args = mapOf(Pair("A", ParamType1(1)), Pair("b", ParamType2("Hello Jack"))) flowLogicRefFactory.createKotlin(KotlinFlowLogic::class.java, args) } @Test fun `create primary`() { - flowLogicRefFactory.createForRPC(KotlinFlowLogic::class.java, ParamType1(1), ParamType2("Hello Jack")) + flowLogicRefFactory.create(KotlinFlowLogic::class.jvmName, ParamType1(1), ParamType2("Hello Jack")) } @Test @@ -76,6 +80,6 @@ class FlowLogicRefTest { @Test(expected = IllegalFlowLogicException::class) fun `create for non-schedulable flow logic`() { - flowLogicRefFactory.create(NonSchedulableFlow::class.java) + flowLogicRefFactory.create(NonSchedulableFlow::class.jvmName) } } diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt index 70cbc90835..77c5cf797b 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt @@ -24,6 +24,7 @@ import net.corda.testing.node.startFlow import org.assertj.core.api.Assertions.assertThat import org.junit.After import org.junit.Before +import org.junit.Ignore import org.junit.Test import java.time.Instant import java.util.* @@ -99,6 +100,7 @@ class NotaryServiceTests { assertThat(ex.error).isInstanceOf(NotaryError.TimeWindowInvalid::class.java) } + @Ignore("Only applies to deterministic signature schemes (e.g. EdDSA) and when deterministic metadata is attached (no timestamps or nonces)") @Test fun `should sign identical transaction multiple times (signing is idempotent)`() { val stx = run { diff --git a/node/src/test/kotlin/net/corda/node/utilities/TLSAuthenticationTests.kt b/node/src/test/kotlin/net/corda/node/utilities/TLSAuthenticationTests.kt index 98ff559fe8..ef933d5ccc 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/TLSAuthenticationTests.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/TLSAuthenticationTests.kt @@ -18,6 +18,7 @@ import java.net.ServerSocket import java.nio.file.Path import java.security.KeyStore import javax.net.ssl.* +import javax.security.auth.x500.X500Principal import kotlin.concurrent.thread import kotlin.test.* @@ -51,9 +52,9 @@ class TLSAuthenticationTests { val tempFolder: TemporaryFolder = TemporaryFolder() // Root CA. - private val ROOT_X500 = CordaX500Name(commonName = "Root_CA_1", organisation = "R3CEV", locality = "London", country = "GB") + private val ROOT_X500 = X500Principal("CN=Root_CA_1,O=R3CEV,L=London,C=GB") // Intermediate CA. - private val INTERMEDIATE_X500 = CordaX500Name(commonName = "Intermediate_CA_1", organisation = "R3CEV", locality = "London", country = "GB") + private val INTERMEDIATE_X500 = X500Principal("CN=Intermediate_CA_1,O=R3CEV,L=London,C=GB") // TLS server (client1). private val CLIENT_1_X500 = CordaX500Name(commonName = "Client_1", organisation = "R3CEV", locality = "London", country = "GB") // TLS client (client2). @@ -274,7 +275,7 @@ class TLSAuthenticationTests { CertificateType.NODE_CA, intermediateCACert, intermediateCAKeyPair, - CLIENT_1_X500, + CLIENT_1_X500.x500Principal, client1CAKeyPair.public ) @@ -283,7 +284,7 @@ class TLSAuthenticationTests { CertificateType.TLS, client1CACert, client1CAKeyPair, - CLIENT_1_X500, + CLIENT_1_X500.x500Principal, client1TLSKeyPair.public ) @@ -301,7 +302,7 @@ class TLSAuthenticationTests { CertificateType.NODE_CA, intermediateCACert, intermediateCAKeyPair, - CLIENT_2_X500, + CLIENT_2_X500.x500Principal, client2CAKeyPair.public ) @@ -310,7 +311,7 @@ class TLSAuthenticationTests { CertificateType.TLS, client2CACert, client2CAKeyPair, - CLIENT_2_X500, + CLIENT_2_X500.x500Principal, client2TLSKeyPair.public ) @@ -323,8 +324,8 @@ class TLSAuthenticationTests { // client2TLSKeyStore.save(client2TLSKeyStorePath, PASSWORD) val trustStore = loadOrCreateKeyStore(trustStorePath, PASSWORD) - trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCACert.cert) - trustStore.addOrReplaceCertificate(X509Utilities.CORDA_INTERMEDIATE_CA, intermediateCACert.cert) + trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCACert) + trustStore.addOrReplaceCertificate(X509Utilities.CORDA_INTERMEDIATE_CA, intermediateCACert) // trustStore.save(trustStorePath, PASSWORD) val client1SSLContext = sslContext(client1TLSKeyStore, PASSWORD, trustStore) diff --git a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt index 13113fccf9..f56c82a6cf 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt @@ -9,21 +9,23 @@ import com.nhaarman.mockito_kotlin.whenever import net.corda.core.crypto.Crypto import net.corda.core.crypto.SecureHash import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.cert import net.corda.core.internal.createDirectories +import net.corda.core.internal.x500Name import net.corda.node.services.config.NodeConfiguration import net.corda.nodeapi.internal.crypto.* import net.corda.testing.ALICE_NAME -import net.corda.testing.internal.createDevNodeCaCertPath +import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.testing.internal.rigorousMock -import org.assertj.core.api.Assertions.assertThat -import org.assertj.core.api.Assertions.assertThatThrownBy +import org.assertj.core.api.Assertions.* +import org.bouncycastle.asn1.x509.GeneralName +import org.bouncycastle.asn1.x509.GeneralSubtree +import org.bouncycastle.asn1.x509.NameConstraints import org.junit.After import org.junit.Before import org.junit.Test import java.security.cert.CertPathValidatorException -import java.security.cert.Certificate import java.security.cert.X509Certificate +import javax.security.auth.x500.X500Principal import kotlin.test.assertFalse import kotlin.test.assertTrue @@ -32,18 +34,10 @@ class NetworkRegistrationHelperTest { private val requestId = SecureHash.randomSHA256().toString() private val nodeLegalName = ALICE_NAME - private lateinit var rootCaCert: X509Certificate - private lateinit var intermediateCaCert: X509Certificate - private lateinit var nodeCaCert: X509Certificate private lateinit var config: NodeConfiguration @Before fun init() { - val (rootCa, intermediateCa, nodeCa) = createDevNodeCaCertPath(nodeLegalName) - this.rootCaCert = rootCa.certificate.cert - this.intermediateCaCert = intermediateCa.certificate.cert - this.nodeCaCert = nodeCa.certificate.cert - val baseDirectory = fs.getPath("/baseDir").createDirectories() abstract class AbstractNodeConfiguration : NodeConfiguration config = rigorousMock().also { @@ -66,9 +60,10 @@ class NetworkRegistrationHelperTest { assertThat(config.sslKeystore).doesNotExist() assertThat(config.trustStoreFile).doesNotExist() - saveTrustStoreWithRootCa(rootCaCert) + val nodeCaCertPath = createNodeCaCertPath() - createRegistrationHelper().buildKeystore() + saveTrustStoreWithRootCa(nodeCaCertPath.last()) + createRegistrationHelper(nodeCaCertPath).buildKeystore() val nodeKeystore = loadKeyStore(config.nodeKeystore, config.keyStorePassword) val sslKeystore = loadKeyStore(config.sslKeystore, config.keyStorePassword) @@ -79,8 +74,7 @@ class NetworkRegistrationHelperTest { assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA)) assertFalse(containsAlias(X509Utilities.CORDA_ROOT_CA)) assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_TLS)) - val nodeCaCertChain = getCertificateChain(X509Utilities.CORDA_CLIENT_CA) - assertThat(nodeCaCertChain).containsExactly(nodeCaCert, intermediateCaCert, rootCaCert) + assertThat(getCertificateChain(X509Utilities.CORDA_CLIENT_CA)).containsExactly(*nodeCaCertPath) } sslKeystore.run { @@ -92,40 +86,77 @@ class NetworkRegistrationHelperTest { assertThat(nodeTlsCertChain).hasSize(4) // The TLS cert has the same subject as the node CA cert assertThat(CordaX500Name.build((nodeTlsCertChain[0] as X509Certificate).subjectX500Principal)).isEqualTo(nodeLegalName) - assertThat(nodeTlsCertChain.drop(1)).containsExactly(nodeCaCert, intermediateCaCert, rootCaCert) + assertThat(nodeTlsCertChain.drop(1)).containsExactly(*nodeCaCertPath) } trustStore.run { assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_CA)) assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA)) assertTrue(containsAlias(X509Utilities.CORDA_ROOT_CA)) - val trustStoreRootCaCert = getCertificate(X509Utilities.CORDA_ROOT_CA) - assertThat(trustStoreRootCaCert).isEqualTo(rootCaCert) + assertThat(getCertificate(X509Utilities.CORDA_ROOT_CA)).isEqualTo(nodeCaCertPath.last()) } } @Test fun `missing truststore`() { + val nodeCaCertPath = createNodeCaCertPath() assertThatThrownBy { - createRegistrationHelper() + createRegistrationHelper(nodeCaCertPath) }.hasMessageContaining("This file must contain the root CA cert of your compatibility zone. Please contact your CZ operator.") } + @Test + fun `node CA with incorrect cert role`() { + val nodeCaCertPath = createNodeCaCertPath(type = CertificateType.TLS) + saveTrustStoreWithRootCa(nodeCaCertPath.last()) + val registrationHelper = createRegistrationHelper(nodeCaCertPath) + assertThatExceptionOfType(CertificateRequestException::class.java) + .isThrownBy { registrationHelper.buildKeystore() } + .withMessageContaining(CertificateType.TLS.toString()) + } + + @Test + fun `node CA with incorrect subject`() { + val invalidName = CordaX500Name("Foo", "MU", "GB") + val nodeCaCertPath = createNodeCaCertPath(legalName = invalidName) + saveTrustStoreWithRootCa(nodeCaCertPath.last()) + val registrationHelper = createRegistrationHelper(nodeCaCertPath) + assertThatExceptionOfType(CertificateRequestException::class.java) + .isThrownBy { registrationHelper.buildKeystore() } + .withMessageContaining(invalidName.toString()) + } + @Test fun `wrong root cert in truststore`() { - val rootKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val rootCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name("Foo", "MU", "GB"), rootKeyPair) - saveTrustStoreWithRootCa(rootCert.cert) - val registrationHelper = createRegistrationHelper() + val wrongRootCert = X509Utilities.createSelfSignedCACertificate( + X500Principal("O=Foo,L=MU,C=GB"), + Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) + saveTrustStoreWithRootCa(wrongRootCert) + val registrationHelper = createRegistrationHelper(createNodeCaCertPath()) assertThatThrownBy { registrationHelper.buildKeystore() }.isInstanceOf(CertPathValidatorException::class.java) } - private fun createRegistrationHelper(): NetworkRegistrationHelper { + private fun createNodeCaCertPath(type: CertificateType = CertificateType.NODE_CA, + legalName: CordaX500Name = nodeLegalName): Array { + 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): NetworkRegistrationHelper { val certService = rigorousMock().also { doReturn(requestId).whenever(it).submitRequest(any()) - doReturn(arrayOf(nodeCaCert, intermediateCaCert, rootCaCert)).whenever(it).retrieveCertificates(eq(requestId)) + doReturn(response).whenever(it).retrieveCertificates(eq(requestId)) } return NetworkRegistrationHelper(config, certService) } diff --git a/samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt b/samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt index 561dcf514a..b65cbb870f 100644 --- a/samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt +++ b/samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt @@ -8,6 +8,9 @@ import net.corda.nodeapi.internal.config.User import net.corda.testing.* import net.corda.testing.driver.PortAllocation import net.corda.testing.driver.driver +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import org.junit.ClassRule import org.junit.Test import java.util.concurrent.CompletableFuture.supplyAsync diff --git a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaCordformTest.kt b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaCordformTest.kt index 90bb37a6b7..43870199c4 100644 --- a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaCordformTest.kt +++ b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaCordformTest.kt @@ -2,7 +2,8 @@ package net.corda.bank import net.corda.finance.DOLLARS import net.corda.finance.POUNDS -import net.corda.testing.* +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.node.internal.demorun.deployNodesThen import org.junit.ClassRule import org.junit.Test diff --git a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt index ce4f4dc62a..ab1395dd00 100644 --- a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt +++ b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt @@ -14,6 +14,9 @@ import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.internal.config.User import net.corda.testing.* import net.corda.testing.driver.driver +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import org.junit.ClassRule import org.junit.Test diff --git a/samples/irs-demo/README.md b/samples/irs-demo/README.md index 12af545191..a22d7acaaf 100644 --- a/samples/irs-demo/README.md +++ b/samples/irs-demo/README.md @@ -9,11 +9,12 @@ webapp which provides REST API and web frontend. Application communicate using C To run from the command line in Unix: 1. Run ``./gradlew samples:irs-demo:cordapp:deployNodes`` to install configs and a command line tool under - ``samples/irs-demo/build`` + ``samples/irs-demo/cordapp/build`` 2. Run ``./gradlew samples:irs-demo:web:deployWebapps`` to install configs and tools for running webservers 3. Move to the ``samples/irs-demo/`` directory 4. Run ``./cordapp/build/nodes/runnodes`` to open up three new terminals with the three nodes (you may have to install xterm) -5. Run ``./web/build/webapps/runwebapps`` to open three more terminals for associated webserver +5. On Linux, run ``./web/build/webapps/runwebapps`` to open three more terminals for associated webservers. On macOS, + use the following command instead: ``osascript ./web/build/webapps/runwebapps.scpt`` To run from the command line in Windows: diff --git a/samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/contract/IRS.kt b/samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/contract/IRS.kt index c81d80592c..735c265ed6 100644 --- a/samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/contract/IRS.kt +++ b/samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/contract/IRS.kt @@ -616,7 +616,7 @@ class InterestRateSwap : Contract { // This is perhaps not how we should determine the time point in the business day, but instead expect the schedule to detail some of these aspects val instant = suggestInterestRateAnnouncementTimeWindow(index = nextFixingOf.name, source = floatingLeg.indexSource, date = nextFixingOf.forDay).fromTime!! - return ScheduledActivity(flowLogicRefFactory.create(FixingFlow.FixingRoleDecider::class.java, thisStateRef), instant) + return ScheduledActivity(flowLogicRefFactory.create("net.corda.irs.flows.FixingFlow\$FixingRoleDecider", thisStateRef), instant) } // DOCEND 1 diff --git a/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt b/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt index 90b689cb83..f981389c6b 100644 --- a/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt +++ b/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt @@ -25,6 +25,9 @@ import net.corda.nodeapi.internal.config.User import net.corda.test.spring.springDriver import net.corda.testing.* import net.corda.testing.http.HttpApi +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import net.corda.testing.node.NotarySpec import org.apache.commons.io.IOUtils import org.assertj.core.api.Assertions.assertThat diff --git a/samples/simm-valuation-demo/README.md b/samples/simm-valuation-demo/README.md index 7931c876e0..8237872b33 100644 --- a/samples/simm-valuation-demo/README.md +++ b/samples/simm-valuation-demo/README.md @@ -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 <<>> http://localhost:10005/api/simmvaluationdemo/<<>>/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/<<>>/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/<<>>/trades/<<>> + +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 <<>> + http://localhost:10005/api/simmvaluationdemo/<<>>/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/<<>>/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 diff --git a/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt b/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt index b95d2f5c4b..afbab2b339 100644 --- a/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt +++ b/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt @@ -6,6 +6,9 @@ import net.corda.core.utilities.getOrThrow import net.corda.testing.* import net.corda.testing.driver.driver import net.corda.testing.http.HttpApi +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import net.corda.vega.api.PortfolioApi import net.corda.vega.api.PortfolioApiUtils import net.corda.vega.api.SwapDataModel diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/PortfolioState.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/PortfolioState.kt index 8b9b1d7414..98e490e68d 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/PortfolioState.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/PortfolioState.kt @@ -7,7 +7,6 @@ import net.corda.core.identity.Party import net.corda.core.serialization.CordaSerializable import net.corda.core.transactions.TransactionBuilder import net.corda.finance.contracts.DealState -import net.corda.vega.flows.SimmRevaluation import java.time.LocalDate import java.time.ZoneOffset import java.time.temporal.ChronoUnit @@ -32,7 +31,7 @@ data class PortfolioState(val portfolio: List, val valuer: AbstractParty get() = participants[0] override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity { - val flow = flowLogicRefFactory.create(SimmRevaluation.Initiator::class.java, thisStateRef, LocalDate.now()) + val flow = flowLogicRefFactory.create("net.corda.vega.flows.SimmRevaluation\$Initiator", thisStateRef, LocalDate.now()) return ScheduledActivity(flow, LocalDate.now().plus(1, ChronoUnit.DAYS).atStartOfDay().toInstant(ZoneOffset.UTC)) } diff --git a/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt b/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt index d3c40a7062..594cffa8e4 100644 --- a/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt +++ b/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt @@ -12,6 +12,9 @@ import net.corda.nodeapi.internal.config.User import net.corda.testing.* import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import net.corda.testing.node.internal.poll import net.corda.traderdemo.flow.BuyerFlow import net.corda.traderdemo.flow.CommercialPaperIssueFlow diff --git a/testing/node-driver/build.gradle b/testing/node-driver/build.gradle index a13ec48018..fb75da9ab7 100644 --- a/testing/node-driver/build.gradle +++ b/testing/node-driver/build.gradle @@ -2,6 +2,7 @@ apply plugin: 'kotlin' apply plugin: 'kotlin-jpa' apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.publish-utils' +apply plugin: 'net.corda.plugins.api-scanner' apply plugin: 'com.jfrog.artifactory' //noinspection GroovyAssignabilityCheck diff --git a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt index c1c29b9de7..43a7762bf1 100644 --- a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt +++ b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt @@ -12,6 +12,9 @@ import net.corda.node.internal.NodeStartup import net.corda.testing.* import net.corda.testing.common.internal.ProjectStructure.projectRootDir import net.corda.testing.http.HttpApi +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import net.corda.testing.node.NotarySpec import net.corda.testing.node.internal.addressMustBeBound import net.corda.testing.node.internal.addressMustNotBeBound diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt index e9808f518e..6c558bf529 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt @@ -3,6 +3,7 @@ package net.corda.testing.driver import net.corda.client.rpc.CordaRPCClient +import net.corda.core.DoNotImplement import net.corda.core.concurrent.CordaFuture import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party @@ -30,6 +31,7 @@ import java.util.concurrent.atomic.AtomicInteger */ data class NotaryHandle(val identity: Party, val validating: Boolean, val nodeHandles: CordaFuture>) +@DoNotImplement sealed class NodeHandle { abstract val nodeInfo: NodeInfo /** @@ -90,6 +92,7 @@ data class WebserverHandle( val process: Process ) +@DoNotImplement sealed class PortAllocation { abstract fun nextPort(): Int fun nextHostAndPort() = NetworkHostAndPort("localhost", nextPort()) diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/DriverDSL.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/DriverDSL.kt index d8d3d1b49a..a4908d5f53 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/DriverDSL.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/DriverDSL.kt @@ -1,5 +1,6 @@ package net.corda.testing.driver +import net.corda.core.DoNotImplement import net.corda.core.concurrent.CordaFuture import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party @@ -10,6 +11,7 @@ import net.corda.nodeapi.internal.config.User import net.corda.testing.node.NotarySpec import java.nio.file.Path +@DoNotImplement interface DriverDSL { /** Returns a list of [NotaryHandle]s matching the list of [NotarySpec]s passed into [driver]. */ val notaryHandles: List diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt index 4ccf3ab00a..3d0dd89a58 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt @@ -1,5 +1,6 @@ package net.corda.testing.node +import net.corda.core.DoNotImplement import net.corda.core.crypto.CompositeKey import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party @@ -123,7 +124,7 @@ class InMemoryMessagingNetwork internal constructor( } } - interface LatencyCalculator { + interface LatencyCalculator { // XXX: Used? fun between(sender: SingleMessageRecipient, receiver: SingleMessageRecipient): Duration } @@ -181,6 +182,7 @@ class InMemoryMessagingNetwork internal constructor( /** * Mock service loadbalancing */ + @DoNotImplement sealed class ServicePeerAllocationStrategy { abstract fun pickNext(service: ServiceHandle, pickFrom: List): A class Random(val random: SplittableRandom = SplittableRandom()) : ServicePeerAllocationStrategy() { diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt index 430e5ecc9b..331f1b940d 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt @@ -4,7 +4,9 @@ import com.google.common.jimfs.Configuration.unix import com.google.common.jimfs.Jimfs import com.nhaarman.mockito_kotlin.doReturn import com.nhaarman.mockito_kotlin.whenever +import net.corda.core.DoNotImplement import net.corda.core.crypto.entropyToKeyPair +import net.corda.core.crypto.Crypto import net.corda.core.crypto.random63BitValue import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party @@ -325,7 +327,8 @@ open class MockNetwork(private val cordappPackages: List, // This is not thread safe, but node construction is done on a single thread, so that should always be fine override fun generateKeyPair(): KeyPair { counter = counter.add(BigInteger.ONE) - return entropyToKeyPair(counter) + // The MockNode specifically uses EdDSA keys as they are fixed and stored in json files for some tests (e.g IRSSimulation). + return Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, counter) } /** @@ -492,6 +495,7 @@ fun StartedNode.setMessagingServiceSpy(messagingServiceSpy } private fun mockNodeConfiguration(): NodeConfiguration { + @DoNotImplement abstract class AbstractNodeConfiguration : NodeConfiguration return rigorousMock().also { doReturn("cordacadevpass").whenever(it).keyStorePassword diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt index cc4dfb2bef..651aadf123 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt @@ -23,6 +23,7 @@ import net.corda.core.transactions.SignedTransaction import net.corda.node.VersionInfo import net.corda.node.internal.configureDatabase import net.corda.node.internal.cordapp.CordappLoader +import net.corda.node.services.api.IdentityServiceInternal import net.corda.node.services.api.SchemaService import net.corda.node.services.api.VaultServiceInternal import net.corda.node.services.api.WritableTransactionStorage @@ -34,12 +35,12 @@ import net.corda.node.services.schema.HibernateObserver import net.corda.node.services.schema.NodeSchemaService import net.corda.node.services.transactions.InMemoryTransactionVerifierService import net.corda.node.services.vault.NodeVaultService -import net.corda.node.services.api.IdentityServiceInternal import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.nodeapi.internal.persistence.HibernateConfiguration import net.corda.nodeapi.internal.persistence.TransactionIsolationLevel -import net.corda.testing.* +import net.corda.testing.DEV_ROOT_CA +import net.corda.testing.TestIdentity import net.corda.testing.database.DatabaseConstants import net.corda.testing.database.DatabaseConstants.DATA_SOURCE_CLASSNAME import net.corda.testing.database.DatabaseConstants.DATA_SOURCE_PASSWORD @@ -59,7 +60,7 @@ import java.sql.Connection import java.time.Clock import java.util.* -fun makeTestIdentityService(vararg identities: PartyAndCertificate) = InMemoryIdentityService(identities, DEV_TRUST_ROOT) +fun makeTestIdentityService(vararg identities: PartyAndCertificate) = InMemoryIdentityService(identities, DEV_ROOT_CA.certificate) /** * A singleton utility that only provides a mock identity, key and storage service. However, this is sufficient for * building chains of transactions and verifying them. It isn't sufficient for testing flows however. @@ -197,7 +198,7 @@ class MockKeyManagementService(val identityService: IdentityServiceInternal, override val keys: Set get() = keyStore.keys - val nextKeys = LinkedList() + private val nextKeys = LinkedList() override fun freshKey(): PublicKey { val k = nextKeys.poll() ?: generateKeyPair() @@ -259,11 +260,10 @@ open class MockTransactionStorage : WritableTransactionStorage, SingletonSeriali } fun createMockCordaService(serviceHub: MockServices, serviceConstructor: (AppServiceHub) -> T): T { - class MockAppServiceHubImpl(val serviceHub: MockServices, serviceConstructor: (AppServiceHub) -> T) : AppServiceHub, ServiceHub by serviceHub { - val serviceInstance: T + class MockAppServiceHubImpl(val serviceHub: MockServices, serviceConstructor: (AppServiceHub) -> T) : AppServiceHub, ServiceHub by serviceHub { + val serviceInstance: T = serviceConstructor(this) init { - serviceInstance = serviceConstructor(this) serviceHub.cordappServices.putInstance(serviceInstance.javaClass, serviceInstance) } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/NotarySpec.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/NotarySpec.kt index b8988e50c7..b6533ebd98 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/NotarySpec.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/NotarySpec.kt @@ -1,5 +1,6 @@ package net.corda.testing.node +import net.corda.core.DoNotImplement import net.corda.core.identity.CordaX500Name import net.corda.node.services.config.VerifierType import net.corda.nodeapi.internal.config.User @@ -12,6 +13,7 @@ data class NotarySpec( val cluster: ClusterSpec? = null ) +@DoNotImplement sealed class ClusterSpec { abstract val clusterSize: Int diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt index 4464724829..c7117e3f96 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt @@ -13,7 +13,7 @@ import net.corda.node.internal.StartedNode import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.services.config.* import net.corda.nodeapi.internal.config.User -import net.corda.testing.IntegrationTest +import net.corda.testing.internal.IntegrationTest import net.corda.testing.SerializationEnvironmentRule import net.corda.nodeapi.internal.network.NetworkParametersCopier import net.corda.testing.common.internal.testNetworkParameters diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/network/NetworkMapServer.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/network/NetworkMapServer.kt index 345eaf84b8..be1a11c293 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/network/NetworkMapServer.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/network/NetworkMapServer.kt @@ -2,8 +2,6 @@ package net.corda.testing.node.internal.network import net.corda.core.crypto.* import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.cert -import net.corda.core.internal.toX509CertHolder import net.corda.core.node.NodeInfo import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize @@ -16,7 +14,7 @@ import net.corda.nodeapi.internal.network.DigitalSignatureWithCert import net.corda.nodeapi.internal.network.NetworkMap import net.corda.nodeapi.internal.network.NetworkParameters import net.corda.nodeapi.internal.network.SignedNetworkMap -import net.corda.testing.ROOT_CA +import net.corda.testing.DEV_ROOT_CA import org.eclipse.jetty.server.Server import org.eclipse.jetty.server.ServerConnector import org.eclipse.jetty.server.handler.HandlerCollection @@ -29,6 +27,7 @@ import java.io.InputStream import java.net.InetSocketAddress import java.time.Duration import java.time.Instant +import javax.security.auth.x500.X500Principal import javax.ws.rs.* import javax.ws.rs.core.MediaType import javax.ws.rs.core.Response @@ -36,7 +35,7 @@ import javax.ws.rs.core.Response.ok class NetworkMapServer(cacheTimeout: Duration, hostAndPort: NetworkHostAndPort, - rootCa: CertificateAndKeyPair = ROOT_CA, // Default to ROOT_CA for testing. + rootCa: CertificateAndKeyPair = DEV_ROOT_CA, private val myHostNameValue: String = "test.host.name", vararg additionalServices: Any) : Closeable { companion object { @@ -48,12 +47,12 @@ class NetworkMapServer(cacheTimeout: Duration, CertificateType.NETWORK_MAP, rootCAKeyAndCert.certificate, rootCAKeyAndCert.keyPair, - CordaX500Name("Corda Network Map", "R3 Ltd", "London","GB"), - networkMapKey.public).cert + X500Principal("CN=Corda Network Map,O=R3 Ltd,L=London,C=GB"), + networkMapKey.public) // Check that the certificate validates. Nodes will perform this check upon receiving a network map, // it's better to fail here than there. - X509Utilities.validateCertificateChain(rootCAKeyAndCert.certificate.cert, networkMapCert) - return CertificateAndKeyPair(networkMapCert.toX509CertHolder(), networkMapKey) + X509Utilities.validateCertificateChain(rootCAKeyAndCert.certificate, networkMapCert) + return CertificateAndKeyPair(networkMapCert, networkMapKey) } } @@ -130,7 +129,7 @@ class NetworkMapServer(cacheTimeout: Duration, val networkMap = NetworkMap(nodeInfoMap.keys.toList(), parametersHash) val serializedNetworkMap = networkMap.serialize() val signature = Crypto.doSign(networkMapKeyAndCert.keyPair.private, serializedNetworkMap.bytes) - val signedNetworkMap = SignedNetworkMap(networkMap.serialize(), DigitalSignatureWithCert(networkMapKeyAndCert.certificate.cert, signature)) + val signedNetworkMap = SignedNetworkMap(networkMap.serialize(), DigitalSignatureWithCert(networkMapKeyAndCert.certificate, signature)) return Response.ok(signedNetworkMap.serialize().bytes).header("Cache-Control", "max-age=${cacheTimeout.seconds}").build() } diff --git a/testing/test-common/build.gradle b/testing/test-common/build.gradle index df37173fc1..e68a9dba67 100644 --- a/testing/test-common/build.gradle +++ b/testing/test-common/build.gradle @@ -1,4 +1,5 @@ apply plugin: 'net.corda.plugins.publish-utils' +apply plugin: 'net.corda.plugins.api-scanner' apply plugin: 'com.jfrog.artifactory' dependencies { diff --git a/testing/test-utils/build.gradle b/testing/test-utils/build.gradle index 74a2ff763b..e927c12eab 100644 --- a/testing/test-utils/build.gradle +++ b/testing/test-utils/build.gradle @@ -2,6 +2,7 @@ apply plugin: 'kotlin' apply plugin: 'kotlin-jpa' apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.publish-utils' +apply plugin: 'net.corda.plugins.api-scanner' apply plugin: 'com.jfrog.artifactory' description 'Testing utilities for Corda' diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt index 362672ce89..e24496c50c 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt @@ -12,7 +12,6 @@ import net.corda.core.crypto.toStringShort import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate -import net.corda.core.internal.cert import net.corda.core.internal.unspecifiedCountry import net.corda.core.node.NodeInfo import net.corda.core.utilities.NetworkHostAndPort @@ -21,10 +20,13 @@ import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509Utilities -import org.bouncycastle.cert.X509CertificateHolder +import org.bouncycastle.asn1.x509.GeneralName +import org.bouncycastle.asn1.x509.GeneralSubtree +import org.bouncycastle.asn1.x509.NameConstraints import java.math.BigInteger import java.security.KeyPair import java.security.PublicKey +import java.security.cert.X509Certificate import java.util.concurrent.atomic.AtomicInteger /** @@ -76,8 +78,8 @@ fun getFreeLocalPorts(hostName: String, numberToAlloc: Int): List S.genericExpectEvents( finishFuture.getOrThrow() } +@DoNotImplement sealed class ExpectCompose { internal class Single(val expect: Expect) : ExpectCompose() internal class Sequential(val sequence: List>) : ExpectCompose() diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/SerializationTestHelpers.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/SerializationTestHelpers.kt index 8aa5a0cc61..b76f571bc3 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/SerializationTestHelpers.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/SerializationTestHelpers.kt @@ -2,6 +2,7 @@ package net.corda.testing import com.nhaarman.mockito_kotlin.* import net.corda.client.rpc.internal.KryoClientSerializationScheme +import net.corda.core.DoNotImplement import net.corda.core.internal.staticField import net.corda.core.serialization.internal.* import net.corda.node.serialization.KryoServerSerializationScheme @@ -62,6 +63,7 @@ class SerializationEnvironmentRule(private val inheritable: Boolean = false) : T } } +@DoNotImplement interface GlobalSerializationEnvironment : SerializationEnvironment { /** Unset this environment. */ fun unset() diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt index 0f700d9938..406477c52b 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt @@ -6,12 +6,10 @@ import net.corda.core.contracts.Command import net.corda.core.contracts.TypeOnlyCommandData import net.corda.core.crypto.generateKeyPair import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.toX509CertHolder import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.getCertificateAndKeyPair import net.corda.nodeapi.internal.crypto.loadKeyStore -import org.bouncycastle.cert.X509CertificateHolder import java.security.PublicKey import java.time.Instant @@ -34,22 +32,17 @@ val ALICE_NAME = CordaX500Name("Alice Corp", "Madrid", "ES") val BOB_NAME = CordaX500Name("Bob Plc", "Rome", "IT") @JvmField val CHARLIE_NAME = CordaX500Name("Charlie Ltd", "Athens", "GR") -val DEV_CA: CertificateAndKeyPair by lazy { +val DEV_INTERMEDIATE_CA: CertificateAndKeyPair by lazy { // TODO: Should be identity scheme val caKeyStore = loadKeyStore(ClassLoader.getSystemResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass") caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass") } -val ROOT_CA: CertificateAndKeyPair by lazy { +val DEV_ROOT_CA: CertificateAndKeyPair by lazy { // TODO: Should be identity scheme val caKeyStore = loadKeyStore(ClassLoader.getSystemResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass") caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_ROOT_CA, "cordacadevkeypass") } -val DEV_TRUST_ROOT: X509CertificateHolder by lazy { - // TODO: Should be identity scheme - val caKeyStore = loadKeyStore(ClassLoader.getSystemResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass") - caKeyStore.getCertificateChain(X509Utilities.CORDA_INTERMEDIATE_CA).last().toX509CertHolder() -} fun dummyCommand(vararg signers: PublicKey = arrayOf(generateKeyPair().public)) = Command(DummyCommandData, signers.toList()) diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContract.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContract.kt index 28341e2e92..3355b1fdf0 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContract.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContract.kt @@ -1,5 +1,6 @@ package net.corda.testing.contracts +import net.corda.core.DoNotImplement import net.corda.core.contracts.* import net.corda.core.identity.AbstractParty import net.corda.core.identity.Party @@ -12,6 +13,7 @@ data class DummyContract(val blank: Any? = null) : Contract { val PROGRAM_ID = "net.corda.testing.contracts.DummyContract" + @DoNotImplement // State is effectively a sealed class. interface State : ContractState { val magicNumber: Int } diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/LedgerDSLInterpreter.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/LedgerDSLInterpreter.kt index 869643d2aa..89e9ecb202 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/LedgerDSLInterpreter.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/LedgerDSLInterpreter.kt @@ -1,5 +1,6 @@ package net.corda.testing.dsl +import net.corda.core.DoNotImplement import net.corda.core.contracts.ContractState import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.TransactionState @@ -14,6 +15,7 @@ import java.io.InputStream * This interface defines output state lookup by label. It is split from the interpreter interfaces so that outputs may * be looked up both in ledger{..} and transaction{..} blocks. */ +@DoNotImplement interface OutputStateLookup { /** * Retrieves an output previously defined by [TransactionDSLInterpreter.output] with a label passed in. @@ -27,6 +29,7 @@ interface OutputStateLookup { /** * This interface asserts that the DSL at hand is capable of verifying its underlying construct(ledger/transaction). */ +@DoNotImplement interface Verifies { /** * Verifies the ledger/transaction, throws if the verification fails. @@ -83,6 +86,7 @@ interface Verifies { * * TODO (Kotlin 1.1): Use type synonyms to make the type params less unwieldy */ +@DoNotImplement interface LedgerDSLInterpreter : Verifies, OutputStateLookup { /** * Creates and adds a transaction to the ledger. diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/TestDSL.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/TestDSL.kt index bf48a07240..3a0e36e5f3 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/TestDSL.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/TestDSL.kt @@ -1,5 +1,6 @@ package net.corda.testing.dsl +import net.corda.core.DoNotImplement import net.corda.core.contracts.* import net.corda.core.cordapp.CordappProvider import net.corda.core.crypto.* @@ -50,6 +51,7 @@ import kotlin.collections.set * will have as the last line either an accept or a failure test. The name is deliberately long to help make sense of * the triggered diagnostic. */ +@DoNotImplement sealed class EnforceVerifyOrFail { internal object Token : EnforceVerifyOrFail() } diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/TransactionDSLInterpreter.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/TransactionDSLInterpreter.kt index d79260592f..f78678801a 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/TransactionDSLInterpreter.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/TransactionDSLInterpreter.kt @@ -1,5 +1,6 @@ package net.corda.testing.dsl +import net.corda.core.DoNotImplement import net.corda.core.contracts.AlwaysAcceptAttachmentConstraint import net.corda.core.contracts.Attachment import net.corda.core.contracts.AttachmentConstraint @@ -22,6 +23,7 @@ import java.time.Instant * @param The return type of [verifies]/[failsWith] and the like. It is generic so that we have control over whether * we want to enforce users to call these methods (see [EnforceVerifyOrFail]) or not. */ +@DoNotImplement interface TransactionDSLInterpreter : Verifies, OutputStateLookup { /** * A reference to the enclosing ledger{..}'s interpreter. diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/IntegrationTest.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/IntegrationTest.kt similarity index 97% rename from testing/test-utils/src/main/kotlin/net/corda/testing/IntegrationTest.kt rename to testing/test-utils/src/main/kotlin/net/corda/testing/internal/IntegrationTest.kt index 79df695f84..66a0e691a5 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/IntegrationTest.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/IntegrationTest.kt @@ -1,7 +1,6 @@ -package net.corda.testing +package net.corda.testing.internal import net.corda.core.identity.CordaX500Name -import net.corda.core.identity.Party import net.corda.testing.database.DbScriptRunner.runDbScript import org.junit.After import org.junit.AfterClass diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt index 87a73491b5..ccf34284e7 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt @@ -15,6 +15,7 @@ import org.mockito.internal.stubbing.answers.ThrowsException import java.lang.reflect.Modifier import java.nio.file.Files import java.util.* +import javax.security.auth.x500.X500Principal @Suppress("unused") inline fun T.kryoSpecific(reason: String, function: () -> Unit) = if (!AMQP_ENABLED) { @@ -64,8 +65,8 @@ fun configureTestSSL(legalName: CordaX500Name): SSLConfiguration { } } -private val defaultRootCaName = CordaX500Name("Corda Root CA", "R3 Ltd", "London", "GB") -private val defaultIntermediateCaName = CordaX500Name("Corda Intermediate CA", "R3 Ltd", "London", "GB") +private val defaultRootCaName = X500Principal("CN=Corda Root CA,O=R3 Ltd,L=London,C=GB") +private val defaultIntermediateCaName = X500Principal("CN=Corda Intermediate CA,O=R3 Ltd,L=London,C=GB") /** * Returns a pair of [CertificateAndKeyPair]s, the first being the root CA and the second the intermediate CA. @@ -73,8 +74,8 @@ private val defaultIntermediateCaName = CordaX500Name("Corda Intermediate CA", " * @param intermediateCaName The subject name for the intermediate CA cert. */ fun createDevIntermediateCaCertPath( - rootCaName: CordaX500Name = defaultRootCaName, - intermediateCaName: CordaX500Name = defaultIntermediateCaName + rootCaName: X500Principal = defaultRootCaName, + intermediateCaName: X500Principal = defaultIntermediateCaName ): Pair { val rootKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val rootCert = X509Utilities.createSelfSignedCACertificate(rootCaName, rootKeyPair) @@ -87,7 +88,10 @@ fun createDevIntermediateCaCertPath( intermediateCaName, intermediateCaKeyPair.public) - return Pair(CertificateAndKeyPair(rootCert, rootKeyPair), CertificateAndKeyPair(intermediateCaCert, intermediateCaKeyPair)) + return Pair( + CertificateAndKeyPair(rootCert, rootKeyPair), + CertificateAndKeyPair(intermediateCaCert, intermediateCaKeyPair) + ) } /** @@ -97,8 +101,8 @@ fun createDevIntermediateCaCertPath( */ fun createDevNodeCaCertPath( legalName: CordaX500Name, - rootCaName: CordaX500Name = defaultRootCaName, - intermediateCaName: CordaX500Name = defaultIntermediateCaName + rootCaName: X500Principal = defaultRootCaName, + intermediateCaName: X500Principal = defaultIntermediateCaName ): Triple { val (rootCa, intermediateCa) = createDevIntermediateCaCertPath(rootCaName, intermediateCaName) val nodeCa = createDevNodeCa(intermediateCa, legalName) diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt index 9bc750a772..1f91cc92e8 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt @@ -152,7 +152,7 @@ class ExplorerSimulation(private val options: OptionSet) { it.startFlow(::CashIssueAndPaymentFlow, request).log(i, "${request.amount.token}Issuer") } is ExitRequest -> issuers[request.amount.token]?.let { - println("${Instant.now()} [$i] EXITING ${request.amount} with ref ${request.issueRef}") + println("${Instant.now()} [$i] EXITING ${request.amount} with ref ${request.issuerRef}") it.startFlow(::CashExitFlow, request).log(i, "${request.amount.token}Exit") } else -> throw IllegalArgumentException("Unsupported command: $request") diff --git a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt index a7383cfce1..647796fec4 100644 --- a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt +++ b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt @@ -13,6 +13,9 @@ import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.config.VerifierType import net.corda.testing.* +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import net.corda.testing.node.NotarySpec import org.junit.ClassRule import org.junit.Rule diff --git a/webserver/src/integration-test/kotlin/net/corda/webserver/WebserverDriverTests.kt b/webserver/src/integration-test/kotlin/net/corda/webserver/WebserverDriverTests.kt index c22f8f30a4..9163f06199 100644 --- a/webserver/src/integration-test/kotlin/net/corda/webserver/WebserverDriverTests.kt +++ b/webserver/src/integration-test/kotlin/net/corda/webserver/WebserverDriverTests.kt @@ -3,13 +3,13 @@ package net.corda.webserver import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.getOrThrow import net.corda.testing.DUMMY_BANK_A_NAME -import net.corda.testing.IntegrationTest -import net.corda.testing.IntegrationTestSchemas +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.driver.WebserverHandle import net.corda.testing.driver.driver import net.corda.testing.node.internal.addressMustBeBound import net.corda.testing.node.internal.addressMustNotBeBound -import net.corda.testing.toDatabaseSchemaName +import net.corda.testing.internal.toDatabaseSchemaName import org.junit.ClassRule import org.junit.Test import java.util.concurrent.Executors