mirror of
https://github.com/corda/corda.git
synced 2024-12-28 08:48:57 +00:00
Merge pull request #292 from corda/shams-os-merge-040118
Shams os merge 040118
This commit is contained in:
commit
dad207ef7d
@ -3579,6 +3579,9 @@ public static final class net.corda.client.jackson.StringToMethodCallParser$Unpa
|
||||
public <init>(String)
|
||||
@org.jetbrains.annotations.NotNull public final String getMethodName()
|
||||
##
|
||||
public static interface net.corda.testing.node.InMemoryMessagingNetwork$LatencyCalculator
|
||||
@org.jetbrains.annotations.NotNull public abstract java.time.Duration between(net.corda.core.messaging.SingleMessageRecipient, net.corda.core.messaging.SingleMessageRecipient)
|
||||
##
|
||||
public final class net.corda.client.rpc.CordaRPCClient extends java.lang.Object
|
||||
public <init>(net.corda.core.utilities.NetworkHostAndPort)
|
||||
public <init>(net.corda.core.utilities.NetworkHostAndPort, net.corda.client.rpc.CordaRPCClientConfiguration)
|
||||
|
@ -31,6 +31,9 @@ import net.corda.node.services.Permissions.Companion.startFlow
|
||||
import net.corda.nodeapi.internal.config.User
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.internal.IntegrationTest
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.internal.toDatabaseSchemaNames
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Test
|
||||
import rx.Observable
|
||||
@ -54,7 +57,7 @@ class NodeMonitorModelTest : IntegrationTest() {
|
||||
companion object {
|
||||
@ClassRule @JvmField
|
||||
val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, CHARLIE_NAME, DUMMY_NOTARY_NAME)
|
||||
.map { it.toDatabaseSchemaNames("","_10000","_10003") }.flatten().toTypedArray())
|
||||
.map { it.toDatabaseSchemaNames("", "_10000", "_10003") }.flatten().toTypedArray())
|
||||
}
|
||||
|
||||
private fun setup(runTest: () -> Unit) {
|
||||
|
@ -12,8 +12,8 @@ import net.corda.node.internal.Node;
|
||||
import net.corda.node.internal.StartedNode;
|
||||
import net.corda.nodeapi.internal.config.User;
|
||||
import net.corda.testing.CoreTestUtils;
|
||||
import net.corda.testing.IntegrationTestKt;
|
||||
import net.corda.testing.IntegrationTestSchemas;
|
||||
import net.corda.testing.internal.IntegrationTestKt;
|
||||
import net.corda.testing.internal.IntegrationTestSchemas;
|
||||
import net.corda.testing.node.internal.NodeBasedTest;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
@ -32,7 +32,6 @@ import static net.corda.finance.contracts.GetBalances.getCashBalance;
|
||||
import static net.corda.node.services.Permissions.invokeRpc;
|
||||
import static net.corda.node.services.Permissions.startFlow;
|
||||
import static net.corda.testing.TestConstants.ALICE_NAME;
|
||||
import static net.corda.testing.TestConstants.DUMMY_NOTARY_NAME;
|
||||
|
||||
public class CordaRPCJavaClientTest extends NodeBasedTest {
|
||||
public CordaRPCJavaClientTest() {
|
||||
|
@ -9,6 +9,9 @@ import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.internal.IntegrationTest
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.internal.toDatabaseSchemaName
|
||||
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Test
|
||||
|
@ -22,6 +22,8 @@ import net.corda.node.services.Permissions.Companion.invokeRpc
|
||||
import net.corda.node.services.Permissions.Companion.startFlow
|
||||
import net.corda.nodeapi.internal.config.User
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.internal.toDatabaseSchemaName
|
||||
import net.corda.testing.node.internal.NodeBasedTest
|
||||
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
|
@ -13,7 +13,10 @@ import net.corda.core.utilities.*
|
||||
import net.corda.node.services.messaging.RPCServerConfiguration
|
||||
import net.corda.nodeapi.RPCApi
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.internal.IntegrationTest
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.internal.testThreadFactory
|
||||
import net.corda.testing.internal.toDatabaseSchemaNames
|
||||
import net.corda.testing.node.internal.*
|
||||
import org.apache.activemq.artemis.api.core.SimpleString
|
||||
import org.junit.After
|
||||
@ -45,7 +48,7 @@ class RPCStabilityTests : IntegrationTest() {
|
||||
companion object {
|
||||
@ClassRule @JvmField
|
||||
val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, DUMMY_BANK_A_NAME)
|
||||
.map { it.toDatabaseSchemaNames("","_10000","_10003","_10012") }.flatten().toTypedArray())
|
||||
.map { it.toDatabaseSchemaNames("", "_10000", "_10003", "_10012") }.flatten().toTypedArray())
|
||||
}
|
||||
|
||||
object DummyOps : RPCOps {
|
||||
|
@ -10,7 +10,6 @@ import net.corda.core.identity.AnonymousParty
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
import net.corda.core.node.services.IdentityService
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.serialization.SerializedBytes
|
||||
@ -18,14 +17,8 @@ import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.utilities.ProgressTracker
|
||||
import net.corda.core.utilities.unwrap
|
||||
import org.bouncycastle.asn1.DERSet
|
||||
import org.bouncycastle.asn1.pkcs.CertificationRequestInfo
|
||||
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.nio.charset.Charset
|
||||
import java.security.PublicKey
|
||||
import java.security.SignatureException
|
||||
import java.security.cert.CertPath
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
|
@ -1,4 +1,4 @@
|
||||
gradlePluginsVersion=3.0.2
|
||||
gradlePluginsVersion=3.0.3
|
||||
kotlinVersion=1.1.60
|
||||
platformVersion=1
|
||||
guavaVersion=21.0
|
||||
|
@ -30,6 +30,7 @@ import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPublicKey
|
||||
import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter
|
||||
import org.bouncycastle.jce.ECNamedCurveTable
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
||||
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec
|
||||
import org.bouncycastle.jce.spec.ECParameterSpec
|
||||
import org.bouncycastle.jce.spec.ECPrivateKeySpec
|
||||
import org.bouncycastle.jce.spec.ECPublicKeySpec
|
||||
@ -808,7 +809,7 @@ object Crypto {
|
||||
/**
|
||||
* Returns a key pair derived from the given [BigInteger] entropy. This is useful for unit tests
|
||||
* and other cases where you want hard-coded private keys.
|
||||
* Currently, [EDDSA_ED25519_SHA512] is the sole scheme supported for this operation.
|
||||
* Currently, the following schemes are supported: [EDDSA_ED25519_SHA512], [ECDSA_SECP256R1_SHA256] and [ECDSA_SECP256K1_SHA256].
|
||||
* @param signatureScheme a supported [SignatureScheme], see [Crypto].
|
||||
* @param entropy a [BigInteger] value.
|
||||
* @return a new [KeyPair] from an entropy input.
|
||||
@ -818,6 +819,7 @@ object Crypto {
|
||||
fun deriveKeyPairFromEntropy(signatureScheme: SignatureScheme, entropy: BigInteger): KeyPair {
|
||||
return when (signatureScheme) {
|
||||
EDDSA_ED25519_SHA512 -> deriveEdDSAKeyPairFromEntropy(entropy)
|
||||
ECDSA_SECP256R1_SHA256, ECDSA_SECP256K1_SHA256 -> deriveECDSAKeyPairFromEntropy(signatureScheme, entropy)
|
||||
else -> throw IllegalArgumentException("Unsupported signature scheme for fixed entropy-based key pair " +
|
||||
"generation: ${signatureScheme.schemeCodeName}")
|
||||
}
|
||||
@ -832,6 +834,9 @@ object Crypto {
|
||||
fun deriveKeyPairFromEntropy(entropy: BigInteger): KeyPair = deriveKeyPairFromEntropy(DEFAULT_SIGNATURE_SCHEME, entropy)
|
||||
|
||||
// Custom key pair generator from entropy.
|
||||
// The BigIntenger.toByteArray() uses the two's-complement representation.
|
||||
// The entropy is transformed to a byte array in big-endian byte-order and
|
||||
// only the first ed25519.field.getb() / 8 bytes are used.
|
||||
private fun deriveEdDSAKeyPairFromEntropy(entropy: BigInteger): KeyPair {
|
||||
val params = EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec
|
||||
val bytes = entropy.toByteArray().copyOf(params.curve.field.getb() / 8) // Need to pad the entropy to the valid seed length.
|
||||
@ -840,6 +845,32 @@ object Crypto {
|
||||
return KeyPair(EdDSAPublicKey(pub), EdDSAPrivateKey(priv))
|
||||
}
|
||||
|
||||
// Custom key pair generator from an entropy required for various tests. It is similar to deriveKeyPairECDSA,
|
||||
// but the accepted range of the input entropy is more relaxed:
|
||||
// 2 <= entropy < N, where N is the order of base-point G.
|
||||
private fun deriveECDSAKeyPairFromEntropy(signatureScheme: SignatureScheme, entropy: BigInteger): KeyPair {
|
||||
val parameterSpec = signatureScheme.algSpec as ECNamedCurveParameterSpec
|
||||
|
||||
// The entropy might be a negative number and/or out of range (e.g. PRNG output).
|
||||
// In such cases we retry with hash(currentEntropy).
|
||||
while (entropy < ECConstants.TWO || entropy >= parameterSpec.n) {
|
||||
return deriveECDSAKeyPairFromEntropy(signatureScheme, BigInteger(1, entropy.toByteArray().sha256().bytes))
|
||||
}
|
||||
|
||||
val privateKeySpec = ECPrivateKeySpec(entropy, parameterSpec)
|
||||
val priv = BCECPrivateKey("EC", privateKeySpec, BouncyCastleProvider.CONFIGURATION)
|
||||
|
||||
val pointQ = FixedPointCombMultiplier().multiply(parameterSpec.g, entropy)
|
||||
while (pointQ.isInfinity) {
|
||||
// Instead of throwing an exception, we retry with hash(entropy).
|
||||
return deriveECDSAKeyPairFromEntropy(signatureScheme, BigInteger(1, entropy.toByteArray().sha256().bytes))
|
||||
}
|
||||
val publicKeySpec = ECPublicKeySpec(pointQ, parameterSpec)
|
||||
val pub = BCECPublicKey("EC", publicKeySpec, BouncyCastleProvider.CONFIGURATION)
|
||||
|
||||
return KeyPair(pub, priv)
|
||||
}
|
||||
|
||||
// Compute the HMAC-SHA512 using a privateKey as the MAC_key and a seed ByteArray.
|
||||
private fun deriveHMAC(privateKey: PrivateKey, seed: ByteArray): ByteArray {
|
||||
// Compute hmac(privateKey, seed).
|
||||
|
@ -118,18 +118,19 @@ fun Iterable<TransactionSignature>.byKeys() = map { it.by }.toSet()
|
||||
|
||||
// Allow Kotlin destructuring:
|
||||
// val (private, public) = keyPair
|
||||
/* The [PrivateKey] of this [KeyPair] .*/
|
||||
/* The [PrivateKey] of this [KeyPair]. */
|
||||
operator fun KeyPair.component1(): PrivateKey = this.private
|
||||
/* The [PublicKey] of this [KeyPair] .*/
|
||||
/* The [PublicKey] of this [KeyPair]. */
|
||||
operator fun KeyPair.component2(): PublicKey = this.public
|
||||
|
||||
/** A simple wrapper that will make it easier to swap out the EC algorithm we use in future. */
|
||||
/** A simple wrapper that will make it easier to swap out the signature algorithm we use in future. */
|
||||
fun generateKeyPair(): KeyPair = Crypto.generateKeyPair()
|
||||
|
||||
/**
|
||||
* Returns a key pair derived from the given private key entropy. This is useful for unit tests and other cases where
|
||||
* you want hard-coded private keys.
|
||||
* This currently works for the default signature scheme EdDSA ed25519 only.
|
||||
* @param entropy a [BigInteger] value.
|
||||
* @return a deterministically generated [KeyPair] for the [Crypto.DEFAULT_SIGNATURE_SCHEME].
|
||||
*/
|
||||
fun entropyToKeyPair(entropy: BigInteger): KeyPair = Crypto.deriveKeyPairFromEntropy(entropy)
|
||||
|
||||
|
@ -10,7 +10,17 @@ import net.corda.core.serialization.CordaSerializable
|
||||
*/
|
||||
@DoNotImplement
|
||||
interface FlowLogicRefFactory {
|
||||
/**
|
||||
* Construct a FlowLogicRef. This is intended for cases where the calling code has the relevant class already
|
||||
* and can provide it directly.
|
||||
*/
|
||||
@Deprecated("This should be avoided, and the version which takes a class name used instead to avoid requiring the class on the classpath to deserialize calling code")
|
||||
fun create(flowClass: Class<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 toFlowLogic(ref: FlowLogicRef): FlowLogic<*>
|
||||
}
|
||||
|
@ -80,8 +80,10 @@ data class CordaX500Name(val commonName: String?,
|
||||
const val MAX_LENGTH_STATE = 64
|
||||
const val MAX_LENGTH_ORGANISATION_UNIT = 64
|
||||
const val MAX_LENGTH_COMMON_NAME = 64
|
||||
|
||||
private val supportedAttributes = setOf(BCStyle.O, BCStyle.C, BCStyle.L, BCStyle.CN, BCStyle.ST, BCStyle.OU)
|
||||
private val countryCodes: Set<String> = ImmutableSet.copyOf(Locale.getISOCountries() + unspecifiedCountry)
|
||||
|
||||
@JvmStatic
|
||||
fun build(principal: X500Principal): CordaX500Name {
|
||||
val x500Name = X500Name.getInstance(principal.encoded)
|
||||
@ -115,20 +117,12 @@ data class CordaX500Name(val commonName: String?,
|
||||
}
|
||||
|
||||
@Transient
|
||||
private var x500Cache: X500Name? = null
|
||||
private var _x500Principal: X500Principal? = null
|
||||
|
||||
val x500Principal: X500Principal
|
||||
get() {
|
||||
if (x500Cache == null) {
|
||||
x500Cache = this.x500Name
|
||||
}
|
||||
return X500Principal(x500Cache!!.encoded)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
if (x500Cache == null) {
|
||||
x500Cache = this.x500Name
|
||||
}
|
||||
return x500Cache.toString()
|
||||
/** Return the [X500Principal] equivalent of this name. */
|
||||
val x500Principal: X500Principal get() {
|
||||
return _x500Principal ?: X500Principal(this.x500Name.encoded).also { _x500Principal = it }
|
||||
}
|
||||
|
||||
override fun toString(): String = x500Principal.toString()
|
||||
}
|
@ -10,8 +10,9 @@ import net.corda.core.node.ServicesForResolution
|
||||
import net.corda.core.serialization.SerializationContext
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.transactions.WireTransaction
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import org.bouncycastle.asn1.x500.X500NameBuilder
|
||||
import org.bouncycastle.asn1.x500.style.BCStyle
|
||||
import org.slf4j.Logger
|
||||
import rx.Observable
|
||||
import rx.Observer
|
||||
@ -26,8 +27,6 @@ import java.nio.charset.Charset
|
||||
import java.nio.charset.StandardCharsets.UTF_8
|
||||
import java.nio.file.*
|
||||
import java.nio.file.attribute.FileAttribute
|
||||
import java.security.cert.Certificate
|
||||
import java.security.cert.X509Certificate
|
||||
import java.time.Duration
|
||||
import java.time.temporal.Temporal
|
||||
import java.util.*
|
||||
@ -186,9 +185,6 @@ fun <T> logElapsedTime(label: String, logger: Logger? = null, body: () -> T): T
|
||||
}
|
||||
}
|
||||
|
||||
fun Certificate.toX509CertHolder() = X509CertificateHolder(encoded)
|
||||
val X509CertificateHolder.cert: X509Certificate get() = JcaX509CertificateConverter().getCertificate(this)
|
||||
|
||||
/** Convert a [ByteArrayOutputStream] to [InputStreamAndHash]. */
|
||||
fun ByteArrayOutputStream.toInputStreamAndHash(): InputStreamAndHash {
|
||||
val bytes = toByteArray()
|
||||
@ -320,6 +316,22 @@ fun ExecutorService.join() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the underlying X.500 name from this Corda-safe X.500 name. These are guaranteed to have a consistent
|
||||
* ordering, such that their `toString()` function returns the same value every time for the same [CordaX500Name].
|
||||
*/
|
||||
val CordaX500Name.x500Name: X500Name
|
||||
get() {
|
||||
return X500NameBuilder(BCStyle.INSTANCE).apply {
|
||||
addRDN(BCStyle.C, country)
|
||||
state?.let { addRDN(BCStyle.ST, it) }
|
||||
addRDN(BCStyle.L, locality)
|
||||
addRDN(BCStyle.O, organisation)
|
||||
organisationUnit?.let { addRDN(BCStyle.OU, it) }
|
||||
commonName?.let { addRDN(BCStyle.CN, it) }
|
||||
}.build()
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
@VisibleForTesting
|
||||
val CordaX500Name.Companion.unspecifiedCountry
|
||||
|
@ -1,34 +0,0 @@
|
||||
@file:JvmName("X500NameUtils")
|
||||
|
||||
package net.corda.core.internal
|
||||
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import org.bouncycastle.asn1.ASN1ObjectIdentifier
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import org.bouncycastle.asn1.x500.X500NameBuilder
|
||||
import org.bouncycastle.asn1.x500.style.BCStyle
|
||||
|
||||
val X500Name.commonName: String? get() = getRDNValueString(BCStyle.CN)
|
||||
val X500Name.state: String? get() = getRDNValueString(BCStyle.ST)
|
||||
val X500Name.organisation: String get() = getRDNValueString(BCStyle.O) ?: throw IllegalArgumentException("Malformed X500 name, organisation attribute (O) cannot be empty.")
|
||||
val X500Name.locality: String get() = getRDNValueString(BCStyle.L) ?: throw IllegalArgumentException("Malformed X500 name, locality attribute (L) cannot be empty.")
|
||||
val X500Name.country: String get() = getRDNValueString(BCStyle.C) ?: throw IllegalArgumentException("Malformed X500 name, country attribute (C) cannot be empty.")
|
||||
|
||||
private fun X500Name.getRDNValueString(identifier: ASN1ObjectIdentifier): String? = getRDNs(identifier).firstOrNull()?.first?.value?.toString()
|
||||
|
||||
|
||||
/**
|
||||
* Return the underlying X.500 name from this Corda-safe X.500 name. These are guaranteed to have a consistent
|
||||
* ordering, such that their `toString()` function returns the same value every time for the same [CordaX500Name].
|
||||
*/
|
||||
val CordaX500Name.x500Name: X500Name
|
||||
get() {
|
||||
return X500NameBuilder(BCStyle.INSTANCE).apply {
|
||||
addRDN(BCStyle.C, country)
|
||||
state?.let { addRDN(BCStyle.ST, it) }
|
||||
addRDN(BCStyle.L, locality)
|
||||
addRDN(BCStyle.O, organisation)
|
||||
organisationUnit?.let { addRDN(BCStyle.OU, it) }
|
||||
commonName?.let { addRDN(BCStyle.CN, it) }
|
||||
}.build()
|
||||
}
|
@ -1,8 +1,6 @@
|
||||
package net.corda.core.crypto
|
||||
|
||||
import net.corda.core.crypto.CompositeKey.NodeAndWeight
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.declaredField
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.serialization.serialize
|
||||
@ -15,6 +13,7 @@ import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import java.security.PublicKey
|
||||
import javax.security.auth.x500.X500Principal
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertFalse
|
||||
@ -334,7 +333,7 @@ class CompositeKeyTests {
|
||||
|
||||
// Create self sign CA.
|
||||
val caKeyPair = Crypto.generateKeyPair()
|
||||
val caName = CordaX500Name(commonName = "Test CA", organisation = "R3 Ltd", locality = "London", country = "GB")
|
||||
val caName = X500Principal("CN=Test CA,O=R3 Ltd,L=London,C=GB")
|
||||
val ca = X509Utilities.createSelfSignedCACertificate(caName, caKeyPair)
|
||||
|
||||
// Sign the composite key with the self sign CA.
|
||||
@ -343,7 +342,7 @@ class CompositeKeyTests {
|
||||
// Store certificate to keystore.
|
||||
val keystorePath = tempFolder.root.toPath() / "keystore.jks"
|
||||
val keystore = loadOrCreateKeyStore(keystorePath, "password")
|
||||
keystore.setCertificateEntry("CompositeKey", compositeKeyCert.cert)
|
||||
keystore.setCertificateEntry("CompositeKey", compositeKeyCert)
|
||||
keystore.save(keystorePath, "password")
|
||||
|
||||
// Load keystore from disk.
|
||||
|
@ -14,21 +14,22 @@ import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey
|
||||
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey
|
||||
import org.bouncycastle.jce.ECNamedCurveTable
|
||||
import org.bouncycastle.jce.interfaces.ECKey
|
||||
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec
|
||||
import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PrivateKey
|
||||
import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PublicKey
|
||||
import org.junit.Assert.assertNotEquals
|
||||
import org.junit.Test
|
||||
import java.math.BigInteger
|
||||
import java.security.KeyPairGenerator
|
||||
import java.util.*
|
||||
import kotlin.test.*
|
||||
|
||||
/**
|
||||
* Run tests for cryptographic algorithms
|
||||
* Run tests for cryptographic algorithms.
|
||||
*/
|
||||
class CryptoUtilsTest {
|
||||
|
||||
private val testString = "Hello World"
|
||||
private val testBytes = testString.toByteArray()
|
||||
private val testBytes = "Hello World".toByteArray()
|
||||
|
||||
// key generation test
|
||||
@Test
|
||||
@ -781,4 +782,113 @@ class CryptoUtilsTest {
|
||||
assertEquals(dpriv2, dpriv_2)
|
||||
assertEquals(dpub2, dpub_2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `EdDSA ed25519 keyPair from entropy`() {
|
||||
val keyPairPositive = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("10"))
|
||||
assertEquals("DLBL3iHCp9uRReWhhCGfCsrxZZpfAm9h9GLbfN8ijqXTq", keyPairPositive.public.toStringShort())
|
||||
|
||||
val keyPairNegative = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("-10"))
|
||||
assertEquals("DLC5HXnYsJAFqmM9hgPj5G8whQ4TpyE9WMBssqCayLBwA2", keyPairNegative.public.toStringShort())
|
||||
|
||||
val keyPairZero = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("0"))
|
||||
assertEquals("DL4UVhGh4tqu1G86UVoGNaDDNCMsBtNHzE6BSZuNNJN7W2", keyPairZero.public.toStringShort())
|
||||
|
||||
val keyPairOne = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("1"))
|
||||
assertEquals("DL8EZUdHixovcCynKMQzrMWBnXQAcbVDHi6ArPphqwJVzq", keyPairOne.public.toStringShort())
|
||||
|
||||
val keyPairBiggerThan256bits = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("2").pow(258).minus(BigInteger.TEN))
|
||||
assertEquals("DLB9K1UiBrWonn481z6NzkqoWHjMBXpfDeaet3wiwRNWSU", keyPairBiggerThan256bits.public.toStringShort())
|
||||
// The underlying implementation uses the first 256 bytes of the entropy. Thus, 2^258-10 and 2^258-50 and 2^514-10 have the same impact.
|
||||
val keyPairBiggerThan256bitsV2 = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("2").pow(258).minus(BigInteger("50")))
|
||||
assertEquals("DLB9K1UiBrWonn481z6NzkqoWHjMBXpfDeaet3wiwRNWSU", keyPairBiggerThan256bitsV2.public.toStringShort())
|
||||
val keyPairBiggerThan512bits = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("2").pow(514).minus(BigInteger.TEN))
|
||||
assertEquals("DLB9K1UiBrWonn481z6NzkqoWHjMBXpfDeaet3wiwRNWSU", keyPairBiggerThan512bits.public.toStringShort())
|
||||
|
||||
// Try another big number.
|
||||
val keyPairBiggerThan258bits = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("2").pow(259).plus(BigInteger.ONE))
|
||||
assertEquals("DL5tEFVMXMGrzwjfCAW34JjkhsRkPfFyJ38iEnmpB6L2Z9", keyPairBiggerThan258bits.public.toStringShort())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ECDSA R1 keyPair from entropy`() {
|
||||
val keyPairPositive = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("10"))
|
||||
assertEquals("DLHDcxuSt9J3cbjd2Dsx4rAgYYA7BAP7A8VLrFiq1tH9yy", keyPairPositive.public.toStringShort())
|
||||
// The underlying implementation uses the hash of entropy if it is out of range 2 < entropy < N, where N the order of the group.
|
||||
val keyPairNegative = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("-10"))
|
||||
assertEquals("DLBASmjiMZuu1g3EtdHJxfSueXE8PRoUWbkdU61Qcnpamt", keyPairNegative.public.toStringShort())
|
||||
|
||||
val keyPairZero = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("0"))
|
||||
assertEquals("DLH2FEHEnsT3MpCJt2gfyNjpqRqcBxeupK4YRPXvDsVEkb", keyPairZero.public.toStringShort())
|
||||
// BigIntenger.Zero is out or range, so 1 and hash(1.toByteArray) would have the same impact.
|
||||
val zeroHashed = BigInteger(1, BigInteger("0").toByteArray().sha256().bytes)
|
||||
// Check oneHashed < N (order of the group), otherwise we would need an extra hash.
|
||||
assertEquals(-1, zeroHashed.compareTo((Crypto.ECDSA_SECP256R1_SHA256.algSpec as ECNamedCurveParameterSpec).n))
|
||||
val keyPairZeroHashed = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, zeroHashed)
|
||||
assertEquals("DLH2FEHEnsT3MpCJt2gfyNjpqRqcBxeupK4YRPXvDsVEkb", keyPairZeroHashed.public.toStringShort())
|
||||
|
||||
val keyPairOne = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("1"))
|
||||
assertEquals("DLHrtKwjv6onq9HcrQDJPs8Cgtai5mZU5ZU6sb1ivJjx3z", keyPairOne.public.toStringShort())
|
||||
// BigIntenger.ONE is out or range, so 1 and hash(1.toByteArray) would have the same impact.
|
||||
val oneHashed = BigInteger(1, BigInteger("1").toByteArray().sha256().bytes)
|
||||
// Check oneHashed < N (order of the group), otherwise we would need an extra hash.
|
||||
assertEquals(-1, oneHashed.compareTo((Crypto.ECDSA_SECP256R1_SHA256.algSpec as ECNamedCurveParameterSpec).n))
|
||||
val keyPairOneHashed = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, oneHashed)
|
||||
assertEquals("DLHrtKwjv6onq9HcrQDJPs8Cgtai5mZU5ZU6sb1ivJjx3z", keyPairOneHashed.public.toStringShort())
|
||||
|
||||
// 2 is in the range.
|
||||
val keyPairTwo = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2"))
|
||||
assertEquals("DLFoz6txJ3vHcKNSM1vFxHJUoEQ69PorBwW64dHsAnEoZB", keyPairTwo.public.toStringShort())
|
||||
|
||||
// Try big numbers that are out of range.
|
||||
val keyPairBiggerThan256bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2").pow(258).minus(BigInteger.TEN))
|
||||
assertEquals("DLBv6fZqaCTbE4L7sgjbt19biXHMgU9CzR5s8g8XBJjZ11", keyPairBiggerThan256bits.public.toStringShort())
|
||||
val keyPairBiggerThan256bitsV2 = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2").pow(258).minus(BigInteger("50")))
|
||||
assertEquals("DLANmjhGSVdLyghxcPHrn3KuGatscf6LtvqifUDxw7SGU8", keyPairBiggerThan256bitsV2.public.toStringShort())
|
||||
val keyPairBiggerThan512bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2").pow(514).minus(BigInteger.TEN))
|
||||
assertEquals("DL9sKwMExBTD3MnJN6LWGqo496Erkebs9fxZtXLVJUBY9Z", keyPairBiggerThan512bits.public.toStringShort())
|
||||
val keyPairBiggerThan258bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2").pow(259).plus(BigInteger.ONE))
|
||||
assertEquals("DLBwjWwPJSF9E7b1NWaSbEJ4oK8CF7RDGWd648TiBhZoL1", keyPairBiggerThan258bits.public.toStringShort())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ECDSA K1 keyPair from entropy`() {
|
||||
val keyPairPositive = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("10"))
|
||||
assertEquals("DL6pYKUgH17az8MLdonvvUtUPN8TqwpCGcdgLr7vg3skCU", keyPairPositive.public.toStringShort())
|
||||
// The underlying implementation uses the hash of entropy if it is out of range 2 <= entropy < N, where N the order of the group.
|
||||
val keyPairNegative = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("-10"))
|
||||
assertEquals("DLnpXhxece69Nyqgm3pPt3yV7ESQYDJKoYxs1hKgfBAEu", keyPairNegative.public.toStringShort())
|
||||
|
||||
val keyPairZero = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("0"))
|
||||
assertEquals("DLBC28e18T6KsYwjTFfUWJfhvHjvYVapyVf6antnqUkbgd", keyPairZero.public.toStringShort())
|
||||
// BigIntenger.Zero is out or range, so 1 and hash(1.toByteArray) would have the same impact.
|
||||
val zeroHashed = BigInteger(1, BigInteger("0").toByteArray().sha256().bytes)
|
||||
// Check oneHashed < N (order of the group), otherwise we would need an extra hash.
|
||||
assertEquals(-1, zeroHashed.compareTo((Crypto.ECDSA_SECP256K1_SHA256.algSpec as ECNamedCurveParameterSpec).n))
|
||||
val keyPairZeroHashed = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, zeroHashed)
|
||||
assertEquals("DLBC28e18T6KsYwjTFfUWJfhvHjvYVapyVf6antnqUkbgd", keyPairZeroHashed.public.toStringShort())
|
||||
|
||||
val keyPairOne = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("1"))
|
||||
assertEquals("DLBimRXdEQhJUTpL6f9ri9woNdsze6mwkRrhsML13Eh7ET", keyPairOne.public.toStringShort())
|
||||
// BigIntenger.ONE is out or range, so 1 and hash(1.toByteArray) would have the same impact.
|
||||
val oneHashed = BigInteger(1, BigInteger("1").toByteArray().sha256().bytes)
|
||||
// Check oneHashed < N (order of the group), otherwise we would need an extra hash.
|
||||
assertEquals(-1, oneHashed.compareTo((Crypto.ECDSA_SECP256K1_SHA256.algSpec as ECNamedCurveParameterSpec).n))
|
||||
val keyPairOneHashed = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, oneHashed)
|
||||
assertEquals("DLBimRXdEQhJUTpL6f9ri9woNdsze6mwkRrhsML13Eh7ET", keyPairOneHashed.public.toStringShort())
|
||||
|
||||
// 2 is in the range.
|
||||
val keyPairTwo = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2"))
|
||||
assertEquals("DLG32UWaevGw9YY7w1Rf9mmK88biavgpDnJA9bG4GapVPs", keyPairTwo.public.toStringShort())
|
||||
|
||||
// Try big numbers that are out of range.
|
||||
val keyPairBiggerThan256bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2").pow(258).minus(BigInteger.TEN))
|
||||
assertEquals("DLGHsdv2xeAuM7n3sBc6mFfiphXe6VSf3YxqvviKDU6Vbd", keyPairBiggerThan256bits.public.toStringShort())
|
||||
val keyPairBiggerThan256bitsV2 = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2").pow(258).minus(BigInteger("50")))
|
||||
assertEquals("DL9yJfiNGqteRrKPjGUkRQkeqzuQ4kwcYQWMCi5YKuUHrk", keyPairBiggerThan256bitsV2.public.toStringShort())
|
||||
val keyPairBiggerThan512bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2").pow(514).minus(BigInteger.TEN))
|
||||
assertEquals("DL3Wr5EQGrMTaKBy5XMvG8rvSfKX1AYZLCRU8kixGbxt1E", keyPairBiggerThan512bits.public.toStringShort())
|
||||
val keyPairBiggerThan258bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2").pow(259).plus(BigInteger.ONE))
|
||||
assertEquals("DL7NbssqvuuJ4cqFkkaVYu9j1MsVswESGgCfbqBS9ULwuM", keyPairBiggerThan258bits.public.toStringShort())
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package net.corda.core.crypto
|
||||
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.nodeapi.internal.crypto.*
|
||||
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
@ -14,6 +13,7 @@ import java.security.KeyStore
|
||||
import java.security.cert.CertPathValidator
|
||||
import java.security.cert.CertPathValidatorException
|
||||
import java.security.cert.PKIXParameters
|
||||
import javax.security.auth.x500.X500Principal
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@ -26,17 +26,22 @@ class X509NameConstraintsTest {
|
||||
CertificateType.NODE_CA,
|
||||
intermediateCa.certificate,
|
||||
intermediateCa.keyPair,
|
||||
CordaX500Name("Corda Client CA", "R3 Ltd", "London", "GB"),
|
||||
CordaX500Name("Corda Client CA", "R3 Ltd", "London", "GB").x500Principal,
|
||||
nodeCaKeyPair.public,
|
||||
nameConstraints = nameConstraints)
|
||||
|
||||
val keyPass = "password"
|
||||
val trustStore = KeyStore.getInstance(KEYSTORE_TYPE)
|
||||
trustStore.load(null, keyPass.toCharArray())
|
||||
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCa.certificate.cert)
|
||||
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCa.certificate)
|
||||
|
||||
val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, subjectName, tlsKeyPair.public)
|
||||
val tlsCert = X509Utilities.createCertificate(
|
||||
CertificateType.TLS,
|
||||
nodeCaCert,
|
||||
nodeCaKeyPair,
|
||||
X500Principal(subjectName.encoded),
|
||||
tlsKeyPair.public)
|
||||
|
||||
val keyStore = KeyStore.getInstance(KEYSTORE_TYPE)
|
||||
keyStore.load(null, keyPass.toCharArray())
|
||||
|
@ -7,30 +7,36 @@ import kotlin.test.assertNull
|
||||
|
||||
class CordaX500NameTest {
|
||||
@Test
|
||||
fun `parse service name with organisational unit`() {
|
||||
fun `service name with organisational unit`() {
|
||||
val name = CordaX500Name.parse("O=Bank A, L=New York, C=US, OU=Org Unit, CN=Service Name")
|
||||
assertEquals("Service Name", name.commonName)
|
||||
assertEquals("Org Unit", name.organisationUnit)
|
||||
assertEquals("Bank A", name.organisation)
|
||||
assertEquals("New York", name.locality)
|
||||
assertEquals(CordaX500Name.parse(name.toString()), name)
|
||||
assertEquals(CordaX500Name.build(name.x500Principal), name)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parse service name`() {
|
||||
fun `service name`() {
|
||||
val name = CordaX500Name.parse("O=Bank A, L=New York, C=US, CN=Service Name")
|
||||
assertEquals("Service Name", name.commonName)
|
||||
assertNull(name.organisationUnit)
|
||||
assertEquals("Bank A", name.organisation)
|
||||
assertEquals("New York", name.locality)
|
||||
assertEquals(CordaX500Name.parse(name.toString()), name)
|
||||
assertEquals(CordaX500Name.build(name.x500Principal), name)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parse legal entity name`() {
|
||||
fun `legal entity name`() {
|
||||
val name = CordaX500Name.parse("O=Bank A, L=New York, C=US")
|
||||
assertNull(name.commonName)
|
||||
assertNull(name.organisationUnit)
|
||||
assertEquals("Bank A", name.organisation)
|
||||
assertEquals("New York", name.locality)
|
||||
assertEquals(CordaX500Name.parse(name.toString()), name)
|
||||
assertEquals(CordaX500Name.build(name.x500Principal), name)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1,14 +1,13 @@
|
||||
package net.corda.core.identity
|
||||
|
||||
import net.corda.core.crypto.entropyToKeyPair
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.read
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.nodeapi.internal.crypto.KEYSTORE_TYPE
|
||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||
import net.corda.nodeapi.internal.crypto.save
|
||||
import net.corda.testing.DEV_CA
|
||||
import net.corda.testing.DEV_ROOT_CA
|
||||
import net.corda.testing.SerializationEnvironmentRule
|
||||
import net.corda.testing.getTestPartyAndCertificate
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
@ -25,8 +24,8 @@ class PartyAndCertificateTest {
|
||||
val testSerialization = SerializationEnvironmentRule()
|
||||
|
||||
@Test
|
||||
fun `should reject a path with no roles`() {
|
||||
val path = X509CertificateFactory().generateCertPath(DEV_CA.certificate.cert)
|
||||
fun `reject a path with no roles`() {
|
||||
val path = X509CertificateFactory().generateCertPath(DEV_ROOT_CA.certificate)
|
||||
assertFailsWith<IllegalArgumentException> { PartyAndCertificate(path) }
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,9 @@ import net.corda.node.services.Permissions.Companion.startFlow
|
||||
import net.corda.nodeapi.internal.config.User
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.internal.IntegrationTest
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.internal.toDatabaseSchemaName
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
@ -103,7 +103,7 @@ Go to the terminal window displaying the CRaSH shell of PartyA. Typing ``help``
|
||||
commands.
|
||||
|
||||
.. note:: Local terminal shell is available only in a development mode. In production environment SSH server can be enabled.
|
||||
More about SSH and how to connect can be found on :doc:`Shell` page.
|
||||
More about SSH and how to connect can be found on the :doc:`shell` page.
|
||||
|
||||
We want to create an IOU of 100 with PartyB. We start the ``IOUFlow`` by typing:
|
||||
|
||||
|
@ -192,9 +192,6 @@ The following 3rd party types are supported.
|
||||
|
||||
org.apache.activemq.artemis.api.core.SimpleString
|
||||
|
||||
org.bouncycastle.asn1.x500.X500Name
|
||||
org.bouncycastle.cert.X509CertificateHolder
|
||||
|
||||
Corda Types
|
||||
```````````
|
||||
|
||||
|
@ -6,6 +6,9 @@ import net.corda.finance.EUR
|
||||
import net.corda.finance.USD
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.internal.IntegrationTest
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.internal.toDatabaseSchemaNames
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Test
|
||||
@ -14,7 +17,7 @@ class CashConfigDataFlowTest : IntegrationTest() {
|
||||
companion object {
|
||||
@ClassRule @JvmField
|
||||
val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, DUMMY_BANK_A_NAME)
|
||||
.map { it.toDatabaseSchemaNames("","_10000","_10003") }.flatten().toTypedArray())
|
||||
.map { it.toDatabaseSchemaNames("", "_10000", "_10003") }.flatten().toTypedArray())
|
||||
}
|
||||
@Test
|
||||
fun `issuable currencies are read in from node config`() {
|
||||
|
@ -29,8 +29,8 @@ import java.util.*
|
||||
class CashExitFlow(private val amount: Amount<Currency>,
|
||||
private val issuerRef: OpaqueBytes,
|
||||
progressTracker: ProgressTracker) : AbstractCashFlow<AbstractCashFlow.Result>(progressTracker) {
|
||||
constructor(amount: Amount<Currency>, issueRef: OpaqueBytes) : this(amount, issueRef, tracker())
|
||||
constructor(request: ExitRequest) : this(request.amount, request.issueRef, tracker())
|
||||
constructor(amount: Amount<Currency>, issuerRef: OpaqueBytes) : this(amount, issuerRef, tracker())
|
||||
constructor(request: ExitRequest) : this(request.amount, request.issuerRef, tracker())
|
||||
|
||||
companion object {
|
||||
fun tracker() = ProgressTracker(GENERATING_TX, SIGNING_TX, FINALISING_TX)
|
||||
@ -78,5 +78,5 @@ class CashExitFlow(private val amount: Amount<Currency>,
|
||||
}
|
||||
|
||||
@CordaSerializable
|
||||
class ExitRequest(amount: Amount<Currency>, val issueRef: OpaqueBytes) : AbstractRequest(amount)
|
||||
class ExitRequest(amount: Amount<Currency>, val issuerRef: OpaqueBytes) : AbstractRequest(amount)
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ Generates a text summary of Corda's public API that we can check for API-breakin
|
||||
$ gradlew generateApi
|
||||
```
|
||||
|
||||
See [here](../../docs/source/api-index.rst) for Corda's public API strategy. We will need to
|
||||
See [here](../../docs/source/corda-api.rst) for Corda's public API strategy. We will need to
|
||||
apply this plugin to other modules in future Corda releases as those modules' APIs stabilise.
|
||||
|
||||
Basically, this plugin will document a module's `public` and `protected` classes/methods/fields,
|
||||
|
@ -22,6 +22,7 @@ import java.util.jar.JarInputStream
|
||||
@Suppress("unused")
|
||||
open class Cordform : DefaultTask() {
|
||||
private companion object {
|
||||
val nodeJarName = "corda.jar"
|
||||
private val defaultDirectory: Path = Paths.get("build", "nodes")
|
||||
}
|
||||
|
||||
@ -132,9 +133,26 @@ open class Cordform : DefaultTask() {
|
||||
fun build() {
|
||||
project.logger.info("Running Cordform task")
|
||||
initializeConfiguration()
|
||||
nodes.forEach(Node::installConfig)
|
||||
installCordaJar()
|
||||
installRunScript()
|
||||
nodes.forEach(Node::build)
|
||||
bootstrapNetwork()
|
||||
nodes.forEach(Node::build)
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs the corda fat JAR to the root directory, for the network bootstrapper to use.
|
||||
*/
|
||||
private fun installCordaJar() {
|
||||
val cordaJar = Cordformation.verifyAndGetRuntimeJar(project, "corda")
|
||||
project.copy {
|
||||
it.apply {
|
||||
from(cordaJar)
|
||||
into(directory)
|
||||
rename(cordaJar.name, nodeJarName)
|
||||
fileMode = Cordformation.executableFileMode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initializeConfiguration() {
|
||||
@ -150,8 +168,8 @@ open class Cordform : DefaultTask() {
|
||||
cd.nodeConfigurers.forEach {
|
||||
val node = node { }
|
||||
it.accept(node)
|
||||
node.additionalCordapps.addAll(cordapps)
|
||||
node.rootDir(directory)
|
||||
node.installCordapps(cordapps)
|
||||
}
|
||||
cd.setup { nodeName -> project.projectDir.toPath().resolve(getNodeByName(nodeName)!!.nodeDir.toPath()) }
|
||||
} else {
|
||||
|
@ -20,14 +20,35 @@ class Cordformation : Plugin<Project> {
|
||||
* @return A file handle to the file in the JAR.
|
||||
*/
|
||||
fun getPluginFile(project: Project, filePathInJar: String): File {
|
||||
val archive: File? = project.rootProject.buildscript.configurations
|
||||
val archive = project.rootProject.buildscript.configurations
|
||||
.single { it.name == "classpath" }
|
||||
.find { it.name.contains("cordformation") }
|
||||
.first { it.name.contains("cordformation") }
|
||||
return project.rootProject.resources.text
|
||||
.fromArchiveEntry(archive, filePathInJar)
|
||||
.asFile()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a current built corda jar file
|
||||
*
|
||||
* @param project The project environment this plugin executes in.
|
||||
* @param jarName The name of the JAR you wish to access.
|
||||
* @return A file handle to the file in the JAR.
|
||||
*/
|
||||
fun verifyAndGetRuntimeJar(project: Project, jarName: String): File {
|
||||
val releaseVersion = project.rootProject.ext<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)
|
||||
}
|
||||
|
||||
|
@ -15,8 +15,6 @@ import java.nio.file.Path
|
||||
*/
|
||||
class Node(private val project: Project) : CordformNode() {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
val nodeJarName = "corda.jar"
|
||||
@JvmStatic
|
||||
val webJarName = "corda-webserver.jar"
|
||||
private val configFileProperty = "configFile"
|
||||
@ -30,10 +28,11 @@ class Node(private val project: Project) : CordformNode() {
|
||||
* @note Type is any due to gradle's use of "GStrings" - each value will have "toString" called on it
|
||||
*/
|
||||
var cordapps = mutableListOf<Any>()
|
||||
|
||||
private val releaseVersion = project.rootProject.ext<String>("corda_release_version")
|
||||
var additionalCordapps = mutableListOf<File>()
|
||||
internal lateinit var nodeDir: File
|
||||
private set
|
||||
internal lateinit var rootDir: File
|
||||
private set
|
||||
|
||||
/**
|
||||
* Sets whether this node will use HTTPS communication.
|
||||
@ -65,16 +64,12 @@ class Node(private val project: Project) : CordformNode() {
|
||||
}
|
||||
|
||||
internal fun build() {
|
||||
configureProperties()
|
||||
installCordaJar()
|
||||
if (config.hasPath("webAddress")) {
|
||||
installWebserverJar()
|
||||
}
|
||||
installAgentJar()
|
||||
installBuiltCordapp()
|
||||
installCordapps()
|
||||
installConfig()
|
||||
appendOptionalConfig()
|
||||
}
|
||||
|
||||
internal fun rootDir(rootDir: Path) {
|
||||
@ -86,7 +81,9 @@ class Node(private val project: Project) : CordformNode() {
|
||||
// with loading our custom X509EdDSAEngine.
|
||||
val organizationName = name.trim().split(",").firstOrNull { it.startsWith("O=") }?.substringAfter("=")
|
||||
val dirName = organizationName ?: name
|
||||
nodeDir = File(rootDir.toFile(), dirName.replace("\\s", ""))
|
||||
this.rootDir = rootDir.toFile()
|
||||
nodeDir = File(this.rootDir, dirName.replace("\\s", ""))
|
||||
Files.createDirectories(nodeDir.toPath())
|
||||
}
|
||||
|
||||
private fun configureProperties() {
|
||||
@ -99,26 +96,11 @@ class Node(private val project: Project) : CordformNode() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs the corda fat JAR to the node directory.
|
||||
*/
|
||||
private fun installCordaJar() {
|
||||
val cordaJar = verifyAndGetRuntimeJar("corda")
|
||||
project.copy {
|
||||
it.apply {
|
||||
from(cordaJar)
|
||||
into(nodeDir)
|
||||
rename(cordaJar.name, nodeJarName)
|
||||
fileMode = Cordformation.executableFileMode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs the corda webserver JAR to the node directory
|
||||
*/
|
||||
private fun installWebserverJar() {
|
||||
val webJar = verifyAndGetRuntimeJar("corda-webserver")
|
||||
val webJar = Cordformation.verifyAndGetRuntimeJar(project, "corda-webserver")
|
||||
project.copy {
|
||||
it.apply {
|
||||
from(webJar)
|
||||
@ -141,19 +123,6 @@ class Node(private val project: Project) : CordformNode() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs other cordapps to this node's cordapps directory.
|
||||
*/
|
||||
internal fun installCordapps(cordapps: Collection<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
|
||||
*/
|
||||
@ -161,8 +130,8 @@ class Node(private val project: Project) : CordformNode() {
|
||||
val jolokiaVersion = project.rootProject.ext<String>("jolokia_version")
|
||||
val agentJar = project.configuration("runtime").files {
|
||||
(it.group == "org.jolokia") &&
|
||||
(it.name == "jolokia-jvm") &&
|
||||
(it.version == jolokiaVersion)
|
||||
(it.name == "jolokia-jvm") &&
|
||||
(it.version == jolokiaVersion)
|
||||
// TODO: revisit when classifier attribute is added. eg && (it.classifier = "agent")
|
||||
}.first() // should always be the jolokia agent fat jar: eg. jolokia-jvm-1.3.7-agent.jar
|
||||
project.logger.info("Jolokia agent jar: $agentJar")
|
||||
@ -177,10 +146,7 @@ class Node(private val project: Project) : CordformNode() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs the configuration file to this node's directory and detokenises it.
|
||||
*/
|
||||
private fun installConfig() {
|
||||
private fun createTempConfigFile(): File {
|
||||
val options = ConfigRenderOptions
|
||||
.defaults()
|
||||
.setOriginComments(false)
|
||||
@ -188,16 +154,26 @@ class Node(private val project: Project) : CordformNode() {
|
||||
.setFormatted(true)
|
||||
.setJson(false)
|
||||
val configFileText = config.root().render(options).split("\n").toList()
|
||||
|
||||
// Need to write a temporary file first to use the project.copy, which resolves directories correctly.
|
||||
val tmpDir = File(project.buildDir, "tmp")
|
||||
val tmpConfFile = File(tmpDir, "node.conf")
|
||||
Files.createDirectories(tmpDir.toPath())
|
||||
var fileName = "${nodeDir.getName()}.conf"
|
||||
val tmpConfFile = File(tmpDir, fileName)
|
||||
Files.write(tmpConfFile.toPath(), configFileText, StandardCharsets.UTF_8)
|
||||
return tmpConfFile
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs the configuration file to the root directory and detokenises it.
|
||||
*/
|
||||
internal fun installConfig() {
|
||||
configureProperties()
|
||||
val tmpConfFile = createTempConfigFile()
|
||||
appendOptionalConfig(tmpConfFile)
|
||||
project.copy {
|
||||
it.apply {
|
||||
from(tmpConfFile)
|
||||
into(nodeDir)
|
||||
into(rootDir)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -205,7 +181,7 @@ class Node(private val project: Project) : CordformNode() {
|
||||
/**
|
||||
* Appends installed config file with properties from an optional file.
|
||||
*/
|
||||
private fun appendOptionalConfig() {
|
||||
private fun appendOptionalConfig(confFile: File) {
|
||||
val optionalConfig: File? = when {
|
||||
project.findProperty(configFileProperty) != null -> //provided by -PconfigFile command line property when running Gradle task
|
||||
File(project.findProperty(configFileProperty) as String)
|
||||
@ -217,28 +193,22 @@ class Node(private val project: Project) : CordformNode() {
|
||||
if (!optionalConfig.exists()) {
|
||||
project.logger.error("$configFileProperty '$optionalConfig' not found")
|
||||
} else {
|
||||
val confFile = File(project.buildDir.path + "/../" + nodeDir, "node.conf")
|
||||
confFile.appendBytes(optionalConfig.readBytes())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the given JAR amongst the dependencies
|
||||
* @param jarName JAR name without the version part, for example for corda-2.0-SNAPSHOT.jar provide only "corda" as jarName
|
||||
*
|
||||
* @return A file representing found JAR
|
||||
* Installs other cordapps to this node's cordapps directory.
|
||||
*/
|
||||
private fun verifyAndGetRuntimeJar(jarName: String): File {
|
||||
val maybeJar = project.configuration("runtime").filter {
|
||||
"$jarName-$releaseVersion.jar" in it.toString() || "$jarName-enterprise-$releaseVersion.jar" in it.toString()
|
||||
}
|
||||
if (maybeJar.isEmpty) {
|
||||
throw IllegalStateException("No $jarName JAR found. Have you deployed the Corda project to Maven? Looked for \"$jarName-$releaseVersion.jar\"")
|
||||
} else {
|
||||
val jar = maybeJar.singleFile
|
||||
require(jar.isFile)
|
||||
return jar
|
||||
internal fun installCordapps() {
|
||||
additionalCordapps.addAll(getCordappList())
|
||||
val cordappsDir = File(nodeDir, "cordapps")
|
||||
project.copy {
|
||||
it.apply {
|
||||
from(additionalCordapps)
|
||||
into(cordappsDir)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,14 +3,11 @@ package com.r3.corda.networkmanage.doorman
|
||||
import com.nhaarman.mockito_kotlin.doReturn
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import com.r3.corda.networkmanage.common.persistence.configureDatabase
|
||||
import com.r3.corda.networkmanage.common.utils.toX509Certificate
|
||||
import com.r3.corda.networkmanage.doorman.signer.LocalSigner
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.sign
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.node.NodeInfo
|
||||
@ -28,12 +25,13 @@ import net.corda.testing.ALICE_NAME
|
||||
import net.corda.testing.SerializationEnvironmentRule
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import java.net.URL
|
||||
import java.security.cert.X509Certificate
|
||||
import java.util.*
|
||||
import javax.security.auth.x500.X500Principal
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
|
||||
@ -60,7 +58,7 @@ class DoormanIntegrationTest {
|
||||
}
|
||||
config.trustStoreFile.parent.createDirectories()
|
||||
loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also {
|
||||
it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCertAndKey.certificate.cert)
|
||||
it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCertAndKey.certificate)
|
||||
it.save(config.trustStoreFile, config.trustStorePassword)
|
||||
}
|
||||
|
||||
@ -77,18 +75,18 @@ class DoormanIntegrationTest {
|
||||
loadKeyStore(config.nodeKeystore, config.keyStorePassword).apply {
|
||||
assert(containsAlias(X509Utilities.CORDA_CLIENT_CA))
|
||||
assertEquals(ALICE_NAME.x500Principal, getX509Certificate(X509Utilities.CORDA_CLIENT_CA).subjectX500Principal)
|
||||
assertEquals(listOf(intermediateCACert.cert, rootCACert.cert), getCertificateChain(X509Utilities.CORDA_CLIENT_CA).drop(1).toList())
|
||||
assertEquals(listOf(intermediateCACert, rootCACert), getCertificateChain(X509Utilities.CORDA_CLIENT_CA).drop(1).toList())
|
||||
}
|
||||
|
||||
loadKeyStore(config.sslKeystore, config.keyStorePassword).apply {
|
||||
assert(containsAlias(X509Utilities.CORDA_CLIENT_TLS))
|
||||
assertEquals(ALICE_NAME.x500Principal, getX509Certificate(X509Utilities.CORDA_CLIENT_TLS).subjectX500Principal)
|
||||
assertEquals(listOf(intermediateCACert.cert, rootCACert.cert), getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).drop(2).toList())
|
||||
assertEquals(listOf(intermediateCACert, rootCACert), getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).drop(2).toList())
|
||||
}
|
||||
|
||||
loadKeyStore(config.trustStoreFile, config.trustStorePassword).apply {
|
||||
assert(containsAlias(X509Utilities.CORDA_ROOT_CA))
|
||||
assertEquals(rootCACert.cert.subjectX500Principal, getX509Certificate(X509Utilities.CORDA_ROOT_CA).subjectX500Principal)
|
||||
assertEquals(rootCACert.subjectX500Principal, getX509Certificate(X509Utilities.CORDA_ROOT_CA).subjectX500Principal)
|
||||
}
|
||||
|
||||
doorman.close()
|
||||
@ -110,21 +108,26 @@ class DoormanIntegrationTest {
|
||||
}
|
||||
config.trustStoreFile.parent.createDirectories()
|
||||
loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also {
|
||||
it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCertAndKey.certificate.cert)
|
||||
it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCertAndKey.certificate)
|
||||
it.save(config.trustStoreFile, config.trustStorePassword)
|
||||
}
|
||||
|
||||
NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.compatibilityZoneURL!!)).buildKeystore()
|
||||
|
||||
// Publish NodeInfo
|
||||
val networkMapClient = NetworkMapClient(config.compatibilityZoneURL!!, rootCertAndKey.certificate.cert)
|
||||
val networkMapClient = NetworkMapClient(config.compatibilityZoneURL!!, rootCertAndKey.certificate)
|
||||
|
||||
val keyStore = loadKeyStore(config.nodeKeystore, config.keyStorePassword)
|
||||
val clientCertPath = keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
|
||||
val clientCA = keyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, config.keyStorePassword)
|
||||
val identityKeyPair = Crypto.generateKeyPair()
|
||||
val identityCert = X509Utilities.createCertificate(CertificateType.LEGAL_IDENTITY, clientCA.certificate, clientCA.keyPair, ALICE_NAME, identityKeyPair.public)
|
||||
val certPath = X509CertificateFactory().generateCertPath(identityCert.cert, *clientCertPath)
|
||||
val identityCert = X509Utilities.createCertificate(
|
||||
CertificateType.LEGAL_IDENTITY,
|
||||
clientCA.certificate,
|
||||
clientCA.keyPair,
|
||||
ALICE_NAME.x500Principal,
|
||||
identityKeyPair.public)
|
||||
val certPath = X509CertificateFactory().generateCertPath(identityCert, *clientCertPath)
|
||||
val nodeInfo = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(certPath)), 1, serial = 1L)
|
||||
val nodeInfoBytes = nodeInfo.serialize()
|
||||
|
||||
@ -156,23 +159,23 @@ class DoormanIntegrationTest {
|
||||
}
|
||||
|
||||
|
||||
fun createDoormanIntermediateCertificateAndKeyPair(rootCertificateAndKeyPair: CertificateAndKeyPair): CertificateAndKeyPair {
|
||||
val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCertificateAndKeyPair.certificate, rootCertificateAndKeyPair.keyPair,
|
||||
CordaX500Name(commonName = "Integration Test Corda Node Intermediate CA",
|
||||
locality = "London",
|
||||
country = "GB",
|
||||
organisation = "R3 Ltd"), intermediateCAKey.public)
|
||||
return CertificateAndKeyPair(intermediateCACert, intermediateCAKey)
|
||||
fun createDoormanIntermediateCertificateAndKeyPair(rootCa: CertificateAndKeyPair): CertificateAndKeyPair {
|
||||
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val intermediateCACert = X509Utilities.createCertificate(
|
||||
CertificateType.INTERMEDIATE_CA,
|
||||
rootCa.certificate,
|
||||
rootCa.keyPair,
|
||||
X500Principal("CN=Integration Test Corda Node Intermediate CA,O=R3 Ltd,L=London,C=GB"),
|
||||
keyPair.public)
|
||||
return CertificateAndKeyPair(intermediateCACert, keyPair)
|
||||
}
|
||||
|
||||
fun createDoormanRootCertificateAndKeyPair(): CertificateAndKeyPair {
|
||||
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val rootCACert = X509Utilities.createSelfSignedCACertificate(
|
||||
CordaX500Name(commonName = "Integration Test Corda Node Root CA",
|
||||
organisation = "R3 Ltd", locality = "London",
|
||||
country = "GB"), rootCAKey)
|
||||
return CertificateAndKeyPair(rootCACert, rootCAKey)
|
||||
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val rootCaCert = X509Utilities.createSelfSignedCACertificate(
|
||||
X500Principal("CN=Integration Test Corda Node Root CA,O=R3 Ltd,L=London,C=GB"),
|
||||
keyPair)
|
||||
return CertificateAndKeyPair(rootCaCert, keyPair)
|
||||
}
|
||||
|
||||
fun makeTestDataSourceProperties(nodeName: String = SecureHash.randomSHA256().toString()): Properties {
|
||||
@ -184,9 +187,8 @@ fun makeTestDataSourceProperties(nodeName: String = SecureHash.randomSHA256().to
|
||||
return props
|
||||
}
|
||||
|
||||
fun startDoorman(intermediateCACertAndKey: CertificateAndKeyPair, rootCACert: X509CertificateHolder): NetworkManagementServer {
|
||||
val signer = LocalSigner(intermediateCACertAndKey.keyPair,
|
||||
arrayOf(intermediateCACertAndKey.certificate.toX509Certificate(), rootCACert.toX509Certificate()))
|
||||
fun startDoorman(intermediateCACertAndKey: CertificateAndKeyPair, rootCACert: X509Certificate): NetworkManagementServer {
|
||||
val signer = LocalSigner(intermediateCACertAndKey.keyPair, arrayOf(intermediateCACertAndKey.certificate, rootCACert))
|
||||
//Start doorman server
|
||||
return startDoorman(signer)
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ import com.r3.corda.networkmanage.hsm.persistence.DBSignedCertificateRequestStor
|
||||
import com.r3.corda.networkmanage.hsm.persistence.SignedCertificateRequestStorage
|
||||
import com.r3.corda.networkmanage.hsm.signer.HsmCsrSigner
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.uncheckedCast
|
||||
@ -28,12 +27,12 @@ import net.corda.testing.CHARLIE_NAME
|
||||
import net.corda.testing.SerializationEnvironmentRule
|
||||
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
|
||||
import org.h2.tools.Server
|
||||
import org.junit.*
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import java.net.URL
|
||||
import java.security.cert.X509Certificate
|
||||
import java.util.*
|
||||
import javax.persistence.PersistenceException
|
||||
import kotlin.concurrent.scheduleAtFixedRate
|
||||
@ -55,7 +54,7 @@ class SigningServiceIntegrationTest {
|
||||
val testSerialization = SerializationEnvironmentRule(true)
|
||||
|
||||
private lateinit var timer: Timer
|
||||
private lateinit var rootCaCert: X509CertificateHolder
|
||||
private lateinit var rootCaCert: X509Certificate
|
||||
private lateinit var intermediateCa: CertificateAndKeyPair
|
||||
|
||||
@Before
|
||||
@ -79,7 +78,7 @@ class SigningServiceIntegrationTest {
|
||||
for (approvedRequest in approvedRequests) {
|
||||
JcaPKCS10CertificationRequest(approvedRequest.request).run {
|
||||
val nodeCa = createDevNodeCa(intermediateCa, CordaX500Name.parse(subject.toString()))
|
||||
approvedRequest.certPath = buildCertPath(nodeCa.certificate.cert, intermediateCa.certificate.cert, rootCaCert.cert)
|
||||
approvedRequest.certPath = buildCertPath(nodeCa.certificate, intermediateCa.certificate, rootCaCert)
|
||||
}
|
||||
}
|
||||
storage.store(approvedRequests, listOf("TEST"))
|
||||
@ -124,7 +123,7 @@ class SigningServiceIntegrationTest {
|
||||
}
|
||||
config.certificatesDirectory.createDirectories()
|
||||
loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also {
|
||||
it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCaCert.cert)
|
||||
it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCaCert)
|
||||
it.save(config.trustStoreFile, config.trustStorePassword)
|
||||
}
|
||||
NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.compatibilityZoneURL!!)).buildKeystore()
|
||||
@ -168,7 +167,7 @@ class SigningServiceIntegrationTest {
|
||||
}
|
||||
config.certificatesDirectory.createDirectories()
|
||||
loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also {
|
||||
it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCaCert.cert)
|
||||
it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCaCert)
|
||||
it.save(config.trustStoreFile, config.trustStorePassword)
|
||||
}
|
||||
NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.compatibilityZoneURL!!)).buildKeystore()
|
||||
|
@ -5,15 +5,14 @@ import com.r3.corda.networkmanage.common.persistence.entity.CertificateSigningRe
|
||||
import com.r3.corda.networkmanage.common.utils.hashString
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.x500Name
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseTransaction
|
||||
import net.corda.nodeapi.internal.persistence.TransactionIsolationLevel
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
||||
import org.hibernate.Session
|
||||
import java.security.cert.CertPath
|
||||
import java.time.Instant
|
||||
import javax.security.auth.x500.X500Principal
|
||||
|
||||
/**
|
||||
* Database implementation of the [CertificationRequestStorage] interface.
|
||||
@ -48,7 +47,7 @@ class PersistentCertificateRequestStorage(private val database: CordaPersistence
|
||||
val (legalName, rejectReason) = parseAndValidateLegalName(request, session)
|
||||
session.save(CertificateSigningRequestEntity(
|
||||
requestId = requestId,
|
||||
legalName = legalName.toString(),
|
||||
legalName = legalName,
|
||||
requestBytes = request.encoded,
|
||||
remark = rejectReason,
|
||||
modifiedBy = emptyList(),
|
||||
@ -126,25 +125,27 @@ class PersistentCertificateRequestStorage(private val database: CordaPersistence
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseAndValidateLegalName(request: PKCS10CertificationRequest, session: Session): Pair<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 {
|
||||
CordaX500Name.parse(request.subject.toString())
|
||||
CordaX500Name.build(X500Principal(request.subject.encoded)).toString()
|
||||
} catch (e: IllegalArgumentException) {
|
||||
return Pair(request.subject, "Name validation failed with exception : ${e.message}")
|
||||
return Pair(request.subject.toString(), "Name validation failed: ${e.message}")
|
||||
}
|
||||
|
||||
val query = session.criteriaBuilder.run {
|
||||
val criteriaQuery = createQuery(CertificateSigningRequestEntity::class.java)
|
||||
criteriaQuery.from(CertificateSigningRequestEntity::class.java).run {
|
||||
criteriaQuery.where(equal(get<String>(CertificateSigningRequestEntity::legalName.name), legalName.toString()))
|
||||
criteriaQuery.where(equal(get<String>(CertificateSigningRequestEntity::legalName.name), legalName))
|
||||
}
|
||||
}
|
||||
|
||||
val duplicates = session.createQuery(query).resultList.filter {
|
||||
it.status in setOf(RequestStatus.NEW, RequestStatus.TICKET_CREATED, RequestStatus.APPROVED) || it.certificateData?.certificateStatus == CertificateStatus.VALID
|
||||
}
|
||||
return if (duplicates.isEmpty()) {
|
||||
Pair(legalName.x500Name, null)
|
||||
} else {
|
||||
Pair(legalName.x500Name, "Duplicate legal name")
|
||||
it.status in setOf(RequestStatus.NEW, RequestStatus.TICKET_CREATED, RequestStatus.APPROVED) ||
|
||||
it.certificateData?.certificateStatus == CertificateStatus.VALID
|
||||
}
|
||||
|
||||
return Pair(legalName, if (duplicates.isEmpty()) null else "Duplicate legal name")
|
||||
}
|
||||
}
|
@ -6,9 +6,7 @@ import com.atlassian.jira.rest.client.api.domain.Issue
|
||||
import com.atlassian.jira.rest.client.api.domain.IssueType
|
||||
import com.atlassian.jira.rest.client.api.domain.input.IssueInputBuilder
|
||||
import com.atlassian.jira.rest.client.api.domain.input.TransitionInput
|
||||
import net.corda.core.internal.country
|
||||
import net.corda.core.internal.locality
|
||||
import net.corda.core.internal.organisation
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
import org.bouncycastle.asn1.x500.style.BCStyle
|
||||
@ -17,6 +15,7 @@ import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
||||
import org.bouncycastle.util.io.pem.PemObject
|
||||
import java.io.StringWriter
|
||||
import java.security.cert.CertPath
|
||||
import javax.security.auth.x500.X500Principal
|
||||
|
||||
class JiraClient(private val restClient: JiraRestClient, private val projectCode: String, private val doneTransitionCode: Int) {
|
||||
companion object {
|
||||
@ -39,16 +38,17 @@ class JiraClient(private val restClient: JiraRestClient, private val projectCode
|
||||
JcaPEMWriter(request).use {
|
||||
it.writeObject(PemObject("CERTIFICATE REQUEST", signingRequest.encoded))
|
||||
}
|
||||
val organisation = signingRequest.subject.organisation
|
||||
val nearestCity = signingRequest.subject.locality
|
||||
val country = signingRequest.subject.country
|
||||
|
||||
// TODO The subject of the signing request has already been validated and parsed into a CordaX500Name. We shouldn't
|
||||
// have to do it again here.
|
||||
val subject = CordaX500Name.build(X500Principal(signingRequest.subject.encoded))
|
||||
|
||||
val email = signingRequest.getAttributes(BCStyle.E).firstOrNull()?.attrValues?.firstOrNull()?.toString()
|
||||
|
||||
val issue = IssueInputBuilder().setIssueTypeId(taskIssueType.id)
|
||||
.setProjectKey(projectCode)
|
||||
.setDescription("Organisation: $organisation\nNearest City: $nearestCity\nCountry: $country\nEmail: $email\n\n{code}$request{code}")
|
||||
.setSummary(organisation)
|
||||
.setDescription("Organisation: ${subject.organisation}\nNearest City: ${subject.locality}\nCountry: ${subject.country}\nEmail: $email\n\n{code}$request{code}")
|
||||
.setSummary(subject.organisation)
|
||||
.setFieldValue(requestIdField.id, requestId)
|
||||
// This will block until the issue is created.
|
||||
restClient.issueClient.createIssue(issue.build()).fail { logger.error("Exception when creating JIRA issue.", it) }.claim()
|
||||
|
@ -13,7 +13,6 @@ import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService
|
||||
import com.r3.corda.networkmanage.doorman.webservice.RegistrationWebService
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.serialization.internal.SerializationEnvironmentImpl
|
||||
@ -188,8 +187,8 @@ fun generateRootKeyPair(rootStoreFile: Path, rootKeystorePass: String?, rootPriv
|
||||
val selfSignKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
// TODO Make the cert subject configurable
|
||||
val selfSignCert = X509Utilities.createSelfSignedCACertificate(
|
||||
CordaX500Name(commonName = "Corda Root CA", organisation = "R3 Ltd", locality = "London", country = "GB", organisationUnit = "Corda", state = null),
|
||||
selfSignKey).cert
|
||||
CordaX500Name(commonName = "Corda Root CA", organisation = "R3 Ltd", locality = "London", country = "GB", organisationUnit = "Corda", state = null).x500Principal,
|
||||
selfSignKey)
|
||||
rootStore.addOrReplaceKey(X509Utilities.CORDA_ROOT_CA, selfSignKey.private, rootPrivateKeyPassword.toCharArray(), arrayOf(selfSignCert))
|
||||
rootStore.save(rootStoreFile, rootKeystorePassword)
|
||||
|
||||
@ -226,11 +225,20 @@ fun generateCAKeyPair(keystoreFile: Path, rootStoreFile: Path, rootKeystorePass:
|
||||
exitProcess(1)
|
||||
}
|
||||
|
||||
val intermediateKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val intermediateCert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootKeyAndCert.certificate, rootKeyAndCert.keyPair,
|
||||
CordaX500Name(commonName = "Corda Intermediate CA", organisation = "R3 Ltd", organisationUnit = "Corda", locality = "London", country = "GB", state = null), intermediateKey.public)
|
||||
keyStore.addOrReplaceKey(X509Utilities.CORDA_INTERMEDIATE_CA, intermediateKey.private,
|
||||
caPrivateKeyPassword.toCharArray(), arrayOf(intermediateCert, rootKeyAndCert.certificate))
|
||||
val intermediateKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val intermediateCert = X509Utilities.createCertificate(
|
||||
CertificateType.INTERMEDIATE_CA,
|
||||
rootKeyAndCert.certificate,
|
||||
rootKeyAndCert.keyPair,
|
||||
CordaX500Name(commonName = "Corda Intermediate CA", organisation = "R3 Ltd", organisationUnit = "Corda", locality = "London", country = "GB", state = null).x500Principal,
|
||||
intermediateKeyPair.public
|
||||
)
|
||||
keyStore.addOrReplaceKey(
|
||||
X509Utilities.CORDA_INTERMEDIATE_CA,
|
||||
intermediateKeyPair.private,
|
||||
caPrivateKeyPassword.toCharArray(),
|
||||
arrayOf(intermediateCert, rootKeyAndCert.certificate)
|
||||
)
|
||||
keyStore.save(keystoreFile, keystorePassword)
|
||||
println("Intermediate CA keypair and certificate stored in $keystoreFile.")
|
||||
println(loadKeyStore(keystoreFile, keystorePassword).getCertificate(X509Utilities.CORDA_INTERMEDIATE_CA).publicKey)
|
||||
|
@ -4,9 +4,6 @@ import com.r3.corda.networkmanage.common.signer.Signer
|
||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
||||
import com.r3.corda.networkmanage.common.utils.withCert
|
||||
import net.corda.core.crypto.sign
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
import net.corda.nodeapi.internal.network.DigitalSignatureWithCert
|
||||
@ -18,6 +15,7 @@ import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
|
||||
import java.security.KeyPair
|
||||
import java.security.cert.CertPath
|
||||
import java.security.cert.X509Certificate
|
||||
import javax.security.auth.x500.X500Principal
|
||||
|
||||
/**
|
||||
* The [LocalSigner] class signs [PKCS10CertificationRequest] using provided CA key pair and certificate path.
|
||||
@ -35,12 +33,12 @@ class LocalSigner(private val caKeyPair: KeyPair, private val caCertPath: Array<
|
||||
arrayOf())
|
||||
val nodeCaCert = X509Utilities.createCertificate(
|
||||
CertificateType.NODE_CA,
|
||||
caCertPath.first().toX509CertHolder(),
|
||||
caCertPath[0],
|
||||
caKeyPair,
|
||||
CordaX500Name.parse(request.subject.toString()),
|
||||
X500Principal(request.subject.encoded),
|
||||
request.publicKey,
|
||||
nameConstraints = nameConstraints)
|
||||
return buildCertPath(nodeCaCert.cert, *caCertPath)
|
||||
return buildCertPath(nodeCaCert, *caCertPath)
|
||||
}
|
||||
|
||||
override fun sign(data: ByteArray): DigitalSignatureWithCert {
|
||||
|
@ -9,13 +9,12 @@ import com.r3.corda.networkmanage.hsm.authentication.Authenticator
|
||||
import com.r3.corda.networkmanage.hsm.utils.X509Utilities.getAndInitializeKeyStore
|
||||
import com.r3.corda.networkmanage.hsm.utils.X509Utilities.signData
|
||||
import com.r3.corda.networkmanage.hsm.utils.X509Utilities.verify
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.core.utilities.minutes
|
||||
import net.corda.nodeapi.internal.network.DigitalSignatureWithCert
|
||||
import java.security.KeyPair
|
||||
import java.security.PrivateKey
|
||||
import java.security.cert.X509Certificate
|
||||
import java.time.Duration
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.ScheduledExecutorService
|
||||
@ -68,7 +67,7 @@ class HsmNetworkMapSigner(networkMapStorage: NetworkMapStorage,
|
||||
val caKey = keyStore.getKey(caCertificateKeyName, caPrivateKeyPass.toCharArray()) as PrivateKey
|
||||
val signature = signData(data, KeyPair(caCertificateChain.first().publicKey, caKey), provider)
|
||||
verify(data, signature, caCertificateChain.first().publicKey)
|
||||
signature.withCert(caCertificateChain.first().toX509CertHolder().cert)
|
||||
signature.withCert(caCertificateChain[0] as X509Certificate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package com.r3.corda.networkmanage.hsm.utils
|
||||
import CryptoServerJCE.CryptoServerProvider
|
||||
import net.corda.core.crypto.DigitalSignature
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
import net.corda.core.internal.x500Name
|
||||
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||
@ -81,7 +80,7 @@ object X509Utilities {
|
||||
cert.checkValidity(Date())
|
||||
cert.verify(pubKey)
|
||||
|
||||
return CertificateAndKeyPair(cert.toX509CertHolder(), KeyPair(pubKey, keyPair.private))
|
||||
return CertificateAndKeyPair(cert, KeyPair(pubKey, keyPair.private))
|
||||
}
|
||||
|
||||
/**
|
||||
@ -109,7 +108,7 @@ object X509Utilities {
|
||||
fun retrieveCertificateAndKeys(certificateKeyName: String, privateKeyPassword: String, keyStore: KeyStore): CertificateAndKeyPair {
|
||||
val privateKey = keyStore.getKey(certificateKeyName, privateKeyPassword.toCharArray()) as PrivateKey
|
||||
val publicKey = keyStore.getCertificate(certificateKeyName).publicKey
|
||||
val certificate = keyStore.getX509Certificate(certificateKeyName).toX509CertHolder()
|
||||
val certificate = keyStore.getX509Certificate(certificateKeyName)
|
||||
return CertificateAndKeyPair(certificate, getCleanEcdsaKeyPair(publicKey, privateKey))
|
||||
}
|
||||
|
||||
@ -160,7 +159,7 @@ object X509Utilities {
|
||||
cert.checkValidity(Date())
|
||||
cert.verify(certificateAuthority.keyPair.public)
|
||||
|
||||
return CertificateAndKeyPair(cert.toX509CertHolder(), KeyPair(pubKey, keyPair.private))
|
||||
return CertificateAndKeyPair(cert, KeyPair(pubKey, keyPair.private))
|
||||
}
|
||||
|
||||
/**
|
||||
@ -186,7 +185,7 @@ object X509Utilities {
|
||||
val subject = CordaX500Name.parse(jcaRequest.subject.toString()).x500Name
|
||||
val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(jcaRequest.publicKey.encoded))
|
||||
val keyPurposes = DERSequence(ASN1EncodableVector().apply { certificateType.purposes.forEach { add(it) } })
|
||||
val builder = JcaX509v3CertificateBuilder(issuerCertificate.subject, serial, validityWindow.first, validityWindow.second, subject, jcaRequest.publicKey)
|
||||
val builder = JcaX509v3CertificateBuilder(issuerCertificate, serial, validityWindow.first, validityWindow.second, subject, jcaRequest.publicKey)
|
||||
.addExtension(Extension.subjectKeyIdentifier, false, BcX509ExtensionUtils().createSubjectKeyIdentifier(subjectPublicKeyInfo))
|
||||
.addExtension(Extension.basicConstraints, certificateType.isCA, BasicConstraints(certificateType.isCA))
|
||||
.addExtension(Extension.keyUsage, false, certificateType.keyUsage)
|
||||
|
@ -4,15 +4,13 @@ import com.r3.corda.networkmanage.TestBase
|
||||
import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage.Companion.DOORMAN_SIGNATURE
|
||||
import com.r3.corda.networkmanage.common.persistence.entity.CertificateSigningRequestEntity
|
||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
||||
import com.r3.corda.networkmanage.common.utils.toX509Certificate
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.testing.internal.createDevNodeCaCertPath
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
||||
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
|
||||
import org.hibernate.envers.AuditReaderFactory
|
||||
@ -21,9 +19,10 @@ import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.security.KeyPair
|
||||
import java.util.*
|
||||
import javax.security.auth.x500.X500Principal
|
||||
import kotlin.test.*
|
||||
|
||||
class DBCertificateRequestStorageTest : TestBase() {
|
||||
class PersistentCertificateRequestStorageTest : TestBase() {
|
||||
private lateinit var storage: PersistentCertificateRequestStorage
|
||||
private lateinit var persistence: CordaPersistence
|
||||
|
||||
@ -84,14 +83,15 @@ class DBCertificateRequestStorageTest : TestBase() {
|
||||
// New request should be empty.
|
||||
assertTrue(storage.getRequests(RequestStatus.NEW).isEmpty())
|
||||
// Sign certificate
|
||||
storage.putCertificatePath(requestId, JcaPKCS10CertificationRequest(csr).run {
|
||||
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 LTD", country = "GB"), rootCAKey)
|
||||
val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, X500Name("CN=Corda Node Intermediate CA,L=London"), intermediateCAKey.public)
|
||||
val ourCertificate = X509Utilities.createCertificate(CertificateType.TLS, intermediateCACert, intermediateCAKey, subject, publicKey).toX509Certificate()
|
||||
buildCertPath(ourCertificate, intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
|
||||
}, listOf(DOORMAN_SIGNATURE))
|
||||
storage.putCertificatePath(
|
||||
requestId,
|
||||
JcaPKCS10CertificationRequest(csr).run {
|
||||
// TODO We need a utility in InternalUtils for converting X500Name -> CordaX500Name
|
||||
val (rootCa, intermediateCa, nodeCa) = createDevNodeCaCertPath(CordaX500Name.build(X500Principal(subject.encoded)))
|
||||
buildCertPath(nodeCa.certificate, intermediateCa.certificate, rootCa.certificate)
|
||||
},
|
||||
listOf(DOORMAN_SIGNATURE)
|
||||
)
|
||||
// Check request is ready
|
||||
assertNotNull(storage.getRequest(requestId)!!.certData)
|
||||
}
|
||||
@ -104,25 +104,24 @@ class DBCertificateRequestStorageTest : TestBase() {
|
||||
// Store certificate to DB.
|
||||
storage.markRequestTicketCreated(requestId)
|
||||
storage.approveRequest(requestId, DOORMAN_SIGNATURE)
|
||||
storage.putCertificatePath(requestId, JcaPKCS10CertificationRequest(csr).run {
|
||||
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 LTD", country = "GB"), rootCAKey)
|
||||
val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, X500Name("CN=Corda Node Intermediate CA,L=London"), intermediateCAKey.public)
|
||||
val ourCertificate = X509Utilities.createCertificate(CertificateType.TLS, intermediateCACert, intermediateCAKey, subject, publicKey).toX509Certificate()
|
||||
buildCertPath(ourCertificate, intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
|
||||
}, listOf(DOORMAN_SIGNATURE))
|
||||
storage.putCertificatePath(
|
||||
requestId,
|
||||
JcaPKCS10CertificationRequest(csr).run {
|
||||
val (rootCa, intermediateCa, nodeCa) = createDevNodeCaCertPath(CordaX500Name.build(X500Principal(subject.encoded)))
|
||||
buildCertPath(nodeCa.certificate, intermediateCa.certificate, rootCa.certificate)
|
||||
},
|
||||
listOf(DOORMAN_SIGNATURE)
|
||||
)
|
||||
// Sign certificate
|
||||
// When subsequent signature requested
|
||||
assertFailsWith(IllegalArgumentException::class) {
|
||||
storage.putCertificatePath(requestId, JcaPKCS10CertificationRequest(csr).run {
|
||||
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 LTD", country = "GB"), rootCAKey)
|
||||
val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, X500Name("CN=Corda Node Intermediate CA,L=London"), intermediateCAKey.public)
|
||||
val ourCertificate = X509Utilities.createCertificate(CertificateType.TLS, intermediateCACert, intermediateCAKey, subject, publicKey).toX509Certificate()
|
||||
buildCertPath(ourCertificate, intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
|
||||
}, listOf(DOORMAN_SIGNATURE))
|
||||
storage.putCertificatePath(
|
||||
requestId,
|
||||
JcaPKCS10CertificationRequest(csr).run {
|
||||
val (rootCa, intermediateCa, nodeCa) = createDevNodeCaCertPath(CordaX500Name.build(X500Principal(subject.encoded)))
|
||||
buildCertPath(nodeCa.certificate, intermediateCa.certificate, rootCa.certificate)
|
||||
},
|
||||
listOf(DOORMAN_SIGNATURE))
|
||||
}
|
||||
}
|
||||
|
||||
@ -207,6 +206,6 @@ class DBCertificateRequestStorageTest : TestBase() {
|
||||
|
||||
internal fun createRequest(organisation: String): Pair<PKCS10CertificationRequest, KeyPair> {
|
||||
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)
|
||||
}
|
@ -3,25 +3,24 @@ package com.r3.corda.networkmanage.common.persistence
|
||||
import com.r3.corda.networkmanage.TestBase
|
||||
import com.r3.corda.networkmanage.common.utils.withCert
|
||||
import com.r3.corda.networkmanage.doorman.signer.LocalSigner
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.sign
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.nodeapi.internal.SignedNodeInfo
|
||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
import net.corda.nodeapi.internal.network.NetworkMap
|
||||
import net.corda.nodeapi.internal.network.SignedNetworkMap
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.internal.TestNodeInfoBuilder
|
||||
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.security.cert.X509Certificate
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class PersistentNetworkMapStorageTest : TestBase() {
|
||||
@ -30,15 +29,16 @@ class PersistentNetworkMapStorageTest : TestBase() {
|
||||
private lateinit var nodeInfoStorage: PersistentNodeInfoStorage
|
||||
private lateinit var requestStorage: PersistentCertificateRequestStorage
|
||||
|
||||
private val rootCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
private val rootCaCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name("Corda Node Root CA", "R3 LTD", "London", "GB"), rootCaKeyPair)
|
||||
private val intermediateCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
private val intermediateCaCert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCaCert, rootCaKeyPair, CordaX500Name("Corda Node Intermediate CA", "R3 LTD", "London", "GB"), intermediateCaKeyPair.public)
|
||||
private lateinit var rootCaCert: X509Certificate
|
||||
private lateinit var intermediateCa: CertificateAndKeyPair
|
||||
|
||||
@Before
|
||||
fun startDb() {
|
||||
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
|
||||
rootCaCert = rootCa.certificate
|
||||
this.intermediateCa = intermediateCa
|
||||
persistence = configureDatabase(makeTestDataSourceProperties())
|
||||
networkMapStorage = PersistentNetworkMapStorage(persistence, LocalSigner(intermediateCaKeyPair, arrayOf(intermediateCaCert.cert, rootCaCert.cert)))
|
||||
networkMapStorage = PersistentNetworkMapStorage(persistence, LocalSigner(intermediateCa.keyPair, arrayOf(intermediateCa.certificate, rootCaCert)))
|
||||
nodeInfoStorage = PersistentNodeInfoStorage(persistence)
|
||||
requestStorage = PersistentCertificateRequestStorage(persistence)
|
||||
}
|
||||
@ -60,7 +60,7 @@ class PersistentNetworkMapStorageTest : TestBase() {
|
||||
|
||||
val networkMap = NetworkMap(listOf(nodeInfoHash), networkParametersHash)
|
||||
val serializedNetworkMap = networkMap.serialize()
|
||||
val signatureData = intermediateCaKeyPair.sign(serializedNetworkMap).withCert(intermediateCaCert.cert)
|
||||
val signatureData = intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate)
|
||||
val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, signatureData)
|
||||
|
||||
// when
|
||||
@ -70,7 +70,7 @@ class PersistentNetworkMapStorageTest : TestBase() {
|
||||
val persistedSignedNetworkMap = networkMapStorage.getCurrentNetworkMap()
|
||||
|
||||
assertEquals(signedNetworkMap.signature, persistedSignedNetworkMap?.signature)
|
||||
assertEquals(signedNetworkMap.verified(rootCaCert.cert), persistedSignedNetworkMap?.verified(rootCaCert.cert))
|
||||
assertEquals(signedNetworkMap.verified(rootCaCert), persistedSignedNetworkMap?.verified(rootCaCert))
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -96,7 +96,7 @@ class PersistentNetworkMapStorageTest : TestBase() {
|
||||
// Sign network map making it current network map
|
||||
val networkMap = NetworkMap(emptyList(), networkParametersHash)
|
||||
val serializedNetworkMap = networkMap.serialize()
|
||||
val signatureData = intermediateCaKeyPair.sign(serializedNetworkMap).withCert(intermediateCaCert.cert)
|
||||
val signatureData = intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate)
|
||||
val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, signatureData)
|
||||
networkMapStorage.saveNetworkMap(signedNetworkMap)
|
||||
|
||||
@ -117,7 +117,7 @@ class PersistentNetworkMapStorageTest : TestBase() {
|
||||
val netParamsHash = networkMapStorage.saveNetworkParameters(netParams)
|
||||
val signedNetParams = networkMapStorage.getSignedNetworkParameters(netParamsHash)
|
||||
assertThat(signedNetParams?.verified()).isEqualTo(netParams)
|
||||
assertThat(signedNetParams?.sig?.by).isEqualTo(intermediateCaKeyPair.public)
|
||||
assertThat(signedNetParams?.sig?.by).isEqualTo(intermediateCa.keyPair.public)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -135,7 +135,7 @@ class PersistentNetworkMapStorageTest : TestBase() {
|
||||
val networkParametersHash = networkMapStorage.saveNetworkParameters(testNetworkParameters(emptyList()))
|
||||
val networkMap = NetworkMap(listOf(nodeInfoHashA), networkParametersHash)
|
||||
val serializedNetworkMap = networkMap.serialize()
|
||||
val signatureData = intermediateCaKeyPair.sign(serializedNetworkMap).withCert(intermediateCaCert.cert)
|
||||
val signatureData = intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate)
|
||||
val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, signatureData)
|
||||
|
||||
// Sign network map
|
||||
|
@ -6,15 +6,16 @@ import com.r3.corda.networkmanage.common.utils.hashString
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.nodeapi.internal.SignedNodeInfo
|
||||
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.testing.internal.TestNodeInfoBuilder
|
||||
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
||||
import net.corda.testing.internal.signWith
|
||||
import net.corda.testing.node.MockServices
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
@ -22,6 +23,7 @@ import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.security.PrivateKey
|
||||
import java.security.cert.X509Certificate
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertNull
|
||||
@ -31,13 +33,14 @@ class PersitenceNodeInfoStorageTest : TestBase() {
|
||||
private lateinit var nodeInfoStorage: PersistentNodeInfoStorage
|
||||
private lateinit var persistence: CordaPersistence
|
||||
|
||||
private val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
private val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 LTD", country = "GB"), rootCAKey)
|
||||
private val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
private val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, CordaX500Name(commonName = "Corda Node Intermediate CA", locality = "London", organisation = "R3 LTD", country = "GB"), intermediateCAKey.public)
|
||||
private lateinit var rootCaCert: X509Certificate
|
||||
private lateinit var intermediateCa: CertificateAndKeyPair
|
||||
|
||||
@Before
|
||||
fun startDb() {
|
||||
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
|
||||
rootCaCert = rootCa.certificate
|
||||
this.intermediateCa = intermediateCa
|
||||
persistence = configureDatabase(MockServices.makeTestDataSourceProperties())
|
||||
nodeInfoStorage = PersistentNodeInfoStorage(persistence)
|
||||
requestStorage = PersistentCertificateRequestStorage(persistence)
|
||||
@ -53,9 +56,14 @@ class PersitenceNodeInfoStorageTest : TestBase() {
|
||||
// Create node info.
|
||||
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val name = CordaX500Name(organisation = "Test", locality = "London", country = "GB")
|
||||
val nodeCaCert = X509Utilities.createCertificate(CertificateType.NODE_CA, intermediateCACert, intermediateCAKey, name, keyPair.public)
|
||||
val nodeCaCert = X509Utilities.createCertificate(
|
||||
CertificateType.NODE_CA,
|
||||
intermediateCa.certificate,
|
||||
intermediateCa.keyPair,
|
||||
name.x500Principal,
|
||||
keyPair.public)
|
||||
|
||||
val request = X509Utilities.createCertificateSigningRequest(name, "my@mail.com", keyPair)
|
||||
val request = X509Utilities.createCertificateSigningRequest(name.x500Principal, "my@mail.com", keyPair)
|
||||
|
||||
val requestId = requestStorage.saveRequest(request)
|
||||
requestStorage.markRequestTicketCreated(requestId)
|
||||
@ -63,12 +71,15 @@ class PersitenceNodeInfoStorageTest : TestBase() {
|
||||
|
||||
assertNull(nodeInfoStorage.getCertificatePath(SecureHash.parse(keyPair.public.hashString())))
|
||||
|
||||
requestStorage.putCertificatePath(requestId, buildCertPath(nodeCaCert.cert, intermediateCACert.cert, rootCACert.cert), listOf(CertificationRequestStorage.DOORMAN_SIGNATURE))
|
||||
requestStorage.putCertificatePath(
|
||||
requestId,
|
||||
buildCertPath(nodeCaCert, intermediateCa.certificate, rootCaCert),
|
||||
listOf(CertificationRequestStorage.DOORMAN_SIGNATURE))
|
||||
|
||||
val storedCertPath = nodeInfoStorage.getCertificatePath(SecureHash.parse(keyPair.public.hashString()))
|
||||
assertNotNull(storedCertPath)
|
||||
|
||||
assertEquals(nodeCaCert.cert, storedCertPath!!.certificates.first())
|
||||
assertEquals(nodeCaCert, storedCertPath!!.certificates.first())
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -4,20 +4,18 @@ import com.nhaarman.mockito_kotlin.*
|
||||
import com.r3.corda.networkmanage.TestBase
|
||||
import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage
|
||||
import com.r3.corda.networkmanage.common.utils.withCert
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.sha256
|
||||
import net.corda.core.crypto.sign
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||
import net.corda.nodeapi.internal.network.NetworkMap
|
||||
import net.corda.nodeapi.internal.network.SignedNetworkMap
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.security.cert.X509Certificate
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@ -25,12 +23,15 @@ class NetworkMapSignerTest : TestBase() {
|
||||
private lateinit var signer: Signer
|
||||
private lateinit var networkMapStorage: NetworkMapStorage
|
||||
private lateinit var networkMapSigner: NetworkMapSigner
|
||||
private val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
private val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 LTD", country = "GB"), rootCAKey)
|
||||
private val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
private val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, CordaX500Name(commonName = "Corda Node Intermediate CA", locality = "London", organisation = "R3 LTD", country = "GB"), intermediateCAKey.public)
|
||||
|
||||
private lateinit var rootCaCert: X509Certificate
|
||||
private lateinit var intermediateCa: CertificateAndKeyPair
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
|
||||
rootCaCert = rootCa.certificate
|
||||
this.intermediateCa = intermediateCa
|
||||
signer = mock()
|
||||
networkMapStorage = mock()
|
||||
networkMapSigner = NetworkMapSigner(networkMapStorage, signer)
|
||||
@ -43,11 +44,11 @@ class NetworkMapSignerTest : TestBase() {
|
||||
val networkParameters = testNetworkParameters(emptyList())
|
||||
val serializedNetworkMap = NetworkMap(signedNodeInfoHashes, SecureHash.randomSHA256()).serialize()
|
||||
whenever(networkMapStorage.getCurrentNetworkMap())
|
||||
.thenReturn(SignedNetworkMap(serializedNetworkMap, intermediateCAKey.sign(serializedNetworkMap).withCert(intermediateCACert.cert)))
|
||||
.thenReturn(SignedNetworkMap(serializedNetworkMap, intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate)))
|
||||
whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(signedNodeInfoHashes)
|
||||
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters)
|
||||
whenever(signer.sign(any())).then {
|
||||
intermediateCAKey.sign(it.arguments.first() as ByteArray).withCert(intermediateCACert.cert)
|
||||
intermediateCa.keyPair.sign(it.arguments[0] as ByteArray).withCert(intermediateCa.certificate)
|
||||
}
|
||||
|
||||
// when
|
||||
@ -59,7 +60,7 @@ class NetworkMapSignerTest : TestBase() {
|
||||
verify(networkMapStorage).getLatestNetworkParameters()
|
||||
argumentCaptor<SignedNetworkMap>().apply {
|
||||
verify(networkMapStorage).saveNetworkMap(capture())
|
||||
val networkMap = firstValue.verified(rootCACert.cert)
|
||||
val networkMap = firstValue.verified(rootCaCert)
|
||||
assertEquals(networkParameters.serialize().hash, networkMap.networkParameterHash)
|
||||
assertEquals(signedNodeInfoHashes.size, networkMap.nodeInfoHashes.size)
|
||||
assertTrue(networkMap.nodeInfoHashes.containsAll(signedNodeInfoHashes))
|
||||
@ -73,7 +74,7 @@ class NetworkMapSignerTest : TestBase() {
|
||||
val networkMapParametersHash = networkParameters.serialize().bytes.sha256()
|
||||
val networkMap = NetworkMap(emptyList(), networkMapParametersHash)
|
||||
val serializedNetworkMap = networkMap.serialize()
|
||||
val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, intermediateCAKey.sign(serializedNetworkMap).withCert(intermediateCACert.cert))
|
||||
val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate))
|
||||
whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(signedNetworkMap)
|
||||
whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList())
|
||||
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters)
|
||||
@ -94,7 +95,7 @@ class NetworkMapSignerTest : TestBase() {
|
||||
whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList())
|
||||
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters)
|
||||
whenever(signer.sign(any())).then {
|
||||
intermediateCAKey.sign(it.arguments.first() as ByteArray).withCert(intermediateCACert.cert)
|
||||
intermediateCa.keyPair.sign(it.arguments[0] as ByteArray).withCert(intermediateCa.certificate)
|
||||
}
|
||||
// when
|
||||
networkMapSigner.signNetworkMap()
|
||||
@ -105,7 +106,7 @@ class NetworkMapSignerTest : TestBase() {
|
||||
verify(networkMapStorage).getLatestNetworkParameters()
|
||||
argumentCaptor<SignedNetworkMap>().apply {
|
||||
verify(networkMapStorage).saveNetworkMap(capture())
|
||||
val networkMap = firstValue.verified(rootCACert.cert)
|
||||
val networkMap = firstValue.verified(rootCaCert)
|
||||
assertEquals(networkParameters.serialize().hash, networkMap.networkParameterHash)
|
||||
}
|
||||
}
|
||||
|
@ -10,24 +10,23 @@ import com.r3.corda.networkmanage.common.persistence.CertificateStatus
|
||||
import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage
|
||||
import com.r3.corda.networkmanage.common.persistence.RequestStatus
|
||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
||||
import com.r3.corda.networkmanage.common.utils.toX509Certificate
|
||||
import com.r3.corda.networkmanage.doorman.signer.DefaultCsrHandler
|
||||
import com.r3.corda.networkmanage.doorman.signer.LocalSigner
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
import org.junit.Test
|
||||
import javax.security.auth.x500.X500Principal
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class DefaultRequestProcessorTest : TestBase() {
|
||||
@Test
|
||||
fun `get response`() {
|
||||
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val cert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(locality = "London", organisation = "Test", country = "GB"), keyPair)
|
||||
val cert = X509Utilities.createSelfSignedCACertificate(X500Principal("O=Test,L=London,C=GB"), keyPair)
|
||||
|
||||
val requestStorage: CertificationRequestStorage = mock {
|
||||
on { getRequest("New") }.thenReturn(certificateSigningRequest())
|
||||
on { getRequest("Signed") }.thenReturn(certificateSigningRequest(status = RequestStatus.SIGNED, certData = certificateData("", CertificateStatus.VALID, buildCertPath(cert.toX509Certificate()))))
|
||||
on { getRequest("Signed") }.thenReturn(certificateSigningRequest(status = RequestStatus.SIGNED, certData = certificateData("", CertificateStatus.VALID, buildCertPath(cert))))
|
||||
on { getRequest("Rejected") }.thenReturn(certificateSigningRequest(status = RequestStatus.REJECTED, remark = "Random reason"))
|
||||
}
|
||||
val signer: LocalSigner = mock()
|
||||
@ -35,15 +34,15 @@ class DefaultRequestProcessorTest : TestBase() {
|
||||
|
||||
assertEquals(CertificateResponse.NotReady, requestProcessor.getResponse("random"))
|
||||
assertEquals(CertificateResponse.NotReady, requestProcessor.getResponse("New"))
|
||||
assertEquals(CertificateResponse.Ready(buildCertPath(cert.toX509Certificate())), requestProcessor.getResponse("Signed"))
|
||||
assertEquals(CertificateResponse.Ready(buildCertPath(cert)), requestProcessor.getResponse("Signed"))
|
||||
assertEquals(CertificateResponse.Unauthorised("Random reason"), requestProcessor.getResponse("Rejected"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `process request`() {
|
||||
val request1 = X509Utilities.createCertificateSigningRequest(CordaX500Name(locality = "London", organisation = "Test1", country = "GB"), "my@email.com", Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
|
||||
val request2 = X509Utilities.createCertificateSigningRequest(CordaX500Name(locality = "London", organisation = "Test2", country = "GB"), "my@email.com", Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
|
||||
val request3 = X509Utilities.createCertificateSigningRequest(CordaX500Name(locality = "London", organisation = "Test3", country = "GB"), "my@email.com", Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
|
||||
val (request1, request2, request3) = (1..3).map {
|
||||
X509Utilities.createCertificateSigningRequest(X500Principal("O=Test1,L=London,C=GB"), "my@email.com", Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
|
||||
}
|
||||
|
||||
val requestStorage: CertificationRequestStorage = mock {
|
||||
on { getRequests(RequestStatus.APPROVED) }.thenReturn(listOf(
|
||||
|
@ -7,33 +7,32 @@ import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage
|
||||
import com.r3.corda.networkmanage.common.persistence.NodeInfoStorage
|
||||
import com.r3.corda.networkmanage.common.utils.withCert
|
||||
import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.SecureHash.Companion.randomSHA256
|
||||
import net.corda.core.crypto.SignedData
|
||||
import net.corda.core.crypto.sign
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.openHttpConnection
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.nodeapi.internal.SignedNodeInfo
|
||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||
import net.corda.nodeapi.internal.network.NetworkMap
|
||||
import net.corda.nodeapi.internal.network.NetworkParameters
|
||||
import net.corda.nodeapi.internal.network.SignedNetworkMap
|
||||
import net.corda.testing.SerializationEnvironmentRule
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
||||
import net.corda.testing.internal.createNodeInfoAndSigned
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import java.io.FileNotFoundException
|
||||
import java.net.URL
|
||||
import java.security.cert.X509Certificate
|
||||
import javax.ws.rs.core.MediaType
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@ -42,12 +41,18 @@ class NodeInfoWebServiceTest {
|
||||
@JvmField
|
||||
val testSerialization = SerializationEnvironmentRule(true)
|
||||
|
||||
private val rootCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
private val rootCaCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name("Corda Node Root CA", "R3 LTD", "London", "GB"), rootCaKeyPair)
|
||||
private val intermediateCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
private val intermediateCaCert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCaCert, rootCaKeyPair, X500Name("CN=Corda Node Intermediate CA,L=London"), intermediateCaKeyPair.public)
|
||||
private lateinit var rootCaCert: X509Certificate
|
||||
private lateinit var intermediateCa: CertificateAndKeyPair
|
||||
|
||||
private val testNetworkMapConfig = NetworkMapConfig(10.seconds.toMillis(), 10.seconds.toMillis())
|
||||
|
||||
@Before
|
||||
fun init() {
|
||||
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
|
||||
rootCaCert = rootCa.certificate
|
||||
this.intermediateCa = intermediateCa
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `submit nodeInfo`() {
|
||||
// Create node info.
|
||||
@ -65,7 +70,7 @@ class NodeInfoWebServiceTest {
|
||||
fun `get network map`() {
|
||||
val networkMap = NetworkMap(listOf(randomSHA256(), randomSHA256()), randomSHA256())
|
||||
val serializedNetworkMap = networkMap.serialize()
|
||||
val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, intermediateCaKeyPair.sign(serializedNetworkMap).withCert(intermediateCaCert.cert))
|
||||
val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate))
|
||||
|
||||
val networkMapStorage: NetworkMapStorage = mock {
|
||||
on { getCurrentNetworkMap() }.thenReturn(signedNetworkMap)
|
||||
@ -75,7 +80,7 @@ class NodeInfoWebServiceTest {
|
||||
it.start()
|
||||
val signedNetworkMapResponse = it.doGet<SignedNetworkMap>("")
|
||||
verify(networkMapStorage, times(1)).getCurrentNetworkMap()
|
||||
assertEquals(signedNetworkMapResponse.verified(rootCaCert.cert), networkMap)
|
||||
assertEquals(signedNetworkMapResponse.verified(rootCaCert), networkMap)
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,7 +109,7 @@ class NodeInfoWebServiceTest {
|
||||
fun `get network parameters`() {
|
||||
val netParams = testNetworkParameters(emptyList())
|
||||
val serializedNetParams = netParams.serialize()
|
||||
val signedNetParams = SignedData(serializedNetParams, intermediateCaKeyPair.sign(serializedNetParams))
|
||||
val signedNetParams = SignedData(serializedNetParams, intermediateCa.keyPair.sign(serializedNetParams))
|
||||
val netParamsHash = serializedNetParams.hash
|
||||
|
||||
val networkMapStorage: NetworkMapStorage = mock {
|
||||
@ -116,7 +121,7 @@ class NodeInfoWebServiceTest {
|
||||
val netParamsResponse = it.doGet<SignedData<NetworkParameters>>("network-parameter/$netParamsHash")
|
||||
verify(networkMapStorage, times(1)).getSignedNetworkParameters(netParamsHash)
|
||||
assertThat(netParamsResponse.verified()).isEqualTo(netParams)
|
||||
assertThat(netParamsResponse.sig.by).isEqualTo(intermediateCaKeyPair.public)
|
||||
assertThat(netParamsResponse.sig.by).isEqualTo(intermediateCa.keyPair.public)
|
||||
|
||||
assertThatExceptionOfType(FileNotFoundException::class.java).isThrownBy {
|
||||
it.doGet<SignedData<NetworkParameters>>("network-parameter/${randomSHA256()}")
|
||||
|
@ -4,46 +4,54 @@ import com.nhaarman.mockito_kotlin.*
|
||||
import com.r3.corda.networkmanage.TestBase
|
||||
import com.r3.corda.networkmanage.common.persistence.CertificateResponse
|
||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
||||
import com.r3.corda.networkmanage.common.utils.toX509Certificate
|
||||
import com.r3.corda.networkmanage.doorman.signer.CsrHandler
|
||||
import com.r3.corda.networkmanage.doorman.webservice.RegistrationWebService
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME
|
||||
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
||||
import org.apache.commons.io.IOUtils
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import org.bouncycastle.asn1.x509.GeneralName
|
||||
import org.bouncycastle.asn1.x509.GeneralSubtree
|
||||
import org.bouncycastle.asn1.x509.NameConstraints
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
||||
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.io.IOException
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.HttpURLConnection.*
|
||||
import java.net.URL
|
||||
import java.nio.charset.StandardCharsets.UTF_8
|
||||
import java.security.cert.CertPath
|
||||
import java.security.cert.X509Certificate
|
||||
import java.util.*
|
||||
import java.util.zip.ZipInputStream
|
||||
import javax.security.auth.x500.X500Principal
|
||||
import javax.ws.rs.core.MediaType
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class RegistrationWebServiceTest : TestBase() {
|
||||
private val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
private val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 Ltd", country = "GB"), rootCAKey)
|
||||
private val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
private val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, X500Name("CN=Corda Node Intermediate CA,L=London"), intermediateCAKey.public)
|
||||
private lateinit var rootCaCert: X509Certificate
|
||||
private lateinit var intermediateCa: CertificateAndKeyPair
|
||||
private lateinit var webServer: NetworkManagementWebServer
|
||||
|
||||
@Before
|
||||
fun init() {
|
||||
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
|
||||
rootCaCert = rootCa.certificate
|
||||
this.intermediateCa = intermediateCa
|
||||
}
|
||||
|
||||
private fun startSigningServer(csrHandler: CsrHandler) {
|
||||
webServer = NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), RegistrationWebService(csrHandler))
|
||||
webServer.start()
|
||||
@ -65,7 +73,10 @@ class RegistrationWebServiceTest : TestBase() {
|
||||
startSigningServer(requestProcessor)
|
||||
|
||||
val keyPair = Crypto.generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val request = X509Utilities.createCertificateSigningRequest(CordaX500Name(locality = "London", organisation = "Legal Name", country = "GB"), "my@mail.com", keyPair)
|
||||
val request = X509Utilities.createCertificateSigningRequest(
|
||||
CordaX500Name(locality = "London", organisation = "Legal Name", country = "GB").x500Principal,
|
||||
"my@mail.com",
|
||||
keyPair)
|
||||
// Post request to signing server via http.
|
||||
|
||||
assertEquals(id, submitRequest(request))
|
||||
@ -79,6 +90,8 @@ class RegistrationWebServiceTest : TestBase() {
|
||||
val keyPair = Crypto.generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val id = SecureHash.randomSHA256().toString()
|
||||
|
||||
val subject = CordaX500Name(locality = "London", organisation = "LegalName", country = "GB").x500Principal
|
||||
|
||||
// Mock Storage behaviour.
|
||||
val certificateStore = mutableMapOf<String, CertPath>()
|
||||
val requestProcessor = mock<CsrHandler> {
|
||||
@ -88,10 +101,15 @@ class RegistrationWebServiceTest : TestBase() {
|
||||
} ?: CertificateResponse.NotReady
|
||||
}
|
||||
on { processApprovedRequests() }.then {
|
||||
val request = X509Utilities.createCertificateSigningRequest(CordaX500Name(locality = "London", organisation = "LegalName", country = "GB"), "my@mail.com", keyPair)
|
||||
val request = X509Utilities.createCertificateSigningRequest(subject, "my@mail.com", keyPair)
|
||||
certificateStore[id] = JcaPKCS10CertificationRequest(request).run {
|
||||
val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, intermediateCACert, intermediateCAKey, subject, publicKey).toX509Certificate()
|
||||
buildCertPath(tlsCert, intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
|
||||
val tlsCert = X509Utilities.createCertificate(
|
||||
CertificateType.TLS,
|
||||
intermediateCa.certificate,
|
||||
intermediateCa.keyPair,
|
||||
X500Principal(subject.encoded),
|
||||
publicKey)
|
||||
buildCertPath(tlsCert, intermediateCa.certificate, rootCaCert)
|
||||
}
|
||||
null
|
||||
}
|
||||
@ -104,22 +122,15 @@ class RegistrationWebServiceTest : TestBase() {
|
||||
|
||||
val certificates = (pollForResponse(id) as PollResponse.Ready).certChain
|
||||
verify(requestProcessor, times(2)).getResponse(any())
|
||||
assertEquals(3, certificates.size)
|
||||
|
||||
certificates.first().run {
|
||||
assertThat(subjectDN.name).contains("O=LegalName")
|
||||
assertThat(subjectDN.name).contains("L=London")
|
||||
}
|
||||
|
||||
certificates.last().run {
|
||||
assertThat(subjectDN.name).contains("CN=Corda Node Root CA")
|
||||
assertThat(subjectDN.name).contains("L=London")
|
||||
}
|
||||
assertThat(certificates).hasSize(3)
|
||||
assertThat(certificates[0].subjectX500Principal).isEqualTo(subject)
|
||||
assertThat(certificates).endsWith(intermediateCa.certificate, rootCaCert)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `retrieve certificate and create valid TLS certificate`() {
|
||||
val keyPair = Crypto.generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val nodeCaKeyPair = Crypto.generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val id = SecureHash.randomSHA256().toString()
|
||||
|
||||
// Mock Storage behaviour.
|
||||
@ -131,11 +142,22 @@ class RegistrationWebServiceTest : TestBase() {
|
||||
} ?: CertificateResponse.NotReady
|
||||
}
|
||||
on { processApprovedRequests() }.then {
|
||||
val request = X509Utilities.createCertificateSigningRequest(CordaX500Name(locality = "London", organisation = "Legal Name", country = "GB"), "my@mail.com", keyPair)
|
||||
val request = X509Utilities.createCertificateSigningRequest(
|
||||
CordaX500Name(locality = "London", organisation = "Legal Name", country = "GB").x500Principal,
|
||||
"my@mail.com",
|
||||
nodeCaKeyPair)
|
||||
certificateStore[id] = JcaPKCS10CertificationRequest(request).run {
|
||||
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, X500Name("CN=LegalName, L=London")))), arrayOf())
|
||||
val clientCert = X509Utilities.createCertificate(CertificateType.NODE_CA, intermediateCACert, intermediateCAKey, subject, publicKey, nameConstraints = nameConstraints).toX509Certificate()
|
||||
buildCertPath(clientCert, intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
|
||||
val nameConstraints = NameConstraints(
|
||||
arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, X500Name("CN=LegalName, L=London")))),
|
||||
arrayOf())
|
||||
val clientCert = X509Utilities.createCertificate(
|
||||
CertificateType.NODE_CA,
|
||||
intermediateCa.certificate,
|
||||
intermediateCa.keyPair,
|
||||
X500Principal(subject.encoded),
|
||||
publicKey,
|
||||
nameConstraints = nameConstraints)
|
||||
buildCertPath(clientCert, intermediateCa.certificate, rootCaCert)
|
||||
}
|
||||
true
|
||||
}
|
||||
@ -149,11 +171,17 @@ class RegistrationWebServiceTest : TestBase() {
|
||||
verify(storage, times(2)).getResponse(any())
|
||||
assertEquals(3, certificates.size)
|
||||
|
||||
val sslKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val sslCert = X509Utilities.createCertificate(CertificateType.TLS, X509CertificateHolder(certificates.first().encoded), keyPair, X500Name("CN=LegalName,L=London"), sslKey.public).toX509Certificate()
|
||||
val sslKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val sslCert = X509Utilities.createCertificate(
|
||||
CertificateType.TLS,
|
||||
certificates[0],
|
||||
nodeCaKeyPair,
|
||||
// TODO Investigate why X500Principal("CN=LegalName, L=London") results in a name constraints violation
|
||||
X500Principal(X500Name("CN=LegalName, L=London").encoded),
|
||||
sslKeyPair.public)
|
||||
|
||||
// TODO: This is temporary solution, remove all certificate re-shaping after identity refactoring is done.
|
||||
X509Utilities.validateCertificateChain(certificates.last(), sslCert, *certificates.toTypedArray())
|
||||
X509Utilities.validateCertificateChain(rootCaCert, sslCert, *certificates.toTypedArray())
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -192,7 +220,7 @@ class RegistrationWebServiceTest : TestBase() {
|
||||
PollResponse.Ready(certificates)
|
||||
}
|
||||
HTTP_NO_CONTENT -> PollResponse.NotReady
|
||||
HTTP_UNAUTHORIZED -> PollResponse.Unauthorised(IOUtils.toString(conn.errorStream))
|
||||
HTTP_UNAUTHORIZED -> PollResponse.Unauthorised(IOUtils.toString(conn.errorStream, UTF_8))
|
||||
else -> throw IOException("Cannot connect to Certificate Signing Server, HTTP response code : ${conn.responseCode}")
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,10 @@ class JiraCsrHandlerTest {
|
||||
private lateinit var certificateResponse: CertificateResponse.Ready
|
||||
|
||||
private val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
private val pkcS10CertificationRequest = X509Utilities.createCertificateSigningRequest(CordaX500Name(locality = "London", organisation = "LegalName", country = "GB"), "my@mail.com", keyPair)
|
||||
private val pkcS10CertificationRequest = X509Utilities.createCertificateSigningRequest(
|
||||
CordaX500Name(locality = "London", organisation = "LegalName", country = "GB").x500Principal,
|
||||
"my@mail.com",
|
||||
keyPair)
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
|
@ -5,10 +5,8 @@ import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.generateKeyPair
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
import net.corda.core.utilities.trace
|
||||
import net.corda.nodeapi.internal.config.NodeSSLConfiguration
|
||||
import net.corda.nodeapi.internal.crypto.*
|
||||
@ -39,10 +37,10 @@ object DevIdentityGenerator {
|
||||
// TODO The passwords for the dev key stores are spread everywhere and should be constants in a single location
|
||||
val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
|
||||
val intermediateCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
|
||||
val rootCert = caKeyStore.getCertificate(X509Utilities.CORDA_ROOT_CA)
|
||||
val rootCert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA)
|
||||
|
||||
nodeSslConfig.certificatesDirectory.createDirectories()
|
||||
nodeSslConfig.createDevKeyStores(rootCert.toX509CertHolder(), intermediateCa, legalName)
|
||||
nodeSslConfig.createDevKeyStores(rootCert, intermediateCa, legalName)
|
||||
|
||||
val keyStoreWrapper = KeyStoreWrapper(nodeSslConfig.nodeKeystore, nodeSslConfig.keyStorePassword)
|
||||
val identity = keyStoreWrapper.storeLegalIdentity(legalName, "$NODE_IDENTITY_ALIAS_PREFIX-private-key", Crypto.generateKeyPair())
|
||||
@ -62,16 +60,21 @@ object DevIdentityGenerator {
|
||||
|
||||
keyPairs.zip(dirs) { keyPair, nodeDir ->
|
||||
val (serviceKeyCert, compositeKeyCert) = listOf(keyPair.public, compositeKey).map { publicKey ->
|
||||
X509Utilities.createCertificate(CertificateType.SERVICE_IDENTITY, intermediateCa.certificate, intermediateCa.keyPair, notaryName, publicKey)
|
||||
X509Utilities.createCertificate(
|
||||
CertificateType.SERVICE_IDENTITY,
|
||||
intermediateCa.certificate,
|
||||
intermediateCa.keyPair,
|
||||
notaryName.x500Principal,
|
||||
publicKey)
|
||||
}
|
||||
val distServKeyStoreFile = (nodeDir / "certificates").createDirectories() / "distributedService.jks"
|
||||
val keystore = loadOrCreateKeyStore(distServKeyStoreFile, "cordacadevpass")
|
||||
keystore.setCertificateEntry("$DISTRIBUTED_NOTARY_ALIAS_PREFIX-composite-key", compositeKeyCert.cert)
|
||||
keystore.setCertificateEntry("$DISTRIBUTED_NOTARY_ALIAS_PREFIX-composite-key", compositeKeyCert)
|
||||
keystore.setKeyEntry(
|
||||
"$DISTRIBUTED_NOTARY_ALIAS_PREFIX-private-key",
|
||||
keyPair.private,
|
||||
"cordacadevkeypass".toCharArray(),
|
||||
arrayOf(serviceKeyCert.cert, intermediateCa.certificate.cert, rootCert))
|
||||
arrayOf(serviceKeyCert, intermediateCa.certificate, rootCert))
|
||||
keystore.save(distServKeyStoreFile, "cordacadevpass")
|
||||
}
|
||||
|
||||
|
@ -8,13 +8,13 @@ import net.corda.nodeapi.internal.crypto.*
|
||||
import org.bouncycastle.asn1.x509.GeneralName
|
||||
import org.bouncycastle.asn1.x509.GeneralSubtree
|
||||
import org.bouncycastle.asn1.x509.NameConstraints
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import java.security.cert.X509Certificate
|
||||
|
||||
/**
|
||||
* Create the node and SSL key stores needed by a node. The node key store will be populated with a node CA cert (using
|
||||
* the given legal name), and the SSL key store will store the TLS cert which is a sub-cert of the node CA.
|
||||
*/
|
||||
fun SSLConfiguration.createDevKeyStores(rootCert: X509CertificateHolder, intermediateCa: CertificateAndKeyPair, legalName: CordaX500Name) {
|
||||
fun SSLConfiguration.createDevKeyStores(rootCert: X509Certificate, intermediateCa: CertificateAndKeyPair, legalName: CordaX500Name) {
|
||||
val (nodeCaCert, nodeCaKeyPair) = createDevNodeCa(intermediateCa, legalName)
|
||||
|
||||
loadOrCreateKeyStore(nodeKeystore, keyStorePassword).apply {
|
||||
@ -27,7 +27,7 @@ fun SSLConfiguration.createDevKeyStores(rootCert: X509CertificateHolder, interme
|
||||
}
|
||||
|
||||
val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, legalName, tlsKeyPair.public)
|
||||
val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, legalName.x500Principal, tlsKeyPair.public)
|
||||
|
||||
loadOrCreateKeyStore(sslKeystore, keyStorePassword).apply {
|
||||
addOrReplaceKey(
|
||||
@ -50,7 +50,7 @@ fun createDevNodeCa(intermediateCa: CertificateAndKeyPair, legalName: CordaX500N
|
||||
CertificateType.NODE_CA,
|
||||
intermediateCa.certificate,
|
||||
intermediateCa.keyPair,
|
||||
legalName,
|
||||
legalName.x500Principal,
|
||||
keyPair.public,
|
||||
nameConstraints = nameConstraints)
|
||||
return CertificateAndKeyPair(cert, keyPair)
|
||||
|
@ -3,8 +3,9 @@
|
||||
package net.corda.nodeapi.internal.crypto
|
||||
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.internal.*
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import net.corda.core.internal.exists
|
||||
import net.corda.core.internal.read
|
||||
import net.corda.core.internal.write
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
@ -67,18 +68,6 @@ fun loadKeyStore(input: InputStream, storePassword: String): KeyStore {
|
||||
return keyStore
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper extension method to add, or overwrite any key data in store.
|
||||
* @param alias name to record the private key and certificate chain under.
|
||||
* @param key cryptographic key to store.
|
||||
* @param password password for unlocking the key entry in the future. This does not have to be the same password as any keys stored,
|
||||
* but for SSL purposes this is recommended.
|
||||
* @param chain the sequence of certificates starting with the public key certificate for this key and extending to the root CA cert.
|
||||
*/
|
||||
fun KeyStore.addOrReplaceKey(alias: String, key: Key, password: CharArray, chain: Array<out X509CertificateHolder>) {
|
||||
addOrReplaceKey(alias, key, password, chain.map { it.cert }.toTypedArray<Certificate>())
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper extension method to add, or overwrite any key data in store.
|
||||
* @param alias name to record the private key and certificate chain under.
|
||||
@ -132,9 +121,9 @@ fun KeyStore.getKeyPair(alias: String, keyPassword: String): KeyPair = getCertif
|
||||
* @param keyPassword The password for the PrivateKey (not the store access password).
|
||||
*/
|
||||
fun KeyStore.getCertificateAndKeyPair(alias: String, keyPassword: String): CertificateAndKeyPair {
|
||||
val cert = getX509Certificate(alias).toX509CertHolder()
|
||||
val publicKey = Crypto.toSupportedPublicKey(cert.subjectPublicKeyInfo)
|
||||
return CertificateAndKeyPair(cert, KeyPair(publicKey, getSupportedKey(alias, keyPassword)))
|
||||
val certificate = getX509Certificate(alias)
|
||||
val publicKey = Crypto.toSupportedPublicKey(certificate.publicKey)
|
||||
return CertificateAndKeyPair(certificate, KeyPair(publicKey, getSupportedKey(alias, keyPassword)))
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2,7 +2,6 @@ package net.corda.nodeapi.internal.crypto
|
||||
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.read
|
||||
import java.nio.file.Path
|
||||
import java.security.KeyPair
|
||||
@ -15,8 +14,13 @@ class KeyStoreWrapper(private val storePath: Path, private val storePassword: St
|
||||
fun storeLegalIdentity(legalName: CordaX500Name, alias: String, keyPair: KeyPair): PartyAndCertificate {
|
||||
val nodeCaCertChain = keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
|
||||
val nodeCa = getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)
|
||||
val identityCert = X509Utilities.createCertificate(CertificateType.LEGAL_IDENTITY, nodeCa.certificate, nodeCa.keyPair, legalName, keyPair.public)
|
||||
val identityCertPath = X509CertificateFactory().generateCertPath(identityCert.cert, *nodeCaCertChain)
|
||||
val identityCert = X509Utilities.createCertificate(
|
||||
CertificateType.LEGAL_IDENTITY,
|
||||
nodeCa.certificate,
|
||||
nodeCa.keyPair,
|
||||
legalName.x500Principal,
|
||||
keyPair.public)
|
||||
val identityCertPath = X509CertificateFactory().generateCertPath(identityCert, *nodeCaCertChain)
|
||||
// Assume key password = store password.
|
||||
keyStore.addOrReplaceKey(alias, keyPair.private, storePassword.toCharArray(), identityCertPath.certificates.toTypedArray())
|
||||
keyStore.save(storePath, storePassword)
|
||||
|
@ -4,12 +4,14 @@ import net.corda.core.CordaOID
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.SignatureScheme
|
||||
import net.corda.core.crypto.random63BitValue
|
||||
import net.corda.core.internal.CertRole
|
||||
import net.corda.core.internal.reader
|
||||
import net.corda.core.internal.writer
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.*
|
||||
import net.corda.core.utilities.days
|
||||
import net.corda.core.utilities.millis
|
||||
import org.bouncycastle.asn1.*
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import org.bouncycastle.asn1.x500.style.BCStyle
|
||||
import org.bouncycastle.asn1.x509.*
|
||||
import org.bouncycastle.asn1.x509.Extension
|
||||
@ -34,6 +36,7 @@ import java.time.Duration
|
||||
import java.time.Instant
|
||||
import java.time.temporal.ChronoUnit
|
||||
import java.util.*
|
||||
import javax.security.auth.x500.X500Principal
|
||||
|
||||
object X509Utilities {
|
||||
val DEFAULT_IDENTITY_SIGNATURE_SCHEME = Crypto.EDDSA_ED25519_SHA512
|
||||
@ -74,7 +77,7 @@ object X509Utilities {
|
||||
* @param after duration to roll forward returned end date relative to current date.
|
||||
* @param parent if provided certificate whose validity should bound the date interval returned.
|
||||
*/
|
||||
fun getCertificateValidityWindow(before: Duration, after: Duration, parent: X509CertificateHolder? = null): Pair<Date, Date> {
|
||||
fun getCertificateValidityWindow(before: Duration, after: Duration, parent: X509Certificate? = null): Pair<Date, Date> {
|
||||
val startOfDayUTC = Instant.now().truncatedTo(ChronoUnit.DAYS)
|
||||
val notBefore = max(startOfDayUTC - before, parent?.notBefore)
|
||||
val notAfter = min(startOfDayUTC + after, parent?.notAfter)
|
||||
@ -85,60 +88,11 @@ object X509Utilities {
|
||||
* Create a de novo root self-signed X509 v3 CA cert.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun createSelfSignedCACertificate(subject: CordaX500Name,
|
||||
fun createSelfSignedCACertificate(subject: X500Principal,
|
||||
keyPair: KeyPair,
|
||||
validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW): X509CertificateHolder {
|
||||
validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW): X509Certificate {
|
||||
val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second)
|
||||
return createCertificate(CertificateType.ROOT_CA, subject.x500Name, keyPair, subject.x500Name, keyPair.public, window)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a X509 v3 certificate for use as a CA or for TLS. This does not require a [CordaX500Name] because the
|
||||
* constraints are inappropriate for TLS/CA usage, however as a result this is unsuitable for Corda identity
|
||||
* certificate generation.
|
||||
*
|
||||
* @param issuerCertificate The Public certificate of the root CA above this used to sign it.
|
||||
* @param issuerKeyPair The KeyPair of the root CA above this used to sign it.
|
||||
* @param subject subject of the generated certificate.
|
||||
* @param subjectPublicKey subject's public key.
|
||||
* @param validityWindow The certificate's validity window. Default to [DEFAULT_VALIDITY_WINDOW] if not provided.
|
||||
* @return A data class is returned containing the new intermediate CA Cert and its KeyPair for signing downstream certificates.
|
||||
* Note the generated certificate tree is capped at max depth of 1 below this to be in line with commercially available certificates.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun createCertificate(certificateType: CertificateType,
|
||||
issuerCertificate: X509CertificateHolder,
|
||||
issuerKeyPair: KeyPair,
|
||||
subject: CordaX500Name,
|
||||
subjectPublicKey: PublicKey,
|
||||
validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW,
|
||||
nameConstraints: NameConstraints? = null): X509CertificateHolder {
|
||||
return createCertificate(certificateType, issuerCertificate, issuerKeyPair, subject.x500Name, subjectPublicKey, validityWindow, nameConstraints)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a X509 v3 certificate for use as a CA or for TLS. This does not require a [CordaX500Name] because the
|
||||
* constraints are inappropriate for TLS/CA usage, however as a result this is unsuitable for Corda identity
|
||||
* certificate generation.
|
||||
*
|
||||
* @param issuerCertificate The Public certificate of the root CA above this used to sign it.
|
||||
* @param issuerKeyPair The KeyPair of the root CA above this used to sign it.
|
||||
* @param subject subject of the generated certificate.
|
||||
* @param subjectPublicKey subject's public key.
|
||||
* @param validityWindow The certificate's validity window. Default to [DEFAULT_VALIDITY_WINDOW] if not provided.
|
||||
* @return A data class is returned containing the new intermediate CA Cert and its KeyPair for signing downstream certificates.
|
||||
* Note the generated certificate tree is capped at max depth of 1 below this to be in line with commercially available certificates.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun createCertificate(certificateType: CertificateType,
|
||||
issuerCertificate: X509CertificateHolder,
|
||||
issuerKeyPair: KeyPair,
|
||||
subject: X500Name,
|
||||
subjectPublicKey: PublicKey,
|
||||
validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW,
|
||||
nameConstraints: NameConstraints? = null): X509CertificateHolder {
|
||||
val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second, issuerCertificate)
|
||||
return createCertificate(certificateType, issuerCertificate.subject, issuerKeyPair, subject, subjectPublicKey, window, nameConstraints)
|
||||
return createCertificate(CertificateType.ROOT_CA, subject, keyPair, subject, keyPair.public, window)
|
||||
}
|
||||
|
||||
@Throws(CertPathValidatorException::class)
|
||||
@ -153,13 +107,13 @@ object X509Utilities {
|
||||
|
||||
/**
|
||||
* Helper method to store a .pem/.cer format file copy of a certificate if required for import into a PC/Mac, or for inspection.
|
||||
* @param x509Certificate certificate to save.
|
||||
* @param certificate certificate to save.
|
||||
* @param file Target file.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun saveCertificateAsPEMFile(x509Certificate: X509Certificate, file: Path) {
|
||||
fun saveCertificateAsPEMFile(certificate: X509Certificate, file: Path) {
|
||||
JcaPEMWriter(file.writer()).use {
|
||||
it.writeObject(x509Certificate)
|
||||
it.writeObject(certificate)
|
||||
}
|
||||
}
|
||||
|
||||
@ -172,9 +126,10 @@ object X509Utilities {
|
||||
fun loadCertificateFromPEMFile(file: Path): X509Certificate {
|
||||
return file.reader().use {
|
||||
val pemObject = PemReader(it).readPemObject()
|
||||
val certHolder = X509CertificateHolder(pemObject.content)
|
||||
certHolder.isValidOn(Date())
|
||||
certHolder.cert
|
||||
X509CertificateHolder(pemObject.content).run {
|
||||
isValidOn(Date())
|
||||
toJca()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -187,38 +142,18 @@ object X509Utilities {
|
||||
* @param validityWindow the time period the certificate is valid for.
|
||||
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
|
||||
*/
|
||||
fun createCertificate(certificateType: CertificateType,
|
||||
issuer: CordaX500Name,
|
||||
subject: CordaX500Name,
|
||||
subjectPublicKey: PublicKey,
|
||||
validityWindow: Pair<Date, Date>,
|
||||
nameConstraints: NameConstraints? = null): X509v3CertificateBuilder {
|
||||
return createCertificate(certificateType, issuer.x500Name, subject.x500Name, subjectPublicKey, validityWindow, nameConstraints)
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a partial X.509 certificate ready for signing.
|
||||
*
|
||||
* @param issuer name of the issuing entity.
|
||||
* @param subject name of the certificate subject.
|
||||
* @param subjectPublicKey public key of the certificate subject.
|
||||
* @param validityWindow the time period the certificate is valid for.
|
||||
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
|
||||
*/
|
||||
internal fun createCertificate(certificateType: CertificateType,
|
||||
issuer: X500Name,
|
||||
subject: X500Name,
|
||||
subjectPublicKey: PublicKey,
|
||||
validityWindow: Pair<Date, Date>,
|
||||
nameConstraints: NameConstraints? = null): X509v3CertificateBuilder {
|
||||
|
||||
fun createPartialCertificate(certificateType: CertificateType,
|
||||
issuer: X500Principal,
|
||||
subject: X500Principal,
|
||||
subjectPublicKey: PublicKey,
|
||||
validityWindow: Pair<Date, Date>,
|
||||
nameConstraints: NameConstraints? = null): X509v3CertificateBuilder {
|
||||
val serial = BigInteger.valueOf(random63BitValue())
|
||||
val keyPurposes = DERSequence(ASN1EncodableVector().apply { certificateType.purposes.forEach { add(it) } })
|
||||
val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(subjectPublicKey.encoded))
|
||||
val role = certificateType.role
|
||||
|
||||
val builder = JcaX509v3CertificateBuilder(issuer, serial, validityWindow.first, validityWindow.second,
|
||||
subject, subjectPublicKey)
|
||||
val builder = JcaX509v3CertificateBuilder(issuer, serial, validityWindow.first, validityWindow.second, subject, subjectPublicKey)
|
||||
.addExtension(Extension.subjectKeyIdentifier, false, BcX509ExtensionUtils().createSubjectKeyIdentifier(subjectPublicKeyInfo))
|
||||
.addExtension(Extension.basicConstraints, certificateType.isCA, BasicConstraints(certificateType.isCA))
|
||||
.addExtension(Extension.keyUsage, false, certificateType.keyUsage)
|
||||
@ -234,6 +169,37 @@ object X509Utilities {
|
||||
return builder
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a X509 v3 certificate using the given issuer certificate and key pair.
|
||||
*
|
||||
* @param issuerCertificate The Public certificate of the root CA above this used to sign it.
|
||||
* @param issuerKeyPair The KeyPair of the root CA above this used to sign it.
|
||||
* @param subject subject of the generated certificate.
|
||||
* @param subjectPublicKey subject's public key.
|
||||
* @param validityWindow The certificate's validity window. Default to [DEFAULT_VALIDITY_WINDOW] if not provided.
|
||||
* @return A data class is returned containing the new intermediate CA Cert and its KeyPair for signing downstream certificates.
|
||||
* Note the generated certificate tree is capped at max depth of 1 below this to be in line with commercially available certificates.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun createCertificate(certificateType: CertificateType,
|
||||
issuerCertificate: X509Certificate,
|
||||
issuerKeyPair: KeyPair,
|
||||
subject: X500Principal,
|
||||
subjectPublicKey: PublicKey,
|
||||
validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW,
|
||||
nameConstraints: NameConstraints? = null): X509Certificate {
|
||||
val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second, issuerCertificate)
|
||||
return createCertificate(
|
||||
certificateType,
|
||||
issuerCertificate.subjectX500Principal,
|
||||
issuerKeyPair,
|
||||
subject,
|
||||
subjectPublicKey,
|
||||
window,
|
||||
nameConstraints
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Build and sign an X.509 certificate with the given signer.
|
||||
*
|
||||
@ -245,15 +211,16 @@ object X509Utilities {
|
||||
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
|
||||
*/
|
||||
fun createCertificate(certificateType: CertificateType,
|
||||
issuer: X500Name,
|
||||
issuer: X500Principal,
|
||||
issuerSigner: ContentSigner,
|
||||
subject: CordaX500Name,
|
||||
subject: X500Principal,
|
||||
subjectPublicKey: PublicKey,
|
||||
validityWindow: Pair<Date, Date>,
|
||||
nameConstraints: NameConstraints? = null): X509CertificateHolder {
|
||||
val builder = createCertificate(certificateType, issuer, subject.x500Name, subjectPublicKey, validityWindow, nameConstraints)
|
||||
return builder.build(issuerSigner).apply {
|
||||
nameConstraints: NameConstraints? = null): X509Certificate {
|
||||
val builder = createPartialCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints)
|
||||
return builder.build(issuerSigner).run {
|
||||
require(isValidOn(Date()))
|
||||
toJca()
|
||||
}
|
||||
}
|
||||
|
||||
@ -268,39 +235,47 @@ object X509Utilities {
|
||||
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
|
||||
*/
|
||||
fun createCertificate(certificateType: CertificateType,
|
||||
issuer: X500Name,
|
||||
issuer: X500Principal,
|
||||
issuerKeyPair: KeyPair,
|
||||
subject: X500Name,
|
||||
subject: X500Principal,
|
||||
subjectPublicKey: PublicKey,
|
||||
validityWindow: Pair<Date, Date>,
|
||||
nameConstraints: NameConstraints? = null): X509CertificateHolder {
|
||||
nameConstraints: NameConstraints? = null): X509Certificate {
|
||||
val signatureScheme = Crypto.findSignatureScheme(issuerKeyPair.private)
|
||||
val provider = Crypto.findProvider(signatureScheme.providerName)
|
||||
val builder = createCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints)
|
||||
|
||||
val signer = ContentSignerBuilder.build(signatureScheme, issuerKeyPair.private, provider)
|
||||
return builder.build(signer).apply {
|
||||
val builder = createPartialCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints)
|
||||
return builder.build(signer).run {
|
||||
require(isValidOn(Date()))
|
||||
require(isSignatureValid(JcaContentVerifierProviderBuilder().build(issuerKeyPair.public)))
|
||||
toJca()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create certificate signing request using provided information.
|
||||
*/
|
||||
private fun createCertificateSigningRequest(subject: CordaX500Name,
|
||||
private fun createCertificateSigningRequest(subject: X500Principal,
|
||||
email: String,
|
||||
keyPair: KeyPair,
|
||||
signatureScheme: SignatureScheme): PKCS10CertificationRequest {
|
||||
val signer = ContentSignerBuilder.build(signatureScheme, keyPair.private, Crypto.findProvider(signatureScheme.providerName))
|
||||
return JcaPKCS10CertificationRequestBuilder(subject.x500Name, keyPair.public).addAttribute(BCStyle.E, DERUTF8String(email)).build(signer)
|
||||
return JcaPKCS10CertificationRequestBuilder(subject, keyPair.public).addAttribute(BCStyle.E, DERUTF8String(email)).build(signer)
|
||||
}
|
||||
|
||||
fun createCertificateSigningRequest(subject: CordaX500Name, email: String, keyPair: KeyPair): PKCS10CertificationRequest {
|
||||
fun createCertificateSigningRequest(subject: X500Principal, email: String, keyPair: KeyPair): PKCS10CertificationRequest {
|
||||
return createCertificateSigningRequest(subject, email, keyPair, DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a [X509Certificate] into Bouncycastle's [X509CertificateHolder].
|
||||
*
|
||||
* NOTE: To avoid unnecessary copying use [X509Certificate] where possible.
|
||||
*/
|
||||
fun X509Certificate.toBc() = X509CertificateHolder(encoded)
|
||||
fun X509CertificateHolder.toJca(): X509Certificate = X509CertificateFactory().generateCertificate(encoded.inputStream())
|
||||
|
||||
/**
|
||||
* Wraps a [CertificateFactory] to remove boilerplate. It's unclear whether [CertificateFactory] is threadsafe so best
|
||||
* so assume this class is not.
|
||||
@ -396,4 +371,4 @@ enum class CertificateType(val keyUsage: KeyUsage, vararg val purposes: KeyPurpo
|
||||
)
|
||||
}
|
||||
|
||||
data class CertificateAndKeyPair(val certificate: X509CertificateHolder, val keyPair: KeyPair)
|
||||
data class CertificateAndKeyPair(val certificate: X509Certificate, val keyPair: KeyPair)
|
||||
|
@ -83,16 +83,20 @@ class NetworkBootstrapper {
|
||||
for (confFile in confFiles) {
|
||||
val nodeName = confFile.fileName.toString().removeSuffix(".conf")
|
||||
println("Generating directory for $nodeName")
|
||||
val nodeDir = (directory / nodeName).createDirectory()
|
||||
confFile.moveTo(nodeDir / "node.conf")
|
||||
Files.copy(cordaJar, (nodeDir / "corda.jar"))
|
||||
val nodeDir = (directory / nodeName)
|
||||
if (!nodeDir.exists()) { nodeDir.createDirectory() }
|
||||
confFile.moveTo(nodeDir / "node.conf", StandardCopyOption.REPLACE_EXISTING)
|
||||
Files.copy(cordaJar, (nodeDir / "corda.jar"), StandardCopyOption.REPLACE_EXISTING)
|
||||
}
|
||||
Files.delete(cordaJar)
|
||||
}
|
||||
|
||||
private fun extractCordaJarTo(directory: Path): Path {
|
||||
val cordaJarPath = (directory / "corda.jar")
|
||||
Thread.currentThread().contextClassLoader.getResourceAsStream("corda.jar").copyTo(cordaJarPath)
|
||||
if (!cordaJarPath.exists()) {
|
||||
println("No corda jar found in root directory. Extracting from jar")
|
||||
Thread.currentThread().contextClassLoader.getResourceAsStream("corda.jar").copyTo(cordaJarPath)
|
||||
}
|
||||
return cordaJarPath
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
package net.corda.nodeapi.internal.serialization.amqp
|
||||
|
||||
import net.corda.core.serialization.ClassWhitelist
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import com.google.common.primitives.Primitives
|
||||
import com.google.common.reflect.TypeToken
|
||||
import net.corda.core.serialization.ClassWhitelist
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.serialization.SerializationContext
|
||||
import org.apache.qpid.proton.codec.Data
|
||||
import java.beans.IndexedPropertyDescriptor
|
||||
@ -81,10 +81,17 @@ private fun <T : Any> propertiesForSerializationFromConstructor(kotlinConstructo
|
||||
val rc: MutableList<PropertySerializer> = ArrayList(kotlinConstructor.parameters.size)
|
||||
for (param in kotlinConstructor.parameters) {
|
||||
val name = param.name ?: throw NotSerializableException("Constructor parameter of $clazz has no name.")
|
||||
|
||||
val matchingProperty = properties[name] ?:
|
||||
throw NotSerializableException("No property matching constructor parameter named '$name' of '$clazz'. " +
|
||||
"If using Java, check that you have the -parameters option specified in the Java compiler. " +
|
||||
"Alternately, provide a proxy serializer (SerializationCustomSerializer) if recompiling isn't an option")
|
||||
try {
|
||||
clazz.getDeclaredField(param.name)
|
||||
throw NotSerializableException("Property '$name' or its getter is non public, this renders class '$clazz' unserializable")
|
||||
} catch (e: NoSuchFieldException) {
|
||||
throw NotSerializableException("No property matching constructor parameter named '$name' of '$clazz'. " +
|
||||
"If using Java, check that you have the -parameters option specified in the Java compiler. " +
|
||||
"Alternately, provide a proxy serializer (SerializationCustomSerializer) if recompiling isn't an option")
|
||||
}
|
||||
|
||||
// Check that the method has a getter in java.
|
||||
val getter = matchingProperty.readMethod ?: throw NotSerializableException("Property has no getter method for $name of $clazz. " +
|
||||
"If using Java and the parameter name looks anonymous, check that you have the -parameters option specified in the Java compiler." +
|
||||
|
@ -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()));
|
||||
}
|
||||
|
||||
}
|
@ -4,10 +4,7 @@ import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.Crypto.EDDSA_ED25519_SHA512
|
||||
import net.corda.core.crypto.Crypto.generateKeyPair
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.toTypedArray
|
||||
import net.corda.core.internal.x500Name
|
||||
import net.corda.core.serialization.SerializationContext
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.serialize
|
||||
@ -23,12 +20,9 @@ import net.corda.testing.BOB_NAME
|
||||
import net.corda.testing.TestIdentity
|
||||
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import org.bouncycastle.asn1.x509.BasicConstraints
|
||||
import org.bouncycastle.asn1.x509.Extension
|
||||
import org.bouncycastle.asn1.x509.KeyUsage
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
@ -42,18 +36,16 @@ import java.security.SecureRandom
|
||||
import java.security.cert.CertPath
|
||||
import java.security.cert.X509Certificate
|
||||
import java.util.*
|
||||
import java.util.stream.Stream
|
||||
import javax.net.ssl.*
|
||||
import javax.security.auth.x500.X500Principal
|
||||
import kotlin.concurrent.thread
|
||||
import kotlin.test.*
|
||||
|
||||
class X509UtilitiesTest {
|
||||
private companion object {
|
||||
val ALICE = TestIdentity(ALICE_NAME, 70).party
|
||||
val bob = TestIdentity(BOB_NAME, 80)
|
||||
val BOB = TestIdentity(BOB_NAME, 80)
|
||||
val MEGA_CORP = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).party
|
||||
val BOB get() = bob.party
|
||||
val BOB_PUBKEY get() = bob.publicKey
|
||||
val CIPHER_SUITES = arrayOf(
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
@ -63,27 +55,30 @@ class X509UtilitiesTest {
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
val tempFolder: TemporaryFolder = TemporaryFolder()
|
||||
val tempFolder = TemporaryFolder()
|
||||
|
||||
@Test
|
||||
fun `create valid self-signed CA certificate`() {
|
||||
val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val caCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Test Cert", organisation = "R3 Ltd", locality = "London", country = "GB"), caKey)
|
||||
assertEquals(X500Name("CN=Test Cert,O=R3 Ltd,L=London,C=GB"), caCert.subject) // using our subject common name
|
||||
assertEquals(caCert.issuer, caCert.subject) //self-signed
|
||||
caCert.isValidOn(Date()) // throws on verification problems
|
||||
caCert.isSignatureValid(JcaContentVerifierProviderBuilder().build(caKey.public)) // throws on verification problems
|
||||
val basicConstraints = BasicConstraints.getInstance(caCert.getExtension(Extension.basicConstraints).parsedValue)
|
||||
val keyUsage = KeyUsage.getInstance(caCert.getExtension(Extension.keyUsage).parsedValue)
|
||||
assertFalse { keyUsage.hasUsages(5) } // Bit 5 == keyCertSign according to ASN.1 spec (see full comment on KeyUsage property)
|
||||
assertNull(basicConstraints.pathLenConstraint) // No length constraint specified on this CA certificate
|
||||
val subject = X500Principal("CN=Test Cert,O=R3 Ltd,L=London,C=GB")
|
||||
val caCert = X509Utilities.createSelfSignedCACertificate(subject, caKey)
|
||||
assertEquals(subject, caCert.subjectX500Principal) // using our subject common name
|
||||
assertEquals(caCert.issuerX500Principal, caCert.subjectX500Principal) //self-signed
|
||||
caCert.checkValidity(Date()) // throws on verification problems
|
||||
caCert.verify(caKey.public) // throws on verification problems
|
||||
caCert.toBc().run {
|
||||
val basicConstraints = BasicConstraints.getInstance(getExtension(Extension.basicConstraints).parsedValue)
|
||||
val keyUsage = KeyUsage.getInstance(getExtension(Extension.keyUsage).parsedValue)
|
||||
assertFalse { keyUsage.hasUsages(5) } // Bit 5 == keyCertSign according to ASN.1 spec (see full comment on KeyUsage property)
|
||||
assertNull(basicConstraints.pathLenConstraint) // No length constraint specified on this CA certificate
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `load and save a PEM file certificate`() {
|
||||
val tmpCertificateFile = tempFile("cacert.pem")
|
||||
val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val caCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Test Cert", organisation = "R3 Ltd", locality = "London", country = "GB"), caKey).cert
|
||||
val caCert = X509Utilities.createSelfSignedCACertificate(X500Principal("CN=Test Cert,O=R3 Ltd,L=London,C=GB"), caKey)
|
||||
X509Utilities.saveCertificateAsPEMFile(caCert, tmpCertificateFile)
|
||||
val readCertificate = X509Utilities.loadCertificateFromPEMFile(tmpCertificateFile)
|
||||
assertEquals(caCert, readCertificate)
|
||||
@ -92,18 +87,20 @@ class X509UtilitiesTest {
|
||||
@Test
|
||||
fun `create valid server certificate chain`() {
|
||||
val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val caCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Test CA Cert", organisation = "R3 Ltd", locality = "London", country = "GB"), caKey)
|
||||
val subject = CordaX500Name(commonName = "Server Cert", organisation = "R3 Ltd", locality = "London", country = "GB")
|
||||
val caCert = X509Utilities.createSelfSignedCACertificate(X500Principal("CN=Test CA Cert,O=R3 Ltd,L=London,C=GB"), caKey)
|
||||
val subject = X500Principal("CN=Server Cert,O=R3 Ltd,L=London,C=GB")
|
||||
val keyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val serverCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, caKey, subject, keyPair.public)
|
||||
assertEquals(X500Name("C=GB,L=London,O=R3 Ltd,CN=Server Cert"), serverCert.subject) // using our subject common name
|
||||
assertEquals(caCert.issuer, serverCert.issuer) // Issued by our CA cert
|
||||
serverCert.isValidOn(Date()) // throws on verification problems
|
||||
serverCert.isSignatureValid(JcaContentVerifierProviderBuilder().build(caKey.public)) // throws on verification problems
|
||||
val basicConstraints = BasicConstraints.getInstance(serverCert.getExtension(Extension.basicConstraints).parsedValue)
|
||||
val keyUsage = KeyUsage.getInstance(serverCert.getExtension(Extension.keyUsage).parsedValue)
|
||||
assertFalse { keyUsage.hasUsages(5) } // Bit 5 == keyCertSign according to ASN.1 spec (see full comment on KeyUsage property)
|
||||
assertNull(basicConstraints.pathLenConstraint) // Non-CA certificate
|
||||
assertEquals(subject, serverCert.subjectX500Principal) // using our subject common name
|
||||
assertEquals(caCert.issuerX500Principal, serverCert.issuerX500Principal) // Issued by our CA cert
|
||||
serverCert.checkValidity(Date()) // throws on verification problems
|
||||
serverCert.verify(caKey.public) // throws on verification problems
|
||||
serverCert.toBc().run {
|
||||
val basicConstraints = BasicConstraints.getInstance(getExtension(Extension.basicConstraints).parsedValue)
|
||||
val keyUsage = KeyUsage.getInstance(getExtension(Extension.keyUsage).parsedValue)
|
||||
assertFalse { keyUsage.hasUsages(5) } // Bit 5 == keyCertSign according to ASN.1 spec (see full comment on KeyUsage property)
|
||||
assertNull(basicConstraints.pathLenConstraint) // Non-CA certificate
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -111,15 +108,14 @@ class X509UtilitiesTest {
|
||||
val tmpKeyStore = tempFile("keystore.jks")
|
||||
|
||||
val keyPair = generateKeyPair(EDDSA_ED25519_SHA512)
|
||||
val testName = CordaX500Name(commonName = "Test", organisation = "R3 Ltd", locality = "London", country = "GB")
|
||||
val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB")
|
||||
val selfSignCert = X509Utilities.createSelfSignedCACertificate(testName, keyPair)
|
||||
|
||||
assertTrue(Arrays.equals(selfSignCert.subjectPublicKeyInfo.encoded, keyPair.public.encoded))
|
||||
assertTrue(Arrays.equals(selfSignCert.publicKey.encoded, keyPair.public.encoded))
|
||||
|
||||
// Save the EdDSA private key with self sign cert in the keystore.
|
||||
val keyStore = loadOrCreateKeyStore(tmpKeyStore, "keystorepass")
|
||||
keyStore.setKeyEntry("Key", keyPair.private, "password".toCharArray(),
|
||||
Stream.of(selfSignCert).map { it.cert }.toTypedArray())
|
||||
keyStore.setKeyEntry("Key", keyPair.private, "password".toCharArray(), arrayOf(selfSignCert))
|
||||
keyStore.save(tmpKeyStore, "keystorepass")
|
||||
|
||||
// Load the keystore from file and make sure keys are intact.
|
||||
@ -137,15 +133,14 @@ class X509UtilitiesTest {
|
||||
fun `signing EdDSA key with EcDSA certificate`() {
|
||||
val tmpKeyStore = tempFile("keystore.jks")
|
||||
val ecDSAKey = generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
|
||||
val testName = CordaX500Name(commonName = "Test", organisation = "R3 Ltd", locality = "London", country = "GB")
|
||||
val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB")
|
||||
val ecDSACert = X509Utilities.createSelfSignedCACertificate(testName, ecDSAKey)
|
||||
val edDSAKeypair = generateKeyPair(EDDSA_ED25519_SHA512)
|
||||
val edDSACert = X509Utilities.createCertificate(CertificateType.TLS, ecDSACert, ecDSAKey, X500Name("CN=TestEdDSA"), edDSAKeypair.public)
|
||||
val edDSACert = X509Utilities.createCertificate(CertificateType.TLS, ecDSACert, ecDSAKey, BOB.name.x500Principal, edDSAKeypair.public)
|
||||
|
||||
// Save the EdDSA private key with cert chains.
|
||||
val keyStore = loadOrCreateKeyStore(tmpKeyStore, "keystorepass")
|
||||
keyStore.setKeyEntry("Key", edDSAKeypair.private, "password".toCharArray(),
|
||||
Stream.of(ecDSACert, edDSACert).map { it.cert }.toTypedArray())
|
||||
keyStore.setKeyEntry("Key", edDSAKeypair.private, "password".toCharArray(), arrayOf(ecDSACert, edDSACert))
|
||||
keyStore.save(tmpKeyStore, "keystorepass")
|
||||
|
||||
// Load the keystore from file and make sure keys are intact.
|
||||
@ -179,23 +174,22 @@ class X509UtilitiesTest {
|
||||
val serverKeyStore = loadKeyStore(sslConfig.nodeKeystore, sslConfig.keyStorePassword)
|
||||
val (serverCert, serverKeyPair) = serverKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, sslConfig.keyStorePassword)
|
||||
|
||||
serverCert.cert.checkValidity()
|
||||
serverCert.cert.verify(intermediateCa.certificate.cert.publicKey)
|
||||
assertThat(CordaX500Name.parse(serverCert.subject.toString())).isEqualTo(MEGA_CORP.name)
|
||||
serverCert.checkValidity()
|
||||
serverCert.verify(intermediateCa.certificate.publicKey)
|
||||
assertThat(CordaX500Name.build(serverCert.subjectX500Principal)).isEqualTo(MEGA_CORP.name)
|
||||
|
||||
// Load back SSL certificate
|
||||
val sslKeyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword)
|
||||
val (sslCert) = sslKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_TLS, sslConfig.keyStorePassword)
|
||||
|
||||
sslCert.cert.checkValidity()
|
||||
sslCert.cert.verify(serverCert.cert.publicKey)
|
||||
assertThat(CordaX500Name.parse(sslCert.subject.toString())).isEqualTo(MEGA_CORP.name)
|
||||
sslCert.checkValidity()
|
||||
sslCert.verify(serverCert.publicKey)
|
||||
assertThat(CordaX500Name.build(sslCert.subjectX500Principal)).isEqualTo(MEGA_CORP.name)
|
||||
|
||||
// Now sign something with private key and verify against certificate public key
|
||||
val testData = "123456".toByteArray()
|
||||
val signature = Crypto.doSign(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME, serverKeyPair.private, testData)
|
||||
val publicKey = Crypto.toSupportedPublicKey(serverCert.subjectPublicKeyInfo)
|
||||
assertTrue { Crypto.isValid(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME, publicKey, signature, testData) }
|
||||
assertTrue { Crypto.isValid(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME, serverCert.publicKey, signature, testData) }
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -210,7 +204,7 @@ class X509UtilitiesTest {
|
||||
|
||||
// Generate server cert and private key and populate another keystore suitable for SSL
|
||||
sslConfig.createDevKeyStores(rootCa.certificate, intermediateCa, MEGA_CORP.name)
|
||||
sslConfig.createTrustStore(rootCa.certificate.cert)
|
||||
sslConfig.createTrustStore(rootCa.certificate)
|
||||
|
||||
val keyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword)
|
||||
val trustStore = loadKeyStore(sslConfig.trustStoreFile, sslConfig.trustStorePassword)
|
||||
@ -303,10 +297,10 @@ class X509UtilitiesTest {
|
||||
@Test
|
||||
fun `get correct private key type from Keystore`() {
|
||||
val keyPair = generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
|
||||
val testName = CordaX500Name(commonName = "Test", organisation = "R3 Ltd", locality = "London", country = "GB")
|
||||
val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB")
|
||||
val selfSignCert = X509Utilities.createSelfSignedCACertificate(testName, keyPair)
|
||||
val keyStore = loadOrCreateKeyStore(tempFile("testKeystore.jks"), "keystorepassword")
|
||||
keyStore.setKeyEntry("Key", keyPair.private, "keypassword".toCharArray(), arrayOf(selfSignCert.cert))
|
||||
keyStore.setKeyEntry("Key", keyPair.private, "keypassword".toCharArray(), arrayOf(selfSignCert))
|
||||
|
||||
val keyFromKeystore = keyStore.getKey("Key", "keypassword".toCharArray())
|
||||
val keyFromKeystoreCasted = keyStore.getSupportedKey("Key", "keypassword")
|
||||
@ -316,7 +310,7 @@ class X509UtilitiesTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `serialize - deserialize X509CertififcateHolder`() {
|
||||
fun `serialize - deserialize X509Certififcate`() {
|
||||
val factory = SerializationFactoryImpl().apply { registerScheme(KryoServerSerializationScheme()) }
|
||||
val context = SerializationContextImpl(KryoHeaderV0_1,
|
||||
javaClass.classLoader,
|
||||
@ -324,9 +318,9 @@ class X509UtilitiesTest {
|
||||
emptyMap(),
|
||||
true,
|
||||
SerializationContext.UseCase.P2P)
|
||||
val expected: X509CertificateHolder = X509Utilities.createSelfSignedCACertificate(ALICE.name, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
|
||||
val expected = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Principal, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
|
||||
val serialized = expected.serialize(factory, context).bytes
|
||||
val actual: X509CertificateHolder = serialized.deserialize(factory, context)
|
||||
val actual = serialized.deserialize<X509Certificate>(factory, context)
|
||||
assertEquals(expected, actual)
|
||||
}
|
||||
|
||||
@ -340,9 +334,9 @@ class X509UtilitiesTest {
|
||||
true,
|
||||
SerializationContext.UseCase.P2P)
|
||||
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val rootCACert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootCAKey)
|
||||
val certificate = X509Utilities.createCertificate(CertificateType.TLS, rootCACert, rootCAKey, BOB.name.x500Name, BOB_PUBKEY)
|
||||
val expected = X509CertificateFactory().generateCertPath(certificate.cert, rootCACert.cert)
|
||||
val rootCACert = X509Utilities.createSelfSignedCACertificate(ALICE_NAME.x500Principal, rootCAKey)
|
||||
val certificate = X509Utilities.createCertificate(CertificateType.TLS, rootCACert, rootCAKey, BOB_NAME.x500Principal, BOB.publicKey)
|
||||
val expected = X509CertificateFactory().generateCertPath(certificate, rootCACert)
|
||||
val serialized = expected.serialize(factory, context).bytes
|
||||
val actual: CertPath = serialized.deserialize(factory, context)
|
||||
assertEquals(expected, actual)
|
||||
|
@ -0,0 +1,69 @@
|
||||
package net.corda.nodeapi.internal.serialization.amqp
|
||||
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.junit.Test
|
||||
import java.io.NotSerializableException
|
||||
|
||||
class ErrorMessagesTests {
|
||||
companion object {
|
||||
val VERBOSE get() = false
|
||||
}
|
||||
|
||||
private fun errMsg(property:String, testname: String) =
|
||||
"Property '$property' or 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))
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package net.corda.node.services
|
||||
package net.corda.node
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.client.rpc.CordaRPCClient
|
||||
@ -11,33 +11,94 @@ import net.corda.core.messaging.startFlow
|
||||
import net.corda.finance.flows.CashIssueFlow
|
||||
import net.corda.node.internal.Node
|
||||
import net.corda.node.internal.StartedNode
|
||||
import net.corda.node.services.config.AuthDataSourceType
|
||||
import net.corda.node.services.Permissions
|
||||
import net.corda.node.services.config.PasswordEncryption
|
||||
import net.corda.node.services.config.SecurityConfiguration
|
||||
import net.corda.nodeapi.internal.config.User
|
||||
import net.corda.nodeapi.internal.config.toConfig
|
||||
import net.corda.testing.ALICE_NAME
|
||||
import net.corda.testing.IntegrationTestSchemas
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.internal.toDatabaseSchemaName
|
||||
import net.corda.testing.node.internal.NodeBasedTest
|
||||
import net.corda.testing.toDatabaseSchemaName
|
||||
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
|
||||
import org.apache.shiro.authc.credential.DefaultPasswordService
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.Parameterized
|
||||
import java.sql.DriverManager
|
||||
import java.sql.Statement
|
||||
import java.util.*
|
||||
import kotlin.test.assertFailsWith
|
||||
|
||||
abstract class UserAuthServiceTest : NodeBasedTest() {
|
||||
/*
|
||||
* Starts Node's instance configured to load clients credentials and permissions from an external DB, then
|
||||
* check authentication/authorization of RPC connections.
|
||||
*/
|
||||
@RunWith(Parameterized::class)
|
||||
class AuthDBTests : NodeBasedTest() {
|
||||
companion object {
|
||||
@ClassRule @JvmField
|
||||
val databaseSchemas = IntegrationTestSchemas(ALICE_NAME.toDatabaseSchemaName())
|
||||
|
||||
private val cacheExpireAfterSecs: Long = 1
|
||||
|
||||
@JvmStatic
|
||||
@Parameterized.Parameters(name = "password encryption format = {0}")
|
||||
fun encFormats() = arrayOf(PasswordEncryption.NONE, PasswordEncryption.SHIRO_1_CRYPT)
|
||||
}
|
||||
|
||||
protected lateinit var node: StartedNode<Node>
|
||||
protected lateinit var client: CordaRPCClient
|
||||
private lateinit var node: StartedNode<Node>
|
||||
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
|
||||
fun `login with correct credentials`() {
|
||||
@ -65,7 +126,7 @@ abstract class UserAuthServiceTest : NodeBasedTest() {
|
||||
val proxy = it.proxy
|
||||
proxy.startFlowDynamic(DummyFlow::class.java)
|
||||
proxy.startTrackedFlowDynamic(DummyFlow::class.java)
|
||||
proxy.startFlow(::DummyFlow)
|
||||
proxy.startFlow(AuthDBTests::DummyFlow)
|
||||
assertFailsWith(
|
||||
PermissionException::class,
|
||||
"This user should not be authorized to start flow `CashIssueFlow`") {
|
||||
@ -92,77 +153,8 @@ abstract class UserAuthServiceTest : NodeBasedTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@StartableByRPC
|
||||
@InitiatingFlow
|
||||
class DummyFlow : FlowLogic<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
|
||||
fun `Add new users on-the-fly`() {
|
||||
fun `Add new users dynamically`() {
|
||||
assertFailsWith(
|
||||
ActiveMQSecurityException::class,
|
||||
"Login with incorrect password should fail") {
|
||||
@ -171,7 +163,7 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() {
|
||||
|
||||
db.insert(UserAndRoles(
|
||||
username = "user2",
|
||||
password = "bar",
|
||||
password = encodePassword("bar"),
|
||||
roles = listOf("default")))
|
||||
|
||||
client.start("user2", "bar")
|
||||
@ -181,10 +173,9 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() {
|
||||
fun `Modify user permissions during RPC session`() {
|
||||
db.insert(UserAndRoles(
|
||||
username = "user3",
|
||||
password = "bar",
|
||||
password = encodePassword("bar"),
|
||||
roles = emptyList()))
|
||||
|
||||
|
||||
client.start("user3", "bar").use {
|
||||
val proxy = it.proxy
|
||||
assertFailsWith(
|
||||
@ -193,6 +184,7 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() {
|
||||
proxy.stateMachinesFeed()
|
||||
}
|
||||
db.addRoleToUser("user3", "default")
|
||||
Thread.sleep(1500)
|
||||
proxy.stateMachinesFeed()
|
||||
}
|
||||
}
|
||||
@ -201,13 +193,14 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() {
|
||||
fun `Revoke user permissions during RPC session`() {
|
||||
db.insert(UserAndRoles(
|
||||
username = "user4",
|
||||
password = "test",
|
||||
password = encodePassword("test"),
|
||||
roles = listOf("default")))
|
||||
|
||||
client.start("user4", "test").use {
|
||||
val proxy = it.proxy
|
||||
proxy.stateMachinesFeed()
|
||||
db.deleteUser("user4")
|
||||
Thread.sleep(1500)
|
||||
assertFailsWith(
|
||||
PermissionException::class,
|
||||
"This user should not be authorized to call 'nodeInfo'") {
|
||||
@ -216,15 +209,27 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@StartableByRPC
|
||||
@InitiatingFlow
|
||||
class DummyFlow : FlowLogic<Unit>() {
|
||||
@Suspendable
|
||||
override fun call() = Unit
|
||||
}
|
||||
|
||||
@After
|
||||
override fun tearDown() {
|
||||
db.close()
|
||||
db.close()
|
||||
}
|
||||
|
||||
private fun encodePassword(s: String) = encodePassword(s, passwordEncryption)
|
||||
}
|
||||
|
||||
private data class UserAndRoles(val username: String, val password: String, val roles: List<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 {
|
||||
|
||||
val jdbcUrl: String
|
||||
@ -261,12 +266,6 @@ private class UsersDB : AutoCloseable {
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteRole(role: String) {
|
||||
session {
|
||||
it.execute("DELETE FROM role_permissions WHERE role_name = '$role'")
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteUser(username: String) {
|
||||
session {
|
||||
it.execute("DELETE FROM users WHERE username = '$username'")
|
||||
@ -307,4 +306,22 @@ private class UsersDB : AutoCloseable {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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())
|
||||
}
|
@ -12,6 +12,9 @@ import net.corda.nodeapi.internal.config.User
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.common.internal.ProjectStructure.projectRootDir
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.internal.IntegrationTest
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.internal.toDatabaseSchemaNames
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.ClassRule
|
||||
@ -24,7 +27,7 @@ class BootTests : IntegrationTest() {
|
||||
companion object {
|
||||
@ClassRule @JvmField
|
||||
val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, DUMMY_BANK_A_NAME)
|
||||
.map { it.toDatabaseSchemaNames("", "_10000","_10003") }.flatten().toTypedArray())
|
||||
.map { it.toDatabaseSchemaNames("", "_10000", "_10003") }.flatten().toTypedArray())
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -11,6 +11,9 @@ import net.corda.node.services.Permissions.Companion.startFlow
|
||||
import net.corda.nodeapi.internal.config.User
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.internal.IntegrationTest
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.internal.toDatabaseSchemaName
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Test
|
||||
|
@ -1,22 +1,21 @@
|
||||
package net.corda.node
|
||||
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.node.services.config.configureDevKeyAndTrustStores
|
||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
||||
import net.corda.nodeapi.internal.crypto.*
|
||||
import net.corda.testing.ALICE_NAME
|
||||
import net.corda.testing.IntegrationTest
|
||||
import net.corda.testing.IntegrationTestSchemas
|
||||
import net.corda.testing.internal.IntegrationTest
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.toDatabaseSchemaName
|
||||
import net.corda.testing.internal.toDatabaseSchemaName
|
||||
import org.junit.ClassRule
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.Test
|
||||
import java.nio.file.Path
|
||||
import javax.security.auth.x500.X500Principal
|
||||
|
||||
class NodeKeystoreCheckTest : IntegrationTest() {
|
||||
companion object {
|
||||
@ -59,10 +58,10 @@ class NodeKeystoreCheckTest : IntegrationTest() {
|
||||
|
||||
// Self signed root
|
||||
val badRootKeyPair = Crypto.generateKeyPair()
|
||||
val badRoot = X509Utilities.createSelfSignedCACertificate(CordaX500Name("Bad Root", "Lodnon", "GB"), badRootKeyPair)
|
||||
val badRoot = X509Utilities.createSelfSignedCACertificate(X500Principal("O=Bad Root,L=Lodnon,C=GB"), badRootKeyPair)
|
||||
val nodeCA = keystore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, config.keyStorePassword)
|
||||
val badNodeCACert = X509Utilities.createCertificate(CertificateType.NODE_CA, badRoot, badRootKeyPair, ALICE_NAME, nodeCA.keyPair.public)
|
||||
keystore.setKeyEntry(X509Utilities.CORDA_CLIENT_CA, nodeCA.keyPair.private, config.keyStorePassword.toCharArray(), arrayOf(badNodeCACert.cert, badRoot.cert))
|
||||
val badNodeCACert = X509Utilities.createCertificate(CertificateType.NODE_CA, badRoot, badRootKeyPair, ALICE_NAME.x500Principal, nodeCA.keyPair.public)
|
||||
keystore.setKeyEntry(X509Utilities.CORDA_CLIENT_CA, nodeCA.keyPair.private, config.keyStorePassword.toCharArray(), arrayOf(badNodeCACert, badRoot))
|
||||
keystore.save(config.nodeKeystore, config.keyStorePassword)
|
||||
|
||||
assertThatThrownBy {
|
||||
|
@ -18,7 +18,11 @@ import net.corda.testing.*
|
||||
import net.corda.testing.driver.NodeHandle
|
||||
import net.corda.testing.driver.PortAllocation
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.internal.IntegrationTest
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.internal.performance.div
|
||||
import net.corda.testing.internal.toDatabaseSchemaName
|
||||
import net.corda.testing.internal.toDatabaseSchemaNames
|
||||
import net.corda.testing.node.NotarySpec
|
||||
import net.corda.testing.node.internal.InternalDriverDSL
|
||||
import net.corda.testing.node.internal.performance.startPublishingFixedRateInjector
|
||||
|
@ -3,6 +3,9 @@ package net.corda.node
|
||||
import com.google.common.base.Stopwatch
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.internal.IntegrationTest
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.internal.toDatabaseSchemaName
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
|
@ -14,13 +14,14 @@ import net.corda.core.utilities.unwrap
|
||||
import net.corda.node.services.Permissions.Companion.startFlow
|
||||
import net.corda.nodeapi.internal.config.User
|
||||
import net.corda.testing.ALICE_NAME
|
||||
import net.corda.testing.IntegrationTest
|
||||
import net.corda.testing.IntegrationTestSchemas
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.toDatabaseSchemaName
|
||||
import net.corda.testing.internal.IntegrationTest
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.internal.toDatabaseSchemaName
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.bouncycastle.util.io.Streams
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
import java.net.ConnectException
|
||||
import java.util.regex.Pattern
|
||||
@ -33,6 +34,7 @@ class SSHServerTest : IntegrationTest() {
|
||||
val databaseSchemas = IntegrationTestSchemas(ALICE_NAME.toDatabaseSchemaName())
|
||||
}
|
||||
|
||||
@Ignore("Test has undeterministic capacity to hang, ignore till fixed")
|
||||
@Test()
|
||||
fun `ssh server does not start be default`() {
|
||||
val user = User("u", "p", setOf())
|
||||
@ -54,6 +56,7 @@ class SSHServerTest : IntegrationTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore("Test has undeterministic capacity to hang, ignore till fixed")
|
||||
@Test
|
||||
fun `ssh server starts when configured`() {
|
||||
val user = User("u", "p", setOf())
|
||||
@ -74,6 +77,7 @@ class SSHServerTest : IntegrationTest() {
|
||||
}
|
||||
|
||||
|
||||
@Ignore("Test has undeterministic capacity to hang, ignore till fixed")
|
||||
@Test
|
||||
fun `ssh server verify credentials`() {
|
||||
val user = User("u", "p", setOf())
|
||||
@ -97,6 +101,7 @@ class SSHServerTest : IntegrationTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore("Test has undeterministic capacity to hang, ignore till fixed")
|
||||
@Test
|
||||
fun `ssh respects permissions`() {
|
||||
val user = User("u", "p", setOf(startFlow<FlowICanRun>()))
|
||||
@ -127,6 +132,7 @@ class SSHServerTest : IntegrationTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore("Test has undeterministic capacity to hang, ignore till fixed")
|
||||
@Test
|
||||
fun `ssh runs flows`() {
|
||||
val user = User("u", "p", setOf(startFlow<FlowICanRun>()))
|
||||
|
@ -59,10 +59,10 @@ class ProtonWrapperTests {
|
||||
amqpClient.start()
|
||||
val serverConnect = serverConnected.get()
|
||||
assertEquals(true, serverConnect.connected)
|
||||
assertEquals(BOB_NAME, CordaX500Name.parse(serverConnect.remoteCert!!.subject.toString()))
|
||||
assertEquals(BOB_NAME, CordaX500Name.build(serverConnect.remoteCert!!.subjectX500Principal))
|
||||
val clientConnect = clientConnected.get()
|
||||
assertEquals(true, clientConnect.connected)
|
||||
assertEquals(ALICE_NAME, CordaX500Name.parse(clientConnect.remoteCert!!.subject.toString()))
|
||||
assertEquals(ALICE_NAME, CordaX500Name.build(clientConnect.remoteCert!!.subjectX500Principal))
|
||||
val msg = amqpClient.createMessage("Test".toByteArray(),
|
||||
"p2p.inbound",
|
||||
ALICE_NAME.toString(),
|
||||
@ -102,10 +102,10 @@ class ProtonWrapperTests {
|
||||
amqpClient.start()
|
||||
val serverConn1 = serverConnected.get()
|
||||
assertEquals(true, serverConn1.connected)
|
||||
assertEquals(BOB_NAME, CordaX500Name.parse(serverConn1.remoteCert!!.subject.toString()))
|
||||
assertEquals(BOB_NAME, CordaX500Name.build(serverConn1.remoteCert!!.subjectX500Principal))
|
||||
val connState1 = clientConnected.next()
|
||||
assertEquals(true, connState1.connected)
|
||||
assertEquals(ALICE_NAME, CordaX500Name.parse(connState1.remoteCert!!.subject.toString()))
|
||||
assertEquals(ALICE_NAME, CordaX500Name.build(connState1.remoteCert!!.subjectX500Principal))
|
||||
assertEquals(serverPort, connState1.remoteAddress.port)
|
||||
|
||||
// Fail over
|
||||
@ -116,10 +116,10 @@ class ProtonWrapperTests {
|
||||
assertEquals(serverPort, connState2.remoteAddress.port)
|
||||
val serverConn2 = serverConnected2.get()
|
||||
assertEquals(true, serverConn2.connected)
|
||||
assertEquals(BOB_NAME, CordaX500Name.parse(serverConn2.remoteCert!!.subject.toString()))
|
||||
assertEquals(BOB_NAME, CordaX500Name.build(serverConn2.remoteCert!!.subjectX500Principal))
|
||||
val connState3 = clientConnected.next()
|
||||
assertEquals(true, connState3.connected)
|
||||
assertEquals(ALICE_NAME, CordaX500Name.parse(connState3.remoteCert!!.subject.toString()))
|
||||
assertEquals(ALICE_NAME, CordaX500Name.build(connState3.remoteCert!!.subjectX500Principal))
|
||||
assertEquals(serverPort2, connState3.remoteAddress.port)
|
||||
|
||||
// Fail back
|
||||
@ -130,10 +130,10 @@ class ProtonWrapperTests {
|
||||
assertEquals(serverPort2, connState4.remoteAddress.port)
|
||||
val serverConn3 = serverConnected.get()
|
||||
assertEquals(true, serverConn3.connected)
|
||||
assertEquals(BOB_NAME, CordaX500Name.parse(serverConn3.remoteCert!!.subject.toString()))
|
||||
assertEquals(BOB_NAME, CordaX500Name.build(serverConn3.remoteCert!!.subjectX500Principal))
|
||||
val connState5 = clientConnected.next()
|
||||
assertEquals(true, connState5.connected)
|
||||
assertEquals(ALICE_NAME, CordaX500Name.parse(connState5.remoteCert!!.subject.toString()))
|
||||
assertEquals(ALICE_NAME, CordaX500Name.build(connState5.remoteCert!!.subjectX500Principal))
|
||||
assertEquals(serverPort, connState5.remoteAddress.port)
|
||||
} finally {
|
||||
amqpClient.close()
|
||||
@ -149,7 +149,7 @@ class ProtonWrapperTests {
|
||||
val clientConnected = amqpClient.onConnection.toFuture()
|
||||
amqpClient.start()
|
||||
assertEquals(true, clientConnected.get().connected)
|
||||
assertEquals(CHARLIE_NAME, CordaX500Name.parse(clientConnected.get().remoteCert!!.subject.toString()))
|
||||
assertEquals(CHARLIE_NAME, CordaX500Name.build(clientConnected.get().remoteCert!!.subjectX500Principal))
|
||||
val artemis = artemisClient.started!!
|
||||
val sendAddress = "p2p.inbound"
|
||||
artemis.session.createQueue(sendAddress, RoutingType.MULTICAST, "queue", true)
|
||||
@ -180,13 +180,13 @@ class ProtonWrapperTests {
|
||||
amqpClient1.start()
|
||||
val connection1 = connectionEvents.next()
|
||||
assertEquals(true, connection1.connected)
|
||||
val connection1ID = CordaX500Name.parse(connection1.remoteCert!!.subject.toString())
|
||||
val connection1ID = CordaX500Name.build(connection1.remoteCert!!.subjectX500Principal)
|
||||
assertEquals("client 0", connection1ID.organisationUnit)
|
||||
val source1 = connection1.remoteAddress
|
||||
amqpClient2.start()
|
||||
val connection2 = connectionEvents.next()
|
||||
assertEquals(true, connection2.connected)
|
||||
val connection2ID = CordaX500Name.parse(connection2.remoteCert!!.subject.toString())
|
||||
val connection2ID = CordaX500Name.build(connection2.remoteCert!!.subjectX500Principal)
|
||||
assertEquals("client 1", connection2ID.organisationUnit)
|
||||
val source2 = connection2.remoteAddress
|
||||
// Stopping one shouldn't disconnect the other
|
||||
@ -207,7 +207,7 @@ class ProtonWrapperTests {
|
||||
amqpClient1.start()
|
||||
val connection5 = connectionEvents.next()
|
||||
assertEquals(true, connection5.connected)
|
||||
val connection5ID = CordaX500Name.parse(connection5.remoteCert!!.subject.toString())
|
||||
val connection5ID = CordaX500Name.build(connection5.remoteCert!!.subjectX500Principal)
|
||||
assertEquals("client 0", connection5ID.organisationUnit)
|
||||
assertEquals(true, amqpClient1.connected)
|
||||
assertEquals(false, amqpClient2.connected)
|
||||
@ -252,7 +252,8 @@ class ProtonWrapperTests {
|
||||
|
||||
val clientTruststore = loadKeyStore(clientConfig.trustStoreFile, clientConfig.trustStorePassword)
|
||||
val clientKeystore = loadKeyStore(clientConfig.sslKeystore, clientConfig.keyStorePassword)
|
||||
val amqpClient = AMQPClient(listOf(NetworkHostAndPort("localhost", serverPort),
|
||||
return AMQPClient(
|
||||
listOf(NetworkHostAndPort("localhost", serverPort),
|
||||
NetworkHostAndPort("localhost", serverPort2),
|
||||
NetworkHostAndPort("localhost", artemisPort)),
|
||||
setOf(ALICE_NAME, CHARLIE_NAME),
|
||||
@ -261,7 +262,6 @@ class ProtonWrapperTests {
|
||||
clientKeystore,
|
||||
clientConfig.keyStorePassword,
|
||||
clientTruststore, true)
|
||||
return amqpClient
|
||||
}
|
||||
|
||||
private fun createSharedThreadsClient(sharedEventGroup: EventLoopGroup, id: Int): AMQPClient {
|
||||
@ -275,14 +275,14 @@ class ProtonWrapperTests {
|
||||
|
||||
val clientTruststore = loadKeyStore(clientConfig.trustStoreFile, clientConfig.trustStorePassword)
|
||||
val clientKeystore = loadKeyStore(clientConfig.sslKeystore, clientConfig.keyStorePassword)
|
||||
val amqpClient = AMQPClient(listOf(NetworkHostAndPort("localhost", serverPort)),
|
||||
return AMQPClient(
|
||||
listOf(NetworkHostAndPort("localhost", serverPort)),
|
||||
setOf(ALICE_NAME),
|
||||
PEER_USER,
|
||||
PEER_USER,
|
||||
clientKeystore,
|
||||
clientConfig.keyStorePassword,
|
||||
clientTruststore, true, sharedEventGroup)
|
||||
return amqpClient
|
||||
}
|
||||
|
||||
private fun createServer(port: Int, name: CordaX500Name = ALICE_NAME): AMQPServer {
|
||||
@ -296,14 +296,13 @@ class ProtonWrapperTests {
|
||||
|
||||
val serverTruststore = loadKeyStore(serverConfig.trustStoreFile, serverConfig.trustStorePassword)
|
||||
val serverKeystore = loadKeyStore(serverConfig.sslKeystore, serverConfig.keyStorePassword)
|
||||
val amqpServer = AMQPServer("0.0.0.0",
|
||||
return AMQPServer(
|
||||
"0.0.0.0",
|
||||
port,
|
||||
PEER_USER,
|
||||
PEER_USER,
|
||||
serverKeystore,
|
||||
serverConfig.keyStorePassword,
|
||||
serverTruststore)
|
||||
return amqpServer
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -24,8 +24,7 @@ import net.corda.testing.*
|
||||
import net.corda.testing.driver.DriverDSL
|
||||
import net.corda.testing.driver.NodeHandle
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
import net.corda.testing.internal.withoutTestSerialization
|
||||
import net.corda.testing.internal.*
|
||||
import net.corda.testing.services.MockAttachmentStorage
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.ClassRule
|
||||
|
@ -26,8 +26,8 @@ import net.corda.node.services.transactions.minCorrectReplicas
|
||||
import net.corda.nodeapi.internal.DevIdentityGenerator
|
||||
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||
import net.corda.nodeapi.internal.network.NotaryInfo
|
||||
import net.corda.testing.IntegrationTest
|
||||
import net.corda.testing.IntegrationTestSchemas
|
||||
import net.corda.testing.internal.IntegrationTest
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.chooseIdentity
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
|
@ -11,13 +11,16 @@ import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.finance.POUNDS
|
||||
import net.corda.finance.flows.CashIssueFlow
|
||||
import net.corda.finance.flows.CashPaymentFlow
|
||||
import net.corda.finance.schemas.CashSchemaV1
|
||||
import net.corda.node.services.Permissions.Companion.invokeRpc
|
||||
import net.corda.node.services.Permissions.Companion.startFlow
|
||||
import net.corda.nodeapi.internal.config.User
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.driver.NodeHandle
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.internal.IntegrationTest
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.internal.toDatabaseSchemaName
|
||||
import net.corda.testing.internal.toDatabaseSchemaNames
|
||||
import net.corda.testing.node.ClusterSpec
|
||||
import net.corda.testing.node.NotarySpec
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
|
@ -20,6 +20,8 @@ import net.corda.nodeapi.internal.network.NotaryInfo
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.internal.IntegrationTest
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.node.MockNodeParameters
|
||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||
|
@ -15,6 +15,9 @@ import net.corda.testing.*
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.driver.NodeHandle
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.internal.IntegrationTest
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.internal.toDatabaseSchemaName
|
||||
import net.corda.testing.node.ClusterSpec
|
||||
import net.corda.testing.node.NotarySpec
|
||||
import net.corda.testing.node.startFlow
|
||||
@ -27,7 +30,7 @@ import kotlin.test.assertFailsWith
|
||||
class RaftNotaryServiceTests : IntegrationTest() {
|
||||
companion object {
|
||||
@ClassRule @JvmField
|
||||
val databaseSchemas = IntegrationTestSchemas( "RAFTNotaryService_0", "RAFTNotaryService_1", "RAFTNotaryService_2",
|
||||
val databaseSchemas = IntegrationTestSchemas("RAFTNotaryService_0", "RAFTNotaryService_1", "RAFTNotaryService_2",
|
||||
DUMMY_BANK_A_NAME.toDatabaseSchemaName())
|
||||
}
|
||||
private val notaryName = CordaX500Name("RAFT Notary Service", "London", "GB")
|
||||
|
@ -21,6 +21,9 @@ import net.corda.testing.SerializationEnvironmentRule
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.driver.NodeHandle
|
||||
import net.corda.testing.driver.PortAllocation
|
||||
import net.corda.testing.internal.IntegrationTest
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.internal.toDatabaseSchemaName
|
||||
import net.corda.testing.node.internal.CompatibilityZoneParams
|
||||
import net.corda.testing.node.internal.internalDriver
|
||||
import net.corda.testing.node.internal.network.NetworkMapServer
|
||||
|
@ -7,6 +7,8 @@ import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.node.internal.Node
|
||||
import net.corda.node.internal.StartedNode
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.internal.toDatabaseSchemaName
|
||||
import net.corda.testing.node.internal.NodeBasedTest
|
||||
import org.junit.Before
|
||||
import org.junit.ClassRule
|
||||
|
@ -8,6 +8,8 @@ import net.corda.core.identity.Party
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.unwrap
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.internal.toDatabaseSchemaName
|
||||
import net.corda.testing.node.internal.NodeBasedTest
|
||||
import net.corda.testing.node.startFlow
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
|
@ -13,6 +13,9 @@ import net.corda.testing.*
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.contracts.DummyState
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.internal.IntegrationTest
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.internal.toDatabaseSchemaName
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
@ -3,6 +3,9 @@ package net.corda.node.services.vault
|
||||
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.internal.IntegrationTest
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.internal.toDatabaseSchemaName
|
||||
import org.junit.*
|
||||
|
||||
class VaultQueryIntegrationTests : VaultQueryTests() {
|
||||
|
@ -2,9 +2,7 @@ package net.corda.node.utilities.registration
|
||||
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.concurrent.transpose
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
import net.corda.core.messaging.startFlow
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
@ -22,6 +20,8 @@ import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.driver.PortAllocation
|
||||
import net.corda.testing.internal.IntegrationTest
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.node.NotarySpec
|
||||
import net.corda.testing.node.internal.CompatibilityZoneParams
|
||||
import net.corda.testing.node.internal.internalDriver
|
||||
@ -38,8 +38,10 @@ import java.security.KeyPair
|
||||
import java.security.cert.CertPath
|
||||
import java.security.cert.CertPathValidatorException
|
||||
import java.security.cert.Certificate
|
||||
import java.security.cert.X509Certificate
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipOutputStream
|
||||
import javax.security.auth.x500.X500Principal
|
||||
import javax.ws.rs.*
|
||||
import javax.ws.rs.core.MediaType
|
||||
import javax.ws.rs.core.Response
|
||||
@ -59,14 +61,13 @@ class NodeRegistrationTest : IntegrationTest() {
|
||||
val testSerialization = SerializationEnvironmentRule(true)
|
||||
|
||||
private val portAllocation = PortAllocation.Incremental(13000)
|
||||
private val registrationHandler = RegistrationHandler(ROOT_CA)
|
||||
|
||||
private val registrationHandler = RegistrationHandler(DEV_ROOT_CA)
|
||||
private lateinit var server: NetworkMapServer
|
||||
private lateinit var serverHostAndPort: NetworkHostAndPort
|
||||
|
||||
@Before
|
||||
fun startServer() {
|
||||
server = NetworkMapServer(1.minutes, portAllocation.nextHostAndPort(), ROOT_CA, "localhost", registrationHandler)
|
||||
server = NetworkMapServer(1.minutes, portAllocation.nextHostAndPort(), DEV_ROOT_CA, "localhost", registrationHandler)
|
||||
serverHostAndPort = server.start()
|
||||
}
|
||||
|
||||
@ -80,7 +81,7 @@ class NodeRegistrationTest : IntegrationTest() {
|
||||
val compatibilityZone = CompatibilityZoneParams(
|
||||
URL("http://$serverHostAndPort"),
|
||||
publishNotaries = { server.networkParameters = testNetworkParameters(it) },
|
||||
rootCert = ROOT_CA.certificate.cert)
|
||||
rootCert = DEV_ROOT_CA.certificate)
|
||||
internalDriver(
|
||||
portAllocation = portAllocation,
|
||||
compatibilityZone = compatibilityZone,
|
||||
@ -116,12 +117,12 @@ class NodeRegistrationTest : IntegrationTest() {
|
||||
@Test
|
||||
fun `node registration wrong root cert`() {
|
||||
val someRootCert = X509Utilities.createSelfSignedCACertificate(
|
||||
CordaX500Name("Integration Test Corda Node Root CA", "R3 Ltd", "London", "GB"),
|
||||
X500Principal("CN=Integration Test Corda Node Root CA,O=R3 Ltd,L=London,C=GB"),
|
||||
Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
|
||||
val compatibilityZone = CompatibilityZoneParams(
|
||||
URL("http://$serverHostAndPort"),
|
||||
publishNotaries = { server.networkParameters = testNetworkParameters(it) },
|
||||
rootCert = someRootCert.cert)
|
||||
rootCert = someRootCert)
|
||||
internalDriver(
|
||||
portAllocation = portAllocation,
|
||||
compatibilityZone = compatibilityZone,
|
||||
@ -149,7 +150,7 @@ class RegistrationHandler(private val rootCertAndKeyPair: CertificateAndKeyPair)
|
||||
val (certPath, name) = createSignedClientCertificate(
|
||||
certificationRequest,
|
||||
rootCertAndKeyPair.keyPair,
|
||||
arrayOf(rootCertAndKeyPair.certificate.cert))
|
||||
arrayOf(rootCertAndKeyPair.certificate))
|
||||
require(!name.organisation.contains("\\s".toRegex())) { "Whitespace in the organisation name not supported" }
|
||||
certPaths[name.organisation] = certPath
|
||||
return Response.ok(name.organisation).build()
|
||||
@ -183,12 +184,12 @@ class RegistrationHandler(private val rootCertAndKeyPair: CertificateAndKeyPair)
|
||||
val name = CordaX500Name.parse(request.subject.toString())
|
||||
val nodeCaCert = X509Utilities.createCertificate(
|
||||
CertificateType.NODE_CA,
|
||||
caCertPath.first().toX509CertHolder(),
|
||||
caCertPath[0] as X509Certificate ,
|
||||
caKeyPair,
|
||||
name,
|
||||
name.x500Principal,
|
||||
request.publicKey,
|
||||
nameConstraints = null)
|
||||
val certPath = X509CertificateFactory().generateCertPath(nodeCaCert.cert, *caCertPath)
|
||||
val certPath = X509CertificateFactory().generateCertPath(nodeCaCert, *caCertPath)
|
||||
return Pair(certPath, name)
|
||||
}
|
||||
}
|
||||
|
@ -94,22 +94,34 @@ class MQSecurityAsNodeTest : MQSecurityTest() {
|
||||
javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"),
|
||||
"cordacadevpass")
|
||||
|
||||
val rootCACert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA).toX509CertHolder()
|
||||
val rootCACert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA)
|
||||
val intermediateCA = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
|
||||
val clientKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
|
||||
val clientKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
// Set name constrain to the legal name.
|
||||
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.x500Name))), arrayOf())
|
||||
val clientCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, intermediateCA.certificate,
|
||||
intermediateCA.keyPair, legalName, clientKey.public, nameConstraints = nameConstraints)
|
||||
val tlsKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val clientCACert = X509Utilities.createCertificate(
|
||||
CertificateType.INTERMEDIATE_CA,
|
||||
intermediateCA.certificate,
|
||||
intermediateCA.keyPair,
|
||||
legalName.x500Principal,
|
||||
clientKeyPair.public,
|
||||
nameConstraints = nameConstraints)
|
||||
|
||||
val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
// Using different x500 name in the TLS cert which is not allowed in the name constraints.
|
||||
val clientTLSCert = X509Utilities.createCertificate(CertificateType.TLS, clientCACert, clientKey, CordaX500Name("MiniCorp", "London", "GB"), tlsKey.public)
|
||||
val clientTLSCert = X509Utilities.createCertificate(
|
||||
CertificateType.TLS,
|
||||
clientCACert,
|
||||
clientKeyPair,
|
||||
CordaX500Name("MiniCorp", "London", "GB").x500Principal,
|
||||
tlsKeyPair.public)
|
||||
|
||||
val keyPass = keyStorePassword.toCharArray()
|
||||
val clientCAKeystore = loadOrCreateKeyStore(nodeKeystore, keyStorePassword)
|
||||
clientCAKeystore.addOrReplaceKey(
|
||||
X509Utilities.CORDA_CLIENT_CA,
|
||||
clientKey.private,
|
||||
clientKeyPair.private,
|
||||
keyPass,
|
||||
arrayOf(clientCACert, intermediateCA.certificate, rootCACert))
|
||||
clientCAKeystore.save(nodeKeystore, keyStorePassword)
|
||||
@ -117,7 +129,7 @@ class MQSecurityAsNodeTest : MQSecurityTest() {
|
||||
val tlsKeystore = loadOrCreateKeyStore(sslKeystore, keyStorePassword)
|
||||
tlsKeystore.addOrReplaceKey(
|
||||
X509Utilities.CORDA_CLIENT_TLS,
|
||||
tlsKey.private,
|
||||
tlsKeyPair.private,
|
||||
keyPass,
|
||||
arrayOf(clientTLSCert, clientCACert, intermediateCA.certificate, rootCACert))
|
||||
tlsKeystore.save(sslKeystore, keyStorePassword)
|
||||
|
@ -26,7 +26,9 @@ import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEERS_PREF
|
||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
||||
import net.corda.nodeapi.internal.config.User
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.internal.configureTestSSL
|
||||
import net.corda.testing.internal.toDatabaseSchemaName
|
||||
import net.corda.testing.node.internal.NodeBasedTest
|
||||
import net.corda.testing.node.startFlow
|
||||
import org.apache.activemq.artemis.api.core.ActiveMQNonExistentQueueException
|
||||
|
@ -17,14 +17,14 @@ import net.corda.node.internal.StartedNode
|
||||
import net.corda.node.services.messaging.MessagingService
|
||||
import net.corda.node.services.messaging.ReceivedMessage
|
||||
import net.corda.node.services.messaging.send
|
||||
import net.corda.node.services.transactions.RaftValidatingNotaryService
|
||||
import net.corda.testing.*
|
||||
import net.corda.node.services.messaging.*
|
||||
import net.corda.testing.ALICE_NAME
|
||||
import net.corda.testing.chooseIdentity
|
||||
import net.corda.testing.driver.DriverDSL
|
||||
import net.corda.testing.driver.NodeHandle
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.internal.IntegrationTest
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.internal.toDatabaseSchemaName
|
||||
import net.corda.testing.node.ClusterSpec
|
||||
import net.corda.testing.node.NotarySpec
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
|
@ -22,6 +22,10 @@ import net.corda.node.services.Permissions.Companion.startFlow
|
||||
import net.corda.nodeapi.internal.config.User
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.internal.IntegrationTest
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.internal.toDatabaseSchemaName
|
||||
import net.corda.testing.internal.toDatabaseSchemaNames
|
||||
import org.junit.Assume.assumeFalse
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Test
|
||||
@ -36,7 +40,7 @@ class NodeStatePersistenceTests : IntegrationTest() {
|
||||
companion object {
|
||||
@ClassRule @JvmField
|
||||
val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, DUMMY_BANK_A_NAME)
|
||||
.map { it.toDatabaseSchemaNames("", "_10000","_10003","_10006") }.flatten().toTypedArray(),
|
||||
.map { it.toDatabaseSchemaNames("", "_10000", "_10003", "_10006") }.flatten().toTypedArray(),
|
||||
DUMMY_NOTARY_NAME.toDatabaseSchemaName())
|
||||
}
|
||||
@Test
|
||||
|
@ -726,7 +726,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
||||
val caKeyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword)
|
||||
val trustRoot = trustStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA)
|
||||
val clientCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)
|
||||
val caCertificates = arrayOf(identityCert, clientCa.certificate.cert)
|
||||
val caCertificates = arrayOf(identityCert, clientCa.certificate)
|
||||
return PersistentIdentityService(trustRoot, *caCertificates)
|
||||
}
|
||||
|
||||
@ -784,7 +784,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
||||
listOf(certificate) + keyStore.getCertificateChain(privateKeyAlias).drop(1)
|
||||
} else {
|
||||
keyStore.getCertificateChain(privateKeyAlias).let {
|
||||
check(it[0].toX509CertHolder() == x509Cert) { "Certificates from key store do not line up!" }
|
||||
check(it[0] == x509Cert) { "Certificates from key store do not line up!" }
|
||||
it.asList()
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ import io.netty.handler.ssl.SslHandler
|
||||
import io.netty.handler.ssl.SslHandshakeCompletionEvent
|
||||
import io.netty.util.ReferenceCountUtil
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
import net.corda.core.utilities.debug
|
||||
import net.corda.node.internal.protonwrapper.engine.EventProcessor
|
||||
import net.corda.node.internal.protonwrapper.messages.ReceivedMessage
|
||||
@ -19,9 +18,9 @@ import org.apache.qpid.proton.engine.ProtonJTransport
|
||||
import org.apache.qpid.proton.engine.Transport
|
||||
import org.apache.qpid.proton.engine.impl.ProtocolTracer
|
||||
import org.apache.qpid.proton.framing.TransportFrame
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.net.InetSocketAddress
|
||||
import java.security.cert.X509Certificate
|
||||
|
||||
/**
|
||||
* An instance of AMQPChannelHandler sits inside the netty pipeline and controls the socket level lifecycle.
|
||||
@ -38,20 +37,20 @@ internal class AMQPChannelHandler(private val serverMode: Boolean,
|
||||
private val onReceive: (ReceivedMessage) -> Unit) : ChannelDuplexHandler() {
|
||||
private val log = LoggerFactory.getLogger(allowedRemoteLegalNames?.firstOrNull()?.toString() ?: "AMQPChannelHandler")
|
||||
private lateinit var remoteAddress: InetSocketAddress
|
||||
private lateinit var localCert: X509CertificateHolder
|
||||
private lateinit var remoteCert: X509CertificateHolder
|
||||
private lateinit var localCert: X509Certificate
|
||||
private lateinit var remoteCert: X509Certificate
|
||||
private var eventProcessor: EventProcessor? = null
|
||||
|
||||
override fun channelActive(ctx: ChannelHandlerContext) {
|
||||
val ch = ctx.channel()
|
||||
remoteAddress = ch.remoteAddress() as InetSocketAddress
|
||||
val localAddress = ch.localAddress() as InetSocketAddress
|
||||
log.info("New client connection ${ch.id()} from ${remoteAddress} to ${localAddress}")
|
||||
log.info("New client connection ${ch.id()} from $remoteAddress to $localAddress")
|
||||
}
|
||||
|
||||
private fun createAMQPEngine(ctx: ChannelHandlerContext) {
|
||||
val ch = ctx.channel()
|
||||
eventProcessor = EventProcessor(ch, serverMode, localCert.subject.toString(), remoteCert.subject.toString(), userName, password)
|
||||
eventProcessor = EventProcessor(ch, serverMode, localCert.subjectX500Principal.toString(), remoteCert.subjectX500Principal.toString(), userName, password)
|
||||
val connection = eventProcessor!!.connection
|
||||
val transport = connection.transport as ProtonJTransport
|
||||
if (trace) {
|
||||
@ -71,7 +70,7 @@ internal class AMQPChannelHandler(private val serverMode: Boolean,
|
||||
|
||||
override fun channelInactive(ctx: ChannelHandlerContext) {
|
||||
val ch = ctx.channel()
|
||||
log.info("Closed client connection ${ch.id()} from ${remoteAddress} to ${ch.localAddress()}")
|
||||
log.info("Closed client connection ${ch.id()} from $remoteAddress to ${ch.localAddress()}")
|
||||
onClose(Pair(ch as SocketChannel, ConnectionChange(remoteAddress, null, false)))
|
||||
eventProcessor?.close()
|
||||
ctx.fireChannelInactive()
|
||||
@ -81,12 +80,12 @@ internal class AMQPChannelHandler(private val serverMode: Boolean,
|
||||
if (evt is SslHandshakeCompletionEvent) {
|
||||
if (evt.isSuccess) {
|
||||
val sslHandler = ctx.pipeline().get(SslHandler::class.java)
|
||||
localCert = sslHandler.engine().session.localCertificates.first().toX509CertHolder()
|
||||
remoteCert = sslHandler.engine().session.peerCertificates.first().toX509CertHolder()
|
||||
localCert = sslHandler.engine().session.localCertificates[0] as X509Certificate
|
||||
remoteCert = sslHandler.engine().session.peerCertificates[0] as X509Certificate
|
||||
try {
|
||||
val remoteX500Name = CordaX500Name.parse(remoteCert.subject.toString())
|
||||
val remoteX500Name = CordaX500Name.build(remoteCert.subjectX500Principal)
|
||||
require(allowedRemoteLegalNames == null || remoteX500Name in allowedRemoteLegalNames)
|
||||
log.info("handshake completed subject: ${remoteX500Name}")
|
||||
log.info("handshake completed subject: $remoteX500Name")
|
||||
} catch (ex: IllegalArgumentException) {
|
||||
log.error("Invalid certificate subject", ex)
|
||||
ctx.close()
|
||||
@ -124,7 +123,7 @@ internal class AMQPChannelHandler(private val serverMode: Boolean,
|
||||
require(inetAddress == remoteAddress) {
|
||||
"Message for incorrect endpoint"
|
||||
}
|
||||
require(CordaX500Name.parse(msg.destinationLegalName) == CordaX500Name.parse(remoteCert.subject.toString())) {
|
||||
require(CordaX500Name.parse(msg.destinationLegalName) == CordaX500Name.build(remoteCert.subjectX500Principal)) {
|
||||
"Message for incorrect legal identity"
|
||||
}
|
||||
log.debug { "channel write ${msg.applicationProperties["_AMQ_DUPL_ID"]}" }
|
||||
|
@ -1,6 +1,6 @@
|
||||
package net.corda.node.internal.protonwrapper.netty
|
||||
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import java.net.InetSocketAddress
|
||||
import java.security.cert.X509Certificate
|
||||
|
||||
data class ConnectionChange(val remoteAddress: InetSocketAddress, val remoteCert: X509CertificateHolder?, val connected: Boolean)
|
||||
data class ConnectionChange(val remoteAddress: InetSocketAddress, val remoteCert: X509Certificate?, val connected: Boolean)
|
@ -11,7 +11,6 @@ import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.exists
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
||||
import net.corda.nodeapi.internal.config.toProperties
|
||||
import net.corda.nodeapi.internal.createDevKeyStores
|
||||
@ -73,7 +72,7 @@ fun SSLConfiguration.configureDevKeyAndTrustStores(myLegalName: CordaX500Name) {
|
||||
}
|
||||
if (!sslKeystore.exists() || !nodeKeystore.exists()) {
|
||||
val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
|
||||
val rootCert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA).toX509CertHolder()
|
||||
val rootCert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA)
|
||||
val intermediateCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
|
||||
createDevKeyStores(rootCert, intermediateCa, myLegalName)
|
||||
|
||||
|
@ -203,8 +203,16 @@ data class SecurityConfiguration(val authService: SecurityConfiguration.AuthServ
|
||||
data class Options(val cache: Options.Cache?) {
|
||||
|
||||
// Cache parameters
|
||||
data class Cache(val expireAfterSecs: Long, val maxEntries: Long)
|
||||
|
||||
data class Cache(val expireAfterSecs: Long, val maxEntries: Long) {
|
||||
init {
|
||||
require(expireAfterSecs >= 0) {
|
||||
"Expected positive value for 'cache.expireAfterSecs'"
|
||||
}
|
||||
require(maxEntries > 0) {
|
||||
"Expected positive value for 'cache.maxEntries'"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Provider of users credentials and permissions data
|
||||
@ -228,12 +236,13 @@ data class SecurityConfiguration(val authService: SecurityConfiguration.AuthServ
|
||||
AuthDataSourceType.DB -> AuthServiceId("REMOTE_DATABASE")
|
||||
}
|
||||
|
||||
fun fromUsers(users: List<User>) = AuthService(
|
||||
dataSource = DataSource(
|
||||
type = AuthDataSourceType.INMEMORY,
|
||||
users = users,
|
||||
passwordEncryption = PasswordEncryption.NONE),
|
||||
id = AuthServiceId("NODE_CONFIG"))
|
||||
fun fromUsers(users: List<User>, encryption: PasswordEncryption = PasswordEncryption.NONE) =
|
||||
AuthService(
|
||||
dataSource = DataSource(
|
||||
type = AuthDataSourceType.INMEMORY,
|
||||
users = users,
|
||||
passwordEncryption = encryption),
|
||||
id = AuthServiceId("NODE_CONFIG"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,15 +4,12 @@ import net.corda.core.contracts.PartyAndReference
|
||||
import net.corda.core.crypto.toStringShort
|
||||
import net.corda.core.identity.*
|
||||
import net.corda.core.internal.CertRole
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
import net.corda.core.node.services.UnknownAnonymousPartyException
|
||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.core.utilities.trace
|
||||
import net.corda.node.services.api.IdentityServiceInternal
|
||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import java.security.InvalidAlgorithmParameterException
|
||||
import java.security.PublicKey
|
||||
import java.security.cert.*
|
||||
@ -27,7 +24,7 @@ import javax.annotation.concurrent.ThreadSafe
|
||||
// TODO There is duplicated logic between this and PersistentIdentityService
|
||||
@ThreadSafe
|
||||
class InMemoryIdentityService(identities: Array<out PartyAndCertificate>,
|
||||
trustRoot: X509CertificateHolder) : SingletonSerializeAsToken(), IdentityServiceInternal {
|
||||
override val trustRoot: X509Certificate) : SingletonSerializeAsToken(), IdentityServiceInternal {
|
||||
companion object {
|
||||
private val log = contextLogger()
|
||||
}
|
||||
@ -35,14 +32,12 @@ class InMemoryIdentityService(identities: Array<out PartyAndCertificate>,
|
||||
/**
|
||||
* Certificate store for certificate authority and intermediary certificates.
|
||||
*/
|
||||
override val caCertStore: CertStore
|
||||
override val trustRoot = trustRoot.cert
|
||||
override val trustAnchor: TrustAnchor = TrustAnchor(this.trustRoot, null)
|
||||
override val caCertStore: CertStore = CertStore.getInstance("Collection", CollectionCertStoreParameters(setOf(trustRoot)))
|
||||
override val trustAnchor: TrustAnchor = TrustAnchor(trustRoot, null)
|
||||
private val keyToParties = ConcurrentHashMap<PublicKey, PartyAndCertificate>()
|
||||
private val principalToParties = ConcurrentHashMap<CordaX500Name, PartyAndCertificate>()
|
||||
|
||||
init {
|
||||
caCertStore = CertStore.getInstance("Collection", CollectionCertStoreParameters(setOf(this.trustRoot)))
|
||||
keyToParties.putAll(identities.associateBy { it.owningKey })
|
||||
principalToParties.putAll(identities.associateBy { it.name })
|
||||
}
|
||||
@ -57,7 +52,7 @@ class InMemoryIdentityService(identities: Array<out PartyAndCertificate>,
|
||||
log.warn("Certificate path :")
|
||||
identity.certPath.certificates.reversed().forEachIndexed { index, certificate ->
|
||||
val space = (0 until index).joinToString("") { " " }
|
||||
log.warn("$space${certificate.toX509CertHolder().subject}")
|
||||
log.warn("$space${(certificate as X509Certificate).subjectX500Principal}")
|
||||
}
|
||||
throw e
|
||||
}
|
||||
|
@ -5,8 +5,6 @@ import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.toStringShort
|
||||
import net.corda.core.identity.*
|
||||
import net.corda.core.internal.CertRole
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
import net.corda.core.node.services.UnknownAnonymousPartyException
|
||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||
import net.corda.core.utilities.MAX_HASH_HEX_SIZE
|
||||
@ -16,7 +14,6 @@ import net.corda.node.services.api.IdentityServiceInternal
|
||||
import net.corda.node.utilities.AppendOnlyPersistentMap
|
||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import java.security.InvalidAlgorithmParameterException
|
||||
import java.security.PublicKey
|
||||
import java.security.cert.*
|
||||
@ -30,7 +27,6 @@ import javax.persistence.Lob
|
||||
@ThreadSafe
|
||||
class PersistentIdentityService(override val trustRoot: X509Certificate,
|
||||
vararg caCertificates: X509Certificate) : SingletonSerializeAsToken(), IdentityServiceInternal {
|
||||
constructor(trustRoot: X509CertificateHolder) : this(trustRoot.cert)
|
||||
|
||||
companion object {
|
||||
private val log = contextLogger()
|
||||
@ -121,7 +117,7 @@ class PersistentIdentityService(override val trustRoot: X509Certificate,
|
||||
log.warn(e.localizedMessage)
|
||||
log.warn("Path = ")
|
||||
identity.certPath.certificates.reversed().forEach {
|
||||
log.warn(it.toX509CertHolder().subject.toString())
|
||||
log.warn((it as X509Certificate).subjectX500Principal.toString())
|
||||
}
|
||||
throw e
|
||||
}
|
||||
|
@ -3,8 +3,6 @@ package net.corda.node.services.keys
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.internal.CertRole
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
import net.corda.core.utilities.days
|
||||
import net.corda.node.services.api.IdentityServiceInternal
|
||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||
@ -36,11 +34,16 @@ fun freshCertificate(identityService: IdentityServiceInternal,
|
||||
revocationEnabled: Boolean = false): PartyAndCertificate {
|
||||
val issuerRole = CertRole.extract(issuer.certificate)
|
||||
require(issuerRole == CertRole.LEGAL_IDENTITY) { "Confidential identities can only be issued from well known identities, provided issuer ${issuer.name} has role $issuerRole" }
|
||||
val issuerCert = issuer.certificate.toX509CertHolder()
|
||||
val issuerCert = issuer.certificate
|
||||
val window = X509Utilities.getCertificateValidityWindow(Duration.ZERO, 3650.days, issuerCert)
|
||||
val ourCertificate = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuerCert.subject,
|
||||
issuerSigner, issuer.name, subjectPublicKey, window)
|
||||
val ourCertPath = X509CertificateFactory().generateCertPath(listOf(ourCertificate.cert) + issuer.certPath.certificates)
|
||||
val ourCertificate = X509Utilities.createCertificate(
|
||||
CertificateType.CONFIDENTIAL_LEGAL_IDENTITY,
|
||||
issuerCert.subjectX500Principal,
|
||||
issuerSigner,
|
||||
issuer.name.x500Principal,
|
||||
subjectPublicKey,
|
||||
window)
|
||||
val ourCertPath = X509CertificateFactory().generateCertPath(listOf(ourCertificate) + issuer.certPath.certificates)
|
||||
val anonymisedIdentity = PartyAndCertificate(ourCertPath)
|
||||
identityService.justVerifyAndRegisterIdentity(anonymisedIdentity)
|
||||
return anonymisedIdentity
|
||||
|
@ -40,6 +40,18 @@ class FlowLogicRefFactoryImpl(private val classloader: ClassLoader) : SingletonS
|
||||
return createForRPC(flowClass, *args)
|
||||
}
|
||||
|
||||
override fun create(flowClassName: String, vararg args: Any?): FlowLogicRef {
|
||||
val flowClass = Class.forName(flowClassName, true, classloader).asSubclass(FlowLogic::class.java)
|
||||
if (flowClass == null) {
|
||||
throw IllegalArgumentException("The class $flowClassName is not a subclass of FlowLogic.")
|
||||
} else {
|
||||
if (!flowClass.isAnnotationPresent(SchedulableFlow::class.java)) {
|
||||
throw IllegalFlowLogicException(flowClass, "because it's not a schedulable flow")
|
||||
}
|
||||
return createForRPC(flowClass, *args)
|
||||
}
|
||||
}
|
||||
|
||||
override fun createForRPC(flowClass: Class<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
|
||||
// to avoid requiring only a single constructor.
|
||||
|
@ -22,9 +22,9 @@ import java.security.cert.X509Certificate
|
||||
* needed.
|
||||
*/
|
||||
class NetworkRegistrationHelper(private val config: NodeConfiguration, private val certService: NetworkRegistrationService) {
|
||||
companion object {
|
||||
private companion object {
|
||||
val pollInterval = 10.seconds
|
||||
val SELF_SIGNED_PRIVATE_KEY = "Self Signed Private Key"
|
||||
const val SELF_SIGNED_PRIVATE_KEY = "Self Signed Private Key"
|
||||
}
|
||||
|
||||
private val requestIdStore = config.certificatesDirectory / "certificate-request-id.txt"
|
||||
@ -62,54 +62,81 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v
|
||||
*/
|
||||
fun buildKeystore() {
|
||||
config.certificatesDirectory.createDirectories()
|
||||
val caKeyStore = loadOrCreateKeyStore(config.nodeKeystore, keystorePassword)
|
||||
if (!caKeyStore.containsAlias(CORDA_CLIENT_CA)) {
|
||||
// Create or load self signed keypair from the key store.
|
||||
// We use the self sign certificate to store the key temporarily in the keystore while waiting for the request approval.
|
||||
if (!caKeyStore.containsAlias(SELF_SIGNED_PRIVATE_KEY)) {
|
||||
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val selfSignCert = X509Utilities.createSelfSignedCACertificate(config.myLegalName, keyPair)
|
||||
// Save to the key store.
|
||||
caKeyStore.addOrReplaceKey(SELF_SIGNED_PRIVATE_KEY, keyPair.private, privateKeyPassword.toCharArray(),
|
||||
arrayOf(selfSignCert))
|
||||
caKeyStore.save(config.nodeKeystore, keystorePassword)
|
||||
}
|
||||
val keyPair = caKeyStore.getKeyPair(SELF_SIGNED_PRIVATE_KEY, privateKeyPassword)
|
||||
val requestId = submitOrResumeCertificateSigningRequest(keyPair)
|
||||
|
||||
val certificates = try {
|
||||
pollServerForCertificates(requestId)
|
||||
} catch (certificateRequestException: CertificateRequestException) {
|
||||
System.err.println(certificateRequestException.message)
|
||||
System.err.println("Please make sure the details in configuration file are correct and try again.")
|
||||
System.err.println("Corda node will now terminate.")
|
||||
requestIdStore.deleteIfExists()
|
||||
throw certificateRequestException
|
||||
}
|
||||
|
||||
println("Certificate signing request approved, storing private key with the certificate chain.")
|
||||
// Save private key and certificate chain to the key store.
|
||||
caKeyStore.addOrReplaceKey(CORDA_CLIENT_CA, keyPair.private, privateKeyPassword.toCharArray(), certificates)
|
||||
caKeyStore.deleteEntry(SELF_SIGNED_PRIVATE_KEY)
|
||||
caKeyStore.save(config.nodeKeystore, keystorePassword)
|
||||
println("Node private key and certificate stored in ${config.nodeKeystore}.")
|
||||
|
||||
println("Checking root of the certificate path is what we expect.")
|
||||
X509Utilities.validateCertificateChain(rootCert, *certificates)
|
||||
|
||||
println("Generating SSL certificate for node messaging service.")
|
||||
val sslKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val caCert = caKeyStore.getX509Certificate(CORDA_CLIENT_CA).toX509CertHolder()
|
||||
val sslCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, keyPair, CordaX500Name.build(caCert.cert.subjectX500Principal), sslKey.public)
|
||||
val sslKeyStore = loadOrCreateKeyStore(config.sslKeystore, keystorePassword)
|
||||
sslKeyStore.addOrReplaceKey(CORDA_CLIENT_TLS, sslKey.private, privateKeyPassword.toCharArray(), arrayOf(sslCert.cert, *certificates))
|
||||
sslKeyStore.save(config.sslKeystore, config.keyStorePassword)
|
||||
println("SSL private key and certificate stored in ${config.sslKeystore}.")
|
||||
// All done, clean up temp files.
|
||||
requestIdStore.deleteIfExists()
|
||||
} else {
|
||||
val nodeKeyStore = loadOrCreateKeyStore(config.nodeKeystore, keystorePassword)
|
||||
if (nodeKeyStore.containsAlias(CORDA_CLIENT_CA)) {
|
||||
println("Certificate already exists, Corda node will now terminate...")
|
||||
return
|
||||
}
|
||||
|
||||
// Create or load self signed keypair from the key store.
|
||||
// We use the self sign certificate to store the key temporarily in the keystore while waiting for the request approval.
|
||||
if (!nodeKeyStore.containsAlias(SELF_SIGNED_PRIVATE_KEY)) {
|
||||
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val selfSignCert = X509Utilities.createSelfSignedCACertificate(config.myLegalName.x500Principal, keyPair)
|
||||
// Save to the key store.
|
||||
nodeKeyStore.addOrReplaceKey(SELF_SIGNED_PRIVATE_KEY, keyPair.private, privateKeyPassword.toCharArray(),
|
||||
arrayOf(selfSignCert))
|
||||
nodeKeyStore.save(config.nodeKeystore, keystorePassword)
|
||||
}
|
||||
|
||||
val keyPair = nodeKeyStore.getKeyPair(SELF_SIGNED_PRIVATE_KEY, privateKeyPassword)
|
||||
val requestId = submitOrResumeCertificateSigningRequest(keyPair)
|
||||
|
||||
val certificates = try {
|
||||
pollServerForCertificates(requestId)
|
||||
} catch (certificateRequestException: CertificateRequestException) {
|
||||
System.err.println(certificateRequestException.message)
|
||||
System.err.println("Please make sure the details in configuration file are correct and try again.")
|
||||
System.err.println("Corda node will now terminate.")
|
||||
requestIdStore.deleteIfExists()
|
||||
throw certificateRequestException
|
||||
}
|
||||
|
||||
val nodeCaCert = certificates[0] as X509Certificate
|
||||
|
||||
val nodeCaSubject = try {
|
||||
CordaX500Name.build(nodeCaCert.subjectX500Principal)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
throw CertificateRequestException("Received node CA cert has invalid subject name: ${e.message}")
|
||||
}
|
||||
if (nodeCaSubject != config.myLegalName) {
|
||||
throw CertificateRequestException("Subject of received node CA cert doesn't match with node legal name: $nodeCaSubject")
|
||||
}
|
||||
|
||||
val nodeCaCertRole = try {
|
||||
CertRole.extract(nodeCaCert)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
throw CertificateRequestException("Unable to extract cert role from received node CA cert: ${e.message}")
|
||||
}
|
||||
if (nodeCaCertRole != CertRole.NODE_CA) {
|
||||
throw CertificateRequestException("Received node CA cert has invalid role: $nodeCaCertRole")
|
||||
}
|
||||
|
||||
println("Checking root of the certificate path is what we expect.")
|
||||
X509Utilities.validateCertificateChain(rootCert, *certificates)
|
||||
|
||||
println("Certificate signing request approved, storing private key with the certificate chain.")
|
||||
// Save private key and certificate chain to the key store.
|
||||
nodeKeyStore.addOrReplaceKey(CORDA_CLIENT_CA, keyPair.private, privateKeyPassword.toCharArray(), certificates)
|
||||
nodeKeyStore.deleteEntry(SELF_SIGNED_PRIVATE_KEY)
|
||||
nodeKeyStore.save(config.nodeKeystore, keystorePassword)
|
||||
println("Node private key and certificate stored in ${config.nodeKeystore}.")
|
||||
|
||||
println("Generating SSL certificate for node messaging service.")
|
||||
val sslKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val sslCert = X509Utilities.createCertificate(
|
||||
CertificateType.TLS,
|
||||
nodeCaCert,
|
||||
keyPair,
|
||||
config.myLegalName.x500Principal,
|
||||
sslKeyPair.public)
|
||||
val sslKeyStore = loadOrCreateKeyStore(config.sslKeystore, keystorePassword)
|
||||
sslKeyStore.addOrReplaceKey(CORDA_CLIENT_TLS, sslKeyPair.private, privateKeyPassword.toCharArray(), arrayOf(sslCert, *certificates))
|
||||
sslKeyStore.save(config.sslKeystore, config.keyStorePassword)
|
||||
println("SSL private key and certificate stored in ${config.sslKeystore}.")
|
||||
|
||||
// All done, clean up temp files.
|
||||
requestIdStore.deleteIfExists()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -138,7 +165,7 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v
|
||||
private fun submitOrResumeCertificateSigningRequest(keyPair: KeyPair): String {
|
||||
// Retrieve request id from file if exists, else post a request to server.
|
||||
return if (!requestIdStore.exists()) {
|
||||
val request = X509Utilities.createCertificateSigningRequest(config.myLegalName, config.emailAddress, keyPair)
|
||||
val request = X509Utilities.createCertificateSigningRequest(config.myLegalName.x500Principal, config.emailAddress, keyPair)
|
||||
val writer = StringWriter()
|
||||
JcaPEMWriter(writer).use {
|
||||
it.writeObject(PemObject("CERTIFICATE REQUEST", request.encoded))
|
||||
|
@ -7,6 +7,7 @@ import net.corda.node.internal.security.Password
|
||||
import net.corda.node.internal.security.RPCSecurityManagerImpl
|
||||
import net.corda.node.internal.security.tryAuthenticate
|
||||
import net.corda.nodeapi.internal.config.User
|
||||
import net.corda.node.services.config.SecurityConfiguration
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.Test
|
||||
import javax.security.auth.login.FailedLoginException
|
||||
@ -26,7 +27,7 @@ class RPCSecurityManagerTest {
|
||||
|
||||
@Test
|
||||
fun `Generic RPC call authorization`() {
|
||||
checkUserPermissions(
|
||||
checkUserActions(
|
||||
permitted = setOf(arrayListOf("nodeInfo"), arrayListOf("notaryIdentities")),
|
||||
permissions = setOf(
|
||||
Permissions.invokeRpc(CordaRPCOps::nodeInfo),
|
||||
@ -35,7 +36,7 @@ class RPCSecurityManagerTest {
|
||||
|
||||
@Test
|
||||
fun `Flow invocation authorization`() {
|
||||
checkUserPermissions(
|
||||
checkUserActions(
|
||||
permissions = setOf(Permissions.startFlow<DummyFlow>()),
|
||||
permitted = setOf(
|
||||
arrayListOf("startTrackedFlowDynamic", "net.corda.node.services.RPCSecurityManagerTest\$DummyFlow"),
|
||||
@ -44,21 +45,21 @@ class RPCSecurityManagerTest {
|
||||
|
||||
@Test
|
||||
fun `Check startFlow RPC permission implies startFlowDynamic`() {
|
||||
checkUserPermissions(
|
||||
checkUserActions(
|
||||
permissions = setOf(Permissions.invokeRpc("startFlow")),
|
||||
permitted = setOf(arrayListOf("startFlow"), arrayListOf("startFlowDynamic")))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Check startTrackedFlow RPC permission implies startTrackedFlowDynamic`() {
|
||||
checkUserPermissions(
|
||||
checkUserActions(
|
||||
permitted = setOf(arrayListOf("startTrackedFlow"), arrayListOf("startTrackedFlowDynamic")),
|
||||
permissions = setOf(Permissions.invokeRpc("startTrackedFlow")))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Admin authorization`() {
|
||||
checkUserPermissions(
|
||||
checkUserActions(
|
||||
permissions = setOf("all"),
|
||||
permitted = allActions.map { arrayListOf(it) }.toSet())
|
||||
}
|
||||
@ -118,9 +119,9 @@ class RPCSecurityManagerTest {
|
||||
users = listOf(User(username, "password", setOf())), id = AuthServiceId("TEST"))
|
||||
}
|
||||
|
||||
private fun checkUserPermissions(permissions: Set<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 userRealms = RPCSecurityManagerImpl.fromUserList(users = listOf(user), id = AuthServiceId("TEST"))
|
||||
val userRealms = RPCSecurityManagerImpl(SecurityConfiguration.AuthService.fromUsers(listOf(user)))
|
||||
val disabled = allActions.filter { !permitted.contains(listOf(it)) }
|
||||
for (subject in listOf(
|
||||
userRealms.authenticate("user", Password("password")),
|
||||
|
@ -30,6 +30,7 @@ import org.junit.Assert.*
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.time.Instant
|
||||
import kotlin.reflect.jvm.jvmName
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class ScheduledFlowTests {
|
||||
@ -52,7 +53,7 @@ class ScheduledFlowTests {
|
||||
override val linearId: UniqueIdentifier = UniqueIdentifier()) : SchedulableState, LinearState {
|
||||
override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? {
|
||||
return if (!processed) {
|
||||
val logicRef = flowLogicRefFactory.create(ScheduledFlow::class.java, thisStateRef)
|
||||
val logicRef = flowLogicRefFactory.create(ScheduledFlow::class.jvmName, thisStateRef)
|
||||
ScheduledActivity(logicRef, creationTime)
|
||||
} else {
|
||||
null
|
||||
|
@ -6,8 +6,6 @@ import net.corda.core.identity.AnonymousParty
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
import net.corda.core.node.services.UnknownAnonymousPartyException
|
||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||
@ -32,7 +30,7 @@ class InMemoryIdentityServiceTests {
|
||||
val BOB get() = bob.party
|
||||
val BOB_IDENTITY get() = bob.identity
|
||||
val BOB_PUBKEY get() = bob.publicKey
|
||||
fun createService(vararg identities: PartyAndCertificate) = InMemoryIdentityService(identities, DEV_TRUST_ROOT)
|
||||
fun createService(vararg identities: PartyAndCertificate) = InMemoryIdentityService(identities, DEV_ROOT_CA.certificate)
|
||||
}
|
||||
|
||||
@Rule
|
||||
@ -100,11 +98,11 @@ class InMemoryIdentityServiceTests {
|
||||
@Test
|
||||
fun `assert unknown anonymous key is unrecognised`() {
|
||||
val rootKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootKey)
|
||||
val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Principal, rootKey)
|
||||
val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val service = createService()
|
||||
// TODO: Generate certificate with an EdDSA key rather than ECDSA
|
||||
val identity = Party(rootCert.cert)
|
||||
val identity = Party(rootCert)
|
||||
val txIdentity = AnonymousParty(txKey.public)
|
||||
|
||||
assertFailsWith<UnknownAnonymousPartyException> {
|
||||
@ -159,8 +157,8 @@ class InMemoryIdentityServiceTests {
|
||||
}
|
||||
|
||||
assertFailsWith<IllegalArgumentException> {
|
||||
val owningKey = Crypto.decodePublicKey(DEV_CA.certificate.subjectPublicKeyInfo.encoded)
|
||||
val subject = CordaX500Name.build(DEV_CA.certificate.cert.subjectX500Principal)
|
||||
val owningKey = DEV_INTERMEDIATE_CA.certificate.publicKey
|
||||
val subject = CordaX500Name.build(DEV_INTERMEDIATE_CA.certificate.subjectX500Principal)
|
||||
service.assertOwnership(Party(subject, owningKey), anonymousAlice.party.anonymise())
|
||||
}
|
||||
}
|
||||
@ -168,9 +166,14 @@ class InMemoryIdentityServiceTests {
|
||||
private fun createConfidentialIdentity(x500Name: CordaX500Name): Pair<PartyAndCertificate, PartyAndCertificate> {
|
||||
val issuerKeyPair = generateKeyPair()
|
||||
val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public)
|
||||
val txKey = Crypto.generateKeyPair()
|
||||
val txCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuer.certificate.toX509CertHolder(), issuerKeyPair, x500Name, txKey.public)
|
||||
val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates)
|
||||
val txKeyPair = Crypto.generateKeyPair()
|
||||
val txCert = X509Utilities.createCertificate(
|
||||
CertificateType.CONFIDENTIAL_LEGAL_IDENTITY,
|
||||
issuer.certificate,
|
||||
issuerKeyPair,
|
||||
x500Name.x500Principal,
|
||||
txKeyPair.public)
|
||||
val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert) + issuer.certPath.certificates)
|
||||
return Pair(issuer, PartyAndCertificate(txCertPath))
|
||||
}
|
||||
|
||||
|
@ -6,8 +6,6 @@ import net.corda.core.identity.AnonymousParty
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
import net.corda.core.node.services.IdentityService
|
||||
import net.corda.core.node.services.UnknownAnonymousPartyException
|
||||
import net.corda.node.internal.configureDatabase
|
||||
@ -50,7 +48,7 @@ class PersistentIdentityServiceTests {
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
identityService = PersistentIdentityService(DEV_TRUST_ROOT)
|
||||
identityService = PersistentIdentityService(DEV_ROOT_CA.certificate)
|
||||
database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), identityService)
|
||||
}
|
||||
|
||||
@ -143,9 +141,9 @@ class PersistentIdentityServiceTests {
|
||||
@Test
|
||||
fun `assert unknown anonymous key is unrecognised`() {
|
||||
val rootKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootKey)
|
||||
val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Principal, rootKey)
|
||||
val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_IDENTITY_SIGNATURE_SCHEME)
|
||||
val identity = Party(rootCert.cert)
|
||||
val identity = Party(rootCert)
|
||||
val txIdentity = AnonymousParty(txKey.public)
|
||||
|
||||
assertFailsWith<UnknownAnonymousPartyException> {
|
||||
@ -219,9 +217,9 @@ class PersistentIdentityServiceTests {
|
||||
}
|
||||
|
||||
assertFailsWith<IllegalArgumentException> {
|
||||
val owningKey = Crypto.decodePublicKey(DEV_CA.certificate.subjectPublicKeyInfo.encoded)
|
||||
val owningKey = DEV_INTERMEDIATE_CA.certificate.publicKey
|
||||
database.transaction {
|
||||
val subject = CordaX500Name.build(DEV_CA.certificate.cert.subjectX500Principal)
|
||||
val subject = CordaX500Name.build(DEV_INTERMEDIATE_CA.certificate.subjectX500Principal)
|
||||
identityService.assertOwnership(Party(subject, owningKey), anonymousAlice.party.anonymise())
|
||||
}
|
||||
}
|
||||
@ -243,7 +241,7 @@ class PersistentIdentityServiceTests {
|
||||
|
||||
// Create new identity service mounted onto same DB
|
||||
val newPersistentIdentityService = database.transaction {
|
||||
PersistentIdentityService(DEV_TRUST_ROOT)
|
||||
PersistentIdentityService(DEV_ROOT_CA.certificate)
|
||||
}
|
||||
|
||||
database.transaction {
|
||||
@ -266,8 +264,8 @@ class PersistentIdentityServiceTests {
|
||||
val issuerKeyPair = generateKeyPair()
|
||||
val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public)
|
||||
val txKey = Crypto.generateKeyPair()
|
||||
val txCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuer.certificate.toX509CertHolder(), issuerKeyPair, x500Name, txKey.public)
|
||||
val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates)
|
||||
val txCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuer.certificate, issuerKeyPair, x500Name.x500Principal, txKey.public)
|
||||
val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert) + issuer.certPath.certificates)
|
||||
return Pair(issuer, PartyAndCertificate(txCertPath))
|
||||
}
|
||||
|
||||
|
@ -2,12 +2,11 @@ package net.corda.node.services.network
|
||||
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.sha256
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.testing.ALICE_NAME
|
||||
import net.corda.testing.BOB_NAME
|
||||
import net.corda.testing.DEV_TRUST_ROOT
|
||||
import net.corda.testing.DEV_ROOT_CA
|
||||
import net.corda.testing.SerializationEnvironmentRule
|
||||
import net.corda.testing.driver.PortAllocation
|
||||
import net.corda.testing.internal.createNodeInfoAndSigned
|
||||
@ -35,7 +34,7 @@ class NetworkMapClientTest {
|
||||
fun setUp() {
|
||||
server = NetworkMapServer(cacheTimeout, PortAllocation.Incremental(10000).nextHostAndPort())
|
||||
val hostAndPort = server.start()
|
||||
networkMapClient = NetworkMapClient(URL("http://${hostAndPort.host}:${hostAndPort.port}"), DEV_TRUST_ROOT.cert)
|
||||
networkMapClient = NetworkMapClient(URL("http://${hostAndPort.host}:${hostAndPort.port}"), DEV_ROOT_CA.certificate)
|
||||
}
|
||||
|
||||
@After
|
||||
|
@ -1,17 +1,20 @@
|
||||
package net.corda.node.services.events
|
||||
package net.corda.node.services.statemachine
|
||||
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.IllegalFlowLogicException
|
||||
import net.corda.node.services.statemachine.FlowLogicRefFactoryImpl
|
||||
import net.corda.core.flows.SchedulableFlow
|
||||
import org.junit.Test
|
||||
import java.time.Duration
|
||||
import kotlin.reflect.jvm.jvmName
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class FlowLogicRefTest {
|
||||
class FlowLogicRefFactoryImplTest {
|
||||
|
||||
data class ParamType1(val value: Int)
|
||||
data class ParamType2(val value: String)
|
||||
|
||||
@Suppress("UNUSED_PARAMETER", "unused") // Things are used via reflection.
|
||||
@SchedulableFlow
|
||||
class KotlinFlowLogic(A: ParamType1, b: ParamType2) : FlowLogic<Unit>() {
|
||||
constructor() : this(ParamType1(1), ParamType2("2"))
|
||||
|
||||
@ -26,6 +29,7 @@ class FlowLogicRefTest {
|
||||
override fun call() = Unit
|
||||
}
|
||||
|
||||
@SchedulableFlow
|
||||
class KotlinNoArgFlowLogic : FlowLogic<Unit>() {
|
||||
override fun call() = Unit
|
||||
}
|
||||
@ -37,18 +41,18 @@ class FlowLogicRefTest {
|
||||
private val flowLogicRefFactory = FlowLogicRefFactoryImpl(FlowLogicRefFactoryImpl::class.java.classLoader)
|
||||
@Test
|
||||
fun `create kotlin no arg`() {
|
||||
flowLogicRefFactory.createForRPC(KotlinNoArgFlowLogic::class.java)
|
||||
flowLogicRefFactory.create(KotlinNoArgFlowLogic::class.jvmName)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `create kotlin`() {
|
||||
fun `should create kotlin types`() {
|
||||
val args = mapOf(Pair("A", ParamType1(1)), Pair("b", ParamType2("Hello Jack")))
|
||||
flowLogicRefFactory.createKotlin(KotlinFlowLogic::class.java, args)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `create primary`() {
|
||||
flowLogicRefFactory.createForRPC(KotlinFlowLogic::class.java, ParamType1(1), ParamType2("Hello Jack"))
|
||||
flowLogicRefFactory.create(KotlinFlowLogic::class.jvmName, ParamType1(1), ParamType2("Hello Jack"))
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -76,6 +80,6 @@ class FlowLogicRefTest {
|
||||
|
||||
@Test(expected = IllegalFlowLogicException::class)
|
||||
fun `create for non-schedulable flow logic`() {
|
||||
flowLogicRefFactory.create(NonSchedulableFlow::class.java)
|
||||
flowLogicRefFactory.create(NonSchedulableFlow::class.jvmName)
|
||||
}
|
||||
}
|
@ -24,6 +24,7 @@ import net.corda.testing.node.startFlow
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
@ -99,6 +100,7 @@ class NotaryServiceTests {
|
||||
assertThat(ex.error).isInstanceOf(NotaryError.TimeWindowInvalid::class.java)
|
||||
}
|
||||
|
||||
@Ignore("Only applies to deterministic signature schemes (e.g. EdDSA) and when deterministic metadata is attached (no timestamps or nonces)")
|
||||
@Test
|
||||
fun `should sign identical transaction multiple times (signing is idempotent)`() {
|
||||
val stx = run {
|
||||
|
@ -18,6 +18,7 @@ import java.net.ServerSocket
|
||||
import java.nio.file.Path
|
||||
import java.security.KeyStore
|
||||
import javax.net.ssl.*
|
||||
import javax.security.auth.x500.X500Principal
|
||||
import kotlin.concurrent.thread
|
||||
import kotlin.test.*
|
||||
|
||||
@ -51,9 +52,9 @@ class TLSAuthenticationTests {
|
||||
val tempFolder: TemporaryFolder = TemporaryFolder()
|
||||
|
||||
// Root CA.
|
||||
private val ROOT_X500 = CordaX500Name(commonName = "Root_CA_1", organisation = "R3CEV", locality = "London", country = "GB")
|
||||
private val ROOT_X500 = X500Principal("CN=Root_CA_1,O=R3CEV,L=London,C=GB")
|
||||
// Intermediate CA.
|
||||
private val INTERMEDIATE_X500 = CordaX500Name(commonName = "Intermediate_CA_1", organisation = "R3CEV", locality = "London", country = "GB")
|
||||
private val INTERMEDIATE_X500 = X500Principal("CN=Intermediate_CA_1,O=R3CEV,L=London,C=GB")
|
||||
// TLS server (client1).
|
||||
private val CLIENT_1_X500 = CordaX500Name(commonName = "Client_1", organisation = "R3CEV", locality = "London", country = "GB")
|
||||
// TLS client (client2).
|
||||
@ -274,7 +275,7 @@ class TLSAuthenticationTests {
|
||||
CertificateType.NODE_CA,
|
||||
intermediateCACert,
|
||||
intermediateCAKeyPair,
|
||||
CLIENT_1_X500,
|
||||
CLIENT_1_X500.x500Principal,
|
||||
client1CAKeyPair.public
|
||||
)
|
||||
|
||||
@ -283,7 +284,7 @@ class TLSAuthenticationTests {
|
||||
CertificateType.TLS,
|
||||
client1CACert,
|
||||
client1CAKeyPair,
|
||||
CLIENT_1_X500,
|
||||
CLIENT_1_X500.x500Principal,
|
||||
client1TLSKeyPair.public
|
||||
)
|
||||
|
||||
@ -301,7 +302,7 @@ class TLSAuthenticationTests {
|
||||
CertificateType.NODE_CA,
|
||||
intermediateCACert,
|
||||
intermediateCAKeyPair,
|
||||
CLIENT_2_X500,
|
||||
CLIENT_2_X500.x500Principal,
|
||||
client2CAKeyPair.public
|
||||
)
|
||||
|
||||
@ -310,7 +311,7 @@ class TLSAuthenticationTests {
|
||||
CertificateType.TLS,
|
||||
client2CACert,
|
||||
client2CAKeyPair,
|
||||
CLIENT_2_X500,
|
||||
CLIENT_2_X500.x500Principal,
|
||||
client2TLSKeyPair.public
|
||||
)
|
||||
|
||||
@ -323,8 +324,8 @@ class TLSAuthenticationTests {
|
||||
// client2TLSKeyStore.save(client2TLSKeyStorePath, PASSWORD)
|
||||
|
||||
val trustStore = loadOrCreateKeyStore(trustStorePath, PASSWORD)
|
||||
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCACert.cert)
|
||||
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_INTERMEDIATE_CA, intermediateCACert.cert)
|
||||
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCACert)
|
||||
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_INTERMEDIATE_CA, intermediateCACert)
|
||||
// trustStore.save(trustStorePath, PASSWORD)
|
||||
|
||||
val client1SSLContext = sslContext(client1TLSKeyStore, PASSWORD, trustStore)
|
||||
|
@ -9,21 +9,23 @@ import com.nhaarman.mockito_kotlin.whenever
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.x500Name
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.nodeapi.internal.crypto.*
|
||||
import net.corda.testing.ALICE_NAME
|
||||
import net.corda.testing.internal.createDevNodeCaCertPath
|
||||
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.assertj.core.api.Assertions.*
|
||||
import org.bouncycastle.asn1.x509.GeneralName
|
||||
import org.bouncycastle.asn1.x509.GeneralSubtree
|
||||
import org.bouncycastle.asn1.x509.NameConstraints
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.security.cert.CertPathValidatorException
|
||||
import java.security.cert.Certificate
|
||||
import java.security.cert.X509Certificate
|
||||
import javax.security.auth.x500.X500Principal
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@ -32,18 +34,10 @@ class NetworkRegistrationHelperTest {
|
||||
private val requestId = SecureHash.randomSHA256().toString()
|
||||
private val nodeLegalName = ALICE_NAME
|
||||
|
||||
private lateinit var rootCaCert: X509Certificate
|
||||
private lateinit var intermediateCaCert: X509Certificate
|
||||
private lateinit var nodeCaCert: X509Certificate
|
||||
private lateinit var config: NodeConfiguration
|
||||
|
||||
@Before
|
||||
fun init() {
|
||||
val (rootCa, intermediateCa, nodeCa) = createDevNodeCaCertPath(nodeLegalName)
|
||||
this.rootCaCert = rootCa.certificate.cert
|
||||
this.intermediateCaCert = intermediateCa.certificate.cert
|
||||
this.nodeCaCert = nodeCa.certificate.cert
|
||||
|
||||
val baseDirectory = fs.getPath("/baseDir").createDirectories()
|
||||
abstract class AbstractNodeConfiguration : NodeConfiguration
|
||||
config = rigorousMock<AbstractNodeConfiguration>().also {
|
||||
@ -66,9 +60,10 @@ class NetworkRegistrationHelperTest {
|
||||
assertThat(config.sslKeystore).doesNotExist()
|
||||
assertThat(config.trustStoreFile).doesNotExist()
|
||||
|
||||
saveTrustStoreWithRootCa(rootCaCert)
|
||||
val nodeCaCertPath = createNodeCaCertPath()
|
||||
|
||||
createRegistrationHelper().buildKeystore()
|
||||
saveTrustStoreWithRootCa(nodeCaCertPath.last())
|
||||
createRegistrationHelper(nodeCaCertPath).buildKeystore()
|
||||
|
||||
val nodeKeystore = loadKeyStore(config.nodeKeystore, config.keyStorePassword)
|
||||
val sslKeystore = loadKeyStore(config.sslKeystore, config.keyStorePassword)
|
||||
@ -79,8 +74,7 @@ class NetworkRegistrationHelperTest {
|
||||
assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA))
|
||||
assertFalse(containsAlias(X509Utilities.CORDA_ROOT_CA))
|
||||
assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_TLS))
|
||||
val nodeCaCertChain = getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
|
||||
assertThat(nodeCaCertChain).containsExactly(nodeCaCert, intermediateCaCert, rootCaCert)
|
||||
assertThat(getCertificateChain(X509Utilities.CORDA_CLIENT_CA)).containsExactly(*nodeCaCertPath)
|
||||
}
|
||||
|
||||
sslKeystore.run {
|
||||
@ -92,40 +86,77 @@ class NetworkRegistrationHelperTest {
|
||||
assertThat(nodeTlsCertChain).hasSize(4)
|
||||
// The TLS cert has the same subject as the node CA cert
|
||||
assertThat(CordaX500Name.build((nodeTlsCertChain[0] as X509Certificate).subjectX500Principal)).isEqualTo(nodeLegalName)
|
||||
assertThat(nodeTlsCertChain.drop(1)).containsExactly(nodeCaCert, intermediateCaCert, rootCaCert)
|
||||
assertThat(nodeTlsCertChain.drop(1)).containsExactly(*nodeCaCertPath)
|
||||
}
|
||||
|
||||
trustStore.run {
|
||||
assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_CA))
|
||||
assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA))
|
||||
assertTrue(containsAlias(X509Utilities.CORDA_ROOT_CA))
|
||||
val trustStoreRootCaCert = getCertificate(X509Utilities.CORDA_ROOT_CA)
|
||||
assertThat(trustStoreRootCaCert).isEqualTo(rootCaCert)
|
||||
assertThat(getCertificate(X509Utilities.CORDA_ROOT_CA)).isEqualTo(nodeCaCertPath.last())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `missing truststore`() {
|
||||
val nodeCaCertPath = createNodeCaCertPath()
|
||||
assertThatThrownBy {
|
||||
createRegistrationHelper()
|
||||
createRegistrationHelper(nodeCaCertPath)
|
||||
}.hasMessageContaining("This file must contain the root CA cert of your compatibility zone. Please contact your CZ operator.")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `node CA with incorrect cert role`() {
|
||||
val nodeCaCertPath = createNodeCaCertPath(type = CertificateType.TLS)
|
||||
saveTrustStoreWithRootCa(nodeCaCertPath.last())
|
||||
val registrationHelper = createRegistrationHelper(nodeCaCertPath)
|
||||
assertThatExceptionOfType(CertificateRequestException::class.java)
|
||||
.isThrownBy { registrationHelper.buildKeystore() }
|
||||
.withMessageContaining(CertificateType.TLS.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `node CA with incorrect subject`() {
|
||||
val invalidName = CordaX500Name("Foo", "MU", "GB")
|
||||
val nodeCaCertPath = createNodeCaCertPath(legalName = invalidName)
|
||||
saveTrustStoreWithRootCa(nodeCaCertPath.last())
|
||||
val registrationHelper = createRegistrationHelper(nodeCaCertPath)
|
||||
assertThatExceptionOfType(CertificateRequestException::class.java)
|
||||
.isThrownBy { registrationHelper.buildKeystore() }
|
||||
.withMessageContaining(invalidName.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `wrong root cert in truststore`() {
|
||||
val rootKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val rootCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name("Foo", "MU", "GB"), rootKeyPair)
|
||||
saveTrustStoreWithRootCa(rootCert.cert)
|
||||
val registrationHelper = createRegistrationHelper()
|
||||
val wrongRootCert = X509Utilities.createSelfSignedCACertificate(
|
||||
X500Principal("O=Foo,L=MU,C=GB"),
|
||||
Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
|
||||
saveTrustStoreWithRootCa(wrongRootCert)
|
||||
val registrationHelper = createRegistrationHelper(createNodeCaCertPath())
|
||||
assertThatThrownBy {
|
||||
registrationHelper.buildKeystore()
|
||||
}.isInstanceOf(CertPathValidatorException::class.java)
|
||||
}
|
||||
|
||||
private fun createRegistrationHelper(): NetworkRegistrationHelper {
|
||||
private fun createNodeCaCertPath(type: CertificateType = CertificateType.NODE_CA,
|
||||
legalName: CordaX500Name = nodeLegalName): Array<X509Certificate> {
|
||||
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
|
||||
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.x500Name))), arrayOf())
|
||||
val nodeCaCert = X509Utilities.createCertificate(
|
||||
type,
|
||||
intermediateCa.certificate,
|
||||
intermediateCa.keyPair,
|
||||
legalName.x500Principal,
|
||||
keyPair.public,
|
||||
nameConstraints = nameConstraints)
|
||||
return arrayOf(nodeCaCert, intermediateCa.certificate, rootCa.certificate)
|
||||
}
|
||||
|
||||
private fun createRegistrationHelper(response: Array<X509Certificate>): NetworkRegistrationHelper {
|
||||
val certService = rigorousMock<NetworkRegistrationService>().also {
|
||||
doReturn(requestId).whenever(it).submitRequest(any())
|
||||
doReturn(arrayOf<Certificate>(nodeCaCert, intermediateCaCert, rootCaCert)).whenever(it).retrieveCertificates(eq(requestId))
|
||||
doReturn(response).whenever(it).retrieveCertificates(eq(requestId))
|
||||
}
|
||||
return NetworkRegistrationHelper(config, certService)
|
||||
}
|
||||
|
@ -8,6 +8,9 @@ import net.corda.nodeapi.internal.config.User
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.driver.PortAllocation
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.internal.IntegrationTest
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.internal.toDatabaseSchemaName
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Test
|
||||
import java.util.concurrent.CompletableFuture.supplyAsync
|
||||
|
@ -2,7 +2,8 @@ package net.corda.bank
|
||||
|
||||
import net.corda.finance.DOLLARS
|
||||
import net.corda.finance.POUNDS
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.internal.IntegrationTest
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.node.internal.demorun.deployNodesThen
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Test
|
||||
|
@ -14,6 +14,9 @@ import net.corda.node.services.Permissions.Companion.startFlow
|
||||
import net.corda.nodeapi.internal.config.User
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.internal.IntegrationTest
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.internal.toDatabaseSchemaName
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Test
|
||||
|
||||
|
@ -9,11 +9,12 @@ webapp which provides REST API and web frontend. Application communicate using C
|
||||
|
||||
To run from the command line in Unix:
|
||||
1. Run ``./gradlew samples:irs-demo:cordapp:deployNodes`` to install configs and a command line tool under
|
||||
``samples/irs-demo/build``
|
||||
``samples/irs-demo/cordapp/build``
|
||||
2. Run ``./gradlew samples:irs-demo:web:deployWebapps`` to install configs and tools for running webservers
|
||||
3. Move to the ``samples/irs-demo/`` directory
|
||||
4. Run ``./cordapp/build/nodes/runnodes`` to open up three new terminals with the three nodes (you may have to install xterm)
|
||||
5. Run ``./web/build/webapps/runwebapps`` to open three more terminals for associated webserver
|
||||
5. On Linux, run ``./web/build/webapps/runwebapps`` to open three more terminals for associated webservers. On macOS,
|
||||
use the following command instead: ``osascript ./web/build/webapps/runwebapps.scpt``
|
||||
|
||||
To run from the command line in Windows:
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user