Merge pull request #292 from corda/shams-os-merge-040118

Shams os merge 040118
This commit is contained in:
Shams Asari 2018-01-05 17:13:17 +00:00 committed by GitHub
commit dad207ef7d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
131 changed files with 1602 additions and 941 deletions

View File

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

View File

@ -31,6 +31,9 @@ import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.internal.config.User import net.corda.nodeapi.internal.config.User
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.driver.driver 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.ClassRule
import org.junit.Test import org.junit.Test
import rx.Observable import rx.Observable
@ -54,7 +57,7 @@ class NodeMonitorModelTest : IntegrationTest() {
companion object { companion object {
@ClassRule @JvmField @ClassRule @JvmField
val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, CHARLIE_NAME, DUMMY_NOTARY_NAME) 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) { private fun setup(runTest: () -> Unit) {

View File

@ -12,8 +12,8 @@ import net.corda.node.internal.Node;
import net.corda.node.internal.StartedNode; import net.corda.node.internal.StartedNode;
import net.corda.nodeapi.internal.config.User; import net.corda.nodeapi.internal.config.User;
import net.corda.testing.CoreTestUtils; import net.corda.testing.CoreTestUtils;
import net.corda.testing.IntegrationTestKt; import net.corda.testing.internal.IntegrationTestKt;
import net.corda.testing.IntegrationTestSchemas; import net.corda.testing.internal.IntegrationTestSchemas;
import net.corda.testing.node.internal.NodeBasedTest; import net.corda.testing.node.internal.NodeBasedTest;
import org.junit.After; import org.junit.After;
import org.junit.Before; 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.invokeRpc;
import static net.corda.node.services.Permissions.startFlow; import static net.corda.node.services.Permissions.startFlow;
import static net.corda.testing.TestConstants.ALICE_NAME; import static net.corda.testing.TestConstants.ALICE_NAME;
import static net.corda.testing.TestConstants.DUMMY_NOTARY_NAME;
public class CordaRPCJavaClientTest extends NodeBasedTest { public class CordaRPCJavaClientTest extends NodeBasedTest {
public CordaRPCJavaClientTest() { public CordaRPCJavaClientTest() {

View File

@ -9,6 +9,9 @@ import net.corda.core.serialization.CordaSerializable
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.driver.driver 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.assertj.core.api.Assertions.assertThatExceptionOfType
import org.junit.ClassRule import org.junit.ClassRule
import org.junit.Test import org.junit.Test

View File

@ -22,6 +22,8 @@ import net.corda.node.services.Permissions.Companion.invokeRpc
import net.corda.node.services.Permissions.Companion.startFlow import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.internal.config.User import net.corda.nodeapi.internal.config.User
import net.corda.testing.* 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.internal.NodeBasedTest
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat

View File

@ -13,7 +13,10 @@ import net.corda.core.utilities.*
import net.corda.node.services.messaging.RPCServerConfiguration import net.corda.node.services.messaging.RPCServerConfiguration
import net.corda.nodeapi.RPCApi import net.corda.nodeapi.RPCApi
import net.corda.testing.* 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.testThreadFactory
import net.corda.testing.internal.toDatabaseSchemaNames
import net.corda.testing.node.internal.* import net.corda.testing.node.internal.*
import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.api.core.SimpleString
import org.junit.After import org.junit.After
@ -45,7 +48,7 @@ class RPCStabilityTests : IntegrationTest() {
companion object { companion object {
@ClassRule @JvmField @ClassRule @JvmField
val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, DUMMY_BANK_A_NAME) 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 { object DummyOps : RPCOps {

View File

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

View File

@ -1,4 +1,4 @@
gradlePluginsVersion=3.0.2 gradlePluginsVersion=3.0.3
kotlinVersion=1.1.60 kotlinVersion=1.1.60
platformVersion=1 platformVersion=1
guavaVersion=21.0 guavaVersion=21.0

View File

@ -30,6 +30,7 @@ import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPublicKey
import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter
import org.bouncycastle.jce.ECNamedCurveTable import org.bouncycastle.jce.ECNamedCurveTable
import org.bouncycastle.jce.provider.BouncyCastleProvider import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec
import org.bouncycastle.jce.spec.ECParameterSpec import org.bouncycastle.jce.spec.ECParameterSpec
import org.bouncycastle.jce.spec.ECPrivateKeySpec import org.bouncycastle.jce.spec.ECPrivateKeySpec
import org.bouncycastle.jce.spec.ECPublicKeySpec 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 * 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. * 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 signatureScheme a supported [SignatureScheme], see [Crypto].
* @param entropy a [BigInteger] value. * @param entropy a [BigInteger] value.
* @return a new [KeyPair] from an entropy input. * @return a new [KeyPair] from an entropy input.
@ -818,6 +819,7 @@ object Crypto {
fun deriveKeyPairFromEntropy(signatureScheme: SignatureScheme, entropy: BigInteger): KeyPair { fun deriveKeyPairFromEntropy(signatureScheme: SignatureScheme, entropy: BigInteger): KeyPair {
return when (signatureScheme) { return when (signatureScheme) {
EDDSA_ED25519_SHA512 -> deriveEdDSAKeyPairFromEntropy(entropy) 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 " + else -> throw IllegalArgumentException("Unsupported signature scheme for fixed entropy-based key pair " +
"generation: ${signatureScheme.schemeCodeName}") "generation: ${signatureScheme.schemeCodeName}")
} }
@ -832,6 +834,9 @@ object Crypto {
fun deriveKeyPairFromEntropy(entropy: BigInteger): KeyPair = deriveKeyPairFromEntropy(DEFAULT_SIGNATURE_SCHEME, entropy) fun deriveKeyPairFromEntropy(entropy: BigInteger): KeyPair = deriveKeyPairFromEntropy(DEFAULT_SIGNATURE_SCHEME, entropy)
// Custom key pair generator from 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 { private fun deriveEdDSAKeyPairFromEntropy(entropy: BigInteger): KeyPair {
val params = EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec 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. 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)) 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. // Compute the HMAC-SHA512 using a privateKey as the MAC_key and a seed ByteArray.
private fun deriveHMAC(privateKey: PrivateKey, seed: ByteArray): ByteArray { private fun deriveHMAC(privateKey: PrivateKey, seed: ByteArray): ByteArray {
// Compute hmac(privateKey, seed). // Compute hmac(privateKey, seed).

View File

@ -118,18 +118,19 @@ fun Iterable<TransactionSignature>.byKeys() = map { it.by }.toSet()
// Allow Kotlin destructuring: // Allow Kotlin destructuring:
// val (private, public) = keyPair // val (private, public) = keyPair
/* The [PrivateKey] of this [KeyPair] .*/ /* The [PrivateKey] of this [KeyPair]. */
operator fun KeyPair.component1(): PrivateKey = this.private operator fun KeyPair.component1(): PrivateKey = this.private
/* The [PublicKey] of this [KeyPair] .*/ /* The [PublicKey] of this [KeyPair]. */
operator fun KeyPair.component2(): PublicKey = this.public 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() 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 * 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. * 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) fun entropyToKeyPair(entropy: BigInteger): KeyPair = Crypto.deriveKeyPairFromEntropy(entropy)

View File

@ -10,7 +10,17 @@ import net.corda.core.serialization.CordaSerializable
*/ */
@DoNotImplement @DoNotImplement
interface FlowLogicRefFactory { 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<out FlowLogic<*>>, vararg args: Any?): FlowLogicRef fun create(flowClass: Class<out FlowLogic<*>>, 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<out FlowLogic<*>>, vararg args: Any?): FlowLogicRef fun createForRPC(flowClass: Class<out FlowLogic<*>>, vararg args: Any?): FlowLogicRef
fun toFlowLogic(ref: FlowLogicRef): FlowLogic<*> fun toFlowLogic(ref: FlowLogicRef): FlowLogic<*>
} }

View File

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

View File

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

View File

@ -1,34 +0,0 @@
@file:JvmName("X500NameUtils")
package net.corda.core.internal
import net.corda.core.identity.CordaX500Name
import org.bouncycastle.asn1.ASN1ObjectIdentifier
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.asn1.x500.X500NameBuilder
import org.bouncycastle.asn1.x500.style.BCStyle
val X500Name.commonName: String? get() = getRDNValueString(BCStyle.CN)
val X500Name.state: String? get() = getRDNValueString(BCStyle.ST)
val X500Name.organisation: String get() = getRDNValueString(BCStyle.O) ?: throw IllegalArgumentException("Malformed X500 name, organisation attribute (O) cannot be empty.")
val X500Name.locality: String get() = getRDNValueString(BCStyle.L) ?: throw IllegalArgumentException("Malformed X500 name, locality attribute (L) cannot be empty.")
val X500Name.country: String get() = getRDNValueString(BCStyle.C) ?: throw IllegalArgumentException("Malformed X500 name, country attribute (C) cannot be empty.")
private fun X500Name.getRDNValueString(identifier: ASN1ObjectIdentifier): String? = getRDNs(identifier).firstOrNull()?.first?.value?.toString()
/**
* Return the underlying X.500 name from this Corda-safe X.500 name. These are guaranteed to have a consistent
* ordering, such that their `toString()` function returns the same value every time for the same [CordaX500Name].
*/
val CordaX500Name.x500Name: X500Name
get() {
return X500NameBuilder(BCStyle.INSTANCE).apply {
addRDN(BCStyle.C, country)
state?.let { addRDN(BCStyle.ST, it) }
addRDN(BCStyle.L, locality)
addRDN(BCStyle.O, organisation)
organisationUnit?.let { addRDN(BCStyle.OU, it) }
commonName?.let { addRDN(BCStyle.CN, it) }
}.build()
}

View File

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

View File

@ -14,21 +14,22 @@ import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey
import org.bouncycastle.jce.ECNamedCurveTable import org.bouncycastle.jce.ECNamedCurveTable
import org.bouncycastle.jce.interfaces.ECKey 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.BCSphincs256PrivateKey
import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PublicKey import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PublicKey
import org.junit.Assert.assertNotEquals import org.junit.Assert.assertNotEquals
import org.junit.Test import org.junit.Test
import java.math.BigInteger
import java.security.KeyPairGenerator import java.security.KeyPairGenerator
import java.util.* import java.util.*
import kotlin.test.* import kotlin.test.*
/** /**
* Run tests for cryptographic algorithms * Run tests for cryptographic algorithms.
*/ */
class CryptoUtilsTest { class CryptoUtilsTest {
private val testString = "Hello World" private val testBytes = "Hello World".toByteArray()
private val testBytes = testString.toByteArray()
// key generation test // key generation test
@Test @Test
@ -781,4 +782,113 @@ class CryptoUtilsTest {
assertEquals(dpriv2, dpriv_2) assertEquals(dpriv2, dpriv_2)
assertEquals(dpub2, dpub_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())
}
} }

View File

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

View File

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

View File

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

View File

@ -16,6 +16,9 @@ import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.internal.config.User import net.corda.nodeapi.internal.config.User
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.driver.driver 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.ClassRule
import org.junit.Test import org.junit.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals

View File

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

View File

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

View File

@ -6,6 +6,9 @@ import net.corda.finance.EUR
import net.corda.finance.USD import net.corda.finance.USD
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.driver.driver 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.assertThat
import org.junit.ClassRule import org.junit.ClassRule
import org.junit.Test import org.junit.Test
@ -14,7 +17,7 @@ class CashConfigDataFlowTest : IntegrationTest() {
companion object { companion object {
@ClassRule @JvmField @ClassRule @JvmField
val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, DUMMY_BANK_A_NAME) 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 @Test
fun `issuable currencies are read in from node config`() { fun `issuable currencies are read in from node config`() {

View File

@ -29,8 +29,8 @@ import java.util.*
class CashExitFlow(private val amount: Amount<Currency>, class CashExitFlow(private val amount: Amount<Currency>,
private val issuerRef: OpaqueBytes, private val issuerRef: OpaqueBytes,
progressTracker: ProgressTracker) : AbstractCashFlow<AbstractCashFlow.Result>(progressTracker) { progressTracker: ProgressTracker) : AbstractCashFlow<AbstractCashFlow.Result>(progressTracker) {
constructor(amount: Amount<Currency>, issueRef: OpaqueBytes) : this(amount, issueRef, tracker()) constructor(amount: Amount<Currency>, issuerRef: OpaqueBytes) : this(amount, issuerRef, tracker())
constructor(request: ExitRequest) : this(request.amount, request.issueRef, tracker()) constructor(request: ExitRequest) : this(request.amount, request.issuerRef, tracker())
companion object { companion object {
fun tracker() = ProgressTracker(GENERATING_TX, SIGNING_TX, FINALISING_TX) fun tracker() = ProgressTracker(GENERATING_TX, SIGNING_TX, FINALISING_TX)
@ -78,5 +78,5 @@ class CashExitFlow(private val amount: Amount<Currency>,
} }
@CordaSerializable @CordaSerializable
class ExitRequest(amount: Amount<Currency>, val issueRef: OpaqueBytes) : AbstractRequest(amount) class ExitRequest(amount: Amount<Currency>, val issuerRef: OpaqueBytes) : AbstractRequest(amount)
} }

View File

@ -6,7 +6,7 @@ Generates a text summary of Corda's public API that we can check for API-breakin
$ gradlew generateApi $ 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. 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, Basically, this plugin will document a module's `public` and `protected` classes/methods/fields,

View File

@ -22,6 +22,7 @@ import java.util.jar.JarInputStream
@Suppress("unused") @Suppress("unused")
open class Cordform : DefaultTask() { open class Cordform : DefaultTask() {
private companion object { private companion object {
val nodeJarName = "corda.jar"
private val defaultDirectory: Path = Paths.get("build", "nodes") private val defaultDirectory: Path = Paths.get("build", "nodes")
} }
@ -132,9 +133,26 @@ open class Cordform : DefaultTask() {
fun build() { fun build() {
project.logger.info("Running Cordform task") project.logger.info("Running Cordform task")
initializeConfiguration() initializeConfiguration()
nodes.forEach(Node::installConfig)
installCordaJar()
installRunScript() installRunScript()
nodes.forEach(Node::build)
bootstrapNetwork() 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() { private fun initializeConfiguration() {
@ -150,8 +168,8 @@ open class Cordform : DefaultTask() {
cd.nodeConfigurers.forEach { cd.nodeConfigurers.forEach {
val node = node { } val node = node { }
it.accept(node) it.accept(node)
node.additionalCordapps.addAll(cordapps)
node.rootDir(directory) node.rootDir(directory)
node.installCordapps(cordapps)
} }
cd.setup { nodeName -> project.projectDir.toPath().resolve(getNodeByName(nodeName)!!.nodeDir.toPath()) } cd.setup { nodeName -> project.projectDir.toPath().resolve(getNodeByName(nodeName)!!.nodeDir.toPath()) }
} else { } else {

View File

@ -20,14 +20,35 @@ class Cordformation : Plugin<Project> {
* @return A file handle to the file in the JAR. * @return A file handle to the file in the JAR.
*/ */
fun getPluginFile(project: Project, filePathInJar: String): File { fun getPluginFile(project: Project, filePathInJar: String): File {
val archive: File? = project.rootProject.buildscript.configurations val archive = project.rootProject.buildscript.configurations
.single { it.name == "classpath" } .single { it.name == "classpath" }
.find { it.name.contains("cordformation") } .first { it.name.contains("cordformation") }
return project.rootProject.resources.text return project.rootProject.resources.text
.fromArchiveEntry(archive, filePathInJar) .fromArchiveEntry(archive, filePathInJar)
.asFile() .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<String>("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) val executableFileMode = "0755".toInt(8)
} }

View File

@ -15,8 +15,6 @@ import java.nio.file.Path
*/ */
class Node(private val project: Project) : CordformNode() { class Node(private val project: Project) : CordformNode() {
companion object { companion object {
@JvmStatic
val nodeJarName = "corda.jar"
@JvmStatic @JvmStatic
val webJarName = "corda-webserver.jar" val webJarName = "corda-webserver.jar"
private val configFileProperty = "configFile" 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 * @note Type is any due to gradle's use of "GStrings" - each value will have "toString" called on it
*/ */
var cordapps = mutableListOf<Any>() var cordapps = mutableListOf<Any>()
var additionalCordapps = mutableListOf<File>()
private val releaseVersion = project.rootProject.ext<String>("corda_release_version")
internal lateinit var nodeDir: File internal lateinit var nodeDir: File
private set private set
internal lateinit var rootDir: File
private set
/** /**
* Sets whether this node will use HTTPS communication. * Sets whether this node will use HTTPS communication.
@ -65,16 +64,12 @@ class Node(private val project: Project) : CordformNode() {
} }
internal fun build() { internal fun build() {
configureProperties()
installCordaJar()
if (config.hasPath("webAddress")) { if (config.hasPath("webAddress")) {
installWebserverJar() installWebserverJar()
} }
installAgentJar() installAgentJar()
installBuiltCordapp() installBuiltCordapp()
installCordapps() installCordapps()
installConfig()
appendOptionalConfig()
} }
internal fun rootDir(rootDir: Path) { internal fun rootDir(rootDir: Path) {
@ -86,7 +81,9 @@ class Node(private val project: Project) : CordformNode() {
// with loading our custom X509EdDSAEngine. // with loading our custom X509EdDSAEngine.
val organizationName = name.trim().split(",").firstOrNull { it.startsWith("O=") }?.substringAfter("=") val organizationName = name.trim().split(",").firstOrNull { it.startsWith("O=") }?.substringAfter("=")
val dirName = organizationName ?: name 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() { 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 * Installs the corda webserver JAR to the node directory
*/ */
private fun installWebserverJar() { private fun installWebserverJar() {
val webJar = verifyAndGetRuntimeJar("corda-webserver") val webJar = Cordformation.verifyAndGetRuntimeJar(project, "corda-webserver")
project.copy { project.copy {
it.apply { it.apply {
from(webJar) 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<File> = 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 * 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<String>("jolokia_version") val jolokiaVersion = project.rootProject.ext<String>("jolokia_version")
val agentJar = project.configuration("runtime").files { val agentJar = project.configuration("runtime").files {
(it.group == "org.jolokia") && (it.group == "org.jolokia") &&
(it.name == "jolokia-jvm") && (it.name == "jolokia-jvm") &&
(it.version == jolokiaVersion) (it.version == jolokiaVersion)
// TODO: revisit when classifier attribute is added. eg && (it.classifier = "agent") // 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 }.first() // should always be the jolokia agent fat jar: eg. jolokia-jvm-1.3.7-agent.jar
project.logger.info("Jolokia agent jar: $agentJar") project.logger.info("Jolokia agent jar: $agentJar")
@ -177,10 +146,7 @@ class Node(private val project: Project) : CordformNode() {
} }
} }
/** private fun createTempConfigFile(): File {
* Installs the configuration file to this node's directory and detokenises it.
*/
private fun installConfig() {
val options = ConfigRenderOptions val options = ConfigRenderOptions
.defaults() .defaults()
.setOriginComments(false) .setOriginComments(false)
@ -188,16 +154,26 @@ class Node(private val project: Project) : CordformNode() {
.setFormatted(true) .setFormatted(true)
.setJson(false) .setJson(false)
val configFileText = config.root().render(options).split("\n").toList() 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. // Need to write a temporary file first to use the project.copy, which resolves directories correctly.
val tmpDir = File(project.buildDir, "tmp") 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) 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 { project.copy {
it.apply { it.apply {
from(tmpConfFile) 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. * Appends installed config file with properties from an optional file.
*/ */
private fun appendOptionalConfig() { private fun appendOptionalConfig(confFile: File) {
val optionalConfig: File? = when { val optionalConfig: File? = when {
project.findProperty(configFileProperty) != null -> //provided by -PconfigFile command line property when running Gradle task project.findProperty(configFileProperty) != null -> //provided by -PconfigFile command line property when running Gradle task
File(project.findProperty(configFileProperty) as String) File(project.findProperty(configFileProperty) as String)
@ -217,28 +193,22 @@ class Node(private val project: Project) : CordformNode() {
if (!optionalConfig.exists()) { if (!optionalConfig.exists()) {
project.logger.error("$configFileProperty '$optionalConfig' not found") project.logger.error("$configFileProperty '$optionalConfig' not found")
} else { } else {
val confFile = File(project.buildDir.path + "/../" + nodeDir, "node.conf")
confFile.appendBytes(optionalConfig.readBytes()) confFile.appendBytes(optionalConfig.readBytes())
} }
} }
} }
/** /**
* Find the given JAR amongst the dependencies * Installs other cordapps to this node's cordapps directory.
* @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
*/ */
private fun verifyAndGetRuntimeJar(jarName: String): File { internal fun installCordapps() {
val maybeJar = project.configuration("runtime").filter { additionalCordapps.addAll(getCordappList())
"$jarName-$releaseVersion.jar" in it.toString() || "$jarName-enterprise-$releaseVersion.jar" in it.toString() val cordappsDir = File(nodeDir, "cordapps")
} project.copy {
if (maybeJar.isEmpty) { it.apply {
throw IllegalStateException("No $jarName JAR found. Have you deployed the Corda project to Maven? Looked for \"$jarName-$releaseVersion.jar\"") from(additionalCordapps)
} else { into(cordappsDir)
val jar = maybeJar.singleFile }
require(jar.isFile)
return jar
} }
} }

View File

@ -3,14 +3,11 @@ package com.r3.corda.networkmanage.doorman
import com.nhaarman.mockito_kotlin.doReturn import com.nhaarman.mockito_kotlin.doReturn
import com.nhaarman.mockito_kotlin.whenever import com.nhaarman.mockito_kotlin.whenever
import com.r3.corda.networkmanage.common.persistence.configureDatabase 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 com.r3.corda.networkmanage.doorman.signer.LocalSigner
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.sign import net.corda.core.crypto.sign
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.PartyAndCertificate import net.corda.core.identity.PartyAndCertificate
import net.corda.core.internal.cert
import net.corda.core.internal.createDirectories import net.corda.core.internal.createDirectories
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.node.NodeInfo 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.SerializationEnvironmentRule
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.internal.rigorousMock import net.corda.testing.internal.rigorousMock
import org.bouncycastle.cert.X509CertificateHolder
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.TemporaryFolder import org.junit.rules.TemporaryFolder
import java.net.URL import java.net.URL
import java.security.cert.X509Certificate
import java.util.* import java.util.*
import javax.security.auth.x500.X500Principal
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertNotNull import kotlin.test.assertNotNull
@ -60,7 +58,7 @@ class DoormanIntegrationTest {
} }
config.trustStoreFile.parent.createDirectories() config.trustStoreFile.parent.createDirectories()
loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also { 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) it.save(config.trustStoreFile, config.trustStorePassword)
} }
@ -77,18 +75,18 @@ class DoormanIntegrationTest {
loadKeyStore(config.nodeKeystore, config.keyStorePassword).apply { loadKeyStore(config.nodeKeystore, config.keyStorePassword).apply {
assert(containsAlias(X509Utilities.CORDA_CLIENT_CA)) assert(containsAlias(X509Utilities.CORDA_CLIENT_CA))
assertEquals(ALICE_NAME.x500Principal, getX509Certificate(X509Utilities.CORDA_CLIENT_CA).subjectX500Principal) 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 { loadKeyStore(config.sslKeystore, config.keyStorePassword).apply {
assert(containsAlias(X509Utilities.CORDA_CLIENT_TLS)) assert(containsAlias(X509Utilities.CORDA_CLIENT_TLS))
assertEquals(ALICE_NAME.x500Principal, getX509Certificate(X509Utilities.CORDA_CLIENT_TLS).subjectX500Principal) 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 { loadKeyStore(config.trustStoreFile, config.trustStorePassword).apply {
assert(containsAlias(X509Utilities.CORDA_ROOT_CA)) 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() doorman.close()
@ -110,21 +108,26 @@ class DoormanIntegrationTest {
} }
config.trustStoreFile.parent.createDirectories() config.trustStoreFile.parent.createDirectories()
loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also { 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) it.save(config.trustStoreFile, config.trustStorePassword)
} }
NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.compatibilityZoneURL!!)).buildKeystore() NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.compatibilityZoneURL!!)).buildKeystore()
// Publish NodeInfo // 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 keyStore = loadKeyStore(config.nodeKeystore, config.keyStorePassword)
val clientCertPath = keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA) val clientCertPath = keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
val clientCA = keyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, config.keyStorePassword) val clientCA = keyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, config.keyStorePassword)
val identityKeyPair = Crypto.generateKeyPair() val identityKeyPair = Crypto.generateKeyPair()
val identityCert = X509Utilities.createCertificate(CertificateType.LEGAL_IDENTITY, clientCA.certificate, clientCA.keyPair, ALICE_NAME, identityKeyPair.public) val identityCert = X509Utilities.createCertificate(
val certPath = X509CertificateFactory().generateCertPath(identityCert.cert, *clientCertPath) 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 nodeInfo = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(certPath)), 1, serial = 1L)
val nodeInfoBytes = nodeInfo.serialize() val nodeInfoBytes = nodeInfo.serialize()
@ -156,23 +159,23 @@ class DoormanIntegrationTest {
} }
fun createDoormanIntermediateCertificateAndKeyPair(rootCertificateAndKeyPair: CertificateAndKeyPair): CertificateAndKeyPair { fun createDoormanIntermediateCertificateAndKeyPair(rootCa: CertificateAndKeyPair): CertificateAndKeyPair {
val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCertificateAndKeyPair.certificate, rootCertificateAndKeyPair.keyPair, val intermediateCACert = X509Utilities.createCertificate(
CordaX500Name(commonName = "Integration Test Corda Node Intermediate CA", CertificateType.INTERMEDIATE_CA,
locality = "London", rootCa.certificate,
country = "GB", rootCa.keyPair,
organisation = "R3 Ltd"), intermediateCAKey.public) X500Principal("CN=Integration Test Corda Node Intermediate CA,O=R3 Ltd,L=London,C=GB"),
return CertificateAndKeyPair(intermediateCACert, intermediateCAKey) keyPair.public)
return CertificateAndKeyPair(intermediateCACert, keyPair)
} }
fun createDoormanRootCertificateAndKeyPair(): CertificateAndKeyPair { fun createDoormanRootCertificateAndKeyPair(): CertificateAndKeyPair {
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val rootCACert = X509Utilities.createSelfSignedCACertificate( val rootCaCert = X509Utilities.createSelfSignedCACertificate(
CordaX500Name(commonName = "Integration Test Corda Node Root CA", X500Principal("CN=Integration Test Corda Node Root CA,O=R3 Ltd,L=London,C=GB"),
organisation = "R3 Ltd", locality = "London", keyPair)
country = "GB"), rootCAKey) return CertificateAndKeyPair(rootCaCert, keyPair)
return CertificateAndKeyPair(rootCACert, rootCAKey)
} }
fun makeTestDataSourceProperties(nodeName: String = SecureHash.randomSHA256().toString()): Properties { fun makeTestDataSourceProperties(nodeName: String = SecureHash.randomSHA256().toString()): Properties {
@ -184,9 +187,8 @@ fun makeTestDataSourceProperties(nodeName: String = SecureHash.randomSHA256().to
return props return props
} }
fun startDoorman(intermediateCACertAndKey: CertificateAndKeyPair, rootCACert: X509CertificateHolder): NetworkManagementServer { fun startDoorman(intermediateCACertAndKey: CertificateAndKeyPair, rootCACert: X509Certificate): NetworkManagementServer {
val signer = LocalSigner(intermediateCACertAndKey.keyPair, val signer = LocalSigner(intermediateCACertAndKey.keyPair, arrayOf(intermediateCACertAndKey.certificate, rootCACert))
arrayOf(intermediateCACertAndKey.certificate.toX509Certificate(), rootCACert.toX509Certificate()))
//Start doorman server //Start doorman server
return startDoorman(signer) return startDoorman(signer)
} }

View File

@ -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.persistence.SignedCertificateRequestStorage
import com.r3.corda.networkmanage.hsm.signer.HsmCsrSigner import com.r3.corda.networkmanage.hsm.signer.HsmCsrSigner
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.cert
import net.corda.core.internal.createDirectories import net.corda.core.internal.createDirectories
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.internal.uncheckedCast 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.SerializationEnvironmentRule
import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.testing.internal.createDevIntermediateCaCertPath
import net.corda.testing.internal.rigorousMock import net.corda.testing.internal.rigorousMock
import org.bouncycastle.cert.X509CertificateHolder
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
import org.h2.tools.Server import org.h2.tools.Server
import org.junit.* import org.junit.*
import org.junit.rules.TemporaryFolder import org.junit.rules.TemporaryFolder
import java.net.URL import java.net.URL
import java.security.cert.X509Certificate
import java.util.* import java.util.*
import javax.persistence.PersistenceException import javax.persistence.PersistenceException
import kotlin.concurrent.scheduleAtFixedRate import kotlin.concurrent.scheduleAtFixedRate
@ -55,7 +54,7 @@ class SigningServiceIntegrationTest {
val testSerialization = SerializationEnvironmentRule(true) val testSerialization = SerializationEnvironmentRule(true)
private lateinit var timer: Timer private lateinit var timer: Timer
private lateinit var rootCaCert: X509CertificateHolder private lateinit var rootCaCert: X509Certificate
private lateinit var intermediateCa: CertificateAndKeyPair private lateinit var intermediateCa: CertificateAndKeyPair
@Before @Before
@ -79,7 +78,7 @@ class SigningServiceIntegrationTest {
for (approvedRequest in approvedRequests) { for (approvedRequest in approvedRequests) {
JcaPKCS10CertificationRequest(approvedRequest.request).run { JcaPKCS10CertificationRequest(approvedRequest.request).run {
val nodeCa = createDevNodeCa(intermediateCa, CordaX500Name.parse(subject.toString())) 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")) storage.store(approvedRequests, listOf("TEST"))
@ -124,7 +123,7 @@ class SigningServiceIntegrationTest {
} }
config.certificatesDirectory.createDirectories() config.certificatesDirectory.createDirectories()
loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also { 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) it.save(config.trustStoreFile, config.trustStorePassword)
} }
NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.compatibilityZoneURL!!)).buildKeystore() NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.compatibilityZoneURL!!)).buildKeystore()
@ -168,7 +167,7 @@ class SigningServiceIntegrationTest {
} }
config.certificatesDirectory.createDirectories() config.certificatesDirectory.createDirectories()
loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also { 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) it.save(config.trustStoreFile, config.trustStorePassword)
} }
NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.compatibilityZoneURL!!)).buildKeystore() NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.compatibilityZoneURL!!)).buildKeystore()

View File

@ -5,15 +5,14 @@ import com.r3.corda.networkmanage.common.persistence.entity.CertificateSigningRe
import com.r3.corda.networkmanage.common.utils.hashString import com.r3.corda.networkmanage.common.utils.hashString
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.x500Name
import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.DatabaseTransaction import net.corda.nodeapi.internal.persistence.DatabaseTransaction
import net.corda.nodeapi.internal.persistence.TransactionIsolationLevel import net.corda.nodeapi.internal.persistence.TransactionIsolationLevel
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.pkcs.PKCS10CertificationRequest import org.bouncycastle.pkcs.PKCS10CertificationRequest
import org.hibernate.Session import org.hibernate.Session
import java.security.cert.CertPath import java.security.cert.CertPath
import java.time.Instant import java.time.Instant
import javax.security.auth.x500.X500Principal
/** /**
* Database implementation of the [CertificationRequestStorage] interface. * Database implementation of the [CertificationRequestStorage] interface.
@ -48,7 +47,7 @@ class PersistentCertificateRequestStorage(private val database: CordaPersistence
val (legalName, rejectReason) = parseAndValidateLegalName(request, session) val (legalName, rejectReason) = parseAndValidateLegalName(request, session)
session.save(CertificateSigningRequestEntity( session.save(CertificateSigningRequestEntity(
requestId = requestId, requestId = requestId,
legalName = legalName.toString(), legalName = legalName,
requestBytes = request.encoded, requestBytes = request.encoded,
remark = rejectReason, remark = rejectReason,
modifiedBy = emptyList(), modifiedBy = emptyList(),
@ -126,25 +125,27 @@ class PersistentCertificateRequestStorage(private val database: CordaPersistence
} }
} }
private fun parseAndValidateLegalName(request: PKCS10CertificationRequest, session: Session): Pair<X500Name, String?> { private fun parseAndValidateLegalName(request: PKCS10CertificationRequest, session: Session): Pair<String, String?> {
// 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 { val legalName = try {
CordaX500Name.parse(request.subject.toString()) CordaX500Name.build(X500Principal(request.subject.encoded)).toString()
} catch (e: IllegalArgumentException) { } 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 query = session.criteriaBuilder.run {
val criteriaQuery = createQuery(CertificateSigningRequestEntity::class.java) val criteriaQuery = createQuery(CertificateSigningRequestEntity::class.java)
criteriaQuery.from(CertificateSigningRequestEntity::class.java).run { criteriaQuery.from(CertificateSigningRequestEntity::class.java).run {
criteriaQuery.where(equal(get<String>(CertificateSigningRequestEntity::legalName.name), legalName.toString())) criteriaQuery.where(equal(get<String>(CertificateSigningRequestEntity::legalName.name), legalName))
} }
} }
val duplicates = session.createQuery(query).resultList.filter { val duplicates = session.createQuery(query).resultList.filter {
it.status in setOf(RequestStatus.NEW, RequestStatus.TICKET_CREATED, RequestStatus.APPROVED) || it.certificateData?.certificateStatus == CertificateStatus.VALID 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")
} }
return Pair(legalName, if (duplicates.isEmpty()) null else "Duplicate legal name")
} }
} }

View File

@ -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.IssueType
import com.atlassian.jira.rest.client.api.domain.input.IssueInputBuilder import com.atlassian.jira.rest.client.api.domain.input.IssueInputBuilder
import com.atlassian.jira.rest.client.api.domain.input.TransitionInput import com.atlassian.jira.rest.client.api.domain.input.TransitionInput
import net.corda.core.internal.country import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.locality
import net.corda.core.internal.organisation
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities
import org.bouncycastle.asn1.x500.style.BCStyle import org.bouncycastle.asn1.x500.style.BCStyle
@ -17,6 +15,7 @@ import org.bouncycastle.pkcs.PKCS10CertificationRequest
import org.bouncycastle.util.io.pem.PemObject import org.bouncycastle.util.io.pem.PemObject
import java.io.StringWriter import java.io.StringWriter
import java.security.cert.CertPath 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) { class JiraClient(private val restClient: JiraRestClient, private val projectCode: String, private val doneTransitionCode: Int) {
companion object { companion object {
@ -39,16 +38,17 @@ class JiraClient(private val restClient: JiraRestClient, private val projectCode
JcaPEMWriter(request).use { JcaPEMWriter(request).use {
it.writeObject(PemObject("CERTIFICATE REQUEST", signingRequest.encoded)) it.writeObject(PemObject("CERTIFICATE REQUEST", signingRequest.encoded))
} }
val organisation = signingRequest.subject.organisation
val nearestCity = signingRequest.subject.locality // TODO The subject of the signing request has already been validated and parsed into a CordaX500Name. We shouldn't
val country = signingRequest.subject.country // 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 email = signingRequest.getAttributes(BCStyle.E).firstOrNull()?.attrValues?.firstOrNull()?.toString()
val issue = IssueInputBuilder().setIssueTypeId(taskIssueType.id) val issue = IssueInputBuilder().setIssueTypeId(taskIssueType.id)
.setProjectKey(projectCode) .setProjectKey(projectCode)
.setDescription("Organisation: $organisation\nNearest City: $nearestCity\nCountry: $country\nEmail: $email\n\n{code}$request{code}") .setDescription("Organisation: ${subject.organisation}\nNearest City: ${subject.locality}\nCountry: ${subject.country}\nEmail: $email\n\n{code}$request{code}")
.setSummary(organisation) .setSummary(subject.organisation)
.setFieldValue(requestIdField.id, requestId) .setFieldValue(requestIdField.id, requestId)
// This will block until the issue is created. // This will block until the issue is created.
restClient.issueClient.createIssue(issue.build()).fail { logger.error("Exception when creating JIRA issue.", it) }.claim() restClient.issueClient.createIssue(issue.build()).fail { logger.error("Exception when creating JIRA issue.", it) }.claim()

View File

@ -13,7 +13,6 @@ import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService
import com.r3.corda.networkmanage.doorman.webservice.RegistrationWebService import com.r3.corda.networkmanage.doorman.webservice.RegistrationWebService
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.cert
import net.corda.core.internal.createDirectories import net.corda.core.internal.createDirectories
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.serialization.internal.SerializationEnvironmentImpl 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) val selfSignKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
// TODO Make the cert subject configurable // TODO Make the cert subject configurable
val selfSignCert = X509Utilities.createSelfSignedCACertificate( val selfSignCert = X509Utilities.createSelfSignedCACertificate(
CordaX500Name(commonName = "Corda Root CA", organisation = "R3 Ltd", locality = "London", country = "GB", organisationUnit = "Corda", state = null), CordaX500Name(commonName = "Corda Root CA", organisation = "R3 Ltd", locality = "London", country = "GB", organisationUnit = "Corda", state = null).x500Principal,
selfSignKey).cert selfSignKey)
rootStore.addOrReplaceKey(X509Utilities.CORDA_ROOT_CA, selfSignKey.private, rootPrivateKeyPassword.toCharArray(), arrayOf(selfSignCert)) rootStore.addOrReplaceKey(X509Utilities.CORDA_ROOT_CA, selfSignKey.private, rootPrivateKeyPassword.toCharArray(), arrayOf(selfSignCert))
rootStore.save(rootStoreFile, rootKeystorePassword) rootStore.save(rootStoreFile, rootKeystorePassword)
@ -226,11 +225,20 @@ fun generateCAKeyPair(keystoreFile: Path, rootStoreFile: Path, rootKeystorePass:
exitProcess(1) exitProcess(1)
} }
val intermediateKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val intermediateKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val intermediateCert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootKeyAndCert.certificate, rootKeyAndCert.keyPair, val intermediateCert = X509Utilities.createCertificate(
CordaX500Name(commonName = "Corda Intermediate CA", organisation = "R3 Ltd", organisationUnit = "Corda", locality = "London", country = "GB", state = null), intermediateKey.public) CertificateType.INTERMEDIATE_CA,
keyStore.addOrReplaceKey(X509Utilities.CORDA_INTERMEDIATE_CA, intermediateKey.private, rootKeyAndCert.certificate,
caPrivateKeyPassword.toCharArray(), arrayOf(intermediateCert, 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) keyStore.save(keystoreFile, keystorePassword)
println("Intermediate CA keypair and certificate stored in $keystoreFile.") println("Intermediate CA keypair and certificate stored in $keystoreFile.")
println(loadKeyStore(keystoreFile, keystorePassword).getCertificate(X509Utilities.CORDA_INTERMEDIATE_CA).publicKey) println(loadKeyStore(keystoreFile, keystorePassword).getCertificate(X509Utilities.CORDA_INTERMEDIATE_CA).publicKey)

View File

@ -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.buildCertPath
import com.r3.corda.networkmanage.common.utils.withCert import com.r3.corda.networkmanage.common.utils.withCert
import net.corda.core.crypto.sign 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.CertificateType
import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.nodeapi.internal.network.DigitalSignatureWithCert import net.corda.nodeapi.internal.network.DigitalSignatureWithCert
@ -18,6 +15,7 @@ import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
import java.security.KeyPair import java.security.KeyPair
import java.security.cert.CertPath import java.security.cert.CertPath
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import javax.security.auth.x500.X500Principal
/** /**
* The [LocalSigner] class signs [PKCS10CertificationRequest] using provided CA key pair and certificate path. * 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()) arrayOf())
val nodeCaCert = X509Utilities.createCertificate( val nodeCaCert = X509Utilities.createCertificate(
CertificateType.NODE_CA, CertificateType.NODE_CA,
caCertPath.first().toX509CertHolder(), caCertPath[0],
caKeyPair, caKeyPair,
CordaX500Name.parse(request.subject.toString()), X500Principal(request.subject.encoded),
request.publicKey, request.publicKey,
nameConstraints = nameConstraints) nameConstraints = nameConstraints)
return buildCertPath(nodeCaCert.cert, *caCertPath) return buildCertPath(nodeCaCert, *caCertPath)
} }
override fun sign(data: ByteArray): DigitalSignatureWithCert { override fun sign(data: ByteArray): DigitalSignatureWithCert {

View File

@ -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.getAndInitializeKeyStore
import com.r3.corda.networkmanage.hsm.utils.X509Utilities.signData import com.r3.corda.networkmanage.hsm.utils.X509Utilities.signData
import com.r3.corda.networkmanage.hsm.utils.X509Utilities.verify 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.loggerFor
import net.corda.core.utilities.minutes import net.corda.core.utilities.minutes
import net.corda.nodeapi.internal.network.DigitalSignatureWithCert import net.corda.nodeapi.internal.network.DigitalSignatureWithCert
import java.security.KeyPair import java.security.KeyPair
import java.security.PrivateKey import java.security.PrivateKey
import java.security.cert.X509Certificate
import java.time.Duration import java.time.Duration
import java.util.concurrent.Executors import java.util.concurrent.Executors
import java.util.concurrent.ScheduledExecutorService import java.util.concurrent.ScheduledExecutorService
@ -68,7 +67,7 @@ class HsmNetworkMapSigner(networkMapStorage: NetworkMapStorage,
val caKey = keyStore.getKey(caCertificateKeyName, caPrivateKeyPass.toCharArray()) as PrivateKey val caKey = keyStore.getKey(caCertificateKeyName, caPrivateKeyPass.toCharArray()) as PrivateKey
val signature = signData(data, KeyPair(caCertificateChain.first().publicKey, caKey), provider) val signature = signData(data, KeyPair(caCertificateChain.first().publicKey, caKey), provider)
verify(data, signature, caCertificateChain.first().publicKey) verify(data, signature, caCertificateChain.first().publicKey)
signature.withCert(caCertificateChain.first().toX509CertHolder().cert) signature.withCert(caCertificateChain[0] as X509Certificate)
} }
} }
} }

View File

@ -3,7 +3,6 @@ package com.r3.corda.networkmanage.hsm.utils
import CryptoServerJCE.CryptoServerProvider import CryptoServerJCE.CryptoServerProvider
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.DigitalSignature
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.toX509CertHolder
import net.corda.core.internal.x500Name import net.corda.core.internal.x500Name
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.CertificateType
@ -81,7 +80,7 @@ object X509Utilities {
cert.checkValidity(Date()) cert.checkValidity(Date())
cert.verify(pubKey) 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 { fun retrieveCertificateAndKeys(certificateKeyName: String, privateKeyPassword: String, keyStore: KeyStore): CertificateAndKeyPair {
val privateKey = keyStore.getKey(certificateKeyName, privateKeyPassword.toCharArray()) as PrivateKey val privateKey = keyStore.getKey(certificateKeyName, privateKeyPassword.toCharArray()) as PrivateKey
val publicKey = keyStore.getCertificate(certificateKeyName).publicKey val publicKey = keyStore.getCertificate(certificateKeyName).publicKey
val certificate = keyStore.getX509Certificate(certificateKeyName).toX509CertHolder() val certificate = keyStore.getX509Certificate(certificateKeyName)
return CertificateAndKeyPair(certificate, getCleanEcdsaKeyPair(publicKey, privateKey)) return CertificateAndKeyPair(certificate, getCleanEcdsaKeyPair(publicKey, privateKey))
} }
@ -160,7 +159,7 @@ object X509Utilities {
cert.checkValidity(Date()) cert.checkValidity(Date())
cert.verify(certificateAuthority.keyPair.public) 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 subject = CordaX500Name.parse(jcaRequest.subject.toString()).x500Name
val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(jcaRequest.publicKey.encoded)) val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(jcaRequest.publicKey.encoded))
val keyPurposes = DERSequence(ASN1EncodableVector().apply { certificateType.purposes.forEach { add(it) } }) 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.subjectKeyIdentifier, false, BcX509ExtensionUtils().createSubjectKeyIdentifier(subjectPublicKeyInfo))
.addExtension(Extension.basicConstraints, certificateType.isCA, BasicConstraints(certificateType.isCA)) .addExtension(Extension.basicConstraints, certificateType.isCA, BasicConstraints(certificateType.isCA))
.addExtension(Extension.keyUsage, false, certificateType.keyUsage) .addExtension(Extension.keyUsage, false, certificateType.keyUsage)

View File

@ -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.CertificationRequestStorage.Companion.DOORMAN_SIGNATURE
import com.r3.corda.networkmanage.common.persistence.entity.CertificateSigningRequestEntity import com.r3.corda.networkmanage.common.persistence.entity.CertificateSigningRequestEntity
import com.r3.corda.networkmanage.common.utils.buildCertPath 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.Crypto
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.nodeapi.internal.crypto.CertificateType
import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.testing.internal.createDevNodeCaCertPath
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.pkcs.PKCS10CertificationRequest import org.bouncycastle.pkcs.PKCS10CertificationRequest
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
import org.hibernate.envers.AuditReaderFactory import org.hibernate.envers.AuditReaderFactory
@ -21,9 +19,10 @@ import org.junit.Before
import org.junit.Test import org.junit.Test
import java.security.KeyPair import java.security.KeyPair
import java.util.* import java.util.*
import javax.security.auth.x500.X500Principal
import kotlin.test.* import kotlin.test.*
class DBCertificateRequestStorageTest : TestBase() { class PersistentCertificateRequestStorageTest : TestBase() {
private lateinit var storage: PersistentCertificateRequestStorage private lateinit var storage: PersistentCertificateRequestStorage
private lateinit var persistence: CordaPersistence private lateinit var persistence: CordaPersistence
@ -84,14 +83,15 @@ class DBCertificateRequestStorageTest : TestBase() {
// New request should be empty. // New request should be empty.
assertTrue(storage.getRequests(RequestStatus.NEW).isEmpty()) assertTrue(storage.getRequests(RequestStatus.NEW).isEmpty())
// Sign certificate // Sign certificate
storage.putCertificatePath(requestId, JcaPKCS10CertificationRequest(csr).run { storage.putCertificatePath(
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) requestId,
val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 LTD", country = "GB"), rootCAKey) JcaPKCS10CertificationRequest(csr).run {
val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) // TODO We need a utility in InternalUtils for converting X500Name -> CordaX500Name
val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, X500Name("CN=Corda Node Intermediate CA,L=London"), intermediateCAKey.public) val (rootCa, intermediateCa, nodeCa) = createDevNodeCaCertPath(CordaX500Name.build(X500Principal(subject.encoded)))
val ourCertificate = X509Utilities.createCertificate(CertificateType.TLS, intermediateCACert, intermediateCAKey, subject, publicKey).toX509Certificate() buildCertPath(nodeCa.certificate, intermediateCa.certificate, rootCa.certificate)
buildCertPath(ourCertificate, intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate()) },
}, listOf(DOORMAN_SIGNATURE)) listOf(DOORMAN_SIGNATURE)
)
// Check request is ready // Check request is ready
assertNotNull(storage.getRequest(requestId)!!.certData) assertNotNull(storage.getRequest(requestId)!!.certData)
} }
@ -104,25 +104,24 @@ class DBCertificateRequestStorageTest : TestBase() {
// Store certificate to DB. // Store certificate to DB.
storage.markRequestTicketCreated(requestId) storage.markRequestTicketCreated(requestId)
storage.approveRequest(requestId, DOORMAN_SIGNATURE) storage.approveRequest(requestId, DOORMAN_SIGNATURE)
storage.putCertificatePath(requestId, JcaPKCS10CertificationRequest(csr).run { storage.putCertificatePath(
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) requestId,
val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 LTD", country = "GB"), rootCAKey) JcaPKCS10CertificationRequest(csr).run {
val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val (rootCa, intermediateCa, nodeCa) = createDevNodeCaCertPath(CordaX500Name.build(X500Principal(subject.encoded)))
val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, X500Name("CN=Corda Node Intermediate CA,L=London"), intermediateCAKey.public) buildCertPath(nodeCa.certificate, intermediateCa.certificate, rootCa.certificate)
val ourCertificate = X509Utilities.createCertificate(CertificateType.TLS, intermediateCACert, intermediateCAKey, subject, publicKey).toX509Certificate() },
buildCertPath(ourCertificate, intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate()) listOf(DOORMAN_SIGNATURE)
}, listOf(DOORMAN_SIGNATURE)) )
// Sign certificate // Sign certificate
// When subsequent signature requested // When subsequent signature requested
assertFailsWith(IllegalArgumentException::class) { assertFailsWith(IllegalArgumentException::class) {
storage.putCertificatePath(requestId, JcaPKCS10CertificationRequest(csr).run { storage.putCertificatePath(
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) requestId,
val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 LTD", country = "GB"), rootCAKey) JcaPKCS10CertificationRequest(csr).run {
val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val (rootCa, intermediateCa, nodeCa) = createDevNodeCaCertPath(CordaX500Name.build(X500Principal(subject.encoded)))
val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, X500Name("CN=Corda Node Intermediate CA,L=London"), intermediateCAKey.public) buildCertPath(nodeCa.certificate, intermediateCa.certificate, rootCa.certificate)
val ourCertificate = X509Utilities.createCertificate(CertificateType.TLS, intermediateCACert, intermediateCAKey, subject, publicKey).toX509Certificate() },
buildCertPath(ourCertificate, intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate()) listOf(DOORMAN_SIGNATURE))
}, listOf(DOORMAN_SIGNATURE))
} }
} }
@ -207,6 +206,6 @@ class DBCertificateRequestStorageTest : TestBase() {
internal fun createRequest(organisation: String): Pair<PKCS10CertificationRequest, KeyPair> { internal fun createRequest(organisation: String): Pair<PKCS10CertificationRequest, KeyPair> {
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) 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) return Pair(request, keyPair)
} }

View File

@ -3,25 +3,24 @@ package com.r3.corda.networkmanage.common.persistence
import com.r3.corda.networkmanage.TestBase import com.r3.corda.networkmanage.TestBase
import com.r3.corda.networkmanage.common.utils.withCert import com.r3.corda.networkmanage.common.utils.withCert
import com.r3.corda.networkmanage.doorman.signer.LocalSigner import com.r3.corda.networkmanage.doorman.signer.LocalSigner
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.sign import net.corda.core.crypto.sign
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.cert
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.nodeapi.internal.SignedNodeInfo 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.X509CertificateFactory
import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.nodeapi.internal.network.NetworkMap import net.corda.nodeapi.internal.network.NetworkMap
import net.corda.nodeapi.internal.network.SignedNetworkMap import net.corda.nodeapi.internal.network.SignedNetworkMap
import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.internal.TestNodeInfoBuilder import net.corda.testing.internal.TestNodeInfoBuilder
import net.corda.testing.internal.createDevIntermediateCaCertPath
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import java.security.cert.X509Certificate
import kotlin.test.assertEquals import kotlin.test.assertEquals
class PersistentNetworkMapStorageTest : TestBase() { class PersistentNetworkMapStorageTest : TestBase() {
@ -30,15 +29,16 @@ class PersistentNetworkMapStorageTest : TestBase() {
private lateinit var nodeInfoStorage: PersistentNodeInfoStorage private lateinit var nodeInfoStorage: PersistentNodeInfoStorage
private lateinit var requestStorage: PersistentCertificateRequestStorage private lateinit var requestStorage: PersistentCertificateRequestStorage
private val rootCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) private lateinit var rootCaCert: X509Certificate
private val rootCaCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name("Corda Node Root CA", "R3 LTD", "London", "GB"), rootCaKeyPair) private lateinit var intermediateCa: CertificateAndKeyPair
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)
@Before @Before
fun startDb() { fun startDb() {
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
rootCaCert = rootCa.certificate
this.intermediateCa = intermediateCa
persistence = configureDatabase(makeTestDataSourceProperties()) 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) nodeInfoStorage = PersistentNodeInfoStorage(persistence)
requestStorage = PersistentCertificateRequestStorage(persistence) requestStorage = PersistentCertificateRequestStorage(persistence)
} }
@ -60,7 +60,7 @@ class PersistentNetworkMapStorageTest : TestBase() {
val networkMap = NetworkMap(listOf(nodeInfoHash), networkParametersHash) val networkMap = NetworkMap(listOf(nodeInfoHash), networkParametersHash)
val serializedNetworkMap = networkMap.serialize() 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) val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, signatureData)
// when // when
@ -70,7 +70,7 @@ class PersistentNetworkMapStorageTest : TestBase() {
val persistedSignedNetworkMap = networkMapStorage.getCurrentNetworkMap() val persistedSignedNetworkMap = networkMapStorage.getCurrentNetworkMap()
assertEquals(signedNetworkMap.signature, persistedSignedNetworkMap?.signature) assertEquals(signedNetworkMap.signature, persistedSignedNetworkMap?.signature)
assertEquals(signedNetworkMap.verified(rootCaCert.cert), persistedSignedNetworkMap?.verified(rootCaCert.cert)) assertEquals(signedNetworkMap.verified(rootCaCert), persistedSignedNetworkMap?.verified(rootCaCert))
} }
@Test @Test
@ -96,7 +96,7 @@ class PersistentNetworkMapStorageTest : TestBase() {
// Sign network map making it current network map // Sign network map making it current network map
val networkMap = NetworkMap(emptyList(), networkParametersHash) val networkMap = NetworkMap(emptyList(), networkParametersHash)
val serializedNetworkMap = networkMap.serialize() 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) val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, signatureData)
networkMapStorage.saveNetworkMap(signedNetworkMap) networkMapStorage.saveNetworkMap(signedNetworkMap)
@ -117,7 +117,7 @@ class PersistentNetworkMapStorageTest : TestBase() {
val netParamsHash = networkMapStorage.saveNetworkParameters(netParams) val netParamsHash = networkMapStorage.saveNetworkParameters(netParams)
val signedNetParams = networkMapStorage.getSignedNetworkParameters(netParamsHash) val signedNetParams = networkMapStorage.getSignedNetworkParameters(netParamsHash)
assertThat(signedNetParams?.verified()).isEqualTo(netParams) assertThat(signedNetParams?.verified()).isEqualTo(netParams)
assertThat(signedNetParams?.sig?.by).isEqualTo(intermediateCaKeyPair.public) assertThat(signedNetParams?.sig?.by).isEqualTo(intermediateCa.keyPair.public)
} }
@Test @Test
@ -135,7 +135,7 @@ class PersistentNetworkMapStorageTest : TestBase() {
val networkParametersHash = networkMapStorage.saveNetworkParameters(testNetworkParameters(emptyList())) val networkParametersHash = networkMapStorage.saveNetworkParameters(testNetworkParameters(emptyList()))
val networkMap = NetworkMap(listOf(nodeInfoHashA), networkParametersHash) val networkMap = NetworkMap(listOf(nodeInfoHashA), networkParametersHash)
val serializedNetworkMap = networkMap.serialize() 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) val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, signatureData)
// Sign network map // Sign network map

View File

@ -6,15 +6,16 @@ import com.r3.corda.networkmanage.common.utils.hashString
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.cert
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.nodeapi.internal.SignedNodeInfo 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.CertificateType
import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509CertificateFactory
import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.testing.internal.TestNodeInfoBuilder import net.corda.testing.internal.TestNodeInfoBuilder
import net.corda.testing.internal.createDevIntermediateCaCertPath
import net.corda.testing.internal.signWith import net.corda.testing.internal.signWith
import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
@ -22,6 +23,7 @@ import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import java.security.PrivateKey import java.security.PrivateKey
import java.security.cert.X509Certificate
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertNotNull import kotlin.test.assertNotNull
import kotlin.test.assertNull import kotlin.test.assertNull
@ -31,13 +33,14 @@ class PersitenceNodeInfoStorageTest : TestBase() {
private lateinit var nodeInfoStorage: PersistentNodeInfoStorage private lateinit var nodeInfoStorage: PersistentNodeInfoStorage
private lateinit var persistence: CordaPersistence private lateinit var persistence: CordaPersistence
private val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) private lateinit var rootCaCert: X509Certificate
private val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 LTD", country = "GB"), rootCAKey) private lateinit var intermediateCa: CertificateAndKeyPair
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)
@Before @Before
fun startDb() { fun startDb() {
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
rootCaCert = rootCa.certificate
this.intermediateCa = intermediateCa
persistence = configureDatabase(MockServices.makeTestDataSourceProperties()) persistence = configureDatabase(MockServices.makeTestDataSourceProperties())
nodeInfoStorage = PersistentNodeInfoStorage(persistence) nodeInfoStorage = PersistentNodeInfoStorage(persistence)
requestStorage = PersistentCertificateRequestStorage(persistence) requestStorage = PersistentCertificateRequestStorage(persistence)
@ -53,9 +56,14 @@ class PersitenceNodeInfoStorageTest : TestBase() {
// Create node info. // Create node info.
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val name = CordaX500Name(organisation = "Test", locality = "London", country = "GB") 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) val requestId = requestStorage.saveRequest(request)
requestStorage.markRequestTicketCreated(requestId) requestStorage.markRequestTicketCreated(requestId)
@ -63,12 +71,15 @@ class PersitenceNodeInfoStorageTest : TestBase() {
assertNull(nodeInfoStorage.getCertificatePath(SecureHash.parse(keyPair.public.hashString()))) 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())) val storedCertPath = nodeInfoStorage.getCertificatePath(SecureHash.parse(keyPair.public.hashString()))
assertNotNull(storedCertPath) assertNotNull(storedCertPath)
assertEquals(nodeCaCert.cert, storedCertPath!!.certificates.first()) assertEquals(nodeCaCert, storedCertPath!!.certificates.first())
} }
@Test @Test

View File

@ -4,20 +4,18 @@ import com.nhaarman.mockito_kotlin.*
import com.r3.corda.networkmanage.TestBase import com.r3.corda.networkmanage.TestBase
import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage
import com.r3.corda.networkmanage.common.utils.withCert 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.SecureHash
import net.corda.core.crypto.sha256 import net.corda.core.crypto.sha256
import net.corda.core.crypto.sign 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.core.serialization.serialize
import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.nodeapi.internal.network.NetworkMap import net.corda.nodeapi.internal.network.NetworkMap
import net.corda.nodeapi.internal.network.SignedNetworkMap import net.corda.nodeapi.internal.network.SignedNetworkMap
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.internal.createDevIntermediateCaCertPath
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import java.security.cert.X509Certificate
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertTrue import kotlin.test.assertTrue
@ -25,12 +23,15 @@ class NetworkMapSignerTest : TestBase() {
private lateinit var signer: Signer private lateinit var signer: Signer
private lateinit var networkMapStorage: NetworkMapStorage private lateinit var networkMapStorage: NetworkMapStorage
private lateinit var networkMapSigner: NetworkMapSigner 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 lateinit var rootCaCert: X509Certificate
private val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) private lateinit var intermediateCa: CertificateAndKeyPair
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)
@Before @Before
fun setUp() { fun setUp() {
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
rootCaCert = rootCa.certificate
this.intermediateCa = intermediateCa
signer = mock() signer = mock()
networkMapStorage = mock() networkMapStorage = mock()
networkMapSigner = NetworkMapSigner(networkMapStorage, signer) networkMapSigner = NetworkMapSigner(networkMapStorage, signer)
@ -43,11 +44,11 @@ class NetworkMapSignerTest : TestBase() {
val networkParameters = testNetworkParameters(emptyList()) val networkParameters = testNetworkParameters(emptyList())
val serializedNetworkMap = NetworkMap(signedNodeInfoHashes, SecureHash.randomSHA256()).serialize() val serializedNetworkMap = NetworkMap(signedNodeInfoHashes, SecureHash.randomSHA256()).serialize()
whenever(networkMapStorage.getCurrentNetworkMap()) 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.getNodeInfoHashes(any())).thenReturn(signedNodeInfoHashes)
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters) whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters)
whenever(signer.sign(any())).then { 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 // when
@ -59,7 +60,7 @@ class NetworkMapSignerTest : TestBase() {
verify(networkMapStorage).getLatestNetworkParameters() verify(networkMapStorage).getLatestNetworkParameters()
argumentCaptor<SignedNetworkMap>().apply { argumentCaptor<SignedNetworkMap>().apply {
verify(networkMapStorage).saveNetworkMap(capture()) verify(networkMapStorage).saveNetworkMap(capture())
val networkMap = firstValue.verified(rootCACert.cert) val networkMap = firstValue.verified(rootCaCert)
assertEquals(networkParameters.serialize().hash, networkMap.networkParameterHash) assertEquals(networkParameters.serialize().hash, networkMap.networkParameterHash)
assertEquals(signedNodeInfoHashes.size, networkMap.nodeInfoHashes.size) assertEquals(signedNodeInfoHashes.size, networkMap.nodeInfoHashes.size)
assertTrue(networkMap.nodeInfoHashes.containsAll(signedNodeInfoHashes)) assertTrue(networkMap.nodeInfoHashes.containsAll(signedNodeInfoHashes))
@ -73,7 +74,7 @@ class NetworkMapSignerTest : TestBase() {
val networkMapParametersHash = networkParameters.serialize().bytes.sha256() val networkMapParametersHash = networkParameters.serialize().bytes.sha256()
val networkMap = NetworkMap(emptyList(), networkMapParametersHash) val networkMap = NetworkMap(emptyList(), networkMapParametersHash)
val serializedNetworkMap = networkMap.serialize() 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.getCurrentNetworkMap()).thenReturn(signedNetworkMap)
whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList()) whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList())
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters) whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters)
@ -94,7 +95,7 @@ class NetworkMapSignerTest : TestBase() {
whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList()) whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList())
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters) whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters)
whenever(signer.sign(any())).then { 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 // when
networkMapSigner.signNetworkMap() networkMapSigner.signNetworkMap()
@ -105,7 +106,7 @@ class NetworkMapSignerTest : TestBase() {
verify(networkMapStorage).getLatestNetworkParameters() verify(networkMapStorage).getLatestNetworkParameters()
argumentCaptor<SignedNetworkMap>().apply { argumentCaptor<SignedNetworkMap>().apply {
verify(networkMapStorage).saveNetworkMap(capture()) verify(networkMapStorage).saveNetworkMap(capture())
val networkMap = firstValue.verified(rootCACert.cert) val networkMap = firstValue.verified(rootCaCert)
assertEquals(networkParameters.serialize().hash, networkMap.networkParameterHash) assertEquals(networkParameters.serialize().hash, networkMap.networkParameterHash)
} }
} }

View File

@ -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.CertificationRequestStorage
import com.r3.corda.networkmanage.common.persistence.RequestStatus import com.r3.corda.networkmanage.common.persistence.RequestStatus
import com.r3.corda.networkmanage.common.utils.buildCertPath 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.DefaultCsrHandler
import com.r3.corda.networkmanage.doorman.signer.LocalSigner import com.r3.corda.networkmanage.doorman.signer.LocalSigner
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.identity.CordaX500Name
import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities
import org.junit.Test import org.junit.Test
import javax.security.auth.x500.X500Principal
import kotlin.test.assertEquals import kotlin.test.assertEquals
class DefaultRequestProcessorTest : TestBase() { class DefaultRequestProcessorTest : TestBase() {
@Test @Test
fun `get response`() { fun `get response`() {
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) 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 { val requestStorage: CertificationRequestStorage = mock {
on { getRequest("New") }.thenReturn(certificateSigningRequest()) 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")) on { getRequest("Rejected") }.thenReturn(certificateSigningRequest(status = RequestStatus.REJECTED, remark = "Random reason"))
} }
val signer: LocalSigner = mock() val signer: LocalSigner = mock()
@ -35,15 +34,15 @@ class DefaultRequestProcessorTest : TestBase() {
assertEquals(CertificateResponse.NotReady, requestProcessor.getResponse("random")) assertEquals(CertificateResponse.NotReady, requestProcessor.getResponse("random"))
assertEquals(CertificateResponse.NotReady, requestProcessor.getResponse("New")) 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")) assertEquals(CertificateResponse.Unauthorised("Random reason"), requestProcessor.getResponse("Rejected"))
} }
@Test @Test
fun `process request`() { 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 (request1, request2, request3) = (1..3).map {
val request2 = X509Utilities.createCertificateSigningRequest(CordaX500Name(locality = "London", organisation = "Test2", country = "GB"), "my@email.com", Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) X509Utilities.createCertificateSigningRequest(X500Principal("O=Test1,L=London,C=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 requestStorage: CertificationRequestStorage = mock { val requestStorage: CertificationRequestStorage = mock {
on { getRequests(RequestStatus.APPROVED) }.thenReturn(listOf( on { getRequests(RequestStatus.APPROVED) }.thenReturn(listOf(

View File

@ -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.persistence.NodeInfoStorage
import com.r3.corda.networkmanage.common.utils.withCert import com.r3.corda.networkmanage.common.utils.withCert
import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService 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.SecureHash.Companion.randomSHA256
import net.corda.core.crypto.SignedData import net.corda.core.crypto.SignedData
import net.corda.core.crypto.sign import net.corda.core.crypto.sign
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.cert
import net.corda.core.internal.openHttpConnection import net.corda.core.internal.openHttpConnection
import net.corda.core.serialization.deserialize import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.seconds import net.corda.core.utilities.seconds
import net.corda.nodeapi.internal.SignedNodeInfo 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.X509Utilities
import net.corda.nodeapi.internal.network.NetworkMap import net.corda.nodeapi.internal.network.NetworkMap
import net.corda.nodeapi.internal.network.NetworkParameters import net.corda.nodeapi.internal.network.NetworkParameters
import net.corda.nodeapi.internal.network.SignedNetworkMap import net.corda.nodeapi.internal.network.SignedNetworkMap
import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.SerializationEnvironmentRule
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.internal.createDevIntermediateCaCertPath
import net.corda.testing.internal.createNodeInfoAndSigned import net.corda.testing.internal.createNodeInfoAndSigned
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatExceptionOfType import org.assertj.core.api.Assertions.assertThatExceptionOfType
import org.bouncycastle.asn1.x500.X500Name import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import java.io.FileNotFoundException import java.io.FileNotFoundException
import java.net.URL import java.net.URL
import java.security.cert.X509Certificate
import javax.ws.rs.core.MediaType import javax.ws.rs.core.MediaType
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -42,12 +41,18 @@ class NodeInfoWebServiceTest {
@JvmField @JvmField
val testSerialization = SerializationEnvironmentRule(true) val testSerialization = SerializationEnvironmentRule(true)
private val rootCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) private lateinit var rootCaCert: X509Certificate
private val rootCaCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name("Corda Node Root CA", "R3 LTD", "London", "GB"), rootCaKeyPair) private lateinit var intermediateCa: CertificateAndKeyPair
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 val testNetworkMapConfig = NetworkMapConfig(10.seconds.toMillis(), 10.seconds.toMillis()) private val testNetworkMapConfig = NetworkMapConfig(10.seconds.toMillis(), 10.seconds.toMillis())
@Before
fun init() {
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
rootCaCert = rootCa.certificate
this.intermediateCa = intermediateCa
}
@Test @Test
fun `submit nodeInfo`() { fun `submit nodeInfo`() {
// Create node info. // Create node info.
@ -65,7 +70,7 @@ class NodeInfoWebServiceTest {
fun `get network map`() { fun `get network map`() {
val networkMap = NetworkMap(listOf(randomSHA256(), randomSHA256()), randomSHA256()) val networkMap = NetworkMap(listOf(randomSHA256(), randomSHA256()), randomSHA256())
val serializedNetworkMap = networkMap.serialize() 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 { val networkMapStorage: NetworkMapStorage = mock {
on { getCurrentNetworkMap() }.thenReturn(signedNetworkMap) on { getCurrentNetworkMap() }.thenReturn(signedNetworkMap)
@ -75,7 +80,7 @@ class NodeInfoWebServiceTest {
it.start() it.start()
val signedNetworkMapResponse = it.doGet<SignedNetworkMap>("") val signedNetworkMapResponse = it.doGet<SignedNetworkMap>("")
verify(networkMapStorage, times(1)).getCurrentNetworkMap() 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`() { fun `get network parameters`() {
val netParams = testNetworkParameters(emptyList()) val netParams = testNetworkParameters(emptyList())
val serializedNetParams = netParams.serialize() val serializedNetParams = netParams.serialize()
val signedNetParams = SignedData(serializedNetParams, intermediateCaKeyPair.sign(serializedNetParams)) val signedNetParams = SignedData(serializedNetParams, intermediateCa.keyPair.sign(serializedNetParams))
val netParamsHash = serializedNetParams.hash val netParamsHash = serializedNetParams.hash
val networkMapStorage: NetworkMapStorage = mock { val networkMapStorage: NetworkMapStorage = mock {
@ -116,7 +121,7 @@ class NodeInfoWebServiceTest {
val netParamsResponse = it.doGet<SignedData<NetworkParameters>>("network-parameter/$netParamsHash") val netParamsResponse = it.doGet<SignedData<NetworkParameters>>("network-parameter/$netParamsHash")
verify(networkMapStorage, times(1)).getSignedNetworkParameters(netParamsHash) verify(networkMapStorage, times(1)).getSignedNetworkParameters(netParamsHash)
assertThat(netParamsResponse.verified()).isEqualTo(netParams) 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 { assertThatExceptionOfType(FileNotFoundException::class.java).isThrownBy {
it.doGet<SignedData<NetworkParameters>>("network-parameter/${randomSHA256()}") it.doGet<SignedData<NetworkParameters>>("network-parameter/${randomSHA256()}")

View File

@ -4,46 +4,54 @@ import com.nhaarman.mockito_kotlin.*
import com.r3.corda.networkmanage.TestBase import com.r3.corda.networkmanage.TestBase
import com.r3.corda.networkmanage.common.persistence.CertificateResponse import com.r3.corda.networkmanage.common.persistence.CertificateResponse
import com.r3.corda.networkmanage.common.utils.buildCertPath 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.signer.CsrHandler
import com.r3.corda.networkmanage.doorman.webservice.RegistrationWebService import com.r3.corda.networkmanage.doorman.webservice.RegistrationWebService
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.utilities.NetworkHostAndPort 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.CertificateType
import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509CertificateFactory
import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.nodeapi.internal.crypto.X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME 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.apache.commons.io.IOUtils
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.asn1.x509.GeneralName import org.bouncycastle.asn1.x509.GeneralName
import org.bouncycastle.asn1.x509.GeneralSubtree import org.bouncycastle.asn1.x509.GeneralSubtree
import org.bouncycastle.asn1.x509.NameConstraints import org.bouncycastle.asn1.x509.NameConstraints
import org.bouncycastle.cert.X509CertificateHolder
import org.bouncycastle.pkcs.PKCS10CertificationRequest import org.bouncycastle.pkcs.PKCS10CertificationRequest
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
import org.junit.After import org.junit.After
import org.junit.Before
import org.junit.Test import org.junit.Test
import java.io.IOException import java.io.IOException
import java.net.HttpURLConnection import java.net.HttpURLConnection
import java.net.HttpURLConnection.* import java.net.HttpURLConnection.*
import java.net.URL import java.net.URL
import java.nio.charset.StandardCharsets.UTF_8
import java.security.cert.CertPath import java.security.cert.CertPath
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import java.util.* import java.util.*
import java.util.zip.ZipInputStream import java.util.zip.ZipInputStream
import javax.security.auth.x500.X500Principal
import javax.ws.rs.core.MediaType import javax.ws.rs.core.MediaType
import kotlin.test.assertEquals import kotlin.test.assertEquals
class RegistrationWebServiceTest : TestBase() { class RegistrationWebServiceTest : TestBase() {
private val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) private lateinit var rootCaCert: X509Certificate
private val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 Ltd", country = "GB"), rootCAKey) private lateinit var intermediateCa: CertificateAndKeyPair
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 webServer: NetworkManagementWebServer private lateinit var webServer: NetworkManagementWebServer
@Before
fun init() {
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
rootCaCert = rootCa.certificate
this.intermediateCa = intermediateCa
}
private fun startSigningServer(csrHandler: CsrHandler) { private fun startSigningServer(csrHandler: CsrHandler) {
webServer = NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), RegistrationWebService(csrHandler)) webServer = NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), RegistrationWebService(csrHandler))
webServer.start() webServer.start()
@ -65,7 +73,10 @@ class RegistrationWebServiceTest : TestBase() {
startSigningServer(requestProcessor) startSigningServer(requestProcessor)
val keyPair = Crypto.generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME) 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. // Post request to signing server via http.
assertEquals(id, submitRequest(request)) assertEquals(id, submitRequest(request))
@ -79,6 +90,8 @@ class RegistrationWebServiceTest : TestBase() {
val keyPair = Crypto.generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME) val keyPair = Crypto.generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME)
val id = SecureHash.randomSHA256().toString() val id = SecureHash.randomSHA256().toString()
val subject = CordaX500Name(locality = "London", organisation = "LegalName", country = "GB").x500Principal
// Mock Storage behaviour. // Mock Storage behaviour.
val certificateStore = mutableMapOf<String, CertPath>() val certificateStore = mutableMapOf<String, CertPath>()
val requestProcessor = mock<CsrHandler> { val requestProcessor = mock<CsrHandler> {
@ -88,10 +101,15 @@ class RegistrationWebServiceTest : TestBase() {
} ?: CertificateResponse.NotReady } ?: CertificateResponse.NotReady
} }
on { processApprovedRequests() }.then { 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 { certificateStore[id] = JcaPKCS10CertificationRequest(request).run {
val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, intermediateCACert, intermediateCAKey, subject, publicKey).toX509Certificate() val tlsCert = X509Utilities.createCertificate(
buildCertPath(tlsCert, intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate()) CertificateType.TLS,
intermediateCa.certificate,
intermediateCa.keyPair,
X500Principal(subject.encoded),
publicKey)
buildCertPath(tlsCert, intermediateCa.certificate, rootCaCert)
} }
null null
} }
@ -104,22 +122,15 @@ class RegistrationWebServiceTest : TestBase() {
val certificates = (pollForResponse(id) as PollResponse.Ready).certChain val certificates = (pollForResponse(id) as PollResponse.Ready).certChain
verify(requestProcessor, times(2)).getResponse(any()) verify(requestProcessor, times(2)).getResponse(any())
assertEquals(3, certificates.size)
certificates.first().run { assertThat(certificates).hasSize(3)
assertThat(subjectDN.name).contains("O=LegalName") assertThat(certificates[0].subjectX500Principal).isEqualTo(subject)
assertThat(subjectDN.name).contains("L=London") assertThat(certificates).endsWith(intermediateCa.certificate, rootCaCert)
}
certificates.last().run {
assertThat(subjectDN.name).contains("CN=Corda Node Root CA")
assertThat(subjectDN.name).contains("L=London")
}
} }
@Test @Test
fun `retrieve certificate and create valid TLS certificate`() { 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() val id = SecureHash.randomSHA256().toString()
// Mock Storage behaviour. // Mock Storage behaviour.
@ -131,11 +142,22 @@ class RegistrationWebServiceTest : TestBase() {
} ?: CertificateResponse.NotReady } ?: CertificateResponse.NotReady
} }
on { processApprovedRequests() }.then { 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 { certificateStore[id] = JcaPKCS10CertificationRequest(request).run {
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, X500Name("CN=LegalName, L=London")))), arrayOf()) val nameConstraints = NameConstraints(
val clientCert = X509Utilities.createCertificate(CertificateType.NODE_CA, intermediateCACert, intermediateCAKey, subject, publicKey, nameConstraints = nameConstraints).toX509Certificate() arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, X500Name("CN=LegalName, L=London")))),
buildCertPath(clientCert, intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate()) arrayOf())
val clientCert = X509Utilities.createCertificate(
CertificateType.NODE_CA,
intermediateCa.certificate,
intermediateCa.keyPair,
X500Principal(subject.encoded),
publicKey,
nameConstraints = nameConstraints)
buildCertPath(clientCert, intermediateCa.certificate, rootCaCert)
} }
true true
} }
@ -149,11 +171,17 @@ class RegistrationWebServiceTest : TestBase() {
verify(storage, times(2)).getResponse(any()) verify(storage, times(2)).getResponse(any())
assertEquals(3, certificates.size) assertEquals(3, certificates.size)
val sslKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val sslKeyPair = 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 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. // 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 @Test
@ -192,7 +220,7 @@ class RegistrationWebServiceTest : TestBase() {
PollResponse.Ready(certificates) PollResponse.Ready(certificates)
} }
HTTP_NO_CONTENT -> PollResponse.NotReady 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}") else -> throw IOException("Cannot connect to Certificate Signing Server, HTTP response code : ${conn.responseCode}")
} }
} }

View File

@ -39,7 +39,10 @@ class JiraCsrHandlerTest {
private lateinit var certificateResponse: CertificateResponse.Ready private lateinit var certificateResponse: CertificateResponse.Ready
private val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) 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 @Before
fun setup() { fun setup() {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -83,16 +83,20 @@ class NetworkBootstrapper {
for (confFile in confFiles) { for (confFile in confFiles) {
val nodeName = confFile.fileName.toString().removeSuffix(".conf") val nodeName = confFile.fileName.toString().removeSuffix(".conf")
println("Generating directory for $nodeName") println("Generating directory for $nodeName")
val nodeDir = (directory / nodeName).createDirectory() val nodeDir = (directory / nodeName)
confFile.moveTo(nodeDir / "node.conf") if (!nodeDir.exists()) { nodeDir.createDirectory() }
Files.copy(cordaJar, (nodeDir / "corda.jar")) confFile.moveTo(nodeDir / "node.conf", StandardCopyOption.REPLACE_EXISTING)
Files.copy(cordaJar, (nodeDir / "corda.jar"), StandardCopyOption.REPLACE_EXISTING)
} }
Files.delete(cordaJar) Files.delete(cordaJar)
} }
private fun extractCordaJarTo(directory: Path): Path { private fun extractCordaJarTo(directory: Path): Path {
val cordaJarPath = (directory / "corda.jar") 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 return cordaJarPath
} }

View File

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

View File

@ -0,0 +1,40 @@
package net.corda.nodeapi.internal.serialization.amqp;
import net.corda.nodeapi.internal.serialization.AllWhitelist;
import org.assertj.core.api.Assertions;
import org.junit.Test;
import java.io.NotSerializableException;
public class ErrorMessageTests {
private String errMsg(String property, String testname) {
return "Property '"
+ property
+ "' or 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()));
}
}

View File

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

View File

@ -0,0 +1,69 @@
package net.corda.nodeapi.internal.serialization.amqp
import org.assertj.core.api.Assertions
import org.junit.Test
import java.io.NotSerializableException
class ErrorMessagesTests {
companion object {
val VERBOSE get() = false
}
private fun errMsg(property:String, testname: String) =
"Property '$property' or 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))
}
}

View File

@ -1,4 +1,4 @@
package net.corda.node.services package net.corda.node
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.client.rpc.CordaRPCClient 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.finance.flows.CashIssueFlow
import net.corda.node.internal.Node import net.corda.node.internal.Node
import net.corda.node.internal.StartedNode 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.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.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.node.internal.NodeBasedTest
import net.corda.testing.toDatabaseSchemaName
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
import org.apache.shiro.authc.credential.DefaultPasswordService
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.ClassRule import org.junit.ClassRule
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import java.sql.DriverManager import java.sql.DriverManager
import java.sql.Statement import java.sql.Statement
import java.util.*
import kotlin.test.assertFailsWith 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 { companion object {
@ClassRule @JvmField @ClassRule @JvmField
val databaseSchemas = IntegrationTestSchemas(ALICE_NAME.toDatabaseSchemaName()) 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<Node> private lateinit var node: StartedNode<Node>
protected lateinit var client: CordaRPCClient 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<DummyFlow>(),
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 @Test
fun `login with correct credentials`() { fun `login with correct credentials`() {
@ -65,7 +126,7 @@ abstract class UserAuthServiceTest : NodeBasedTest() {
val proxy = it.proxy val proxy = it.proxy
proxy.startFlowDynamic(DummyFlow::class.java) proxy.startFlowDynamic(DummyFlow::class.java)
proxy.startTrackedFlowDynamic(DummyFlow::class.java) proxy.startTrackedFlowDynamic(DummyFlow::class.java)
proxy.startFlow(::DummyFlow) proxy.startFlow(AuthDBTests::DummyFlow)
assertFailsWith( assertFailsWith(
PermissionException::class, PermissionException::class,
"This user should not be authorized to start flow `CashIssueFlow`") { "This user should not be authorized to start flow `CashIssueFlow`") {
@ -92,77 +153,8 @@ abstract class UserAuthServiceTest : NodeBasedTest() {
} }
} }
@StartableByRPC
@InitiatingFlow
class DummyFlow : FlowLogic<Unit>() {
@Suspendable
override fun call() = Unit
}
}
class UserAuthServiceEmbedded : UserAuthServiceTest() {
private val rpcUser = User("user", "foo", permissions = setOf(
Permissions.startFlow<DummyFlow>(),
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<DummyFlow>(),
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 @Test
fun `Add new users on-the-fly`() { fun `Add new users dynamically`() {
assertFailsWith( assertFailsWith(
ActiveMQSecurityException::class, ActiveMQSecurityException::class,
"Login with incorrect password should fail") { "Login with incorrect password should fail") {
@ -171,7 +163,7 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() {
db.insert(UserAndRoles( db.insert(UserAndRoles(
username = "user2", username = "user2",
password = "bar", password = encodePassword("bar"),
roles = listOf("default"))) roles = listOf("default")))
client.start("user2", "bar") client.start("user2", "bar")
@ -181,10 +173,9 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() {
fun `Modify user permissions during RPC session`() { fun `Modify user permissions during RPC session`() {
db.insert(UserAndRoles( db.insert(UserAndRoles(
username = "user3", username = "user3",
password = "bar", password = encodePassword("bar"),
roles = emptyList())) roles = emptyList()))
client.start("user3", "bar").use { client.start("user3", "bar").use {
val proxy = it.proxy val proxy = it.proxy
assertFailsWith( assertFailsWith(
@ -193,6 +184,7 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() {
proxy.stateMachinesFeed() proxy.stateMachinesFeed()
} }
db.addRoleToUser("user3", "default") db.addRoleToUser("user3", "default")
Thread.sleep(1500)
proxy.stateMachinesFeed() proxy.stateMachinesFeed()
} }
} }
@ -201,13 +193,14 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() {
fun `Revoke user permissions during RPC session`() { fun `Revoke user permissions during RPC session`() {
db.insert(UserAndRoles( db.insert(UserAndRoles(
username = "user4", username = "user4",
password = "test", password = encodePassword("test"),
roles = listOf("default"))) roles = listOf("default")))
client.start("user4", "test").use { client.start("user4", "test").use {
val proxy = it.proxy val proxy = it.proxy
proxy.stateMachinesFeed() proxy.stateMachinesFeed()
db.deleteUser("user4") db.deleteUser("user4")
Thread.sleep(1500)
assertFailsWith( assertFailsWith(
PermissionException::class, PermissionException::class,
"This user should not be authorized to call 'nodeInfo'") { "This user should not be authorized to call 'nodeInfo'") {
@ -216,15 +209,27 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() {
} }
} }
@StartableByRPC
@InitiatingFlow
class DummyFlow : FlowLogic<Unit>() {
@Suspendable
override fun call() = Unit
}
@After @After
override fun tearDown() { 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<String>) private data class UserAndRoles(val username: String, val password: String, val roles: List<String>)
private data class RoleAndPermissions(val role: String, val permissions: List<String>) private data class RoleAndPermissions(val role: String, val permissions: List<String>)
/*
* Manage in-memory DB mocking a users database with the schema expected by Node's security manager
*/
private class UsersDB : AutoCloseable { private class UsersDB : AutoCloseable {
val jdbcUrl: String 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) { fun deleteUser(username: String) {
session { session {
it.execute("DELETE FROM users WHERE username = '$username'") it.execute("DELETE FROM users WHERE username = '$username'")
@ -307,4 +306,22 @@ private class UsersDB : AutoCloseable {
} }
} }
} }
} }
/*
* 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())
}

View File

@ -12,6 +12,9 @@ import net.corda.nodeapi.internal.config.User
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.common.internal.ProjectStructure.projectRootDir import net.corda.testing.common.internal.ProjectStructure.projectRootDir
import net.corda.testing.driver.driver 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.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.ClassRule import org.junit.ClassRule
@ -24,7 +27,7 @@ class BootTests : IntegrationTest() {
companion object { companion object {
@ClassRule @JvmField @ClassRule @JvmField
val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, DUMMY_BANK_A_NAME) 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 @Test

View File

@ -11,6 +11,9 @@ import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.internal.config.User import net.corda.nodeapi.internal.config.User
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.driver.driver 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.assertj.core.api.Assertions.assertThat
import org.junit.ClassRule import org.junit.ClassRule
import org.junit.Test import org.junit.Test

View File

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

View File

@ -18,7 +18,11 @@ import net.corda.testing.*
import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.NodeHandle
import net.corda.testing.driver.PortAllocation import net.corda.testing.driver.PortAllocation
import net.corda.testing.driver.driver 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.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.NotarySpec
import net.corda.testing.node.internal.InternalDriverDSL import net.corda.testing.node.internal.InternalDriverDSL
import net.corda.testing.node.internal.performance.startPublishingFixedRateInjector import net.corda.testing.node.internal.performance.startPublishingFixedRateInjector

View File

@ -3,6 +3,9 @@ package net.corda.node
import com.google.common.base.Stopwatch import com.google.common.base.Stopwatch
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.driver.driver 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.ClassRule
import org.junit.Ignore import org.junit.Ignore
import org.junit.Test import org.junit.Test

View File

@ -14,13 +14,14 @@ import net.corda.core.utilities.unwrap
import net.corda.node.services.Permissions.Companion.startFlow import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.internal.config.User import net.corda.nodeapi.internal.config.User
import net.corda.testing.ALICE_NAME 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.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.assertj.core.api.Assertions.assertThat
import org.bouncycastle.util.io.Streams import org.bouncycastle.util.io.Streams
import org.junit.ClassRule import org.junit.ClassRule
import org.junit.Ignore
import org.junit.Test import org.junit.Test
import java.net.ConnectException import java.net.ConnectException
import java.util.regex.Pattern import java.util.regex.Pattern
@ -33,6 +34,7 @@ class SSHServerTest : IntegrationTest() {
val databaseSchemas = IntegrationTestSchemas(ALICE_NAME.toDatabaseSchemaName()) val databaseSchemas = IntegrationTestSchemas(ALICE_NAME.toDatabaseSchemaName())
} }
@Ignore("Test has undeterministic capacity to hang, ignore till fixed")
@Test() @Test()
fun `ssh server does not start be default`() { fun `ssh server does not start be default`() {
val user = User("u", "p", setOf()) val user = User("u", "p", setOf())
@ -54,6 +56,7 @@ class SSHServerTest : IntegrationTest() {
} }
} }
@Ignore("Test has undeterministic capacity to hang, ignore till fixed")
@Test @Test
fun `ssh server starts when configured`() { fun `ssh server starts when configured`() {
val user = User("u", "p", setOf()) val user = User("u", "p", setOf())
@ -74,6 +77,7 @@ class SSHServerTest : IntegrationTest() {
} }
@Ignore("Test has undeterministic capacity to hang, ignore till fixed")
@Test @Test
fun `ssh server verify credentials`() { fun `ssh server verify credentials`() {
val user = User("u", "p", setOf()) val user = User("u", "p", setOf())
@ -97,6 +101,7 @@ class SSHServerTest : IntegrationTest() {
} }
} }
@Ignore("Test has undeterministic capacity to hang, ignore till fixed")
@Test @Test
fun `ssh respects permissions`() { fun `ssh respects permissions`() {
val user = User("u", "p", setOf(startFlow<FlowICanRun>())) val user = User("u", "p", setOf(startFlow<FlowICanRun>()))
@ -127,6 +132,7 @@ class SSHServerTest : IntegrationTest() {
} }
} }
@Ignore("Test has undeterministic capacity to hang, ignore till fixed")
@Test @Test
fun `ssh runs flows`() { fun `ssh runs flows`() {
val user = User("u", "p", setOf(startFlow<FlowICanRun>())) val user = User("u", "p", setOf(startFlow<FlowICanRun>()))

View File

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

View File

@ -24,8 +24,7 @@ import net.corda.testing.*
import net.corda.testing.driver.DriverDSL import net.corda.testing.driver.DriverDSL
import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.NodeHandle
import net.corda.testing.driver.driver import net.corda.testing.driver.driver
import net.corda.testing.internal.rigorousMock import net.corda.testing.internal.*
import net.corda.testing.internal.withoutTestSerialization
import net.corda.testing.services.MockAttachmentStorage import net.corda.testing.services.MockAttachmentStorage
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.ClassRule import org.junit.ClassRule

View File

@ -26,8 +26,8 @@ import net.corda.node.services.transactions.minCorrectReplicas
import net.corda.nodeapi.internal.DevIdentityGenerator import net.corda.nodeapi.internal.DevIdentityGenerator
import net.corda.nodeapi.internal.network.NetworkParametersCopier import net.corda.nodeapi.internal.network.NetworkParametersCopier
import net.corda.nodeapi.internal.network.NotaryInfo import net.corda.nodeapi.internal.network.NotaryInfo
import net.corda.testing.IntegrationTest import net.corda.testing.internal.IntegrationTest
import net.corda.testing.IntegrationTestSchemas import net.corda.testing.internal.IntegrationTestSchemas
import net.corda.testing.chooseIdentity import net.corda.testing.chooseIdentity
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract

View File

@ -11,13 +11,16 @@ import net.corda.core.utilities.getOrThrow
import net.corda.finance.POUNDS import net.corda.finance.POUNDS
import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.flows.CashPaymentFlow 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.invokeRpc
import net.corda.node.services.Permissions.Companion.startFlow import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.internal.config.User import net.corda.nodeapi.internal.config.User
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.NodeHandle
import net.corda.testing.driver.driver 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.ClusterSpec
import net.corda.testing.node.NotarySpec import net.corda.testing.node.NotarySpec
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat

View File

@ -20,6 +20,8 @@ import net.corda.nodeapi.internal.network.NotaryInfo
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.contracts.DummyContract 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.MockNetwork
import net.corda.testing.node.MockNodeParameters import net.corda.testing.node.MockNodeParameters
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties

View File

@ -15,6 +15,9 @@ import net.corda.testing.*
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract
import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.NodeHandle
import net.corda.testing.driver.driver 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.ClusterSpec
import net.corda.testing.node.NotarySpec import net.corda.testing.node.NotarySpec
import net.corda.testing.node.startFlow import net.corda.testing.node.startFlow
@ -27,7 +30,7 @@ import kotlin.test.assertFailsWith
class RaftNotaryServiceTests : IntegrationTest() { class RaftNotaryServiceTests : IntegrationTest() {
companion object { companion object {
@ClassRule @JvmField @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()) DUMMY_BANK_A_NAME.toDatabaseSchemaName())
} }
private val notaryName = CordaX500Name("RAFT Notary Service", "London", "GB") private val notaryName = CordaX500Name("RAFT Notary Service", "London", "GB")

View File

@ -21,6 +21,9 @@ import net.corda.testing.SerializationEnvironmentRule
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.NodeHandle
import net.corda.testing.driver.PortAllocation 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.CompatibilityZoneParams
import net.corda.testing.node.internal.internalDriver import net.corda.testing.node.internal.internalDriver
import net.corda.testing.node.internal.network.NetworkMapServer import net.corda.testing.node.internal.network.NetworkMapServer

View File

@ -7,6 +7,8 @@ import net.corda.core.utilities.NetworkHostAndPort
import net.corda.node.internal.Node import net.corda.node.internal.Node
import net.corda.node.internal.StartedNode import net.corda.node.internal.StartedNode
import net.corda.testing.* 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.internal.NodeBasedTest
import org.junit.Before import org.junit.Before
import org.junit.ClassRule import org.junit.ClassRule

View File

@ -8,6 +8,8 @@ import net.corda.core.identity.Party
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.unwrap import net.corda.core.utilities.unwrap
import net.corda.testing.* 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.internal.NodeBasedTest
import net.corda.testing.node.startFlow import net.corda.testing.node.startFlow
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat

View File

@ -13,6 +13,9 @@ import net.corda.testing.*
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract
import net.corda.testing.contracts.DummyState import net.corda.testing.contracts.DummyState
import net.corda.testing.driver.driver 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.ClassRule
import org.junit.Test import org.junit.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals

View File

@ -3,6 +3,9 @@ package net.corda.node.services.vault
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.testing.* 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.* import org.junit.*
class VaultQueryIntegrationTests : VaultQueryTests() { class VaultQueryIntegrationTests : VaultQueryTests() {

View File

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

View File

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

View File

@ -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.SSLConfiguration
import net.corda.nodeapi.internal.config.User import net.corda.nodeapi.internal.config.User
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.internal.IntegrationTestSchemas
import net.corda.testing.internal.configureTestSSL import net.corda.testing.internal.configureTestSSL
import net.corda.testing.internal.toDatabaseSchemaName
import net.corda.testing.node.internal.NodeBasedTest import net.corda.testing.node.internal.NodeBasedTest
import net.corda.testing.node.startFlow import net.corda.testing.node.startFlow
import org.apache.activemq.artemis.api.core.ActiveMQNonExistentQueueException import org.apache.activemq.artemis.api.core.ActiveMQNonExistentQueueException

View File

@ -17,14 +17,14 @@ import net.corda.node.internal.StartedNode
import net.corda.node.services.messaging.MessagingService import net.corda.node.services.messaging.MessagingService
import net.corda.node.services.messaging.ReceivedMessage import net.corda.node.services.messaging.ReceivedMessage
import net.corda.node.services.messaging.send 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.ALICE_NAME
import net.corda.testing.chooseIdentity import net.corda.testing.chooseIdentity
import net.corda.testing.driver.DriverDSL import net.corda.testing.driver.DriverDSL
import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.NodeHandle
import net.corda.testing.driver.driver 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.ClusterSpec
import net.corda.testing.node.NotarySpec import net.corda.testing.node.NotarySpec
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat

View File

@ -22,6 +22,10 @@ import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.internal.config.User import net.corda.nodeapi.internal.config.User
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.driver.driver 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.Assume.assumeFalse
import org.junit.ClassRule import org.junit.ClassRule
import org.junit.Test import org.junit.Test
@ -36,7 +40,7 @@ class NodeStatePersistenceTests : IntegrationTest() {
companion object { companion object {
@ClassRule @JvmField @ClassRule @JvmField
val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, DUMMY_BANK_A_NAME) 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()) DUMMY_NOTARY_NAME.toDatabaseSchemaName())
} }
@Test @Test

View File

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

View File

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

View File

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

View File

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

View File

@ -203,8 +203,16 @@ data class SecurityConfiguration(val authService: SecurityConfiguration.AuthServ
data class Options(val cache: Options.Cache?) { data class Options(val cache: Options.Cache?) {
// Cache parameters // 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 // Provider of users credentials and permissions data
@ -228,12 +236,13 @@ data class SecurityConfiguration(val authService: SecurityConfiguration.AuthServ
AuthDataSourceType.DB -> AuthServiceId("REMOTE_DATABASE") AuthDataSourceType.DB -> AuthServiceId("REMOTE_DATABASE")
} }
fun fromUsers(users: List<User>) = AuthService( fun fromUsers(users: List<User>, encryption: PasswordEncryption = PasswordEncryption.NONE) =
dataSource = DataSource( AuthService(
type = AuthDataSourceType.INMEMORY, dataSource = DataSource(
users = users, type = AuthDataSourceType.INMEMORY,
passwordEncryption = PasswordEncryption.NONE), users = users,
id = AuthServiceId("NODE_CONFIG")) passwordEncryption = encryption),
id = AuthServiceId("NODE_CONFIG"))
} }
} }
} }

View File

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

View File

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

View File

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

View File

@ -40,6 +40,18 @@ class FlowLogicRefFactoryImpl(private val classloader: ClassLoader) : SingletonS
return createForRPC(flowClass, *args) 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<out FlowLogic<*>>, vararg args: Any?): FlowLogicRef { override fun createForRPC(flowClass: Class<out FlowLogic<*>>, vararg args: Any?): FlowLogicRef {
// TODO: This is used via RPC but it's probably better if we pass in argument names and values explicitly // 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. // to avoid requiring only a single constructor.

View File

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

View File

@ -7,6 +7,7 @@ import net.corda.node.internal.security.Password
import net.corda.node.internal.security.RPCSecurityManagerImpl import net.corda.node.internal.security.RPCSecurityManagerImpl
import net.corda.node.internal.security.tryAuthenticate import net.corda.node.internal.security.tryAuthenticate
import net.corda.nodeapi.internal.config.User import net.corda.nodeapi.internal.config.User
import net.corda.node.services.config.SecurityConfiguration
import org.assertj.core.api.Assertions.assertThatThrownBy import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Test import org.junit.Test
import javax.security.auth.login.FailedLoginException import javax.security.auth.login.FailedLoginException
@ -26,7 +27,7 @@ class RPCSecurityManagerTest {
@Test @Test
fun `Generic RPC call authorization`() { fun `Generic RPC call authorization`() {
checkUserPermissions( checkUserActions(
permitted = setOf(arrayListOf("nodeInfo"), arrayListOf("notaryIdentities")), permitted = setOf(arrayListOf("nodeInfo"), arrayListOf("notaryIdentities")),
permissions = setOf( permissions = setOf(
Permissions.invokeRpc(CordaRPCOps::nodeInfo), Permissions.invokeRpc(CordaRPCOps::nodeInfo),
@ -35,7 +36,7 @@ class RPCSecurityManagerTest {
@Test @Test
fun `Flow invocation authorization`() { fun `Flow invocation authorization`() {
checkUserPermissions( checkUserActions(
permissions = setOf(Permissions.startFlow<DummyFlow>()), permissions = setOf(Permissions.startFlow<DummyFlow>()),
permitted = setOf( permitted = setOf(
arrayListOf("startTrackedFlowDynamic", "net.corda.node.services.RPCSecurityManagerTest\$DummyFlow"), arrayListOf("startTrackedFlowDynamic", "net.corda.node.services.RPCSecurityManagerTest\$DummyFlow"),
@ -44,21 +45,21 @@ class RPCSecurityManagerTest {
@Test @Test
fun `Check startFlow RPC permission implies startFlowDynamic`() { fun `Check startFlow RPC permission implies startFlowDynamic`() {
checkUserPermissions( checkUserActions(
permissions = setOf(Permissions.invokeRpc("startFlow")), permissions = setOf(Permissions.invokeRpc("startFlow")),
permitted = setOf(arrayListOf("startFlow"), arrayListOf("startFlowDynamic"))) permitted = setOf(arrayListOf("startFlow"), arrayListOf("startFlowDynamic")))
} }
@Test @Test
fun `Check startTrackedFlow RPC permission implies startTrackedFlowDynamic`() { fun `Check startTrackedFlow RPC permission implies startTrackedFlowDynamic`() {
checkUserPermissions( checkUserActions(
permitted = setOf(arrayListOf("startTrackedFlow"), arrayListOf("startTrackedFlowDynamic")), permitted = setOf(arrayListOf("startTrackedFlow"), arrayListOf("startTrackedFlowDynamic")),
permissions = setOf(Permissions.invokeRpc("startTrackedFlow"))) permissions = setOf(Permissions.invokeRpc("startTrackedFlow")))
} }
@Test @Test
fun `Admin authorization`() { fun `Admin authorization`() {
checkUserPermissions( checkUserActions(
permissions = setOf("all"), permissions = setOf("all"),
permitted = allActions.map { arrayListOf(it) }.toSet()) permitted = allActions.map { arrayListOf(it) }.toSet())
} }
@ -118,9 +119,9 @@ class RPCSecurityManagerTest {
users = listOf(User(username, "password", setOf())), id = AuthServiceId("TEST")) users = listOf(User(username, "password", setOf())), id = AuthServiceId("TEST"))
} }
private fun checkUserPermissions(permissions: Set<String>, permitted: Set<ArrayList<String>>) { private fun checkUserActions(permissions: Set<String>, permitted: Set<ArrayList<String>>) {
val user = User(username = "user", password = "password", permissions = permissions) 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)) } val disabled = allActions.filter { !permitted.contains(listOf(it)) }
for (subject in listOf( for (subject in listOf(
userRealms.authenticate("user", Password("password")), userRealms.authenticate("user", Password("password")),

View File

@ -30,6 +30,7 @@ import org.junit.Assert.*
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import java.time.Instant import java.time.Instant
import kotlin.reflect.jvm.jvmName
import kotlin.test.assertEquals import kotlin.test.assertEquals
class ScheduledFlowTests { class ScheduledFlowTests {
@ -52,7 +53,7 @@ class ScheduledFlowTests {
override val linearId: UniqueIdentifier = UniqueIdentifier()) : SchedulableState, LinearState { override val linearId: UniqueIdentifier = UniqueIdentifier()) : SchedulableState, LinearState {
override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? { override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? {
return if (!processed) { return if (!processed) {
val logicRef = flowLogicRefFactory.create(ScheduledFlow::class.java, thisStateRef) val logicRef = flowLogicRefFactory.create(ScheduledFlow::class.jvmName, thisStateRef)
ScheduledActivity(logicRef, creationTime) ScheduledActivity(logicRef, creationTime)
} else { } else {
null null

View File

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

View File

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

View File

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

View File

@ -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.FlowLogic
import net.corda.core.flows.IllegalFlowLogicException import net.corda.core.flows.IllegalFlowLogicException
import net.corda.node.services.statemachine.FlowLogicRefFactoryImpl import net.corda.core.flows.SchedulableFlow
import org.junit.Test import org.junit.Test
import java.time.Duration 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 ParamType1(val value: Int)
data class ParamType2(val value: String) data class ParamType2(val value: String)
@Suppress("UNUSED_PARAMETER", "unused") // Things are used via reflection. @Suppress("UNUSED_PARAMETER", "unused") // Things are used via reflection.
@SchedulableFlow
class KotlinFlowLogic(A: ParamType1, b: ParamType2) : FlowLogic<Unit>() { class KotlinFlowLogic(A: ParamType1, b: ParamType2) : FlowLogic<Unit>() {
constructor() : this(ParamType1(1), ParamType2("2")) constructor() : this(ParamType1(1), ParamType2("2"))
@ -26,6 +29,7 @@ class FlowLogicRefTest {
override fun call() = Unit override fun call() = Unit
} }
@SchedulableFlow
class KotlinNoArgFlowLogic : FlowLogic<Unit>() { class KotlinNoArgFlowLogic : FlowLogic<Unit>() {
override fun call() = Unit override fun call() = Unit
} }
@ -37,18 +41,18 @@ class FlowLogicRefTest {
private val flowLogicRefFactory = FlowLogicRefFactoryImpl(FlowLogicRefFactoryImpl::class.java.classLoader) private val flowLogicRefFactory = FlowLogicRefFactoryImpl(FlowLogicRefFactoryImpl::class.java.classLoader)
@Test @Test
fun `create kotlin no arg`() { fun `create kotlin no arg`() {
flowLogicRefFactory.createForRPC(KotlinNoArgFlowLogic::class.java) flowLogicRefFactory.create(KotlinNoArgFlowLogic::class.jvmName)
} }
@Test @Test
fun `create kotlin`() { fun `should create kotlin types`() {
val args = mapOf(Pair("A", ParamType1(1)), Pair("b", ParamType2("Hello Jack"))) val args = mapOf(Pair("A", ParamType1(1)), Pair("b", ParamType2("Hello Jack")))
flowLogicRefFactory.createKotlin(KotlinFlowLogic::class.java, args) flowLogicRefFactory.createKotlin(KotlinFlowLogic::class.java, args)
} }
@Test @Test
fun `create primary`() { fun `create primary`() {
flowLogicRefFactory.createForRPC(KotlinFlowLogic::class.java, ParamType1(1), ParamType2("Hello Jack")) flowLogicRefFactory.create(KotlinFlowLogic::class.jvmName, ParamType1(1), ParamType2("Hello Jack"))
} }
@Test @Test
@ -76,6 +80,6 @@ class FlowLogicRefTest {
@Test(expected = IllegalFlowLogicException::class) @Test(expected = IllegalFlowLogicException::class)
fun `create for non-schedulable flow logic`() { fun `create for non-schedulable flow logic`() {
flowLogicRefFactory.create(NonSchedulableFlow::class.java) flowLogicRefFactory.create(NonSchedulableFlow::class.jvmName)
} }
} }

View File

@ -24,6 +24,7 @@ import net.corda.testing.node.startFlow
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Ignore
import org.junit.Test import org.junit.Test
import java.time.Instant import java.time.Instant
import java.util.* import java.util.*
@ -99,6 +100,7 @@ class NotaryServiceTests {
assertThat(ex.error).isInstanceOf(NotaryError.TimeWindowInvalid::class.java) 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 @Test
fun `should sign identical transaction multiple times (signing is idempotent)`() { fun `should sign identical transaction multiple times (signing is idempotent)`() {
val stx = run { val stx = run {

View File

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

View File

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

View File

@ -8,6 +8,9 @@ import net.corda.nodeapi.internal.config.User
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.driver.PortAllocation import net.corda.testing.driver.PortAllocation
import net.corda.testing.driver.driver 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.ClassRule
import org.junit.Test import org.junit.Test
import java.util.concurrent.CompletableFuture.supplyAsync import java.util.concurrent.CompletableFuture.supplyAsync

View File

@ -2,7 +2,8 @@ package net.corda.bank
import net.corda.finance.DOLLARS import net.corda.finance.DOLLARS
import net.corda.finance.POUNDS 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 net.corda.testing.node.internal.demorun.deployNodesThen
import org.junit.ClassRule import org.junit.ClassRule
import org.junit.Test import org.junit.Test

View File

@ -14,6 +14,9 @@ import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.internal.config.User import net.corda.nodeapi.internal.config.User
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.driver.driver 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.ClassRule
import org.junit.Test import org.junit.Test

View File

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

Some files were not shown because too many files have changed in this diff Show More