mirror of
https://github.com/corda/corda.git
synced 2024-12-29 01:08: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)
|
public <init>(String)
|
||||||
@org.jetbrains.annotations.NotNull public final String getMethodName()
|
@org.jetbrains.annotations.NotNull public final String getMethodName()
|
||||||
##
|
##
|
||||||
|
public static interface net.corda.testing.node.InMemoryMessagingNetwork$LatencyCalculator
|
||||||
|
@org.jetbrains.annotations.NotNull public abstract java.time.Duration between(net.corda.core.messaging.SingleMessageRecipient, net.corda.core.messaging.SingleMessageRecipient)
|
||||||
|
##
|
||||||
public final class net.corda.client.rpc.CordaRPCClient extends java.lang.Object
|
public final class net.corda.client.rpc.CordaRPCClient extends java.lang.Object
|
||||||
public <init>(net.corda.core.utilities.NetworkHostAndPort)
|
public <init>(net.corda.core.utilities.NetworkHostAndPort)
|
||||||
public <init>(net.corda.core.utilities.NetworkHostAndPort, net.corda.client.rpc.CordaRPCClientConfiguration)
|
public <init>(net.corda.core.utilities.NetworkHostAndPort, net.corda.client.rpc.CordaRPCClientConfiguration)
|
||||||
|
@ -31,6 +31,9 @@ import net.corda.node.services.Permissions.Companion.startFlow
|
|||||||
import net.corda.nodeapi.internal.config.User
|
import net.corda.nodeapi.internal.config.User
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
|
import net.corda.testing.internal.IntegrationTest
|
||||||
|
import net.corda.testing.internal.IntegrationTestSchemas
|
||||||
|
import net.corda.testing.internal.toDatabaseSchemaNames
|
||||||
import org.junit.ClassRule
|
import org.junit.ClassRule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
@ -54,7 +57,7 @@ class NodeMonitorModelTest : IntegrationTest() {
|
|||||||
companion object {
|
companion object {
|
||||||
@ClassRule @JvmField
|
@ClassRule @JvmField
|
||||||
val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, CHARLIE_NAME, DUMMY_NOTARY_NAME)
|
val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, CHARLIE_NAME, DUMMY_NOTARY_NAME)
|
||||||
.map { it.toDatabaseSchemaNames("","_10000","_10003") }.flatten().toTypedArray())
|
.map { it.toDatabaseSchemaNames("", "_10000", "_10003") }.flatten().toTypedArray())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setup(runTest: () -> Unit) {
|
private fun setup(runTest: () -> Unit) {
|
||||||
|
@ -12,8 +12,8 @@ import net.corda.node.internal.Node;
|
|||||||
import net.corda.node.internal.StartedNode;
|
import net.corda.node.internal.StartedNode;
|
||||||
import net.corda.nodeapi.internal.config.User;
|
import net.corda.nodeapi.internal.config.User;
|
||||||
import net.corda.testing.CoreTestUtils;
|
import net.corda.testing.CoreTestUtils;
|
||||||
import net.corda.testing.IntegrationTestKt;
|
import net.corda.testing.internal.IntegrationTestKt;
|
||||||
import net.corda.testing.IntegrationTestSchemas;
|
import net.corda.testing.internal.IntegrationTestSchemas;
|
||||||
import net.corda.testing.node.internal.NodeBasedTest;
|
import net.corda.testing.node.internal.NodeBasedTest;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@ -32,7 +32,6 @@ import static net.corda.finance.contracts.GetBalances.getCashBalance;
|
|||||||
import static net.corda.node.services.Permissions.invokeRpc;
|
import static net.corda.node.services.Permissions.invokeRpc;
|
||||||
import static net.corda.node.services.Permissions.startFlow;
|
import static net.corda.node.services.Permissions.startFlow;
|
||||||
import static net.corda.testing.TestConstants.ALICE_NAME;
|
import static net.corda.testing.TestConstants.ALICE_NAME;
|
||||||
import static net.corda.testing.TestConstants.DUMMY_NOTARY_NAME;
|
|
||||||
|
|
||||||
public class CordaRPCJavaClientTest extends NodeBasedTest {
|
public class CordaRPCJavaClientTest extends NodeBasedTest {
|
||||||
public CordaRPCJavaClientTest() {
|
public CordaRPCJavaClientTest() {
|
||||||
|
@ -9,6 +9,9 @@ import net.corda.core.serialization.CordaSerializable
|
|||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
|
import net.corda.testing.internal.IntegrationTest
|
||||||
|
import net.corda.testing.internal.IntegrationTestSchemas
|
||||||
|
import net.corda.testing.internal.toDatabaseSchemaName
|
||||||
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
||||||
import org.junit.ClassRule
|
import org.junit.ClassRule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
@ -22,6 +22,8 @@ import net.corda.node.services.Permissions.Companion.invokeRpc
|
|||||||
import net.corda.node.services.Permissions.Companion.startFlow
|
import net.corda.node.services.Permissions.Companion.startFlow
|
||||||
import net.corda.nodeapi.internal.config.User
|
import net.corda.nodeapi.internal.config.User
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
|
import net.corda.testing.internal.IntegrationTestSchemas
|
||||||
|
import net.corda.testing.internal.toDatabaseSchemaName
|
||||||
import net.corda.testing.node.internal.NodeBasedTest
|
import net.corda.testing.node.internal.NodeBasedTest
|
||||||
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
|
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
@ -13,7 +13,10 @@ import net.corda.core.utilities.*
|
|||||||
import net.corda.node.services.messaging.RPCServerConfiguration
|
import net.corda.node.services.messaging.RPCServerConfiguration
|
||||||
import net.corda.nodeapi.RPCApi
|
import net.corda.nodeapi.RPCApi
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
|
import net.corda.testing.internal.IntegrationTest
|
||||||
|
import net.corda.testing.internal.IntegrationTestSchemas
|
||||||
import net.corda.testing.internal.testThreadFactory
|
import net.corda.testing.internal.testThreadFactory
|
||||||
|
import net.corda.testing.internal.toDatabaseSchemaNames
|
||||||
import net.corda.testing.node.internal.*
|
import net.corda.testing.node.internal.*
|
||||||
import org.apache.activemq.artemis.api.core.SimpleString
|
import org.apache.activemq.artemis.api.core.SimpleString
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
@ -45,7 +48,7 @@ class RPCStabilityTests : IntegrationTest() {
|
|||||||
companion object {
|
companion object {
|
||||||
@ClassRule @JvmField
|
@ClassRule @JvmField
|
||||||
val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, DUMMY_BANK_A_NAME)
|
val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, DUMMY_BANK_A_NAME)
|
||||||
.map { it.toDatabaseSchemaNames("","_10000","_10003","_10012") }.flatten().toTypedArray())
|
.map { it.toDatabaseSchemaNames("", "_10000", "_10003", "_10012") }.flatten().toTypedArray())
|
||||||
}
|
}
|
||||||
|
|
||||||
object DummyOps : RPCOps {
|
object DummyOps : RPCOps {
|
||||||
|
@ -10,7 +10,6 @@ import net.corda.core.identity.AnonymousParty
|
|||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.identity.PartyAndCertificate
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
import net.corda.core.internal.toX509CertHolder
|
|
||||||
import net.corda.core.node.services.IdentityService
|
import net.corda.core.node.services.IdentityService
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import net.corda.core.serialization.SerializedBytes
|
import net.corda.core.serialization.SerializedBytes
|
||||||
@ -18,14 +17,8 @@ import net.corda.core.serialization.deserialize
|
|||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.utilities.ProgressTracker
|
import net.corda.core.utilities.ProgressTracker
|
||||||
import net.corda.core.utilities.unwrap
|
import net.corda.core.utilities.unwrap
|
||||||
import org.bouncycastle.asn1.DERSet
|
|
||||||
import org.bouncycastle.asn1.pkcs.CertificationRequestInfo
|
|
||||||
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
|
|
||||||
import java.io.ByteArrayOutputStream
|
|
||||||
import java.nio.charset.Charset
|
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.security.SignatureException
|
import java.security.SignatureException
|
||||||
import java.security.cert.CertPath
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
gradlePluginsVersion=3.0.2
|
gradlePluginsVersion=3.0.3
|
||||||
kotlinVersion=1.1.60
|
kotlinVersion=1.1.60
|
||||||
platformVersion=1
|
platformVersion=1
|
||||||
guavaVersion=21.0
|
guavaVersion=21.0
|
||||||
|
@ -30,6 +30,7 @@ import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPublicKey
|
|||||||
import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter
|
import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter
|
||||||
import org.bouncycastle.jce.ECNamedCurveTable
|
import org.bouncycastle.jce.ECNamedCurveTable
|
||||||
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
||||||
|
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec
|
||||||
import org.bouncycastle.jce.spec.ECParameterSpec
|
import org.bouncycastle.jce.spec.ECParameterSpec
|
||||||
import org.bouncycastle.jce.spec.ECPrivateKeySpec
|
import org.bouncycastle.jce.spec.ECPrivateKeySpec
|
||||||
import org.bouncycastle.jce.spec.ECPublicKeySpec
|
import org.bouncycastle.jce.spec.ECPublicKeySpec
|
||||||
@ -808,7 +809,7 @@ object Crypto {
|
|||||||
/**
|
/**
|
||||||
* Returns a key pair derived from the given [BigInteger] entropy. This is useful for unit tests
|
* Returns a key pair derived from the given [BigInteger] entropy. This is useful for unit tests
|
||||||
* and other cases where you want hard-coded private keys.
|
* and other cases where you want hard-coded private keys.
|
||||||
* Currently, [EDDSA_ED25519_SHA512] is the sole scheme supported for this operation.
|
* Currently, the following schemes are supported: [EDDSA_ED25519_SHA512], [ECDSA_SECP256R1_SHA256] and [ECDSA_SECP256K1_SHA256].
|
||||||
* @param signatureScheme a supported [SignatureScheme], see [Crypto].
|
* @param signatureScheme a supported [SignatureScheme], see [Crypto].
|
||||||
* @param entropy a [BigInteger] value.
|
* @param entropy a [BigInteger] value.
|
||||||
* @return a new [KeyPair] from an entropy input.
|
* @return a new [KeyPair] from an entropy input.
|
||||||
@ -818,6 +819,7 @@ object Crypto {
|
|||||||
fun deriveKeyPairFromEntropy(signatureScheme: SignatureScheme, entropy: BigInteger): KeyPair {
|
fun deriveKeyPairFromEntropy(signatureScheme: SignatureScheme, entropy: BigInteger): KeyPair {
|
||||||
return when (signatureScheme) {
|
return when (signatureScheme) {
|
||||||
EDDSA_ED25519_SHA512 -> deriveEdDSAKeyPairFromEntropy(entropy)
|
EDDSA_ED25519_SHA512 -> deriveEdDSAKeyPairFromEntropy(entropy)
|
||||||
|
ECDSA_SECP256R1_SHA256, ECDSA_SECP256K1_SHA256 -> deriveECDSAKeyPairFromEntropy(signatureScheme, entropy)
|
||||||
else -> throw IllegalArgumentException("Unsupported signature scheme for fixed entropy-based key pair " +
|
else -> throw IllegalArgumentException("Unsupported signature scheme for fixed entropy-based key pair " +
|
||||||
"generation: ${signatureScheme.schemeCodeName}")
|
"generation: ${signatureScheme.schemeCodeName}")
|
||||||
}
|
}
|
||||||
@ -832,6 +834,9 @@ object Crypto {
|
|||||||
fun deriveKeyPairFromEntropy(entropy: BigInteger): KeyPair = deriveKeyPairFromEntropy(DEFAULT_SIGNATURE_SCHEME, entropy)
|
fun deriveKeyPairFromEntropy(entropy: BigInteger): KeyPair = deriveKeyPairFromEntropy(DEFAULT_SIGNATURE_SCHEME, entropy)
|
||||||
|
|
||||||
// Custom key pair generator from entropy.
|
// Custom key pair generator from entropy.
|
||||||
|
// The BigIntenger.toByteArray() uses the two's-complement representation.
|
||||||
|
// The entropy is transformed to a byte array in big-endian byte-order and
|
||||||
|
// only the first ed25519.field.getb() / 8 bytes are used.
|
||||||
private fun deriveEdDSAKeyPairFromEntropy(entropy: BigInteger): KeyPair {
|
private fun deriveEdDSAKeyPairFromEntropy(entropy: BigInteger): KeyPair {
|
||||||
val params = EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec
|
val params = EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec
|
||||||
val bytes = entropy.toByteArray().copyOf(params.curve.field.getb() / 8) // Need to pad the entropy to the valid seed length.
|
val bytes = entropy.toByteArray().copyOf(params.curve.field.getb() / 8) // Need to pad the entropy to the valid seed length.
|
||||||
@ -840,6 +845,32 @@ object Crypto {
|
|||||||
return KeyPair(EdDSAPublicKey(pub), EdDSAPrivateKey(priv))
|
return KeyPair(EdDSAPublicKey(pub), EdDSAPrivateKey(priv))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Custom key pair generator from an entropy required for various tests. It is similar to deriveKeyPairECDSA,
|
||||||
|
// but the accepted range of the input entropy is more relaxed:
|
||||||
|
// 2 <= entropy < N, where N is the order of base-point G.
|
||||||
|
private fun deriveECDSAKeyPairFromEntropy(signatureScheme: SignatureScheme, entropy: BigInteger): KeyPair {
|
||||||
|
val parameterSpec = signatureScheme.algSpec as ECNamedCurveParameterSpec
|
||||||
|
|
||||||
|
// The entropy might be a negative number and/or out of range (e.g. PRNG output).
|
||||||
|
// In such cases we retry with hash(currentEntropy).
|
||||||
|
while (entropy < ECConstants.TWO || entropy >= parameterSpec.n) {
|
||||||
|
return deriveECDSAKeyPairFromEntropy(signatureScheme, BigInteger(1, entropy.toByteArray().sha256().bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
val privateKeySpec = ECPrivateKeySpec(entropy, parameterSpec)
|
||||||
|
val priv = BCECPrivateKey("EC", privateKeySpec, BouncyCastleProvider.CONFIGURATION)
|
||||||
|
|
||||||
|
val pointQ = FixedPointCombMultiplier().multiply(parameterSpec.g, entropy)
|
||||||
|
while (pointQ.isInfinity) {
|
||||||
|
// Instead of throwing an exception, we retry with hash(entropy).
|
||||||
|
return deriveECDSAKeyPairFromEntropy(signatureScheme, BigInteger(1, entropy.toByteArray().sha256().bytes))
|
||||||
|
}
|
||||||
|
val publicKeySpec = ECPublicKeySpec(pointQ, parameterSpec)
|
||||||
|
val pub = BCECPublicKey("EC", publicKeySpec, BouncyCastleProvider.CONFIGURATION)
|
||||||
|
|
||||||
|
return KeyPair(pub, priv)
|
||||||
|
}
|
||||||
|
|
||||||
// Compute the HMAC-SHA512 using a privateKey as the MAC_key and a seed ByteArray.
|
// Compute the HMAC-SHA512 using a privateKey as the MAC_key and a seed ByteArray.
|
||||||
private fun deriveHMAC(privateKey: PrivateKey, seed: ByteArray): ByteArray {
|
private fun deriveHMAC(privateKey: PrivateKey, seed: ByteArray): ByteArray {
|
||||||
// Compute hmac(privateKey, seed).
|
// Compute hmac(privateKey, seed).
|
||||||
|
@ -118,18 +118,19 @@ fun Iterable<TransactionSignature>.byKeys() = map { it.by }.toSet()
|
|||||||
|
|
||||||
// Allow Kotlin destructuring:
|
// Allow Kotlin destructuring:
|
||||||
// val (private, public) = keyPair
|
// val (private, public) = keyPair
|
||||||
/* The [PrivateKey] of this [KeyPair] .*/
|
/* The [PrivateKey] of this [KeyPair]. */
|
||||||
operator fun KeyPair.component1(): PrivateKey = this.private
|
operator fun KeyPair.component1(): PrivateKey = this.private
|
||||||
/* The [PublicKey] of this [KeyPair] .*/
|
/* The [PublicKey] of this [KeyPair]. */
|
||||||
operator fun KeyPair.component2(): PublicKey = this.public
|
operator fun KeyPair.component2(): PublicKey = this.public
|
||||||
|
|
||||||
/** A simple wrapper that will make it easier to swap out the EC algorithm we use in future. */
|
/** A simple wrapper that will make it easier to swap out the signature algorithm we use in future. */
|
||||||
fun generateKeyPair(): KeyPair = Crypto.generateKeyPair()
|
fun generateKeyPair(): KeyPair = Crypto.generateKeyPair()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a key pair derived from the given private key entropy. This is useful for unit tests and other cases where
|
* Returns a key pair derived from the given private key entropy. This is useful for unit tests and other cases where
|
||||||
* you want hard-coded private keys.
|
* you want hard-coded private keys.
|
||||||
* This currently works for the default signature scheme EdDSA ed25519 only.
|
* @param entropy a [BigInteger] value.
|
||||||
|
* @return a deterministically generated [KeyPair] for the [Crypto.DEFAULT_SIGNATURE_SCHEME].
|
||||||
*/
|
*/
|
||||||
fun entropyToKeyPair(entropy: BigInteger): KeyPair = Crypto.deriveKeyPairFromEntropy(entropy)
|
fun entropyToKeyPair(entropy: BigInteger): KeyPair = Crypto.deriveKeyPairFromEntropy(entropy)
|
||||||
|
|
||||||
|
@ -10,7 +10,17 @@ import net.corda.core.serialization.CordaSerializable
|
|||||||
*/
|
*/
|
||||||
@DoNotImplement
|
@DoNotImplement
|
||||||
interface FlowLogicRefFactory {
|
interface FlowLogicRefFactory {
|
||||||
|
/**
|
||||||
|
* Construct a FlowLogicRef. This is intended for cases where the calling code has the relevant class already
|
||||||
|
* and can provide it directly.
|
||||||
|
*/
|
||||||
|
@Deprecated("This should be avoided, and the version which takes a class name used instead to avoid requiring the class on the classpath to deserialize calling code")
|
||||||
fun create(flowClass: Class<out FlowLogic<*>>, vararg args: Any?): FlowLogicRef
|
fun create(flowClass: Class<out FlowLogic<*>>, vararg args: Any?): FlowLogicRef
|
||||||
|
/**
|
||||||
|
* Construct a FlowLogicRef. This is intended for cases where the calling code does not want to require the flow
|
||||||
|
* class on the classpath for all cases where the calling code is loaded.
|
||||||
|
*/
|
||||||
|
fun create(flowClassName: String, vararg args: Any?): FlowLogicRef
|
||||||
fun createForRPC(flowClass: Class<out FlowLogic<*>>, vararg args: Any?): FlowLogicRef
|
fun createForRPC(flowClass: Class<out FlowLogic<*>>, vararg args: Any?): FlowLogicRef
|
||||||
fun toFlowLogic(ref: FlowLogicRef): FlowLogic<*>
|
fun toFlowLogic(ref: FlowLogicRef): FlowLogic<*>
|
||||||
}
|
}
|
||||||
|
@ -80,8 +80,10 @@ data class CordaX500Name(val commonName: String?,
|
|||||||
const val MAX_LENGTH_STATE = 64
|
const val MAX_LENGTH_STATE = 64
|
||||||
const val MAX_LENGTH_ORGANISATION_UNIT = 64
|
const val MAX_LENGTH_ORGANISATION_UNIT = 64
|
||||||
const val MAX_LENGTH_COMMON_NAME = 64
|
const val MAX_LENGTH_COMMON_NAME = 64
|
||||||
|
|
||||||
private val supportedAttributes = setOf(BCStyle.O, BCStyle.C, BCStyle.L, BCStyle.CN, BCStyle.ST, BCStyle.OU)
|
private val supportedAttributes = setOf(BCStyle.O, BCStyle.C, BCStyle.L, BCStyle.CN, BCStyle.ST, BCStyle.OU)
|
||||||
private val countryCodes: Set<String> = ImmutableSet.copyOf(Locale.getISOCountries() + unspecifiedCountry)
|
private val countryCodes: Set<String> = ImmutableSet.copyOf(Locale.getISOCountries() + unspecifiedCountry)
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun build(principal: X500Principal): CordaX500Name {
|
fun build(principal: X500Principal): CordaX500Name {
|
||||||
val x500Name = X500Name.getInstance(principal.encoded)
|
val x500Name = X500Name.getInstance(principal.encoded)
|
||||||
@ -115,20 +117,12 @@ data class CordaX500Name(val commonName: String?,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transient
|
@Transient
|
||||||
private var x500Cache: X500Name? = null
|
private var _x500Principal: X500Principal? = null
|
||||||
|
|
||||||
val x500Principal: X500Principal
|
/** Return the [X500Principal] equivalent of this name. */
|
||||||
get() {
|
val x500Principal: X500Principal get() {
|
||||||
if (x500Cache == null) {
|
return _x500Principal ?: X500Principal(this.x500Name.encoded).also { _x500Principal = it }
|
||||||
x500Cache = this.x500Name
|
|
||||||
}
|
|
||||||
return X500Principal(x500Cache!!.encoded)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
if (x500Cache == null) {
|
|
||||||
x500Cache = this.x500Name
|
|
||||||
}
|
|
||||||
return x500Cache.toString()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun toString(): String = x500Principal.toString()
|
||||||
}
|
}
|
@ -10,8 +10,9 @@ import net.corda.core.node.ServicesForResolution
|
|||||||
import net.corda.core.serialization.SerializationContext
|
import net.corda.core.serialization.SerializationContext
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.transactions.WireTransaction
|
import net.corda.core.transactions.WireTransaction
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter
|
import org.bouncycastle.asn1.x500.X500NameBuilder
|
||||||
|
import org.bouncycastle.asn1.x500.style.BCStyle
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import rx.Observer
|
import rx.Observer
|
||||||
@ -26,8 +27,6 @@ import java.nio.charset.Charset
|
|||||||
import java.nio.charset.StandardCharsets.UTF_8
|
import java.nio.charset.StandardCharsets.UTF_8
|
||||||
import java.nio.file.*
|
import java.nio.file.*
|
||||||
import java.nio.file.attribute.FileAttribute
|
import java.nio.file.attribute.FileAttribute
|
||||||
import java.security.cert.Certificate
|
|
||||||
import java.security.cert.X509Certificate
|
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.temporal.Temporal
|
import java.time.temporal.Temporal
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -186,9 +185,6 @@ fun <T> logElapsedTime(label: String, logger: Logger? = null, body: () -> T): T
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Certificate.toX509CertHolder() = X509CertificateHolder(encoded)
|
|
||||||
val X509CertificateHolder.cert: X509Certificate get() = JcaX509CertificateConverter().getCertificate(this)
|
|
||||||
|
|
||||||
/** Convert a [ByteArrayOutputStream] to [InputStreamAndHash]. */
|
/** Convert a [ByteArrayOutputStream] to [InputStreamAndHash]. */
|
||||||
fun ByteArrayOutputStream.toInputStreamAndHash(): InputStreamAndHash {
|
fun ByteArrayOutputStream.toInputStreamAndHash(): InputStreamAndHash {
|
||||||
val bytes = toByteArray()
|
val bytes = toByteArray()
|
||||||
@ -320,6 +316,22 @@ fun ExecutorService.join() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the underlying X.500 name from this Corda-safe X.500 name. These are guaranteed to have a consistent
|
||||||
|
* ordering, such that their `toString()` function returns the same value every time for the same [CordaX500Name].
|
||||||
|
*/
|
||||||
|
val CordaX500Name.x500Name: X500Name
|
||||||
|
get() {
|
||||||
|
return X500NameBuilder(BCStyle.INSTANCE).apply {
|
||||||
|
addRDN(BCStyle.C, country)
|
||||||
|
state?.let { addRDN(BCStyle.ST, it) }
|
||||||
|
addRDN(BCStyle.L, locality)
|
||||||
|
addRDN(BCStyle.O, organisation)
|
||||||
|
organisationUnit?.let { addRDN(BCStyle.OU, it) }
|
||||||
|
commonName?.let { addRDN(BCStyle.CN, it) }
|
||||||
|
}.build()
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
val CordaX500Name.Companion.unspecifiedCountry
|
val CordaX500Name.Companion.unspecifiedCountry
|
||||||
|
@ -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
|
package net.corda.core.crypto
|
||||||
|
|
||||||
import net.corda.core.crypto.CompositeKey.NodeAndWeight
|
import net.corda.core.crypto.CompositeKey.NodeAndWeight
|
||||||
import net.corda.core.identity.CordaX500Name
|
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.internal.declaredField
|
import net.corda.core.internal.declaredField
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
@ -15,6 +13,7 @@ import org.junit.Rule
|
|||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.rules.TemporaryFolder
|
import org.junit.rules.TemporaryFolder
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertFailsWith
|
import kotlin.test.assertFailsWith
|
||||||
import kotlin.test.assertFalse
|
import kotlin.test.assertFalse
|
||||||
@ -334,7 +333,7 @@ class CompositeKeyTests {
|
|||||||
|
|
||||||
// Create self sign CA.
|
// Create self sign CA.
|
||||||
val caKeyPair = Crypto.generateKeyPair()
|
val caKeyPair = Crypto.generateKeyPair()
|
||||||
val caName = CordaX500Name(commonName = "Test CA", organisation = "R3 Ltd", locality = "London", country = "GB")
|
val caName = X500Principal("CN=Test CA,O=R3 Ltd,L=London,C=GB")
|
||||||
val ca = X509Utilities.createSelfSignedCACertificate(caName, caKeyPair)
|
val ca = X509Utilities.createSelfSignedCACertificate(caName, caKeyPair)
|
||||||
|
|
||||||
// Sign the composite key with the self sign CA.
|
// Sign the composite key with the self sign CA.
|
||||||
@ -343,7 +342,7 @@ class CompositeKeyTests {
|
|||||||
// Store certificate to keystore.
|
// Store certificate to keystore.
|
||||||
val keystorePath = tempFolder.root.toPath() / "keystore.jks"
|
val keystorePath = tempFolder.root.toPath() / "keystore.jks"
|
||||||
val keystore = loadOrCreateKeyStore(keystorePath, "password")
|
val keystore = loadOrCreateKeyStore(keystorePath, "password")
|
||||||
keystore.setCertificateEntry("CompositeKey", compositeKeyCert.cert)
|
keystore.setCertificateEntry("CompositeKey", compositeKeyCert)
|
||||||
keystore.save(keystorePath, "password")
|
keystore.save(keystorePath, "password")
|
||||||
|
|
||||||
// Load keystore from disk.
|
// Load keystore from disk.
|
||||||
|
@ -14,21 +14,22 @@ import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey
|
|||||||
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey
|
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey
|
||||||
import org.bouncycastle.jce.ECNamedCurveTable
|
import org.bouncycastle.jce.ECNamedCurveTable
|
||||||
import org.bouncycastle.jce.interfaces.ECKey
|
import org.bouncycastle.jce.interfaces.ECKey
|
||||||
|
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec
|
||||||
import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PrivateKey
|
import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PrivateKey
|
||||||
import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PublicKey
|
import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PublicKey
|
||||||
import org.junit.Assert.assertNotEquals
|
import org.junit.Assert.assertNotEquals
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import java.math.BigInteger
|
||||||
import java.security.KeyPairGenerator
|
import java.security.KeyPairGenerator
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run tests for cryptographic algorithms
|
* Run tests for cryptographic algorithms.
|
||||||
*/
|
*/
|
||||||
class CryptoUtilsTest {
|
class CryptoUtilsTest {
|
||||||
|
|
||||||
private val testString = "Hello World"
|
private val testBytes = "Hello World".toByteArray()
|
||||||
private val testBytes = testString.toByteArray()
|
|
||||||
|
|
||||||
// key generation test
|
// key generation test
|
||||||
@Test
|
@Test
|
||||||
@ -781,4 +782,113 @@ class CryptoUtilsTest {
|
|||||||
assertEquals(dpriv2, dpriv_2)
|
assertEquals(dpriv2, dpriv_2)
|
||||||
assertEquals(dpub2, dpub_2)
|
assertEquals(dpub2, dpub_2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `EdDSA ed25519 keyPair from entropy`() {
|
||||||
|
val keyPairPositive = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("10"))
|
||||||
|
assertEquals("DLBL3iHCp9uRReWhhCGfCsrxZZpfAm9h9GLbfN8ijqXTq", keyPairPositive.public.toStringShort())
|
||||||
|
|
||||||
|
val keyPairNegative = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("-10"))
|
||||||
|
assertEquals("DLC5HXnYsJAFqmM9hgPj5G8whQ4TpyE9WMBssqCayLBwA2", keyPairNegative.public.toStringShort())
|
||||||
|
|
||||||
|
val keyPairZero = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("0"))
|
||||||
|
assertEquals("DL4UVhGh4tqu1G86UVoGNaDDNCMsBtNHzE6BSZuNNJN7W2", keyPairZero.public.toStringShort())
|
||||||
|
|
||||||
|
val keyPairOne = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("1"))
|
||||||
|
assertEquals("DL8EZUdHixovcCynKMQzrMWBnXQAcbVDHi6ArPphqwJVzq", keyPairOne.public.toStringShort())
|
||||||
|
|
||||||
|
val keyPairBiggerThan256bits = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("2").pow(258).minus(BigInteger.TEN))
|
||||||
|
assertEquals("DLB9K1UiBrWonn481z6NzkqoWHjMBXpfDeaet3wiwRNWSU", keyPairBiggerThan256bits.public.toStringShort())
|
||||||
|
// The underlying implementation uses the first 256 bytes of the entropy. Thus, 2^258-10 and 2^258-50 and 2^514-10 have the same impact.
|
||||||
|
val keyPairBiggerThan256bitsV2 = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("2").pow(258).minus(BigInteger("50")))
|
||||||
|
assertEquals("DLB9K1UiBrWonn481z6NzkqoWHjMBXpfDeaet3wiwRNWSU", keyPairBiggerThan256bitsV2.public.toStringShort())
|
||||||
|
val keyPairBiggerThan512bits = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("2").pow(514).minus(BigInteger.TEN))
|
||||||
|
assertEquals("DLB9K1UiBrWonn481z6NzkqoWHjMBXpfDeaet3wiwRNWSU", keyPairBiggerThan512bits.public.toStringShort())
|
||||||
|
|
||||||
|
// Try another big number.
|
||||||
|
val keyPairBiggerThan258bits = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("2").pow(259).plus(BigInteger.ONE))
|
||||||
|
assertEquals("DL5tEFVMXMGrzwjfCAW34JjkhsRkPfFyJ38iEnmpB6L2Z9", keyPairBiggerThan258bits.public.toStringShort())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `ECDSA R1 keyPair from entropy`() {
|
||||||
|
val keyPairPositive = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("10"))
|
||||||
|
assertEquals("DLHDcxuSt9J3cbjd2Dsx4rAgYYA7BAP7A8VLrFiq1tH9yy", keyPairPositive.public.toStringShort())
|
||||||
|
// The underlying implementation uses the hash of entropy if it is out of range 2 < entropy < N, where N the order of the group.
|
||||||
|
val keyPairNegative = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("-10"))
|
||||||
|
assertEquals("DLBASmjiMZuu1g3EtdHJxfSueXE8PRoUWbkdU61Qcnpamt", keyPairNegative.public.toStringShort())
|
||||||
|
|
||||||
|
val keyPairZero = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("0"))
|
||||||
|
assertEquals("DLH2FEHEnsT3MpCJt2gfyNjpqRqcBxeupK4YRPXvDsVEkb", keyPairZero.public.toStringShort())
|
||||||
|
// BigIntenger.Zero is out or range, so 1 and hash(1.toByteArray) would have the same impact.
|
||||||
|
val zeroHashed = BigInteger(1, BigInteger("0").toByteArray().sha256().bytes)
|
||||||
|
// Check oneHashed < N (order of the group), otherwise we would need an extra hash.
|
||||||
|
assertEquals(-1, zeroHashed.compareTo((Crypto.ECDSA_SECP256R1_SHA256.algSpec as ECNamedCurveParameterSpec).n))
|
||||||
|
val keyPairZeroHashed = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, zeroHashed)
|
||||||
|
assertEquals("DLH2FEHEnsT3MpCJt2gfyNjpqRqcBxeupK4YRPXvDsVEkb", keyPairZeroHashed.public.toStringShort())
|
||||||
|
|
||||||
|
val keyPairOne = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("1"))
|
||||||
|
assertEquals("DLHrtKwjv6onq9HcrQDJPs8Cgtai5mZU5ZU6sb1ivJjx3z", keyPairOne.public.toStringShort())
|
||||||
|
// BigIntenger.ONE is out or range, so 1 and hash(1.toByteArray) would have the same impact.
|
||||||
|
val oneHashed = BigInteger(1, BigInteger("1").toByteArray().sha256().bytes)
|
||||||
|
// Check oneHashed < N (order of the group), otherwise we would need an extra hash.
|
||||||
|
assertEquals(-1, oneHashed.compareTo((Crypto.ECDSA_SECP256R1_SHA256.algSpec as ECNamedCurveParameterSpec).n))
|
||||||
|
val keyPairOneHashed = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, oneHashed)
|
||||||
|
assertEquals("DLHrtKwjv6onq9HcrQDJPs8Cgtai5mZU5ZU6sb1ivJjx3z", keyPairOneHashed.public.toStringShort())
|
||||||
|
|
||||||
|
// 2 is in the range.
|
||||||
|
val keyPairTwo = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2"))
|
||||||
|
assertEquals("DLFoz6txJ3vHcKNSM1vFxHJUoEQ69PorBwW64dHsAnEoZB", keyPairTwo.public.toStringShort())
|
||||||
|
|
||||||
|
// Try big numbers that are out of range.
|
||||||
|
val keyPairBiggerThan256bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2").pow(258).minus(BigInteger.TEN))
|
||||||
|
assertEquals("DLBv6fZqaCTbE4L7sgjbt19biXHMgU9CzR5s8g8XBJjZ11", keyPairBiggerThan256bits.public.toStringShort())
|
||||||
|
val keyPairBiggerThan256bitsV2 = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2").pow(258).minus(BigInteger("50")))
|
||||||
|
assertEquals("DLANmjhGSVdLyghxcPHrn3KuGatscf6LtvqifUDxw7SGU8", keyPairBiggerThan256bitsV2.public.toStringShort())
|
||||||
|
val keyPairBiggerThan512bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2").pow(514).minus(BigInteger.TEN))
|
||||||
|
assertEquals("DL9sKwMExBTD3MnJN6LWGqo496Erkebs9fxZtXLVJUBY9Z", keyPairBiggerThan512bits.public.toStringShort())
|
||||||
|
val keyPairBiggerThan258bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2").pow(259).plus(BigInteger.ONE))
|
||||||
|
assertEquals("DLBwjWwPJSF9E7b1NWaSbEJ4oK8CF7RDGWd648TiBhZoL1", keyPairBiggerThan258bits.public.toStringShort())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `ECDSA K1 keyPair from entropy`() {
|
||||||
|
val keyPairPositive = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("10"))
|
||||||
|
assertEquals("DL6pYKUgH17az8MLdonvvUtUPN8TqwpCGcdgLr7vg3skCU", keyPairPositive.public.toStringShort())
|
||||||
|
// The underlying implementation uses the hash of entropy if it is out of range 2 <= entropy < N, where N the order of the group.
|
||||||
|
val keyPairNegative = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("-10"))
|
||||||
|
assertEquals("DLnpXhxece69Nyqgm3pPt3yV7ESQYDJKoYxs1hKgfBAEu", keyPairNegative.public.toStringShort())
|
||||||
|
|
||||||
|
val keyPairZero = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("0"))
|
||||||
|
assertEquals("DLBC28e18T6KsYwjTFfUWJfhvHjvYVapyVf6antnqUkbgd", keyPairZero.public.toStringShort())
|
||||||
|
// BigIntenger.Zero is out or range, so 1 and hash(1.toByteArray) would have the same impact.
|
||||||
|
val zeroHashed = BigInteger(1, BigInteger("0").toByteArray().sha256().bytes)
|
||||||
|
// Check oneHashed < N (order of the group), otherwise we would need an extra hash.
|
||||||
|
assertEquals(-1, zeroHashed.compareTo((Crypto.ECDSA_SECP256K1_SHA256.algSpec as ECNamedCurveParameterSpec).n))
|
||||||
|
val keyPairZeroHashed = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, zeroHashed)
|
||||||
|
assertEquals("DLBC28e18T6KsYwjTFfUWJfhvHjvYVapyVf6antnqUkbgd", keyPairZeroHashed.public.toStringShort())
|
||||||
|
|
||||||
|
val keyPairOne = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("1"))
|
||||||
|
assertEquals("DLBimRXdEQhJUTpL6f9ri9woNdsze6mwkRrhsML13Eh7ET", keyPairOne.public.toStringShort())
|
||||||
|
// BigIntenger.ONE is out or range, so 1 and hash(1.toByteArray) would have the same impact.
|
||||||
|
val oneHashed = BigInteger(1, BigInteger("1").toByteArray().sha256().bytes)
|
||||||
|
// Check oneHashed < N (order of the group), otherwise we would need an extra hash.
|
||||||
|
assertEquals(-1, oneHashed.compareTo((Crypto.ECDSA_SECP256K1_SHA256.algSpec as ECNamedCurveParameterSpec).n))
|
||||||
|
val keyPairOneHashed = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, oneHashed)
|
||||||
|
assertEquals("DLBimRXdEQhJUTpL6f9ri9woNdsze6mwkRrhsML13Eh7ET", keyPairOneHashed.public.toStringShort())
|
||||||
|
|
||||||
|
// 2 is in the range.
|
||||||
|
val keyPairTwo = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2"))
|
||||||
|
assertEquals("DLG32UWaevGw9YY7w1Rf9mmK88biavgpDnJA9bG4GapVPs", keyPairTwo.public.toStringShort())
|
||||||
|
|
||||||
|
// Try big numbers that are out of range.
|
||||||
|
val keyPairBiggerThan256bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2").pow(258).minus(BigInteger.TEN))
|
||||||
|
assertEquals("DLGHsdv2xeAuM7n3sBc6mFfiphXe6VSf3YxqvviKDU6Vbd", keyPairBiggerThan256bits.public.toStringShort())
|
||||||
|
val keyPairBiggerThan256bitsV2 = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2").pow(258).minus(BigInteger("50")))
|
||||||
|
assertEquals("DL9yJfiNGqteRrKPjGUkRQkeqzuQ4kwcYQWMCi5YKuUHrk", keyPairBiggerThan256bitsV2.public.toStringShort())
|
||||||
|
val keyPairBiggerThan512bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2").pow(514).minus(BigInteger.TEN))
|
||||||
|
assertEquals("DL3Wr5EQGrMTaKBy5XMvG8rvSfKX1AYZLCRU8kixGbxt1E", keyPairBiggerThan512bits.public.toStringShort())
|
||||||
|
val keyPairBiggerThan258bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2").pow(259).plus(BigInteger.ONE))
|
||||||
|
assertEquals("DL7NbssqvuuJ4cqFkkaVYu9j1MsVswESGgCfbqBS9ULwuM", keyPairBiggerThan258bits.public.toStringShort())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package net.corda.core.crypto
|
package net.corda.core.crypto
|
||||||
|
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.nodeapi.internal.crypto.*
|
import net.corda.nodeapi.internal.crypto.*
|
||||||
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
@ -14,6 +13,7 @@ import java.security.KeyStore
|
|||||||
import java.security.cert.CertPathValidator
|
import java.security.cert.CertPathValidator
|
||||||
import java.security.cert.CertPathValidatorException
|
import java.security.cert.CertPathValidatorException
|
||||||
import java.security.cert.PKIXParameters
|
import java.security.cert.PKIXParameters
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
import kotlin.test.assertFailsWith
|
import kotlin.test.assertFailsWith
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
@ -26,17 +26,22 @@ class X509NameConstraintsTest {
|
|||||||
CertificateType.NODE_CA,
|
CertificateType.NODE_CA,
|
||||||
intermediateCa.certificate,
|
intermediateCa.certificate,
|
||||||
intermediateCa.keyPair,
|
intermediateCa.keyPair,
|
||||||
CordaX500Name("Corda Client CA", "R3 Ltd", "London", "GB"),
|
CordaX500Name("Corda Client CA", "R3 Ltd", "London", "GB").x500Principal,
|
||||||
nodeCaKeyPair.public,
|
nodeCaKeyPair.public,
|
||||||
nameConstraints = nameConstraints)
|
nameConstraints = nameConstraints)
|
||||||
|
|
||||||
val keyPass = "password"
|
val keyPass = "password"
|
||||||
val trustStore = KeyStore.getInstance(KEYSTORE_TYPE)
|
val trustStore = KeyStore.getInstance(KEYSTORE_TYPE)
|
||||||
trustStore.load(null, keyPass.toCharArray())
|
trustStore.load(null, keyPass.toCharArray())
|
||||||
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCa.certificate.cert)
|
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCa.certificate)
|
||||||
|
|
||||||
val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, subjectName, tlsKeyPair.public)
|
val tlsCert = X509Utilities.createCertificate(
|
||||||
|
CertificateType.TLS,
|
||||||
|
nodeCaCert,
|
||||||
|
nodeCaKeyPair,
|
||||||
|
X500Principal(subjectName.encoded),
|
||||||
|
tlsKeyPair.public)
|
||||||
|
|
||||||
val keyStore = KeyStore.getInstance(KEYSTORE_TYPE)
|
val keyStore = KeyStore.getInstance(KEYSTORE_TYPE)
|
||||||
keyStore.load(null, keyPass.toCharArray())
|
keyStore.load(null, keyPass.toCharArray())
|
||||||
|
@ -7,30 +7,36 @@ import kotlin.test.assertNull
|
|||||||
|
|
||||||
class CordaX500NameTest {
|
class CordaX500NameTest {
|
||||||
@Test
|
@Test
|
||||||
fun `parse service name with organisational unit`() {
|
fun `service name with organisational unit`() {
|
||||||
val name = CordaX500Name.parse("O=Bank A, L=New York, C=US, OU=Org Unit, CN=Service Name")
|
val name = CordaX500Name.parse("O=Bank A, L=New York, C=US, OU=Org Unit, CN=Service Name")
|
||||||
assertEquals("Service Name", name.commonName)
|
assertEquals("Service Name", name.commonName)
|
||||||
assertEquals("Org Unit", name.organisationUnit)
|
assertEquals("Org Unit", name.organisationUnit)
|
||||||
assertEquals("Bank A", name.organisation)
|
assertEquals("Bank A", name.organisation)
|
||||||
assertEquals("New York", name.locality)
|
assertEquals("New York", name.locality)
|
||||||
|
assertEquals(CordaX500Name.parse(name.toString()), name)
|
||||||
|
assertEquals(CordaX500Name.build(name.x500Principal), name)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `parse service name`() {
|
fun `service name`() {
|
||||||
val name = CordaX500Name.parse("O=Bank A, L=New York, C=US, CN=Service Name")
|
val name = CordaX500Name.parse("O=Bank A, L=New York, C=US, CN=Service Name")
|
||||||
assertEquals("Service Name", name.commonName)
|
assertEquals("Service Name", name.commonName)
|
||||||
assertNull(name.organisationUnit)
|
assertNull(name.organisationUnit)
|
||||||
assertEquals("Bank A", name.organisation)
|
assertEquals("Bank A", name.organisation)
|
||||||
assertEquals("New York", name.locality)
|
assertEquals("New York", name.locality)
|
||||||
|
assertEquals(CordaX500Name.parse(name.toString()), name)
|
||||||
|
assertEquals(CordaX500Name.build(name.x500Principal), name)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `parse legal entity name`() {
|
fun `legal entity name`() {
|
||||||
val name = CordaX500Name.parse("O=Bank A, L=New York, C=US")
|
val name = CordaX500Name.parse("O=Bank A, L=New York, C=US")
|
||||||
assertNull(name.commonName)
|
assertNull(name.commonName)
|
||||||
assertNull(name.organisationUnit)
|
assertNull(name.organisationUnit)
|
||||||
assertEquals("Bank A", name.organisation)
|
assertEquals("Bank A", name.organisation)
|
||||||
assertEquals("New York", name.locality)
|
assertEquals("New York", name.locality)
|
||||||
|
assertEquals(CordaX500Name.parse(name.toString()), name)
|
||||||
|
assertEquals(CordaX500Name.build(name.x500Principal), name)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
package net.corda.core.identity
|
package net.corda.core.identity
|
||||||
|
|
||||||
import net.corda.core.crypto.entropyToKeyPair
|
import net.corda.core.crypto.entropyToKeyPair
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.internal.read
|
import net.corda.core.internal.read
|
||||||
import net.corda.core.serialization.deserialize
|
import net.corda.core.serialization.deserialize
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.nodeapi.internal.crypto.KEYSTORE_TYPE
|
import net.corda.nodeapi.internal.crypto.KEYSTORE_TYPE
|
||||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||||
import net.corda.nodeapi.internal.crypto.save
|
import net.corda.nodeapi.internal.crypto.save
|
||||||
import net.corda.testing.DEV_CA
|
import net.corda.testing.DEV_ROOT_CA
|
||||||
import net.corda.testing.SerializationEnvironmentRule
|
import net.corda.testing.SerializationEnvironmentRule
|
||||||
import net.corda.testing.getTestPartyAndCertificate
|
import net.corda.testing.getTestPartyAndCertificate
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
@ -25,8 +24,8 @@ class PartyAndCertificateTest {
|
|||||||
val testSerialization = SerializationEnvironmentRule()
|
val testSerialization = SerializationEnvironmentRule()
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `should reject a path with no roles`() {
|
fun `reject a path with no roles`() {
|
||||||
val path = X509CertificateFactory().generateCertPath(DEV_CA.certificate.cert)
|
val path = X509CertificateFactory().generateCertPath(DEV_ROOT_CA.certificate)
|
||||||
assertFailsWith<IllegalArgumentException> { PartyAndCertificate(path) }
|
assertFailsWith<IllegalArgumentException> { PartyAndCertificate(path) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,9 @@ import net.corda.node.services.Permissions.Companion.startFlow
|
|||||||
import net.corda.nodeapi.internal.config.User
|
import net.corda.nodeapi.internal.config.User
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
|
import net.corda.testing.internal.IntegrationTest
|
||||||
|
import net.corda.testing.internal.IntegrationTestSchemas
|
||||||
|
import net.corda.testing.internal.toDatabaseSchemaName
|
||||||
import org.junit.ClassRule
|
import org.junit.ClassRule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
@ -103,7 +103,7 @@ Go to the terminal window displaying the CRaSH shell of PartyA. Typing ``help``
|
|||||||
commands.
|
commands.
|
||||||
|
|
||||||
.. note:: Local terminal shell is available only in a development mode. In production environment SSH server can be enabled.
|
.. note:: Local terminal shell is available only in a development mode. In production environment SSH server can be enabled.
|
||||||
More about SSH and how to connect can be found on :doc:`Shell` page.
|
More about SSH and how to connect can be found on the :doc:`shell` page.
|
||||||
|
|
||||||
We want to create an IOU of 100 with PartyB. We start the ``IOUFlow`` by typing:
|
We want to create an IOU of 100 with PartyB. We start the ``IOUFlow`` by typing:
|
||||||
|
|
||||||
|
@ -192,9 +192,6 @@ The following 3rd party types are supported.
|
|||||||
|
|
||||||
org.apache.activemq.artemis.api.core.SimpleString
|
org.apache.activemq.artemis.api.core.SimpleString
|
||||||
|
|
||||||
org.bouncycastle.asn1.x500.X500Name
|
|
||||||
org.bouncycastle.cert.X509CertificateHolder
|
|
||||||
|
|
||||||
Corda Types
|
Corda Types
|
||||||
```````````
|
```````````
|
||||||
|
|
||||||
|
@ -6,6 +6,9 @@ import net.corda.finance.EUR
|
|||||||
import net.corda.finance.USD
|
import net.corda.finance.USD
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
|
import net.corda.testing.internal.IntegrationTest
|
||||||
|
import net.corda.testing.internal.IntegrationTestSchemas
|
||||||
|
import net.corda.testing.internal.toDatabaseSchemaNames
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.ClassRule
|
import org.junit.ClassRule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@ -14,7 +17,7 @@ class CashConfigDataFlowTest : IntegrationTest() {
|
|||||||
companion object {
|
companion object {
|
||||||
@ClassRule @JvmField
|
@ClassRule @JvmField
|
||||||
val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, DUMMY_BANK_A_NAME)
|
val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, DUMMY_BANK_A_NAME)
|
||||||
.map { it.toDatabaseSchemaNames("","_10000","_10003") }.flatten().toTypedArray())
|
.map { it.toDatabaseSchemaNames("", "_10000", "_10003") }.flatten().toTypedArray())
|
||||||
}
|
}
|
||||||
@Test
|
@Test
|
||||||
fun `issuable currencies are read in from node config`() {
|
fun `issuable currencies are read in from node config`() {
|
||||||
|
@ -29,8 +29,8 @@ import java.util.*
|
|||||||
class CashExitFlow(private val amount: Amount<Currency>,
|
class CashExitFlow(private val amount: Amount<Currency>,
|
||||||
private val issuerRef: OpaqueBytes,
|
private val issuerRef: OpaqueBytes,
|
||||||
progressTracker: ProgressTracker) : AbstractCashFlow<AbstractCashFlow.Result>(progressTracker) {
|
progressTracker: ProgressTracker) : AbstractCashFlow<AbstractCashFlow.Result>(progressTracker) {
|
||||||
constructor(amount: Amount<Currency>, issueRef: OpaqueBytes) : this(amount, issueRef, tracker())
|
constructor(amount: Amount<Currency>, issuerRef: OpaqueBytes) : this(amount, issuerRef, tracker())
|
||||||
constructor(request: ExitRequest) : this(request.amount, request.issueRef, tracker())
|
constructor(request: ExitRequest) : this(request.amount, request.issuerRef, tracker())
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun tracker() = ProgressTracker(GENERATING_TX, SIGNING_TX, FINALISING_TX)
|
fun tracker() = ProgressTracker(GENERATING_TX, SIGNING_TX, FINALISING_TX)
|
||||||
@ -78,5 +78,5 @@ class CashExitFlow(private val amount: Amount<Currency>,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@CordaSerializable
|
@CordaSerializable
|
||||||
class ExitRequest(amount: Amount<Currency>, val issueRef: OpaqueBytes) : AbstractRequest(amount)
|
class ExitRequest(amount: Amount<Currency>, val issuerRef: OpaqueBytes) : AbstractRequest(amount)
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ Generates a text summary of Corda's public API that we can check for API-breakin
|
|||||||
$ gradlew generateApi
|
$ gradlew generateApi
|
||||||
```
|
```
|
||||||
|
|
||||||
See [here](../../docs/source/api-index.rst) for Corda's public API strategy. We will need to
|
See [here](../../docs/source/corda-api.rst) for Corda's public API strategy. We will need to
|
||||||
apply this plugin to other modules in future Corda releases as those modules' APIs stabilise.
|
apply this plugin to other modules in future Corda releases as those modules' APIs stabilise.
|
||||||
|
|
||||||
Basically, this plugin will document a module's `public` and `protected` classes/methods/fields,
|
Basically, this plugin will document a module's `public` and `protected` classes/methods/fields,
|
||||||
|
@ -22,6 +22,7 @@ import java.util.jar.JarInputStream
|
|||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
open class Cordform : DefaultTask() {
|
open class Cordform : DefaultTask() {
|
||||||
private companion object {
|
private companion object {
|
||||||
|
val nodeJarName = "corda.jar"
|
||||||
private val defaultDirectory: Path = Paths.get("build", "nodes")
|
private val defaultDirectory: Path = Paths.get("build", "nodes")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,9 +133,26 @@ open class Cordform : DefaultTask() {
|
|||||||
fun build() {
|
fun build() {
|
||||||
project.logger.info("Running Cordform task")
|
project.logger.info("Running Cordform task")
|
||||||
initializeConfiguration()
|
initializeConfiguration()
|
||||||
|
nodes.forEach(Node::installConfig)
|
||||||
|
installCordaJar()
|
||||||
installRunScript()
|
installRunScript()
|
||||||
nodes.forEach(Node::build)
|
|
||||||
bootstrapNetwork()
|
bootstrapNetwork()
|
||||||
|
nodes.forEach(Node::build)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Installs the corda fat JAR to the root directory, for the network bootstrapper to use.
|
||||||
|
*/
|
||||||
|
private fun installCordaJar() {
|
||||||
|
val cordaJar = Cordformation.verifyAndGetRuntimeJar(project, "corda")
|
||||||
|
project.copy {
|
||||||
|
it.apply {
|
||||||
|
from(cordaJar)
|
||||||
|
into(directory)
|
||||||
|
rename(cordaJar.name, nodeJarName)
|
||||||
|
fileMode = Cordformation.executableFileMode
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initializeConfiguration() {
|
private fun initializeConfiguration() {
|
||||||
@ -150,8 +168,8 @@ open class Cordform : DefaultTask() {
|
|||||||
cd.nodeConfigurers.forEach {
|
cd.nodeConfigurers.forEach {
|
||||||
val node = node { }
|
val node = node { }
|
||||||
it.accept(node)
|
it.accept(node)
|
||||||
|
node.additionalCordapps.addAll(cordapps)
|
||||||
node.rootDir(directory)
|
node.rootDir(directory)
|
||||||
node.installCordapps(cordapps)
|
|
||||||
}
|
}
|
||||||
cd.setup { nodeName -> project.projectDir.toPath().resolve(getNodeByName(nodeName)!!.nodeDir.toPath()) }
|
cd.setup { nodeName -> project.projectDir.toPath().resolve(getNodeByName(nodeName)!!.nodeDir.toPath()) }
|
||||||
} else {
|
} else {
|
||||||
|
@ -20,14 +20,35 @@ class Cordformation : Plugin<Project> {
|
|||||||
* @return A file handle to the file in the JAR.
|
* @return A file handle to the file in the JAR.
|
||||||
*/
|
*/
|
||||||
fun getPluginFile(project: Project, filePathInJar: String): File {
|
fun getPluginFile(project: Project, filePathInJar: String): File {
|
||||||
val archive: File? = project.rootProject.buildscript.configurations
|
val archive = project.rootProject.buildscript.configurations
|
||||||
.single { it.name == "classpath" }
|
.single { it.name == "classpath" }
|
||||||
.find { it.name.contains("cordformation") }
|
.first { it.name.contains("cordformation") }
|
||||||
return project.rootProject.resources.text
|
return project.rootProject.resources.text
|
||||||
.fromArchiveEntry(archive, filePathInJar)
|
.fromArchiveEntry(archive, filePathInJar)
|
||||||
.asFile()
|
.asFile()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a current built corda jar file
|
||||||
|
*
|
||||||
|
* @param project The project environment this plugin executes in.
|
||||||
|
* @param jarName The name of the JAR you wish to access.
|
||||||
|
* @return A file handle to the file in the JAR.
|
||||||
|
*/
|
||||||
|
fun verifyAndGetRuntimeJar(project: Project, jarName: String): File {
|
||||||
|
val releaseVersion = project.rootProject.ext<String>("corda_release_version")
|
||||||
|
val maybeJar = project.configuration("runtime").filter {
|
||||||
|
"$jarName-$releaseVersion.jar" in it.toString() || "$jarName-enterprise-$releaseVersion.jar" in it.toString()
|
||||||
|
}
|
||||||
|
if (maybeJar.isEmpty) {
|
||||||
|
throw IllegalStateException("No $jarName JAR found. Have you deployed the Corda project to Maven? Looked for \"$jarName-$releaseVersion.jar\"")
|
||||||
|
} else {
|
||||||
|
val jar = maybeJar.singleFile
|
||||||
|
require(jar.isFile)
|
||||||
|
return jar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val executableFileMode = "0755".toInt(8)
|
val executableFileMode = "0755".toInt(8)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,8 +15,6 @@ import java.nio.file.Path
|
|||||||
*/
|
*/
|
||||||
class Node(private val project: Project) : CordformNode() {
|
class Node(private val project: Project) : CordformNode() {
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
|
||||||
val nodeJarName = "corda.jar"
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val webJarName = "corda-webserver.jar"
|
val webJarName = "corda-webserver.jar"
|
||||||
private val configFileProperty = "configFile"
|
private val configFileProperty = "configFile"
|
||||||
@ -30,10 +28,11 @@ class Node(private val project: Project) : CordformNode() {
|
|||||||
* @note Type is any due to gradle's use of "GStrings" - each value will have "toString" called on it
|
* @note Type is any due to gradle's use of "GStrings" - each value will have "toString" called on it
|
||||||
*/
|
*/
|
||||||
var cordapps = mutableListOf<Any>()
|
var cordapps = mutableListOf<Any>()
|
||||||
|
var additionalCordapps = mutableListOf<File>()
|
||||||
private val releaseVersion = project.rootProject.ext<String>("corda_release_version")
|
|
||||||
internal lateinit var nodeDir: File
|
internal lateinit var nodeDir: File
|
||||||
private set
|
private set
|
||||||
|
internal lateinit var rootDir: File
|
||||||
|
private set
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets whether this node will use HTTPS communication.
|
* Sets whether this node will use HTTPS communication.
|
||||||
@ -65,16 +64,12 @@ class Node(private val project: Project) : CordformNode() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun build() {
|
internal fun build() {
|
||||||
configureProperties()
|
|
||||||
installCordaJar()
|
|
||||||
if (config.hasPath("webAddress")) {
|
if (config.hasPath("webAddress")) {
|
||||||
installWebserverJar()
|
installWebserverJar()
|
||||||
}
|
}
|
||||||
installAgentJar()
|
installAgentJar()
|
||||||
installBuiltCordapp()
|
installBuiltCordapp()
|
||||||
installCordapps()
|
installCordapps()
|
||||||
installConfig()
|
|
||||||
appendOptionalConfig()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun rootDir(rootDir: Path) {
|
internal fun rootDir(rootDir: Path) {
|
||||||
@ -86,7 +81,9 @@ class Node(private val project: Project) : CordformNode() {
|
|||||||
// with loading our custom X509EdDSAEngine.
|
// with loading our custom X509EdDSAEngine.
|
||||||
val organizationName = name.trim().split(",").firstOrNull { it.startsWith("O=") }?.substringAfter("=")
|
val organizationName = name.trim().split(",").firstOrNull { it.startsWith("O=") }?.substringAfter("=")
|
||||||
val dirName = organizationName ?: name
|
val dirName = organizationName ?: name
|
||||||
nodeDir = File(rootDir.toFile(), dirName.replace("\\s", ""))
|
this.rootDir = rootDir.toFile()
|
||||||
|
nodeDir = File(this.rootDir, dirName.replace("\\s", ""))
|
||||||
|
Files.createDirectories(nodeDir.toPath())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun configureProperties() {
|
private fun configureProperties() {
|
||||||
@ -99,26 +96,11 @@ class Node(private val project: Project) : CordformNode() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Installs the corda fat JAR to the node directory.
|
|
||||||
*/
|
|
||||||
private fun installCordaJar() {
|
|
||||||
val cordaJar = verifyAndGetRuntimeJar("corda")
|
|
||||||
project.copy {
|
|
||||||
it.apply {
|
|
||||||
from(cordaJar)
|
|
||||||
into(nodeDir)
|
|
||||||
rename(cordaJar.name, nodeJarName)
|
|
||||||
fileMode = Cordformation.executableFileMode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Installs the corda webserver JAR to the node directory
|
* Installs the corda webserver JAR to the node directory
|
||||||
*/
|
*/
|
||||||
private fun installWebserverJar() {
|
private fun installWebserverJar() {
|
||||||
val webJar = verifyAndGetRuntimeJar("corda-webserver")
|
val webJar = Cordformation.verifyAndGetRuntimeJar(project, "corda-webserver")
|
||||||
project.copy {
|
project.copy {
|
||||||
it.apply {
|
it.apply {
|
||||||
from(webJar)
|
from(webJar)
|
||||||
@ -141,19 +123,6 @@ class Node(private val project: Project) : CordformNode() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Installs other cordapps to this node's cordapps directory.
|
|
||||||
*/
|
|
||||||
internal fun installCordapps(cordapps: Collection<File> = getCordappList()) {
|
|
||||||
val cordappsDir = File(nodeDir, "cordapps")
|
|
||||||
project.copy {
|
|
||||||
it.apply {
|
|
||||||
from(cordapps)
|
|
||||||
into(cordappsDir)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Installs the jolokia monitoring agent JAR to the node/drivers directory
|
* Installs the jolokia monitoring agent JAR to the node/drivers directory
|
||||||
*/
|
*/
|
||||||
@ -161,8 +130,8 @@ class Node(private val project: Project) : CordformNode() {
|
|||||||
val jolokiaVersion = project.rootProject.ext<String>("jolokia_version")
|
val jolokiaVersion = project.rootProject.ext<String>("jolokia_version")
|
||||||
val agentJar = project.configuration("runtime").files {
|
val agentJar = project.configuration("runtime").files {
|
||||||
(it.group == "org.jolokia") &&
|
(it.group == "org.jolokia") &&
|
||||||
(it.name == "jolokia-jvm") &&
|
(it.name == "jolokia-jvm") &&
|
||||||
(it.version == jolokiaVersion)
|
(it.version == jolokiaVersion)
|
||||||
// TODO: revisit when classifier attribute is added. eg && (it.classifier = "agent")
|
// TODO: revisit when classifier attribute is added. eg && (it.classifier = "agent")
|
||||||
}.first() // should always be the jolokia agent fat jar: eg. jolokia-jvm-1.3.7-agent.jar
|
}.first() // should always be the jolokia agent fat jar: eg. jolokia-jvm-1.3.7-agent.jar
|
||||||
project.logger.info("Jolokia agent jar: $agentJar")
|
project.logger.info("Jolokia agent jar: $agentJar")
|
||||||
@ -177,10 +146,7 @@ class Node(private val project: Project) : CordformNode() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private fun createTempConfigFile(): File {
|
||||||
* Installs the configuration file to this node's directory and detokenises it.
|
|
||||||
*/
|
|
||||||
private fun installConfig() {
|
|
||||||
val options = ConfigRenderOptions
|
val options = ConfigRenderOptions
|
||||||
.defaults()
|
.defaults()
|
||||||
.setOriginComments(false)
|
.setOriginComments(false)
|
||||||
@ -188,16 +154,26 @@ class Node(private val project: Project) : CordformNode() {
|
|||||||
.setFormatted(true)
|
.setFormatted(true)
|
||||||
.setJson(false)
|
.setJson(false)
|
||||||
val configFileText = config.root().render(options).split("\n").toList()
|
val configFileText = config.root().render(options).split("\n").toList()
|
||||||
|
|
||||||
// Need to write a temporary file first to use the project.copy, which resolves directories correctly.
|
// Need to write a temporary file first to use the project.copy, which resolves directories correctly.
|
||||||
val tmpDir = File(project.buildDir, "tmp")
|
val tmpDir = File(project.buildDir, "tmp")
|
||||||
val tmpConfFile = File(tmpDir, "node.conf")
|
Files.createDirectories(tmpDir.toPath())
|
||||||
|
var fileName = "${nodeDir.getName()}.conf"
|
||||||
|
val tmpConfFile = File(tmpDir, fileName)
|
||||||
Files.write(tmpConfFile.toPath(), configFileText, StandardCharsets.UTF_8)
|
Files.write(tmpConfFile.toPath(), configFileText, StandardCharsets.UTF_8)
|
||||||
|
return tmpConfFile
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Installs the configuration file to the root directory and detokenises it.
|
||||||
|
*/
|
||||||
|
internal fun installConfig() {
|
||||||
|
configureProperties()
|
||||||
|
val tmpConfFile = createTempConfigFile()
|
||||||
|
appendOptionalConfig(tmpConfFile)
|
||||||
project.copy {
|
project.copy {
|
||||||
it.apply {
|
it.apply {
|
||||||
from(tmpConfFile)
|
from(tmpConfFile)
|
||||||
into(nodeDir)
|
into(rootDir)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -205,7 +181,7 @@ class Node(private val project: Project) : CordformNode() {
|
|||||||
/**
|
/**
|
||||||
* Appends installed config file with properties from an optional file.
|
* Appends installed config file with properties from an optional file.
|
||||||
*/
|
*/
|
||||||
private fun appendOptionalConfig() {
|
private fun appendOptionalConfig(confFile: File) {
|
||||||
val optionalConfig: File? = when {
|
val optionalConfig: File? = when {
|
||||||
project.findProperty(configFileProperty) != null -> //provided by -PconfigFile command line property when running Gradle task
|
project.findProperty(configFileProperty) != null -> //provided by -PconfigFile command line property when running Gradle task
|
||||||
File(project.findProperty(configFileProperty) as String)
|
File(project.findProperty(configFileProperty) as String)
|
||||||
@ -217,28 +193,22 @@ class Node(private val project: Project) : CordformNode() {
|
|||||||
if (!optionalConfig.exists()) {
|
if (!optionalConfig.exists()) {
|
||||||
project.logger.error("$configFileProperty '$optionalConfig' not found")
|
project.logger.error("$configFileProperty '$optionalConfig' not found")
|
||||||
} else {
|
} else {
|
||||||
val confFile = File(project.buildDir.path + "/../" + nodeDir, "node.conf")
|
|
||||||
confFile.appendBytes(optionalConfig.readBytes())
|
confFile.appendBytes(optionalConfig.readBytes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the given JAR amongst the dependencies
|
* Installs other cordapps to this node's cordapps directory.
|
||||||
* @param jarName JAR name without the version part, for example for corda-2.0-SNAPSHOT.jar provide only "corda" as jarName
|
|
||||||
*
|
|
||||||
* @return A file representing found JAR
|
|
||||||
*/
|
*/
|
||||||
private fun verifyAndGetRuntimeJar(jarName: String): File {
|
internal fun installCordapps() {
|
||||||
val maybeJar = project.configuration("runtime").filter {
|
additionalCordapps.addAll(getCordappList())
|
||||||
"$jarName-$releaseVersion.jar" in it.toString() || "$jarName-enterprise-$releaseVersion.jar" in it.toString()
|
val cordappsDir = File(nodeDir, "cordapps")
|
||||||
}
|
project.copy {
|
||||||
if (maybeJar.isEmpty) {
|
it.apply {
|
||||||
throw IllegalStateException("No $jarName JAR found. Have you deployed the Corda project to Maven? Looked for \"$jarName-$releaseVersion.jar\"")
|
from(additionalCordapps)
|
||||||
} else {
|
into(cordappsDir)
|
||||||
val jar = maybeJar.singleFile
|
}
|
||||||
require(jar.isFile)
|
|
||||||
return jar
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,14 +3,11 @@ package com.r3.corda.networkmanage.doorman
|
|||||||
import com.nhaarman.mockito_kotlin.doReturn
|
import com.nhaarman.mockito_kotlin.doReturn
|
||||||
import com.nhaarman.mockito_kotlin.whenever
|
import com.nhaarman.mockito_kotlin.whenever
|
||||||
import com.r3.corda.networkmanage.common.persistence.configureDatabase
|
import com.r3.corda.networkmanage.common.persistence.configureDatabase
|
||||||
import com.r3.corda.networkmanage.common.utils.toX509Certificate
|
|
||||||
import com.r3.corda.networkmanage.doorman.signer.LocalSigner
|
import com.r3.corda.networkmanage.doorman.signer.LocalSigner
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.sign
|
import net.corda.core.crypto.sign
|
||||||
import net.corda.core.identity.CordaX500Name
|
|
||||||
import net.corda.core.identity.PartyAndCertificate
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.internal.createDirectories
|
import net.corda.core.internal.createDirectories
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
@ -28,12 +25,13 @@ import net.corda.testing.ALICE_NAME
|
|||||||
import net.corda.testing.SerializationEnvironmentRule
|
import net.corda.testing.SerializationEnvironmentRule
|
||||||
import net.corda.testing.common.internal.testNetworkParameters
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
import net.corda.testing.internal.rigorousMock
|
import net.corda.testing.internal.rigorousMock
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.rules.TemporaryFolder
|
import org.junit.rules.TemporaryFolder
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertNotNull
|
import kotlin.test.assertNotNull
|
||||||
|
|
||||||
@ -60,7 +58,7 @@ class DoormanIntegrationTest {
|
|||||||
}
|
}
|
||||||
config.trustStoreFile.parent.createDirectories()
|
config.trustStoreFile.parent.createDirectories()
|
||||||
loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also {
|
loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also {
|
||||||
it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCertAndKey.certificate.cert)
|
it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCertAndKey.certificate)
|
||||||
it.save(config.trustStoreFile, config.trustStorePassword)
|
it.save(config.trustStoreFile, config.trustStorePassword)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,18 +75,18 @@ class DoormanIntegrationTest {
|
|||||||
loadKeyStore(config.nodeKeystore, config.keyStorePassword).apply {
|
loadKeyStore(config.nodeKeystore, config.keyStorePassword).apply {
|
||||||
assert(containsAlias(X509Utilities.CORDA_CLIENT_CA))
|
assert(containsAlias(X509Utilities.CORDA_CLIENT_CA))
|
||||||
assertEquals(ALICE_NAME.x500Principal, getX509Certificate(X509Utilities.CORDA_CLIENT_CA).subjectX500Principal)
|
assertEquals(ALICE_NAME.x500Principal, getX509Certificate(X509Utilities.CORDA_CLIENT_CA).subjectX500Principal)
|
||||||
assertEquals(listOf(intermediateCACert.cert, rootCACert.cert), getCertificateChain(X509Utilities.CORDA_CLIENT_CA).drop(1).toList())
|
assertEquals(listOf(intermediateCACert, rootCACert), getCertificateChain(X509Utilities.CORDA_CLIENT_CA).drop(1).toList())
|
||||||
}
|
}
|
||||||
|
|
||||||
loadKeyStore(config.sslKeystore, config.keyStorePassword).apply {
|
loadKeyStore(config.sslKeystore, config.keyStorePassword).apply {
|
||||||
assert(containsAlias(X509Utilities.CORDA_CLIENT_TLS))
|
assert(containsAlias(X509Utilities.CORDA_CLIENT_TLS))
|
||||||
assertEquals(ALICE_NAME.x500Principal, getX509Certificate(X509Utilities.CORDA_CLIENT_TLS).subjectX500Principal)
|
assertEquals(ALICE_NAME.x500Principal, getX509Certificate(X509Utilities.CORDA_CLIENT_TLS).subjectX500Principal)
|
||||||
assertEquals(listOf(intermediateCACert.cert, rootCACert.cert), getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).drop(2).toList())
|
assertEquals(listOf(intermediateCACert, rootCACert), getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).drop(2).toList())
|
||||||
}
|
}
|
||||||
|
|
||||||
loadKeyStore(config.trustStoreFile, config.trustStorePassword).apply {
|
loadKeyStore(config.trustStoreFile, config.trustStorePassword).apply {
|
||||||
assert(containsAlias(X509Utilities.CORDA_ROOT_CA))
|
assert(containsAlias(X509Utilities.CORDA_ROOT_CA))
|
||||||
assertEquals(rootCACert.cert.subjectX500Principal, getX509Certificate(X509Utilities.CORDA_ROOT_CA).subjectX500Principal)
|
assertEquals(rootCACert.subjectX500Principal, getX509Certificate(X509Utilities.CORDA_ROOT_CA).subjectX500Principal)
|
||||||
}
|
}
|
||||||
|
|
||||||
doorman.close()
|
doorman.close()
|
||||||
@ -110,21 +108,26 @@ class DoormanIntegrationTest {
|
|||||||
}
|
}
|
||||||
config.trustStoreFile.parent.createDirectories()
|
config.trustStoreFile.parent.createDirectories()
|
||||||
loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also {
|
loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also {
|
||||||
it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCertAndKey.certificate.cert)
|
it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCertAndKey.certificate)
|
||||||
it.save(config.trustStoreFile, config.trustStorePassword)
|
it.save(config.trustStoreFile, config.trustStorePassword)
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.compatibilityZoneURL!!)).buildKeystore()
|
NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.compatibilityZoneURL!!)).buildKeystore()
|
||||||
|
|
||||||
// Publish NodeInfo
|
// Publish NodeInfo
|
||||||
val networkMapClient = NetworkMapClient(config.compatibilityZoneURL!!, rootCertAndKey.certificate.cert)
|
val networkMapClient = NetworkMapClient(config.compatibilityZoneURL!!, rootCertAndKey.certificate)
|
||||||
|
|
||||||
val keyStore = loadKeyStore(config.nodeKeystore, config.keyStorePassword)
|
val keyStore = loadKeyStore(config.nodeKeystore, config.keyStorePassword)
|
||||||
val clientCertPath = keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
|
val clientCertPath = keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
|
||||||
val clientCA = keyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, config.keyStorePassword)
|
val clientCA = keyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, config.keyStorePassword)
|
||||||
val identityKeyPair = Crypto.generateKeyPair()
|
val identityKeyPair = Crypto.generateKeyPair()
|
||||||
val identityCert = X509Utilities.createCertificate(CertificateType.LEGAL_IDENTITY, clientCA.certificate, clientCA.keyPair, ALICE_NAME, identityKeyPair.public)
|
val identityCert = X509Utilities.createCertificate(
|
||||||
val certPath = X509CertificateFactory().generateCertPath(identityCert.cert, *clientCertPath)
|
CertificateType.LEGAL_IDENTITY,
|
||||||
|
clientCA.certificate,
|
||||||
|
clientCA.keyPair,
|
||||||
|
ALICE_NAME.x500Principal,
|
||||||
|
identityKeyPair.public)
|
||||||
|
val certPath = X509CertificateFactory().generateCertPath(identityCert, *clientCertPath)
|
||||||
val nodeInfo = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(certPath)), 1, serial = 1L)
|
val nodeInfo = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(certPath)), 1, serial = 1L)
|
||||||
val nodeInfoBytes = nodeInfo.serialize()
|
val nodeInfoBytes = nodeInfo.serialize()
|
||||||
|
|
||||||
@ -156,23 +159,23 @@ class DoormanIntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun createDoormanIntermediateCertificateAndKeyPair(rootCertificateAndKeyPair: CertificateAndKeyPair): CertificateAndKeyPair {
|
fun createDoormanIntermediateCertificateAndKeyPair(rootCa: CertificateAndKeyPair): CertificateAndKeyPair {
|
||||||
val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCertificateAndKeyPair.certificate, rootCertificateAndKeyPair.keyPair,
|
val intermediateCACert = X509Utilities.createCertificate(
|
||||||
CordaX500Name(commonName = "Integration Test Corda Node Intermediate CA",
|
CertificateType.INTERMEDIATE_CA,
|
||||||
locality = "London",
|
rootCa.certificate,
|
||||||
country = "GB",
|
rootCa.keyPair,
|
||||||
organisation = "R3 Ltd"), intermediateCAKey.public)
|
X500Principal("CN=Integration Test Corda Node Intermediate CA,O=R3 Ltd,L=London,C=GB"),
|
||||||
return CertificateAndKeyPair(intermediateCACert, intermediateCAKey)
|
keyPair.public)
|
||||||
|
return CertificateAndKeyPair(intermediateCACert, keyPair)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createDoormanRootCertificateAndKeyPair(): CertificateAndKeyPair {
|
fun createDoormanRootCertificateAndKeyPair(): CertificateAndKeyPair {
|
||||||
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val rootCACert = X509Utilities.createSelfSignedCACertificate(
|
val rootCaCert = X509Utilities.createSelfSignedCACertificate(
|
||||||
CordaX500Name(commonName = "Integration Test Corda Node Root CA",
|
X500Principal("CN=Integration Test Corda Node Root CA,O=R3 Ltd,L=London,C=GB"),
|
||||||
organisation = "R3 Ltd", locality = "London",
|
keyPair)
|
||||||
country = "GB"), rootCAKey)
|
return CertificateAndKeyPair(rootCaCert, keyPair)
|
||||||
return CertificateAndKeyPair(rootCACert, rootCAKey)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun makeTestDataSourceProperties(nodeName: String = SecureHash.randomSHA256().toString()): Properties {
|
fun makeTestDataSourceProperties(nodeName: String = SecureHash.randomSHA256().toString()): Properties {
|
||||||
@ -184,9 +187,8 @@ fun makeTestDataSourceProperties(nodeName: String = SecureHash.randomSHA256().to
|
|||||||
return props
|
return props
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startDoorman(intermediateCACertAndKey: CertificateAndKeyPair, rootCACert: X509CertificateHolder): NetworkManagementServer {
|
fun startDoorman(intermediateCACertAndKey: CertificateAndKeyPair, rootCACert: X509Certificate): NetworkManagementServer {
|
||||||
val signer = LocalSigner(intermediateCACertAndKey.keyPair,
|
val signer = LocalSigner(intermediateCACertAndKey.keyPair, arrayOf(intermediateCACertAndKey.certificate, rootCACert))
|
||||||
arrayOf(intermediateCACertAndKey.certificate.toX509Certificate(), rootCACert.toX509Certificate()))
|
|
||||||
//Start doorman server
|
//Start doorman server
|
||||||
return startDoorman(signer)
|
return startDoorman(signer)
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ import com.r3.corda.networkmanage.hsm.persistence.DBSignedCertificateRequestStor
|
|||||||
import com.r3.corda.networkmanage.hsm.persistence.SignedCertificateRequestStorage
|
import com.r3.corda.networkmanage.hsm.persistence.SignedCertificateRequestStorage
|
||||||
import com.r3.corda.networkmanage.hsm.signer.HsmCsrSigner
|
import com.r3.corda.networkmanage.hsm.signer.HsmCsrSigner
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.internal.createDirectories
|
import net.corda.core.internal.createDirectories
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.internal.uncheckedCast
|
import net.corda.core.internal.uncheckedCast
|
||||||
@ -28,12 +27,12 @@ import net.corda.testing.CHARLIE_NAME
|
|||||||
import net.corda.testing.SerializationEnvironmentRule
|
import net.corda.testing.SerializationEnvironmentRule
|
||||||
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
||||||
import net.corda.testing.internal.rigorousMock
|
import net.corda.testing.internal.rigorousMock
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
|
||||||
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
|
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
|
||||||
import org.h2.tools.Server
|
import org.h2.tools.Server
|
||||||
import org.junit.*
|
import org.junit.*
|
||||||
import org.junit.rules.TemporaryFolder
|
import org.junit.rules.TemporaryFolder
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.persistence.PersistenceException
|
import javax.persistence.PersistenceException
|
||||||
import kotlin.concurrent.scheduleAtFixedRate
|
import kotlin.concurrent.scheduleAtFixedRate
|
||||||
@ -55,7 +54,7 @@ class SigningServiceIntegrationTest {
|
|||||||
val testSerialization = SerializationEnvironmentRule(true)
|
val testSerialization = SerializationEnvironmentRule(true)
|
||||||
|
|
||||||
private lateinit var timer: Timer
|
private lateinit var timer: Timer
|
||||||
private lateinit var rootCaCert: X509CertificateHolder
|
private lateinit var rootCaCert: X509Certificate
|
||||||
private lateinit var intermediateCa: CertificateAndKeyPair
|
private lateinit var intermediateCa: CertificateAndKeyPair
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@ -79,7 +78,7 @@ class SigningServiceIntegrationTest {
|
|||||||
for (approvedRequest in approvedRequests) {
|
for (approvedRequest in approvedRequests) {
|
||||||
JcaPKCS10CertificationRequest(approvedRequest.request).run {
|
JcaPKCS10CertificationRequest(approvedRequest.request).run {
|
||||||
val nodeCa = createDevNodeCa(intermediateCa, CordaX500Name.parse(subject.toString()))
|
val nodeCa = createDevNodeCa(intermediateCa, CordaX500Name.parse(subject.toString()))
|
||||||
approvedRequest.certPath = buildCertPath(nodeCa.certificate.cert, intermediateCa.certificate.cert, rootCaCert.cert)
|
approvedRequest.certPath = buildCertPath(nodeCa.certificate, intermediateCa.certificate, rootCaCert)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
storage.store(approvedRequests, listOf("TEST"))
|
storage.store(approvedRequests, listOf("TEST"))
|
||||||
@ -124,7 +123,7 @@ class SigningServiceIntegrationTest {
|
|||||||
}
|
}
|
||||||
config.certificatesDirectory.createDirectories()
|
config.certificatesDirectory.createDirectories()
|
||||||
loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also {
|
loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also {
|
||||||
it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCaCert.cert)
|
it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCaCert)
|
||||||
it.save(config.trustStoreFile, config.trustStorePassword)
|
it.save(config.trustStoreFile, config.trustStorePassword)
|
||||||
}
|
}
|
||||||
NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.compatibilityZoneURL!!)).buildKeystore()
|
NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.compatibilityZoneURL!!)).buildKeystore()
|
||||||
@ -168,7 +167,7 @@ class SigningServiceIntegrationTest {
|
|||||||
}
|
}
|
||||||
config.certificatesDirectory.createDirectories()
|
config.certificatesDirectory.createDirectories()
|
||||||
loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also {
|
loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also {
|
||||||
it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCaCert.cert)
|
it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCaCert)
|
||||||
it.save(config.trustStoreFile, config.trustStorePassword)
|
it.save(config.trustStoreFile, config.trustStorePassword)
|
||||||
}
|
}
|
||||||
NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.compatibilityZoneURL!!)).buildKeystore()
|
NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.compatibilityZoneURL!!)).buildKeystore()
|
||||||
|
@ -5,15 +5,14 @@ import com.r3.corda.networkmanage.common.persistence.entity.CertificateSigningRe
|
|||||||
import com.r3.corda.networkmanage.common.utils.hashString
|
import com.r3.corda.networkmanage.common.utils.hashString
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.x500Name
|
|
||||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||||
import net.corda.nodeapi.internal.persistence.DatabaseTransaction
|
import net.corda.nodeapi.internal.persistence.DatabaseTransaction
|
||||||
import net.corda.nodeapi.internal.persistence.TransactionIsolationLevel
|
import net.corda.nodeapi.internal.persistence.TransactionIsolationLevel
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
|
||||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
||||||
import org.hibernate.Session
|
import org.hibernate.Session
|
||||||
import java.security.cert.CertPath
|
import java.security.cert.CertPath
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Database implementation of the [CertificationRequestStorage] interface.
|
* Database implementation of the [CertificationRequestStorage] interface.
|
||||||
@ -48,7 +47,7 @@ class PersistentCertificateRequestStorage(private val database: CordaPersistence
|
|||||||
val (legalName, rejectReason) = parseAndValidateLegalName(request, session)
|
val (legalName, rejectReason) = parseAndValidateLegalName(request, session)
|
||||||
session.save(CertificateSigningRequestEntity(
|
session.save(CertificateSigningRequestEntity(
|
||||||
requestId = requestId,
|
requestId = requestId,
|
||||||
legalName = legalName.toString(),
|
legalName = legalName,
|
||||||
requestBytes = request.encoded,
|
requestBytes = request.encoded,
|
||||||
remark = rejectReason,
|
remark = rejectReason,
|
||||||
modifiedBy = emptyList(),
|
modifiedBy = emptyList(),
|
||||||
@ -126,25 +125,27 @@ class PersistentCertificateRequestStorage(private val database: CordaPersistence
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseAndValidateLegalName(request: PKCS10CertificationRequest, session: Session): Pair<X500Name, String?> {
|
private fun parseAndValidateLegalName(request: PKCS10CertificationRequest, session: Session): Pair<String, String?> {
|
||||||
|
// It's important that we always use the toString() output of CordaX500Name as it standardises the string format
|
||||||
|
// to make querying possible.
|
||||||
val legalName = try {
|
val legalName = try {
|
||||||
CordaX500Name.parse(request.subject.toString())
|
CordaX500Name.build(X500Principal(request.subject.encoded)).toString()
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
return Pair(request.subject, "Name validation failed with exception : ${e.message}")
|
return Pair(request.subject.toString(), "Name validation failed: ${e.message}")
|
||||||
}
|
}
|
||||||
|
|
||||||
val query = session.criteriaBuilder.run {
|
val query = session.criteriaBuilder.run {
|
||||||
val criteriaQuery = createQuery(CertificateSigningRequestEntity::class.java)
|
val criteriaQuery = createQuery(CertificateSigningRequestEntity::class.java)
|
||||||
criteriaQuery.from(CertificateSigningRequestEntity::class.java).run {
|
criteriaQuery.from(CertificateSigningRequestEntity::class.java).run {
|
||||||
criteriaQuery.where(equal(get<String>(CertificateSigningRequestEntity::legalName.name), legalName.toString()))
|
criteriaQuery.where(equal(get<String>(CertificateSigningRequestEntity::legalName.name), legalName))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val duplicates = session.createQuery(query).resultList.filter {
|
val duplicates = session.createQuery(query).resultList.filter {
|
||||||
it.status in setOf(RequestStatus.NEW, RequestStatus.TICKET_CREATED, RequestStatus.APPROVED) || it.certificateData?.certificateStatus == CertificateStatus.VALID
|
it.status in setOf(RequestStatus.NEW, RequestStatus.TICKET_CREATED, RequestStatus.APPROVED) ||
|
||||||
}
|
it.certificateData?.certificateStatus == CertificateStatus.VALID
|
||||||
return if (duplicates.isEmpty()) {
|
|
||||||
Pair(legalName.x500Name, null)
|
|
||||||
} else {
|
|
||||||
Pair(legalName.x500Name, "Duplicate legal name")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Pair(legalName, if (duplicates.isEmpty()) null else "Duplicate legal name")
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,9 +6,7 @@ import com.atlassian.jira.rest.client.api.domain.Issue
|
|||||||
import com.atlassian.jira.rest.client.api.domain.IssueType
|
import com.atlassian.jira.rest.client.api.domain.IssueType
|
||||||
import com.atlassian.jira.rest.client.api.domain.input.IssueInputBuilder
|
import com.atlassian.jira.rest.client.api.domain.input.IssueInputBuilder
|
||||||
import com.atlassian.jira.rest.client.api.domain.input.TransitionInput
|
import com.atlassian.jira.rest.client.api.domain.input.TransitionInput
|
||||||
import net.corda.core.internal.country
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.locality
|
|
||||||
import net.corda.core.internal.organisation
|
|
||||||
import net.corda.core.utilities.loggerFor
|
import net.corda.core.utilities.loggerFor
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
import org.bouncycastle.asn1.x500.style.BCStyle
|
import org.bouncycastle.asn1.x500.style.BCStyle
|
||||||
@ -17,6 +15,7 @@ import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
|||||||
import org.bouncycastle.util.io.pem.PemObject
|
import org.bouncycastle.util.io.pem.PemObject
|
||||||
import java.io.StringWriter
|
import java.io.StringWriter
|
||||||
import java.security.cert.CertPath
|
import java.security.cert.CertPath
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
|
|
||||||
class JiraClient(private val restClient: JiraRestClient, private val projectCode: String, private val doneTransitionCode: Int) {
|
class JiraClient(private val restClient: JiraRestClient, private val projectCode: String, private val doneTransitionCode: Int) {
|
||||||
companion object {
|
companion object {
|
||||||
@ -39,16 +38,17 @@ class JiraClient(private val restClient: JiraRestClient, private val projectCode
|
|||||||
JcaPEMWriter(request).use {
|
JcaPEMWriter(request).use {
|
||||||
it.writeObject(PemObject("CERTIFICATE REQUEST", signingRequest.encoded))
|
it.writeObject(PemObject("CERTIFICATE REQUEST", signingRequest.encoded))
|
||||||
}
|
}
|
||||||
val organisation = signingRequest.subject.organisation
|
|
||||||
val nearestCity = signingRequest.subject.locality
|
// TODO The subject of the signing request has already been validated and parsed into a CordaX500Name. We shouldn't
|
||||||
val country = signingRequest.subject.country
|
// have to do it again here.
|
||||||
|
val subject = CordaX500Name.build(X500Principal(signingRequest.subject.encoded))
|
||||||
|
|
||||||
val email = signingRequest.getAttributes(BCStyle.E).firstOrNull()?.attrValues?.firstOrNull()?.toString()
|
val email = signingRequest.getAttributes(BCStyle.E).firstOrNull()?.attrValues?.firstOrNull()?.toString()
|
||||||
|
|
||||||
val issue = IssueInputBuilder().setIssueTypeId(taskIssueType.id)
|
val issue = IssueInputBuilder().setIssueTypeId(taskIssueType.id)
|
||||||
.setProjectKey(projectCode)
|
.setProjectKey(projectCode)
|
||||||
.setDescription("Organisation: $organisation\nNearest City: $nearestCity\nCountry: $country\nEmail: $email\n\n{code}$request{code}")
|
.setDescription("Organisation: ${subject.organisation}\nNearest City: ${subject.locality}\nCountry: ${subject.country}\nEmail: $email\n\n{code}$request{code}")
|
||||||
.setSummary(organisation)
|
.setSummary(subject.organisation)
|
||||||
.setFieldValue(requestIdField.id, requestId)
|
.setFieldValue(requestIdField.id, requestId)
|
||||||
// This will block until the issue is created.
|
// This will block until the issue is created.
|
||||||
restClient.issueClient.createIssue(issue.build()).fail { logger.error("Exception when creating JIRA issue.", it) }.claim()
|
restClient.issueClient.createIssue(issue.build()).fail { logger.error("Exception when creating JIRA issue.", it) }.claim()
|
||||||
|
@ -13,7 +13,6 @@ import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService
|
|||||||
import com.r3.corda.networkmanage.doorman.webservice.RegistrationWebService
|
import com.r3.corda.networkmanage.doorman.webservice.RegistrationWebService
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.internal.createDirectories
|
import net.corda.core.internal.createDirectories
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.serialization.internal.SerializationEnvironmentImpl
|
import net.corda.core.serialization.internal.SerializationEnvironmentImpl
|
||||||
@ -188,8 +187,8 @@ fun generateRootKeyPair(rootStoreFile: Path, rootKeystorePass: String?, rootPriv
|
|||||||
val selfSignKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val selfSignKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
// TODO Make the cert subject configurable
|
// TODO Make the cert subject configurable
|
||||||
val selfSignCert = X509Utilities.createSelfSignedCACertificate(
|
val selfSignCert = X509Utilities.createSelfSignedCACertificate(
|
||||||
CordaX500Name(commonName = "Corda Root CA", organisation = "R3 Ltd", locality = "London", country = "GB", organisationUnit = "Corda", state = null),
|
CordaX500Name(commonName = "Corda Root CA", organisation = "R3 Ltd", locality = "London", country = "GB", organisationUnit = "Corda", state = null).x500Principal,
|
||||||
selfSignKey).cert
|
selfSignKey)
|
||||||
rootStore.addOrReplaceKey(X509Utilities.CORDA_ROOT_CA, selfSignKey.private, rootPrivateKeyPassword.toCharArray(), arrayOf(selfSignCert))
|
rootStore.addOrReplaceKey(X509Utilities.CORDA_ROOT_CA, selfSignKey.private, rootPrivateKeyPassword.toCharArray(), arrayOf(selfSignCert))
|
||||||
rootStore.save(rootStoreFile, rootKeystorePassword)
|
rootStore.save(rootStoreFile, rootKeystorePassword)
|
||||||
|
|
||||||
@ -226,11 +225,20 @@ fun generateCAKeyPair(keystoreFile: Path, rootStoreFile: Path, rootKeystorePass:
|
|||||||
exitProcess(1)
|
exitProcess(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
val intermediateKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val intermediateKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val intermediateCert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootKeyAndCert.certificate, rootKeyAndCert.keyPair,
|
val intermediateCert = X509Utilities.createCertificate(
|
||||||
CordaX500Name(commonName = "Corda Intermediate CA", organisation = "R3 Ltd", organisationUnit = "Corda", locality = "London", country = "GB", state = null), intermediateKey.public)
|
CertificateType.INTERMEDIATE_CA,
|
||||||
keyStore.addOrReplaceKey(X509Utilities.CORDA_INTERMEDIATE_CA, intermediateKey.private,
|
rootKeyAndCert.certificate,
|
||||||
caPrivateKeyPassword.toCharArray(), arrayOf(intermediateCert, rootKeyAndCert.certificate))
|
rootKeyAndCert.keyPair,
|
||||||
|
CordaX500Name(commonName = "Corda Intermediate CA", organisation = "R3 Ltd", organisationUnit = "Corda", locality = "London", country = "GB", state = null).x500Principal,
|
||||||
|
intermediateKeyPair.public
|
||||||
|
)
|
||||||
|
keyStore.addOrReplaceKey(
|
||||||
|
X509Utilities.CORDA_INTERMEDIATE_CA,
|
||||||
|
intermediateKeyPair.private,
|
||||||
|
caPrivateKeyPassword.toCharArray(),
|
||||||
|
arrayOf(intermediateCert, rootKeyAndCert.certificate)
|
||||||
|
)
|
||||||
keyStore.save(keystoreFile, keystorePassword)
|
keyStore.save(keystoreFile, keystorePassword)
|
||||||
println("Intermediate CA keypair and certificate stored in $keystoreFile.")
|
println("Intermediate CA keypair and certificate stored in $keystoreFile.")
|
||||||
println(loadKeyStore(keystoreFile, keystorePassword).getCertificate(X509Utilities.CORDA_INTERMEDIATE_CA).publicKey)
|
println(loadKeyStore(keystoreFile, keystorePassword).getCertificate(X509Utilities.CORDA_INTERMEDIATE_CA).publicKey)
|
||||||
|
@ -4,9 +4,6 @@ import com.r3.corda.networkmanage.common.signer.Signer
|
|||||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
||||||
import com.r3.corda.networkmanage.common.utils.withCert
|
import com.r3.corda.networkmanage.common.utils.withCert
|
||||||
import net.corda.core.crypto.sign
|
import net.corda.core.crypto.sign
|
||||||
import net.corda.core.identity.CordaX500Name
|
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.internal.toX509CertHolder
|
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
import net.corda.nodeapi.internal.network.DigitalSignatureWithCert
|
import net.corda.nodeapi.internal.network.DigitalSignatureWithCert
|
||||||
@ -18,6 +15,7 @@ import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
|
|||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.cert.CertPath
|
import java.security.cert.CertPath
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The [LocalSigner] class signs [PKCS10CertificationRequest] using provided CA key pair and certificate path.
|
* The [LocalSigner] class signs [PKCS10CertificationRequest] using provided CA key pair and certificate path.
|
||||||
@ -35,12 +33,12 @@ class LocalSigner(private val caKeyPair: KeyPair, private val caCertPath: Array<
|
|||||||
arrayOf())
|
arrayOf())
|
||||||
val nodeCaCert = X509Utilities.createCertificate(
|
val nodeCaCert = X509Utilities.createCertificate(
|
||||||
CertificateType.NODE_CA,
|
CertificateType.NODE_CA,
|
||||||
caCertPath.first().toX509CertHolder(),
|
caCertPath[0],
|
||||||
caKeyPair,
|
caKeyPair,
|
||||||
CordaX500Name.parse(request.subject.toString()),
|
X500Principal(request.subject.encoded),
|
||||||
request.publicKey,
|
request.publicKey,
|
||||||
nameConstraints = nameConstraints)
|
nameConstraints = nameConstraints)
|
||||||
return buildCertPath(nodeCaCert.cert, *caCertPath)
|
return buildCertPath(nodeCaCert, *caCertPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun sign(data: ByteArray): DigitalSignatureWithCert {
|
override fun sign(data: ByteArray): DigitalSignatureWithCert {
|
||||||
|
@ -9,13 +9,12 @@ import com.r3.corda.networkmanage.hsm.authentication.Authenticator
|
|||||||
import com.r3.corda.networkmanage.hsm.utils.X509Utilities.getAndInitializeKeyStore
|
import com.r3.corda.networkmanage.hsm.utils.X509Utilities.getAndInitializeKeyStore
|
||||||
import com.r3.corda.networkmanage.hsm.utils.X509Utilities.signData
|
import com.r3.corda.networkmanage.hsm.utils.X509Utilities.signData
|
||||||
import com.r3.corda.networkmanage.hsm.utils.X509Utilities.verify
|
import com.r3.corda.networkmanage.hsm.utils.X509Utilities.verify
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.internal.toX509CertHolder
|
|
||||||
import net.corda.core.utilities.loggerFor
|
import net.corda.core.utilities.loggerFor
|
||||||
import net.corda.core.utilities.minutes
|
import net.corda.core.utilities.minutes
|
||||||
import net.corda.nodeapi.internal.network.DigitalSignatureWithCert
|
import net.corda.nodeapi.internal.network.DigitalSignatureWithCert
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.PrivateKey
|
import java.security.PrivateKey
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
import java.util.concurrent.ScheduledExecutorService
|
import java.util.concurrent.ScheduledExecutorService
|
||||||
@ -68,7 +67,7 @@ class HsmNetworkMapSigner(networkMapStorage: NetworkMapStorage,
|
|||||||
val caKey = keyStore.getKey(caCertificateKeyName, caPrivateKeyPass.toCharArray()) as PrivateKey
|
val caKey = keyStore.getKey(caCertificateKeyName, caPrivateKeyPass.toCharArray()) as PrivateKey
|
||||||
val signature = signData(data, KeyPair(caCertificateChain.first().publicKey, caKey), provider)
|
val signature = signData(data, KeyPair(caCertificateChain.first().publicKey, caKey), provider)
|
||||||
verify(data, signature, caCertificateChain.first().publicKey)
|
verify(data, signature, caCertificateChain.first().publicKey)
|
||||||
signature.withCert(caCertificateChain.first().toX509CertHolder().cert)
|
signature.withCert(caCertificateChain[0] as X509Certificate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ package com.r3.corda.networkmanage.hsm.utils
|
|||||||
import CryptoServerJCE.CryptoServerProvider
|
import CryptoServerJCE.CryptoServerProvider
|
||||||
import net.corda.core.crypto.DigitalSignature
|
import net.corda.core.crypto.DigitalSignature
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.toX509CertHolder
|
|
||||||
import net.corda.core.internal.x500Name
|
import net.corda.core.internal.x500Name
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
@ -81,7 +80,7 @@ object X509Utilities {
|
|||||||
cert.checkValidity(Date())
|
cert.checkValidity(Date())
|
||||||
cert.verify(pubKey)
|
cert.verify(pubKey)
|
||||||
|
|
||||||
return CertificateAndKeyPair(cert.toX509CertHolder(), KeyPair(pubKey, keyPair.private))
|
return CertificateAndKeyPair(cert, KeyPair(pubKey, keyPair.private))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -109,7 +108,7 @@ object X509Utilities {
|
|||||||
fun retrieveCertificateAndKeys(certificateKeyName: String, privateKeyPassword: String, keyStore: KeyStore): CertificateAndKeyPair {
|
fun retrieveCertificateAndKeys(certificateKeyName: String, privateKeyPassword: String, keyStore: KeyStore): CertificateAndKeyPair {
|
||||||
val privateKey = keyStore.getKey(certificateKeyName, privateKeyPassword.toCharArray()) as PrivateKey
|
val privateKey = keyStore.getKey(certificateKeyName, privateKeyPassword.toCharArray()) as PrivateKey
|
||||||
val publicKey = keyStore.getCertificate(certificateKeyName).publicKey
|
val publicKey = keyStore.getCertificate(certificateKeyName).publicKey
|
||||||
val certificate = keyStore.getX509Certificate(certificateKeyName).toX509CertHolder()
|
val certificate = keyStore.getX509Certificate(certificateKeyName)
|
||||||
return CertificateAndKeyPair(certificate, getCleanEcdsaKeyPair(publicKey, privateKey))
|
return CertificateAndKeyPair(certificate, getCleanEcdsaKeyPair(publicKey, privateKey))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,7 +159,7 @@ object X509Utilities {
|
|||||||
cert.checkValidity(Date())
|
cert.checkValidity(Date())
|
||||||
cert.verify(certificateAuthority.keyPair.public)
|
cert.verify(certificateAuthority.keyPair.public)
|
||||||
|
|
||||||
return CertificateAndKeyPair(cert.toX509CertHolder(), KeyPair(pubKey, keyPair.private))
|
return CertificateAndKeyPair(cert, KeyPair(pubKey, keyPair.private))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -186,7 +185,7 @@ object X509Utilities {
|
|||||||
val subject = CordaX500Name.parse(jcaRequest.subject.toString()).x500Name
|
val subject = CordaX500Name.parse(jcaRequest.subject.toString()).x500Name
|
||||||
val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(jcaRequest.publicKey.encoded))
|
val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(jcaRequest.publicKey.encoded))
|
||||||
val keyPurposes = DERSequence(ASN1EncodableVector().apply { certificateType.purposes.forEach { add(it) } })
|
val keyPurposes = DERSequence(ASN1EncodableVector().apply { certificateType.purposes.forEach { add(it) } })
|
||||||
val builder = JcaX509v3CertificateBuilder(issuerCertificate.subject, serial, validityWindow.first, validityWindow.second, subject, jcaRequest.publicKey)
|
val builder = JcaX509v3CertificateBuilder(issuerCertificate, serial, validityWindow.first, validityWindow.second, subject, jcaRequest.publicKey)
|
||||||
.addExtension(Extension.subjectKeyIdentifier, false, BcX509ExtensionUtils().createSubjectKeyIdentifier(subjectPublicKeyInfo))
|
.addExtension(Extension.subjectKeyIdentifier, false, BcX509ExtensionUtils().createSubjectKeyIdentifier(subjectPublicKeyInfo))
|
||||||
.addExtension(Extension.basicConstraints, certificateType.isCA, BasicConstraints(certificateType.isCA))
|
.addExtension(Extension.basicConstraints, certificateType.isCA, BasicConstraints(certificateType.isCA))
|
||||||
.addExtension(Extension.keyUsage, false, certificateType.keyUsage)
|
.addExtension(Extension.keyUsage, false, certificateType.keyUsage)
|
||||||
|
@ -4,15 +4,13 @@ import com.r3.corda.networkmanage.TestBase
|
|||||||
import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage.Companion.DOORMAN_SIGNATURE
|
import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage.Companion.DOORMAN_SIGNATURE
|
||||||
import com.r3.corda.networkmanage.common.persistence.entity.CertificateSigningRequestEntity
|
import com.r3.corda.networkmanage.common.persistence.entity.CertificateSigningRequestEntity
|
||||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
||||||
import com.r3.corda.networkmanage.common.utils.toX509Certificate
|
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||||
|
import net.corda.testing.internal.createDevNodeCaCertPath
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
|
||||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
||||||
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
|
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
|
||||||
import org.hibernate.envers.AuditReaderFactory
|
import org.hibernate.envers.AuditReaderFactory
|
||||||
@ -21,9 +19,10 @@ import org.junit.Before
|
|||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
|
|
||||||
class DBCertificateRequestStorageTest : TestBase() {
|
class PersistentCertificateRequestStorageTest : TestBase() {
|
||||||
private lateinit var storage: PersistentCertificateRequestStorage
|
private lateinit var storage: PersistentCertificateRequestStorage
|
||||||
private lateinit var persistence: CordaPersistence
|
private lateinit var persistence: CordaPersistence
|
||||||
|
|
||||||
@ -84,14 +83,15 @@ class DBCertificateRequestStorageTest : TestBase() {
|
|||||||
// New request should be empty.
|
// New request should be empty.
|
||||||
assertTrue(storage.getRequests(RequestStatus.NEW).isEmpty())
|
assertTrue(storage.getRequests(RequestStatus.NEW).isEmpty())
|
||||||
// Sign certificate
|
// Sign certificate
|
||||||
storage.putCertificatePath(requestId, JcaPKCS10CertificationRequest(csr).run {
|
storage.putCertificatePath(
|
||||||
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
requestId,
|
||||||
val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 LTD", country = "GB"), rootCAKey)
|
JcaPKCS10CertificationRequest(csr).run {
|
||||||
val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
// TODO We need a utility in InternalUtils for converting X500Name -> CordaX500Name
|
||||||
val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, X500Name("CN=Corda Node Intermediate CA,L=London"), intermediateCAKey.public)
|
val (rootCa, intermediateCa, nodeCa) = createDevNodeCaCertPath(CordaX500Name.build(X500Principal(subject.encoded)))
|
||||||
val ourCertificate = X509Utilities.createCertificate(CertificateType.TLS, intermediateCACert, intermediateCAKey, subject, publicKey).toX509Certificate()
|
buildCertPath(nodeCa.certificate, intermediateCa.certificate, rootCa.certificate)
|
||||||
buildCertPath(ourCertificate, intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
|
},
|
||||||
}, listOf(DOORMAN_SIGNATURE))
|
listOf(DOORMAN_SIGNATURE)
|
||||||
|
)
|
||||||
// Check request is ready
|
// Check request is ready
|
||||||
assertNotNull(storage.getRequest(requestId)!!.certData)
|
assertNotNull(storage.getRequest(requestId)!!.certData)
|
||||||
}
|
}
|
||||||
@ -104,25 +104,24 @@ class DBCertificateRequestStorageTest : TestBase() {
|
|||||||
// Store certificate to DB.
|
// Store certificate to DB.
|
||||||
storage.markRequestTicketCreated(requestId)
|
storage.markRequestTicketCreated(requestId)
|
||||||
storage.approveRequest(requestId, DOORMAN_SIGNATURE)
|
storage.approveRequest(requestId, DOORMAN_SIGNATURE)
|
||||||
storage.putCertificatePath(requestId, JcaPKCS10CertificationRequest(csr).run {
|
storage.putCertificatePath(
|
||||||
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
requestId,
|
||||||
val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 LTD", country = "GB"), rootCAKey)
|
JcaPKCS10CertificationRequest(csr).run {
|
||||||
val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val (rootCa, intermediateCa, nodeCa) = createDevNodeCaCertPath(CordaX500Name.build(X500Principal(subject.encoded)))
|
||||||
val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, X500Name("CN=Corda Node Intermediate CA,L=London"), intermediateCAKey.public)
|
buildCertPath(nodeCa.certificate, intermediateCa.certificate, rootCa.certificate)
|
||||||
val ourCertificate = X509Utilities.createCertificate(CertificateType.TLS, intermediateCACert, intermediateCAKey, subject, publicKey).toX509Certificate()
|
},
|
||||||
buildCertPath(ourCertificate, intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
|
listOf(DOORMAN_SIGNATURE)
|
||||||
}, listOf(DOORMAN_SIGNATURE))
|
)
|
||||||
// Sign certificate
|
// Sign certificate
|
||||||
// When subsequent signature requested
|
// When subsequent signature requested
|
||||||
assertFailsWith(IllegalArgumentException::class) {
|
assertFailsWith(IllegalArgumentException::class) {
|
||||||
storage.putCertificatePath(requestId, JcaPKCS10CertificationRequest(csr).run {
|
storage.putCertificatePath(
|
||||||
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
requestId,
|
||||||
val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 LTD", country = "GB"), rootCAKey)
|
JcaPKCS10CertificationRequest(csr).run {
|
||||||
val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val (rootCa, intermediateCa, nodeCa) = createDevNodeCaCertPath(CordaX500Name.build(X500Principal(subject.encoded)))
|
||||||
val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, X500Name("CN=Corda Node Intermediate CA,L=London"), intermediateCAKey.public)
|
buildCertPath(nodeCa.certificate, intermediateCa.certificate, rootCa.certificate)
|
||||||
val ourCertificate = X509Utilities.createCertificate(CertificateType.TLS, intermediateCACert, intermediateCAKey, subject, publicKey).toX509Certificate()
|
},
|
||||||
buildCertPath(ourCertificate, intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
|
listOf(DOORMAN_SIGNATURE))
|
||||||
}, listOf(DOORMAN_SIGNATURE))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,6 +206,6 @@ class DBCertificateRequestStorageTest : TestBase() {
|
|||||||
|
|
||||||
internal fun createRequest(organisation: String): Pair<PKCS10CertificationRequest, KeyPair> {
|
internal fun createRequest(organisation: String): Pair<PKCS10CertificationRequest, KeyPair> {
|
||||||
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val request = X509Utilities.createCertificateSigningRequest(CordaX500Name(organisation = organisation, locality = "London", country = "GB"), "my@mail.com", keyPair)
|
val request = X509Utilities.createCertificateSigningRequest(X500Principal("O=$organisation,L=London,C=GB"), "my@mail.com", keyPair)
|
||||||
return Pair(request, keyPair)
|
return Pair(request, keyPair)
|
||||||
}
|
}
|
@ -3,25 +3,24 @@ package com.r3.corda.networkmanage.common.persistence
|
|||||||
import com.r3.corda.networkmanage.TestBase
|
import com.r3.corda.networkmanage.TestBase
|
||||||
import com.r3.corda.networkmanage.common.utils.withCert
|
import com.r3.corda.networkmanage.common.utils.withCert
|
||||||
import com.r3.corda.networkmanage.doorman.signer.LocalSigner
|
import com.r3.corda.networkmanage.doorman.signer.LocalSigner
|
||||||
import net.corda.core.crypto.Crypto
|
|
||||||
import net.corda.core.crypto.sign
|
import net.corda.core.crypto.sign
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.nodeapi.internal.SignedNodeInfo
|
import net.corda.nodeapi.internal.SignedNodeInfo
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
|
||||||
import net.corda.nodeapi.internal.network.NetworkMap
|
import net.corda.nodeapi.internal.network.NetworkMap
|
||||||
import net.corda.nodeapi.internal.network.SignedNetworkMap
|
import net.corda.nodeapi.internal.network.SignedNetworkMap
|
||||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||||
import net.corda.testing.common.internal.testNetworkParameters
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
import net.corda.testing.internal.TestNodeInfoBuilder
|
import net.corda.testing.internal.TestNodeInfoBuilder
|
||||||
|
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class PersistentNetworkMapStorageTest : TestBase() {
|
class PersistentNetworkMapStorageTest : TestBase() {
|
||||||
@ -30,15 +29,16 @@ class PersistentNetworkMapStorageTest : TestBase() {
|
|||||||
private lateinit var nodeInfoStorage: PersistentNodeInfoStorage
|
private lateinit var nodeInfoStorage: PersistentNodeInfoStorage
|
||||||
private lateinit var requestStorage: PersistentCertificateRequestStorage
|
private lateinit var requestStorage: PersistentCertificateRequestStorage
|
||||||
|
|
||||||
private val rootCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
private lateinit var rootCaCert: X509Certificate
|
||||||
private val rootCaCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name("Corda Node Root CA", "R3 LTD", "London", "GB"), rootCaKeyPair)
|
private lateinit var intermediateCa: CertificateAndKeyPair
|
||||||
private val intermediateCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
|
||||||
private val intermediateCaCert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCaCert, rootCaKeyPair, CordaX500Name("Corda Node Intermediate CA", "R3 LTD", "London", "GB"), intermediateCaKeyPair.public)
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun startDb() {
|
fun startDb() {
|
||||||
|
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
|
||||||
|
rootCaCert = rootCa.certificate
|
||||||
|
this.intermediateCa = intermediateCa
|
||||||
persistence = configureDatabase(makeTestDataSourceProperties())
|
persistence = configureDatabase(makeTestDataSourceProperties())
|
||||||
networkMapStorage = PersistentNetworkMapStorage(persistence, LocalSigner(intermediateCaKeyPair, arrayOf(intermediateCaCert.cert, rootCaCert.cert)))
|
networkMapStorage = PersistentNetworkMapStorage(persistence, LocalSigner(intermediateCa.keyPair, arrayOf(intermediateCa.certificate, rootCaCert)))
|
||||||
nodeInfoStorage = PersistentNodeInfoStorage(persistence)
|
nodeInfoStorage = PersistentNodeInfoStorage(persistence)
|
||||||
requestStorage = PersistentCertificateRequestStorage(persistence)
|
requestStorage = PersistentCertificateRequestStorage(persistence)
|
||||||
}
|
}
|
||||||
@ -60,7 +60,7 @@ class PersistentNetworkMapStorageTest : TestBase() {
|
|||||||
|
|
||||||
val networkMap = NetworkMap(listOf(nodeInfoHash), networkParametersHash)
|
val networkMap = NetworkMap(listOf(nodeInfoHash), networkParametersHash)
|
||||||
val serializedNetworkMap = networkMap.serialize()
|
val serializedNetworkMap = networkMap.serialize()
|
||||||
val signatureData = intermediateCaKeyPair.sign(serializedNetworkMap).withCert(intermediateCaCert.cert)
|
val signatureData = intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate)
|
||||||
val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, signatureData)
|
val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, signatureData)
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@ -70,7 +70,7 @@ class PersistentNetworkMapStorageTest : TestBase() {
|
|||||||
val persistedSignedNetworkMap = networkMapStorage.getCurrentNetworkMap()
|
val persistedSignedNetworkMap = networkMapStorage.getCurrentNetworkMap()
|
||||||
|
|
||||||
assertEquals(signedNetworkMap.signature, persistedSignedNetworkMap?.signature)
|
assertEquals(signedNetworkMap.signature, persistedSignedNetworkMap?.signature)
|
||||||
assertEquals(signedNetworkMap.verified(rootCaCert.cert), persistedSignedNetworkMap?.verified(rootCaCert.cert))
|
assertEquals(signedNetworkMap.verified(rootCaCert), persistedSignedNetworkMap?.verified(rootCaCert))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -96,7 +96,7 @@ class PersistentNetworkMapStorageTest : TestBase() {
|
|||||||
// Sign network map making it current network map
|
// Sign network map making it current network map
|
||||||
val networkMap = NetworkMap(emptyList(), networkParametersHash)
|
val networkMap = NetworkMap(emptyList(), networkParametersHash)
|
||||||
val serializedNetworkMap = networkMap.serialize()
|
val serializedNetworkMap = networkMap.serialize()
|
||||||
val signatureData = intermediateCaKeyPair.sign(serializedNetworkMap).withCert(intermediateCaCert.cert)
|
val signatureData = intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate)
|
||||||
val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, signatureData)
|
val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, signatureData)
|
||||||
networkMapStorage.saveNetworkMap(signedNetworkMap)
|
networkMapStorage.saveNetworkMap(signedNetworkMap)
|
||||||
|
|
||||||
@ -117,7 +117,7 @@ class PersistentNetworkMapStorageTest : TestBase() {
|
|||||||
val netParamsHash = networkMapStorage.saveNetworkParameters(netParams)
|
val netParamsHash = networkMapStorage.saveNetworkParameters(netParams)
|
||||||
val signedNetParams = networkMapStorage.getSignedNetworkParameters(netParamsHash)
|
val signedNetParams = networkMapStorage.getSignedNetworkParameters(netParamsHash)
|
||||||
assertThat(signedNetParams?.verified()).isEqualTo(netParams)
|
assertThat(signedNetParams?.verified()).isEqualTo(netParams)
|
||||||
assertThat(signedNetParams?.sig?.by).isEqualTo(intermediateCaKeyPair.public)
|
assertThat(signedNetParams?.sig?.by).isEqualTo(intermediateCa.keyPair.public)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -135,7 +135,7 @@ class PersistentNetworkMapStorageTest : TestBase() {
|
|||||||
val networkParametersHash = networkMapStorage.saveNetworkParameters(testNetworkParameters(emptyList()))
|
val networkParametersHash = networkMapStorage.saveNetworkParameters(testNetworkParameters(emptyList()))
|
||||||
val networkMap = NetworkMap(listOf(nodeInfoHashA), networkParametersHash)
|
val networkMap = NetworkMap(listOf(nodeInfoHashA), networkParametersHash)
|
||||||
val serializedNetworkMap = networkMap.serialize()
|
val serializedNetworkMap = networkMap.serialize()
|
||||||
val signatureData = intermediateCaKeyPair.sign(serializedNetworkMap).withCert(intermediateCaCert.cert)
|
val signatureData = intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate)
|
||||||
val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, signatureData)
|
val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, signatureData)
|
||||||
|
|
||||||
// Sign network map
|
// Sign network map
|
||||||
|
@ -6,15 +6,16 @@ import com.r3.corda.networkmanage.common.utils.hashString
|
|||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.nodeapi.internal.SignedNodeInfo
|
import net.corda.nodeapi.internal.SignedNodeInfo
|
||||||
|
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||||
import net.corda.testing.internal.TestNodeInfoBuilder
|
import net.corda.testing.internal.TestNodeInfoBuilder
|
||||||
|
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
||||||
import net.corda.testing.internal.signWith
|
import net.corda.testing.internal.signWith
|
||||||
import net.corda.testing.node.MockServices
|
import net.corda.testing.node.MockServices
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
@ -22,6 +23,7 @@ import org.junit.After
|
|||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.security.PrivateKey
|
import java.security.PrivateKey
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertNotNull
|
import kotlin.test.assertNotNull
|
||||||
import kotlin.test.assertNull
|
import kotlin.test.assertNull
|
||||||
@ -31,13 +33,14 @@ class PersitenceNodeInfoStorageTest : TestBase() {
|
|||||||
private lateinit var nodeInfoStorage: PersistentNodeInfoStorage
|
private lateinit var nodeInfoStorage: PersistentNodeInfoStorage
|
||||||
private lateinit var persistence: CordaPersistence
|
private lateinit var persistence: CordaPersistence
|
||||||
|
|
||||||
private val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
private lateinit var rootCaCert: X509Certificate
|
||||||
private val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 LTD", country = "GB"), rootCAKey)
|
private lateinit var intermediateCa: CertificateAndKeyPair
|
||||||
private val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
|
||||||
private val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, CordaX500Name(commonName = "Corda Node Intermediate CA", locality = "London", organisation = "R3 LTD", country = "GB"), intermediateCAKey.public)
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun startDb() {
|
fun startDb() {
|
||||||
|
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
|
||||||
|
rootCaCert = rootCa.certificate
|
||||||
|
this.intermediateCa = intermediateCa
|
||||||
persistence = configureDatabase(MockServices.makeTestDataSourceProperties())
|
persistence = configureDatabase(MockServices.makeTestDataSourceProperties())
|
||||||
nodeInfoStorage = PersistentNodeInfoStorage(persistence)
|
nodeInfoStorage = PersistentNodeInfoStorage(persistence)
|
||||||
requestStorage = PersistentCertificateRequestStorage(persistence)
|
requestStorage = PersistentCertificateRequestStorage(persistence)
|
||||||
@ -53,9 +56,14 @@ class PersitenceNodeInfoStorageTest : TestBase() {
|
|||||||
// Create node info.
|
// Create node info.
|
||||||
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val name = CordaX500Name(organisation = "Test", locality = "London", country = "GB")
|
val name = CordaX500Name(organisation = "Test", locality = "London", country = "GB")
|
||||||
val nodeCaCert = X509Utilities.createCertificate(CertificateType.NODE_CA, intermediateCACert, intermediateCAKey, name, keyPair.public)
|
val nodeCaCert = X509Utilities.createCertificate(
|
||||||
|
CertificateType.NODE_CA,
|
||||||
|
intermediateCa.certificate,
|
||||||
|
intermediateCa.keyPair,
|
||||||
|
name.x500Principal,
|
||||||
|
keyPair.public)
|
||||||
|
|
||||||
val request = X509Utilities.createCertificateSigningRequest(name, "my@mail.com", keyPair)
|
val request = X509Utilities.createCertificateSigningRequest(name.x500Principal, "my@mail.com", keyPair)
|
||||||
|
|
||||||
val requestId = requestStorage.saveRequest(request)
|
val requestId = requestStorage.saveRequest(request)
|
||||||
requestStorage.markRequestTicketCreated(requestId)
|
requestStorage.markRequestTicketCreated(requestId)
|
||||||
@ -63,12 +71,15 @@ class PersitenceNodeInfoStorageTest : TestBase() {
|
|||||||
|
|
||||||
assertNull(nodeInfoStorage.getCertificatePath(SecureHash.parse(keyPair.public.hashString())))
|
assertNull(nodeInfoStorage.getCertificatePath(SecureHash.parse(keyPair.public.hashString())))
|
||||||
|
|
||||||
requestStorage.putCertificatePath(requestId, buildCertPath(nodeCaCert.cert, intermediateCACert.cert, rootCACert.cert), listOf(CertificationRequestStorage.DOORMAN_SIGNATURE))
|
requestStorage.putCertificatePath(
|
||||||
|
requestId,
|
||||||
|
buildCertPath(nodeCaCert, intermediateCa.certificate, rootCaCert),
|
||||||
|
listOf(CertificationRequestStorage.DOORMAN_SIGNATURE))
|
||||||
|
|
||||||
val storedCertPath = nodeInfoStorage.getCertificatePath(SecureHash.parse(keyPair.public.hashString()))
|
val storedCertPath = nodeInfoStorage.getCertificatePath(SecureHash.parse(keyPair.public.hashString()))
|
||||||
assertNotNull(storedCertPath)
|
assertNotNull(storedCertPath)
|
||||||
|
|
||||||
assertEquals(nodeCaCert.cert, storedCertPath!!.certificates.first())
|
assertEquals(nodeCaCert, storedCertPath!!.certificates.first())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -4,20 +4,18 @@ import com.nhaarman.mockito_kotlin.*
|
|||||||
import com.r3.corda.networkmanage.TestBase
|
import com.r3.corda.networkmanage.TestBase
|
||||||
import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage
|
import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage
|
||||||
import com.r3.corda.networkmanage.common.utils.withCert
|
import com.r3.corda.networkmanage.common.utils.withCert
|
||||||
import net.corda.core.crypto.Crypto
|
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.sha256
|
import net.corda.core.crypto.sha256
|
||||||
import net.corda.core.crypto.sign
|
import net.corda.core.crypto.sign
|
||||||
import net.corda.core.identity.CordaX500Name
|
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
|
||||||
import net.corda.nodeapi.internal.network.NetworkMap
|
import net.corda.nodeapi.internal.network.NetworkMap
|
||||||
import net.corda.nodeapi.internal.network.SignedNetworkMap
|
import net.corda.nodeapi.internal.network.SignedNetworkMap
|
||||||
import net.corda.testing.common.internal.testNetworkParameters
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
|
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
@ -25,12 +23,15 @@ class NetworkMapSignerTest : TestBase() {
|
|||||||
private lateinit var signer: Signer
|
private lateinit var signer: Signer
|
||||||
private lateinit var networkMapStorage: NetworkMapStorage
|
private lateinit var networkMapStorage: NetworkMapStorage
|
||||||
private lateinit var networkMapSigner: NetworkMapSigner
|
private lateinit var networkMapSigner: NetworkMapSigner
|
||||||
private val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
|
||||||
private val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 LTD", country = "GB"), rootCAKey)
|
private lateinit var rootCaCert: X509Certificate
|
||||||
private val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
private lateinit var intermediateCa: CertificateAndKeyPair
|
||||||
private val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, CordaX500Name(commonName = "Corda Node Intermediate CA", locality = "London", organisation = "R3 LTD", country = "GB"), intermediateCAKey.public)
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
|
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
|
||||||
|
rootCaCert = rootCa.certificate
|
||||||
|
this.intermediateCa = intermediateCa
|
||||||
signer = mock()
|
signer = mock()
|
||||||
networkMapStorage = mock()
|
networkMapStorage = mock()
|
||||||
networkMapSigner = NetworkMapSigner(networkMapStorage, signer)
|
networkMapSigner = NetworkMapSigner(networkMapStorage, signer)
|
||||||
@ -43,11 +44,11 @@ class NetworkMapSignerTest : TestBase() {
|
|||||||
val networkParameters = testNetworkParameters(emptyList())
|
val networkParameters = testNetworkParameters(emptyList())
|
||||||
val serializedNetworkMap = NetworkMap(signedNodeInfoHashes, SecureHash.randomSHA256()).serialize()
|
val serializedNetworkMap = NetworkMap(signedNodeInfoHashes, SecureHash.randomSHA256()).serialize()
|
||||||
whenever(networkMapStorage.getCurrentNetworkMap())
|
whenever(networkMapStorage.getCurrentNetworkMap())
|
||||||
.thenReturn(SignedNetworkMap(serializedNetworkMap, intermediateCAKey.sign(serializedNetworkMap).withCert(intermediateCACert.cert)))
|
.thenReturn(SignedNetworkMap(serializedNetworkMap, intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate)))
|
||||||
whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(signedNodeInfoHashes)
|
whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(signedNodeInfoHashes)
|
||||||
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters)
|
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters)
|
||||||
whenever(signer.sign(any())).then {
|
whenever(signer.sign(any())).then {
|
||||||
intermediateCAKey.sign(it.arguments.first() as ByteArray).withCert(intermediateCACert.cert)
|
intermediateCa.keyPair.sign(it.arguments[0] as ByteArray).withCert(intermediateCa.certificate)
|
||||||
}
|
}
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@ -59,7 +60,7 @@ class NetworkMapSignerTest : TestBase() {
|
|||||||
verify(networkMapStorage).getLatestNetworkParameters()
|
verify(networkMapStorage).getLatestNetworkParameters()
|
||||||
argumentCaptor<SignedNetworkMap>().apply {
|
argumentCaptor<SignedNetworkMap>().apply {
|
||||||
verify(networkMapStorage).saveNetworkMap(capture())
|
verify(networkMapStorage).saveNetworkMap(capture())
|
||||||
val networkMap = firstValue.verified(rootCACert.cert)
|
val networkMap = firstValue.verified(rootCaCert)
|
||||||
assertEquals(networkParameters.serialize().hash, networkMap.networkParameterHash)
|
assertEquals(networkParameters.serialize().hash, networkMap.networkParameterHash)
|
||||||
assertEquals(signedNodeInfoHashes.size, networkMap.nodeInfoHashes.size)
|
assertEquals(signedNodeInfoHashes.size, networkMap.nodeInfoHashes.size)
|
||||||
assertTrue(networkMap.nodeInfoHashes.containsAll(signedNodeInfoHashes))
|
assertTrue(networkMap.nodeInfoHashes.containsAll(signedNodeInfoHashes))
|
||||||
@ -73,7 +74,7 @@ class NetworkMapSignerTest : TestBase() {
|
|||||||
val networkMapParametersHash = networkParameters.serialize().bytes.sha256()
|
val networkMapParametersHash = networkParameters.serialize().bytes.sha256()
|
||||||
val networkMap = NetworkMap(emptyList(), networkMapParametersHash)
|
val networkMap = NetworkMap(emptyList(), networkMapParametersHash)
|
||||||
val serializedNetworkMap = networkMap.serialize()
|
val serializedNetworkMap = networkMap.serialize()
|
||||||
val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, intermediateCAKey.sign(serializedNetworkMap).withCert(intermediateCACert.cert))
|
val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate))
|
||||||
whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(signedNetworkMap)
|
whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(signedNetworkMap)
|
||||||
whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList())
|
whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList())
|
||||||
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters)
|
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters)
|
||||||
@ -94,7 +95,7 @@ class NetworkMapSignerTest : TestBase() {
|
|||||||
whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList())
|
whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList())
|
||||||
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters)
|
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters)
|
||||||
whenever(signer.sign(any())).then {
|
whenever(signer.sign(any())).then {
|
||||||
intermediateCAKey.sign(it.arguments.first() as ByteArray).withCert(intermediateCACert.cert)
|
intermediateCa.keyPair.sign(it.arguments[0] as ByteArray).withCert(intermediateCa.certificate)
|
||||||
}
|
}
|
||||||
// when
|
// when
|
||||||
networkMapSigner.signNetworkMap()
|
networkMapSigner.signNetworkMap()
|
||||||
@ -105,7 +106,7 @@ class NetworkMapSignerTest : TestBase() {
|
|||||||
verify(networkMapStorage).getLatestNetworkParameters()
|
verify(networkMapStorage).getLatestNetworkParameters()
|
||||||
argumentCaptor<SignedNetworkMap>().apply {
|
argumentCaptor<SignedNetworkMap>().apply {
|
||||||
verify(networkMapStorage).saveNetworkMap(capture())
|
verify(networkMapStorage).saveNetworkMap(capture())
|
||||||
val networkMap = firstValue.verified(rootCACert.cert)
|
val networkMap = firstValue.verified(rootCaCert)
|
||||||
assertEquals(networkParameters.serialize().hash, networkMap.networkParameterHash)
|
assertEquals(networkParameters.serialize().hash, networkMap.networkParameterHash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,24 +10,23 @@ import com.r3.corda.networkmanage.common.persistence.CertificateStatus
|
|||||||
import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage
|
import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage
|
||||||
import com.r3.corda.networkmanage.common.persistence.RequestStatus
|
import com.r3.corda.networkmanage.common.persistence.RequestStatus
|
||||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
||||||
import com.r3.corda.networkmanage.common.utils.toX509Certificate
|
|
||||||
import com.r3.corda.networkmanage.doorman.signer.DefaultCsrHandler
|
import com.r3.corda.networkmanage.doorman.signer.DefaultCsrHandler
|
||||||
import com.r3.corda.networkmanage.doorman.signer.LocalSigner
|
import com.r3.corda.networkmanage.doorman.signer.LocalSigner
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.identity.CordaX500Name
|
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class DefaultRequestProcessorTest : TestBase() {
|
class DefaultRequestProcessorTest : TestBase() {
|
||||||
@Test
|
@Test
|
||||||
fun `get response`() {
|
fun `get response`() {
|
||||||
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val cert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(locality = "London", organisation = "Test", country = "GB"), keyPair)
|
val cert = X509Utilities.createSelfSignedCACertificate(X500Principal("O=Test,L=London,C=GB"), keyPair)
|
||||||
|
|
||||||
val requestStorage: CertificationRequestStorage = mock {
|
val requestStorage: CertificationRequestStorage = mock {
|
||||||
on { getRequest("New") }.thenReturn(certificateSigningRequest())
|
on { getRequest("New") }.thenReturn(certificateSigningRequest())
|
||||||
on { getRequest("Signed") }.thenReturn(certificateSigningRequest(status = RequestStatus.SIGNED, certData = certificateData("", CertificateStatus.VALID, buildCertPath(cert.toX509Certificate()))))
|
on { getRequest("Signed") }.thenReturn(certificateSigningRequest(status = RequestStatus.SIGNED, certData = certificateData("", CertificateStatus.VALID, buildCertPath(cert))))
|
||||||
on { getRequest("Rejected") }.thenReturn(certificateSigningRequest(status = RequestStatus.REJECTED, remark = "Random reason"))
|
on { getRequest("Rejected") }.thenReturn(certificateSigningRequest(status = RequestStatus.REJECTED, remark = "Random reason"))
|
||||||
}
|
}
|
||||||
val signer: LocalSigner = mock()
|
val signer: LocalSigner = mock()
|
||||||
@ -35,15 +34,15 @@ class DefaultRequestProcessorTest : TestBase() {
|
|||||||
|
|
||||||
assertEquals(CertificateResponse.NotReady, requestProcessor.getResponse("random"))
|
assertEquals(CertificateResponse.NotReady, requestProcessor.getResponse("random"))
|
||||||
assertEquals(CertificateResponse.NotReady, requestProcessor.getResponse("New"))
|
assertEquals(CertificateResponse.NotReady, requestProcessor.getResponse("New"))
|
||||||
assertEquals(CertificateResponse.Ready(buildCertPath(cert.toX509Certificate())), requestProcessor.getResponse("Signed"))
|
assertEquals(CertificateResponse.Ready(buildCertPath(cert)), requestProcessor.getResponse("Signed"))
|
||||||
assertEquals(CertificateResponse.Unauthorised("Random reason"), requestProcessor.getResponse("Rejected"))
|
assertEquals(CertificateResponse.Unauthorised("Random reason"), requestProcessor.getResponse("Rejected"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `process request`() {
|
fun `process request`() {
|
||||||
val request1 = X509Utilities.createCertificateSigningRequest(CordaX500Name(locality = "London", organisation = "Test1", country = "GB"), "my@email.com", Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
|
val (request1, request2, request3) = (1..3).map {
|
||||||
val request2 = X509Utilities.createCertificateSigningRequest(CordaX500Name(locality = "London", organisation = "Test2", country = "GB"), "my@email.com", Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
|
X509Utilities.createCertificateSigningRequest(X500Principal("O=Test1,L=London,C=GB"), "my@email.com", Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
|
||||||
val request3 = X509Utilities.createCertificateSigningRequest(CordaX500Name(locality = "London", organisation = "Test3", country = "GB"), "my@email.com", Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
|
}
|
||||||
|
|
||||||
val requestStorage: CertificationRequestStorage = mock {
|
val requestStorage: CertificationRequestStorage = mock {
|
||||||
on { getRequests(RequestStatus.APPROVED) }.thenReturn(listOf(
|
on { getRequests(RequestStatus.APPROVED) }.thenReturn(listOf(
|
||||||
|
@ -7,33 +7,32 @@ import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage
|
|||||||
import com.r3.corda.networkmanage.common.persistence.NodeInfoStorage
|
import com.r3.corda.networkmanage.common.persistence.NodeInfoStorage
|
||||||
import com.r3.corda.networkmanage.common.utils.withCert
|
import com.r3.corda.networkmanage.common.utils.withCert
|
||||||
import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService
|
import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService
|
||||||
import net.corda.core.crypto.Crypto
|
|
||||||
import net.corda.core.crypto.SecureHash.Companion.randomSHA256
|
import net.corda.core.crypto.SecureHash.Companion.randomSHA256
|
||||||
import net.corda.core.crypto.SignedData
|
import net.corda.core.crypto.SignedData
|
||||||
import net.corda.core.crypto.sign
|
import net.corda.core.crypto.sign
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.internal.openHttpConnection
|
import net.corda.core.internal.openHttpConnection
|
||||||
import net.corda.core.serialization.deserialize
|
import net.corda.core.serialization.deserialize
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.core.utilities.seconds
|
import net.corda.core.utilities.seconds
|
||||||
import net.corda.nodeapi.internal.SignedNodeInfo
|
import net.corda.nodeapi.internal.SignedNodeInfo
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
|
||||||
import net.corda.nodeapi.internal.network.NetworkMap
|
import net.corda.nodeapi.internal.network.NetworkMap
|
||||||
import net.corda.nodeapi.internal.network.NetworkParameters
|
import net.corda.nodeapi.internal.network.NetworkParameters
|
||||||
import net.corda.nodeapi.internal.network.SignedNetworkMap
|
import net.corda.nodeapi.internal.network.SignedNetworkMap
|
||||||
import net.corda.testing.SerializationEnvironmentRule
|
import net.corda.testing.SerializationEnvironmentRule
|
||||||
import net.corda.testing.common.internal.testNetworkParameters
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
|
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
||||||
import net.corda.testing.internal.createNodeInfoAndSigned
|
import net.corda.testing.internal.createNodeInfoAndSigned
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.junit.Before
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
import javax.ws.rs.core.MediaType
|
import javax.ws.rs.core.MediaType
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
@ -42,12 +41,18 @@ class NodeInfoWebServiceTest {
|
|||||||
@JvmField
|
@JvmField
|
||||||
val testSerialization = SerializationEnvironmentRule(true)
|
val testSerialization = SerializationEnvironmentRule(true)
|
||||||
|
|
||||||
private val rootCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
private lateinit var rootCaCert: X509Certificate
|
||||||
private val rootCaCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name("Corda Node Root CA", "R3 LTD", "London", "GB"), rootCaKeyPair)
|
private lateinit var intermediateCa: CertificateAndKeyPair
|
||||||
private val intermediateCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
|
||||||
private val intermediateCaCert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCaCert, rootCaKeyPair, X500Name("CN=Corda Node Intermediate CA,L=London"), intermediateCaKeyPair.public)
|
|
||||||
private val testNetworkMapConfig = NetworkMapConfig(10.seconds.toMillis(), 10.seconds.toMillis())
|
private val testNetworkMapConfig = NetworkMapConfig(10.seconds.toMillis(), 10.seconds.toMillis())
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun init() {
|
||||||
|
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
|
||||||
|
rootCaCert = rootCa.certificate
|
||||||
|
this.intermediateCa = intermediateCa
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `submit nodeInfo`() {
|
fun `submit nodeInfo`() {
|
||||||
// Create node info.
|
// Create node info.
|
||||||
@ -65,7 +70,7 @@ class NodeInfoWebServiceTest {
|
|||||||
fun `get network map`() {
|
fun `get network map`() {
|
||||||
val networkMap = NetworkMap(listOf(randomSHA256(), randomSHA256()), randomSHA256())
|
val networkMap = NetworkMap(listOf(randomSHA256(), randomSHA256()), randomSHA256())
|
||||||
val serializedNetworkMap = networkMap.serialize()
|
val serializedNetworkMap = networkMap.serialize()
|
||||||
val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, intermediateCaKeyPair.sign(serializedNetworkMap).withCert(intermediateCaCert.cert))
|
val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate))
|
||||||
|
|
||||||
val networkMapStorage: NetworkMapStorage = mock {
|
val networkMapStorage: NetworkMapStorage = mock {
|
||||||
on { getCurrentNetworkMap() }.thenReturn(signedNetworkMap)
|
on { getCurrentNetworkMap() }.thenReturn(signedNetworkMap)
|
||||||
@ -75,7 +80,7 @@ class NodeInfoWebServiceTest {
|
|||||||
it.start()
|
it.start()
|
||||||
val signedNetworkMapResponse = it.doGet<SignedNetworkMap>("")
|
val signedNetworkMapResponse = it.doGet<SignedNetworkMap>("")
|
||||||
verify(networkMapStorage, times(1)).getCurrentNetworkMap()
|
verify(networkMapStorage, times(1)).getCurrentNetworkMap()
|
||||||
assertEquals(signedNetworkMapResponse.verified(rootCaCert.cert), networkMap)
|
assertEquals(signedNetworkMapResponse.verified(rootCaCert), networkMap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,7 +109,7 @@ class NodeInfoWebServiceTest {
|
|||||||
fun `get network parameters`() {
|
fun `get network parameters`() {
|
||||||
val netParams = testNetworkParameters(emptyList())
|
val netParams = testNetworkParameters(emptyList())
|
||||||
val serializedNetParams = netParams.serialize()
|
val serializedNetParams = netParams.serialize()
|
||||||
val signedNetParams = SignedData(serializedNetParams, intermediateCaKeyPair.sign(serializedNetParams))
|
val signedNetParams = SignedData(serializedNetParams, intermediateCa.keyPair.sign(serializedNetParams))
|
||||||
val netParamsHash = serializedNetParams.hash
|
val netParamsHash = serializedNetParams.hash
|
||||||
|
|
||||||
val networkMapStorage: NetworkMapStorage = mock {
|
val networkMapStorage: NetworkMapStorage = mock {
|
||||||
@ -116,7 +121,7 @@ class NodeInfoWebServiceTest {
|
|||||||
val netParamsResponse = it.doGet<SignedData<NetworkParameters>>("network-parameter/$netParamsHash")
|
val netParamsResponse = it.doGet<SignedData<NetworkParameters>>("network-parameter/$netParamsHash")
|
||||||
verify(networkMapStorage, times(1)).getSignedNetworkParameters(netParamsHash)
|
verify(networkMapStorage, times(1)).getSignedNetworkParameters(netParamsHash)
|
||||||
assertThat(netParamsResponse.verified()).isEqualTo(netParams)
|
assertThat(netParamsResponse.verified()).isEqualTo(netParams)
|
||||||
assertThat(netParamsResponse.sig.by).isEqualTo(intermediateCaKeyPair.public)
|
assertThat(netParamsResponse.sig.by).isEqualTo(intermediateCa.keyPair.public)
|
||||||
|
|
||||||
assertThatExceptionOfType(FileNotFoundException::class.java).isThrownBy {
|
assertThatExceptionOfType(FileNotFoundException::class.java).isThrownBy {
|
||||||
it.doGet<SignedData<NetworkParameters>>("network-parameter/${randomSHA256()}")
|
it.doGet<SignedData<NetworkParameters>>("network-parameter/${randomSHA256()}")
|
||||||
|
@ -4,46 +4,54 @@ import com.nhaarman.mockito_kotlin.*
|
|||||||
import com.r3.corda.networkmanage.TestBase
|
import com.r3.corda.networkmanage.TestBase
|
||||||
import com.r3.corda.networkmanage.common.persistence.CertificateResponse
|
import com.r3.corda.networkmanage.common.persistence.CertificateResponse
|
||||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
||||||
import com.r3.corda.networkmanage.common.utils.toX509Certificate
|
|
||||||
import com.r3.corda.networkmanage.doorman.signer.CsrHandler
|
import com.r3.corda.networkmanage.doorman.signer.CsrHandler
|
||||||
import com.r3.corda.networkmanage.doorman.webservice.RegistrationWebService
|
import com.r3.corda.networkmanage.doorman.webservice.RegistrationWebService
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
|
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME
|
import net.corda.nodeapi.internal.crypto.X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME
|
||||||
|
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
||||||
import org.apache.commons.io.IOUtils
|
import org.apache.commons.io.IOUtils
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
import org.bouncycastle.asn1.x509.GeneralName
|
import org.bouncycastle.asn1.x509.GeneralName
|
||||||
import org.bouncycastle.asn1.x509.GeneralSubtree
|
import org.bouncycastle.asn1.x509.GeneralSubtree
|
||||||
import org.bouncycastle.asn1.x509.NameConstraints
|
import org.bouncycastle.asn1.x509.NameConstraints
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
|
||||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
||||||
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
|
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
import java.net.HttpURLConnection.*
|
import java.net.HttpURLConnection.*
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
import java.nio.charset.StandardCharsets.UTF_8
|
||||||
import java.security.cert.CertPath
|
import java.security.cert.CertPath
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.zip.ZipInputStream
|
import java.util.zip.ZipInputStream
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
import javax.ws.rs.core.MediaType
|
import javax.ws.rs.core.MediaType
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class RegistrationWebServiceTest : TestBase() {
|
class RegistrationWebServiceTest : TestBase() {
|
||||||
private val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
private lateinit var rootCaCert: X509Certificate
|
||||||
private val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 Ltd", country = "GB"), rootCAKey)
|
private lateinit var intermediateCa: CertificateAndKeyPair
|
||||||
private val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
|
||||||
private val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, X500Name("CN=Corda Node Intermediate CA,L=London"), intermediateCAKey.public)
|
|
||||||
private lateinit var webServer: NetworkManagementWebServer
|
private lateinit var webServer: NetworkManagementWebServer
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun init() {
|
||||||
|
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
|
||||||
|
rootCaCert = rootCa.certificate
|
||||||
|
this.intermediateCa = intermediateCa
|
||||||
|
}
|
||||||
|
|
||||||
private fun startSigningServer(csrHandler: CsrHandler) {
|
private fun startSigningServer(csrHandler: CsrHandler) {
|
||||||
webServer = NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), RegistrationWebService(csrHandler))
|
webServer = NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), RegistrationWebService(csrHandler))
|
||||||
webServer.start()
|
webServer.start()
|
||||||
@ -65,7 +73,10 @@ class RegistrationWebServiceTest : TestBase() {
|
|||||||
startSigningServer(requestProcessor)
|
startSigningServer(requestProcessor)
|
||||||
|
|
||||||
val keyPair = Crypto.generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME)
|
val keyPair = Crypto.generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val request = X509Utilities.createCertificateSigningRequest(CordaX500Name(locality = "London", organisation = "Legal Name", country = "GB"), "my@mail.com", keyPair)
|
val request = X509Utilities.createCertificateSigningRequest(
|
||||||
|
CordaX500Name(locality = "London", organisation = "Legal Name", country = "GB").x500Principal,
|
||||||
|
"my@mail.com",
|
||||||
|
keyPair)
|
||||||
// Post request to signing server via http.
|
// Post request to signing server via http.
|
||||||
|
|
||||||
assertEquals(id, submitRequest(request))
|
assertEquals(id, submitRequest(request))
|
||||||
@ -79,6 +90,8 @@ class RegistrationWebServiceTest : TestBase() {
|
|||||||
val keyPair = Crypto.generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME)
|
val keyPair = Crypto.generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val id = SecureHash.randomSHA256().toString()
|
val id = SecureHash.randomSHA256().toString()
|
||||||
|
|
||||||
|
val subject = CordaX500Name(locality = "London", organisation = "LegalName", country = "GB").x500Principal
|
||||||
|
|
||||||
// Mock Storage behaviour.
|
// Mock Storage behaviour.
|
||||||
val certificateStore = mutableMapOf<String, CertPath>()
|
val certificateStore = mutableMapOf<String, CertPath>()
|
||||||
val requestProcessor = mock<CsrHandler> {
|
val requestProcessor = mock<CsrHandler> {
|
||||||
@ -88,10 +101,15 @@ class RegistrationWebServiceTest : TestBase() {
|
|||||||
} ?: CertificateResponse.NotReady
|
} ?: CertificateResponse.NotReady
|
||||||
}
|
}
|
||||||
on { processApprovedRequests() }.then {
|
on { processApprovedRequests() }.then {
|
||||||
val request = X509Utilities.createCertificateSigningRequest(CordaX500Name(locality = "London", organisation = "LegalName", country = "GB"), "my@mail.com", keyPair)
|
val request = X509Utilities.createCertificateSigningRequest(subject, "my@mail.com", keyPair)
|
||||||
certificateStore[id] = JcaPKCS10CertificationRequest(request).run {
|
certificateStore[id] = JcaPKCS10CertificationRequest(request).run {
|
||||||
val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, intermediateCACert, intermediateCAKey, subject, publicKey).toX509Certificate()
|
val tlsCert = X509Utilities.createCertificate(
|
||||||
buildCertPath(tlsCert, intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
|
CertificateType.TLS,
|
||||||
|
intermediateCa.certificate,
|
||||||
|
intermediateCa.keyPair,
|
||||||
|
X500Principal(subject.encoded),
|
||||||
|
publicKey)
|
||||||
|
buildCertPath(tlsCert, intermediateCa.certificate, rootCaCert)
|
||||||
}
|
}
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
@ -104,22 +122,15 @@ class RegistrationWebServiceTest : TestBase() {
|
|||||||
|
|
||||||
val certificates = (pollForResponse(id) as PollResponse.Ready).certChain
|
val certificates = (pollForResponse(id) as PollResponse.Ready).certChain
|
||||||
verify(requestProcessor, times(2)).getResponse(any())
|
verify(requestProcessor, times(2)).getResponse(any())
|
||||||
assertEquals(3, certificates.size)
|
|
||||||
|
|
||||||
certificates.first().run {
|
assertThat(certificates).hasSize(3)
|
||||||
assertThat(subjectDN.name).contains("O=LegalName")
|
assertThat(certificates[0].subjectX500Principal).isEqualTo(subject)
|
||||||
assertThat(subjectDN.name).contains("L=London")
|
assertThat(certificates).endsWith(intermediateCa.certificate, rootCaCert)
|
||||||
}
|
|
||||||
|
|
||||||
certificates.last().run {
|
|
||||||
assertThat(subjectDN.name).contains("CN=Corda Node Root CA")
|
|
||||||
assertThat(subjectDN.name).contains("L=London")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `retrieve certificate and create valid TLS certificate`() {
|
fun `retrieve certificate and create valid TLS certificate`() {
|
||||||
val keyPair = Crypto.generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME)
|
val nodeCaKeyPair = Crypto.generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val id = SecureHash.randomSHA256().toString()
|
val id = SecureHash.randomSHA256().toString()
|
||||||
|
|
||||||
// Mock Storage behaviour.
|
// Mock Storage behaviour.
|
||||||
@ -131,11 +142,22 @@ class RegistrationWebServiceTest : TestBase() {
|
|||||||
} ?: CertificateResponse.NotReady
|
} ?: CertificateResponse.NotReady
|
||||||
}
|
}
|
||||||
on { processApprovedRequests() }.then {
|
on { processApprovedRequests() }.then {
|
||||||
val request = X509Utilities.createCertificateSigningRequest(CordaX500Name(locality = "London", organisation = "Legal Name", country = "GB"), "my@mail.com", keyPair)
|
val request = X509Utilities.createCertificateSigningRequest(
|
||||||
|
CordaX500Name(locality = "London", organisation = "Legal Name", country = "GB").x500Principal,
|
||||||
|
"my@mail.com",
|
||||||
|
nodeCaKeyPair)
|
||||||
certificateStore[id] = JcaPKCS10CertificationRequest(request).run {
|
certificateStore[id] = JcaPKCS10CertificationRequest(request).run {
|
||||||
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, X500Name("CN=LegalName, L=London")))), arrayOf())
|
val nameConstraints = NameConstraints(
|
||||||
val clientCert = X509Utilities.createCertificate(CertificateType.NODE_CA, intermediateCACert, intermediateCAKey, subject, publicKey, nameConstraints = nameConstraints).toX509Certificate()
|
arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, X500Name("CN=LegalName, L=London")))),
|
||||||
buildCertPath(clientCert, intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
|
arrayOf())
|
||||||
|
val clientCert = X509Utilities.createCertificate(
|
||||||
|
CertificateType.NODE_CA,
|
||||||
|
intermediateCa.certificate,
|
||||||
|
intermediateCa.keyPair,
|
||||||
|
X500Principal(subject.encoded),
|
||||||
|
publicKey,
|
||||||
|
nameConstraints = nameConstraints)
|
||||||
|
buildCertPath(clientCert, intermediateCa.certificate, rootCaCert)
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@ -149,11 +171,17 @@ class RegistrationWebServiceTest : TestBase() {
|
|||||||
verify(storage, times(2)).getResponse(any())
|
verify(storage, times(2)).getResponse(any())
|
||||||
assertEquals(3, certificates.size)
|
assertEquals(3, certificates.size)
|
||||||
|
|
||||||
val sslKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val sslKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val sslCert = X509Utilities.createCertificate(CertificateType.TLS, X509CertificateHolder(certificates.first().encoded), keyPair, X500Name("CN=LegalName,L=London"), sslKey.public).toX509Certificate()
|
val sslCert = X509Utilities.createCertificate(
|
||||||
|
CertificateType.TLS,
|
||||||
|
certificates[0],
|
||||||
|
nodeCaKeyPair,
|
||||||
|
// TODO Investigate why X500Principal("CN=LegalName, L=London") results in a name constraints violation
|
||||||
|
X500Principal(X500Name("CN=LegalName, L=London").encoded),
|
||||||
|
sslKeyPair.public)
|
||||||
|
|
||||||
// TODO: This is temporary solution, remove all certificate re-shaping after identity refactoring is done.
|
// TODO: This is temporary solution, remove all certificate re-shaping after identity refactoring is done.
|
||||||
X509Utilities.validateCertificateChain(certificates.last(), sslCert, *certificates.toTypedArray())
|
X509Utilities.validateCertificateChain(rootCaCert, sslCert, *certificates.toTypedArray())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -192,7 +220,7 @@ class RegistrationWebServiceTest : TestBase() {
|
|||||||
PollResponse.Ready(certificates)
|
PollResponse.Ready(certificates)
|
||||||
}
|
}
|
||||||
HTTP_NO_CONTENT -> PollResponse.NotReady
|
HTTP_NO_CONTENT -> PollResponse.NotReady
|
||||||
HTTP_UNAUTHORIZED -> PollResponse.Unauthorised(IOUtils.toString(conn.errorStream))
|
HTTP_UNAUTHORIZED -> PollResponse.Unauthorised(IOUtils.toString(conn.errorStream, UTF_8))
|
||||||
else -> throw IOException("Cannot connect to Certificate Signing Server, HTTP response code : ${conn.responseCode}")
|
else -> throw IOException("Cannot connect to Certificate Signing Server, HTTP response code : ${conn.responseCode}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,10 @@ class JiraCsrHandlerTest {
|
|||||||
private lateinit var certificateResponse: CertificateResponse.Ready
|
private lateinit var certificateResponse: CertificateResponse.Ready
|
||||||
|
|
||||||
private val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
private val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
private val pkcS10CertificationRequest = X509Utilities.createCertificateSigningRequest(CordaX500Name(locality = "London", organisation = "LegalName", country = "GB"), "my@mail.com", keyPair)
|
private val pkcS10CertificationRequest = X509Utilities.createCertificateSigningRequest(
|
||||||
|
CordaX500Name(locality = "London", organisation = "LegalName", country = "GB").x500Principal,
|
||||||
|
"my@mail.com",
|
||||||
|
keyPair)
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
|
@ -5,10 +5,8 @@ import net.corda.core.crypto.Crypto
|
|||||||
import net.corda.core.crypto.generateKeyPair
|
import net.corda.core.crypto.generateKeyPair
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.internal.createDirectories
|
import net.corda.core.internal.createDirectories
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.internal.toX509CertHolder
|
|
||||||
import net.corda.core.utilities.trace
|
import net.corda.core.utilities.trace
|
||||||
import net.corda.nodeapi.internal.config.NodeSSLConfiguration
|
import net.corda.nodeapi.internal.config.NodeSSLConfiguration
|
||||||
import net.corda.nodeapi.internal.crypto.*
|
import net.corda.nodeapi.internal.crypto.*
|
||||||
@ -39,10 +37,10 @@ object DevIdentityGenerator {
|
|||||||
// TODO The passwords for the dev key stores are spread everywhere and should be constants in a single location
|
// TODO The passwords for the dev key stores are spread everywhere and should be constants in a single location
|
||||||
val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
|
val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
|
||||||
val intermediateCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
|
val intermediateCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
|
||||||
val rootCert = caKeyStore.getCertificate(X509Utilities.CORDA_ROOT_CA)
|
val rootCert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA)
|
||||||
|
|
||||||
nodeSslConfig.certificatesDirectory.createDirectories()
|
nodeSslConfig.certificatesDirectory.createDirectories()
|
||||||
nodeSslConfig.createDevKeyStores(rootCert.toX509CertHolder(), intermediateCa, legalName)
|
nodeSslConfig.createDevKeyStores(rootCert, intermediateCa, legalName)
|
||||||
|
|
||||||
val keyStoreWrapper = KeyStoreWrapper(nodeSslConfig.nodeKeystore, nodeSslConfig.keyStorePassword)
|
val keyStoreWrapper = KeyStoreWrapper(nodeSslConfig.nodeKeystore, nodeSslConfig.keyStorePassword)
|
||||||
val identity = keyStoreWrapper.storeLegalIdentity(legalName, "$NODE_IDENTITY_ALIAS_PREFIX-private-key", Crypto.generateKeyPair())
|
val identity = keyStoreWrapper.storeLegalIdentity(legalName, "$NODE_IDENTITY_ALIAS_PREFIX-private-key", Crypto.generateKeyPair())
|
||||||
@ -62,16 +60,21 @@ object DevIdentityGenerator {
|
|||||||
|
|
||||||
keyPairs.zip(dirs) { keyPair, nodeDir ->
|
keyPairs.zip(dirs) { keyPair, nodeDir ->
|
||||||
val (serviceKeyCert, compositeKeyCert) = listOf(keyPair.public, compositeKey).map { publicKey ->
|
val (serviceKeyCert, compositeKeyCert) = listOf(keyPair.public, compositeKey).map { publicKey ->
|
||||||
X509Utilities.createCertificate(CertificateType.SERVICE_IDENTITY, intermediateCa.certificate, intermediateCa.keyPair, notaryName, publicKey)
|
X509Utilities.createCertificate(
|
||||||
|
CertificateType.SERVICE_IDENTITY,
|
||||||
|
intermediateCa.certificate,
|
||||||
|
intermediateCa.keyPair,
|
||||||
|
notaryName.x500Principal,
|
||||||
|
publicKey)
|
||||||
}
|
}
|
||||||
val distServKeyStoreFile = (nodeDir / "certificates").createDirectories() / "distributedService.jks"
|
val distServKeyStoreFile = (nodeDir / "certificates").createDirectories() / "distributedService.jks"
|
||||||
val keystore = loadOrCreateKeyStore(distServKeyStoreFile, "cordacadevpass")
|
val keystore = loadOrCreateKeyStore(distServKeyStoreFile, "cordacadevpass")
|
||||||
keystore.setCertificateEntry("$DISTRIBUTED_NOTARY_ALIAS_PREFIX-composite-key", compositeKeyCert.cert)
|
keystore.setCertificateEntry("$DISTRIBUTED_NOTARY_ALIAS_PREFIX-composite-key", compositeKeyCert)
|
||||||
keystore.setKeyEntry(
|
keystore.setKeyEntry(
|
||||||
"$DISTRIBUTED_NOTARY_ALIAS_PREFIX-private-key",
|
"$DISTRIBUTED_NOTARY_ALIAS_PREFIX-private-key",
|
||||||
keyPair.private,
|
keyPair.private,
|
||||||
"cordacadevkeypass".toCharArray(),
|
"cordacadevkeypass".toCharArray(),
|
||||||
arrayOf(serviceKeyCert.cert, intermediateCa.certificate.cert, rootCert))
|
arrayOf(serviceKeyCert, intermediateCa.certificate, rootCert))
|
||||||
keystore.save(distServKeyStoreFile, "cordacadevpass")
|
keystore.save(distServKeyStoreFile, "cordacadevpass")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,13 +8,13 @@ import net.corda.nodeapi.internal.crypto.*
|
|||||||
import org.bouncycastle.asn1.x509.GeneralName
|
import org.bouncycastle.asn1.x509.GeneralName
|
||||||
import org.bouncycastle.asn1.x509.GeneralSubtree
|
import org.bouncycastle.asn1.x509.GeneralSubtree
|
||||||
import org.bouncycastle.asn1.x509.NameConstraints
|
import org.bouncycastle.asn1.x509.NameConstraints
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
import java.security.cert.X509Certificate
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the node and SSL key stores needed by a node. The node key store will be populated with a node CA cert (using
|
* Create the node and SSL key stores needed by a node. The node key store will be populated with a node CA cert (using
|
||||||
* the given legal name), and the SSL key store will store the TLS cert which is a sub-cert of the node CA.
|
* the given legal name), and the SSL key store will store the TLS cert which is a sub-cert of the node CA.
|
||||||
*/
|
*/
|
||||||
fun SSLConfiguration.createDevKeyStores(rootCert: X509CertificateHolder, intermediateCa: CertificateAndKeyPair, legalName: CordaX500Name) {
|
fun SSLConfiguration.createDevKeyStores(rootCert: X509Certificate, intermediateCa: CertificateAndKeyPair, legalName: CordaX500Name) {
|
||||||
val (nodeCaCert, nodeCaKeyPair) = createDevNodeCa(intermediateCa, legalName)
|
val (nodeCaCert, nodeCaKeyPair) = createDevNodeCa(intermediateCa, legalName)
|
||||||
|
|
||||||
loadOrCreateKeyStore(nodeKeystore, keyStorePassword).apply {
|
loadOrCreateKeyStore(nodeKeystore, keyStorePassword).apply {
|
||||||
@ -27,7 +27,7 @@ fun SSLConfiguration.createDevKeyStores(rootCert: X509CertificateHolder, interme
|
|||||||
}
|
}
|
||||||
|
|
||||||
val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, legalName, tlsKeyPair.public)
|
val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, legalName.x500Principal, tlsKeyPair.public)
|
||||||
|
|
||||||
loadOrCreateKeyStore(sslKeystore, keyStorePassword).apply {
|
loadOrCreateKeyStore(sslKeystore, keyStorePassword).apply {
|
||||||
addOrReplaceKey(
|
addOrReplaceKey(
|
||||||
@ -50,7 +50,7 @@ fun createDevNodeCa(intermediateCa: CertificateAndKeyPair, legalName: CordaX500N
|
|||||||
CertificateType.NODE_CA,
|
CertificateType.NODE_CA,
|
||||||
intermediateCa.certificate,
|
intermediateCa.certificate,
|
||||||
intermediateCa.keyPair,
|
intermediateCa.keyPair,
|
||||||
legalName,
|
legalName.x500Principal,
|
||||||
keyPair.public,
|
keyPair.public,
|
||||||
nameConstraints = nameConstraints)
|
nameConstraints = nameConstraints)
|
||||||
return CertificateAndKeyPair(cert, keyPair)
|
return CertificateAndKeyPair(cert, keyPair)
|
||||||
|
@ -3,8 +3,9 @@
|
|||||||
package net.corda.nodeapi.internal.crypto
|
package net.corda.nodeapi.internal.crypto
|
||||||
|
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.internal.*
|
import net.corda.core.internal.exists
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
import net.corda.core.internal.read
|
||||||
|
import net.corda.core.internal.write
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
@ -67,18 +68,6 @@ fun loadKeyStore(input: InputStream, storePassword: String): KeyStore {
|
|||||||
return keyStore
|
return keyStore
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper extension method to add, or overwrite any key data in store.
|
|
||||||
* @param alias name to record the private key and certificate chain under.
|
|
||||||
* @param key cryptographic key to store.
|
|
||||||
* @param password password for unlocking the key entry in the future. This does not have to be the same password as any keys stored,
|
|
||||||
* but for SSL purposes this is recommended.
|
|
||||||
* @param chain the sequence of certificates starting with the public key certificate for this key and extending to the root CA cert.
|
|
||||||
*/
|
|
||||||
fun KeyStore.addOrReplaceKey(alias: String, key: Key, password: CharArray, chain: Array<out X509CertificateHolder>) {
|
|
||||||
addOrReplaceKey(alias, key, password, chain.map { it.cert }.toTypedArray<Certificate>())
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper extension method to add, or overwrite any key data in store.
|
* Helper extension method to add, or overwrite any key data in store.
|
||||||
* @param alias name to record the private key and certificate chain under.
|
* @param alias name to record the private key and certificate chain under.
|
||||||
@ -132,9 +121,9 @@ fun KeyStore.getKeyPair(alias: String, keyPassword: String): KeyPair = getCertif
|
|||||||
* @param keyPassword The password for the PrivateKey (not the store access password).
|
* @param keyPassword The password for the PrivateKey (not the store access password).
|
||||||
*/
|
*/
|
||||||
fun KeyStore.getCertificateAndKeyPair(alias: String, keyPassword: String): CertificateAndKeyPair {
|
fun KeyStore.getCertificateAndKeyPair(alias: String, keyPassword: String): CertificateAndKeyPair {
|
||||||
val cert = getX509Certificate(alias).toX509CertHolder()
|
val certificate = getX509Certificate(alias)
|
||||||
val publicKey = Crypto.toSupportedPublicKey(cert.subjectPublicKeyInfo)
|
val publicKey = Crypto.toSupportedPublicKey(certificate.publicKey)
|
||||||
return CertificateAndKeyPair(cert, KeyPair(publicKey, getSupportedKey(alias, keyPassword)))
|
return CertificateAndKeyPair(certificate, KeyPair(publicKey, getSupportedKey(alias, keyPassword)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2,7 +2,6 @@ package net.corda.nodeapi.internal.crypto
|
|||||||
|
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.identity.PartyAndCertificate
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.internal.read
|
import net.corda.core.internal.read
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
@ -15,8 +14,13 @@ class KeyStoreWrapper(private val storePath: Path, private val storePassword: St
|
|||||||
fun storeLegalIdentity(legalName: CordaX500Name, alias: String, keyPair: KeyPair): PartyAndCertificate {
|
fun storeLegalIdentity(legalName: CordaX500Name, alias: String, keyPair: KeyPair): PartyAndCertificate {
|
||||||
val nodeCaCertChain = keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
|
val nodeCaCertChain = keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
|
||||||
val nodeCa = getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)
|
val nodeCa = getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)
|
||||||
val identityCert = X509Utilities.createCertificate(CertificateType.LEGAL_IDENTITY, nodeCa.certificate, nodeCa.keyPair, legalName, keyPair.public)
|
val identityCert = X509Utilities.createCertificate(
|
||||||
val identityCertPath = X509CertificateFactory().generateCertPath(identityCert.cert, *nodeCaCertChain)
|
CertificateType.LEGAL_IDENTITY,
|
||||||
|
nodeCa.certificate,
|
||||||
|
nodeCa.keyPair,
|
||||||
|
legalName.x500Principal,
|
||||||
|
keyPair.public)
|
||||||
|
val identityCertPath = X509CertificateFactory().generateCertPath(identityCert, *nodeCaCertChain)
|
||||||
// Assume key password = store password.
|
// Assume key password = store password.
|
||||||
keyStore.addOrReplaceKey(alias, keyPair.private, storePassword.toCharArray(), identityCertPath.certificates.toTypedArray())
|
keyStore.addOrReplaceKey(alias, keyPair.private, storePassword.toCharArray(), identityCertPath.certificates.toTypedArray())
|
||||||
keyStore.save(storePath, storePassword)
|
keyStore.save(storePath, storePassword)
|
||||||
|
@ -4,12 +4,14 @@ import net.corda.core.CordaOID
|
|||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.crypto.SignatureScheme
|
import net.corda.core.crypto.SignatureScheme
|
||||||
import net.corda.core.crypto.random63BitValue
|
import net.corda.core.crypto.random63BitValue
|
||||||
|
import net.corda.core.internal.CertRole
|
||||||
|
import net.corda.core.internal.reader
|
||||||
|
import net.corda.core.internal.writer
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.*
|
import net.corda.core.internal.*
|
||||||
import net.corda.core.utilities.days
|
import net.corda.core.utilities.days
|
||||||
import net.corda.core.utilities.millis
|
import net.corda.core.utilities.millis
|
||||||
import org.bouncycastle.asn1.*
|
import org.bouncycastle.asn1.*
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
|
||||||
import org.bouncycastle.asn1.x500.style.BCStyle
|
import org.bouncycastle.asn1.x500.style.BCStyle
|
||||||
import org.bouncycastle.asn1.x509.*
|
import org.bouncycastle.asn1.x509.*
|
||||||
import org.bouncycastle.asn1.x509.Extension
|
import org.bouncycastle.asn1.x509.Extension
|
||||||
@ -34,6 +36,7 @@ import java.time.Duration
|
|||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.temporal.ChronoUnit
|
import java.time.temporal.ChronoUnit
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
|
|
||||||
object X509Utilities {
|
object X509Utilities {
|
||||||
val DEFAULT_IDENTITY_SIGNATURE_SCHEME = Crypto.EDDSA_ED25519_SHA512
|
val DEFAULT_IDENTITY_SIGNATURE_SCHEME = Crypto.EDDSA_ED25519_SHA512
|
||||||
@ -74,7 +77,7 @@ object X509Utilities {
|
|||||||
* @param after duration to roll forward returned end date relative to current date.
|
* @param after duration to roll forward returned end date relative to current date.
|
||||||
* @param parent if provided certificate whose validity should bound the date interval returned.
|
* @param parent if provided certificate whose validity should bound the date interval returned.
|
||||||
*/
|
*/
|
||||||
fun getCertificateValidityWindow(before: Duration, after: Duration, parent: X509CertificateHolder? = null): Pair<Date, Date> {
|
fun getCertificateValidityWindow(before: Duration, after: Duration, parent: X509Certificate? = null): Pair<Date, Date> {
|
||||||
val startOfDayUTC = Instant.now().truncatedTo(ChronoUnit.DAYS)
|
val startOfDayUTC = Instant.now().truncatedTo(ChronoUnit.DAYS)
|
||||||
val notBefore = max(startOfDayUTC - before, parent?.notBefore)
|
val notBefore = max(startOfDayUTC - before, parent?.notBefore)
|
||||||
val notAfter = min(startOfDayUTC + after, parent?.notAfter)
|
val notAfter = min(startOfDayUTC + after, parent?.notAfter)
|
||||||
@ -85,60 +88,11 @@ object X509Utilities {
|
|||||||
* Create a de novo root self-signed X509 v3 CA cert.
|
* Create a de novo root self-signed X509 v3 CA cert.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun createSelfSignedCACertificate(subject: CordaX500Name,
|
fun createSelfSignedCACertificate(subject: X500Principal,
|
||||||
keyPair: KeyPair,
|
keyPair: KeyPair,
|
||||||
validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW): X509CertificateHolder {
|
validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW): X509Certificate {
|
||||||
val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second)
|
val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second)
|
||||||
return createCertificate(CertificateType.ROOT_CA, subject.x500Name, keyPair, subject.x500Name, keyPair.public, window)
|
return createCertificate(CertificateType.ROOT_CA, subject, keyPair, subject, keyPair.public, window)
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a X509 v3 certificate for use as a CA or for TLS. This does not require a [CordaX500Name] because the
|
|
||||||
* constraints are inappropriate for TLS/CA usage, however as a result this is unsuitable for Corda identity
|
|
||||||
* certificate generation.
|
|
||||||
*
|
|
||||||
* @param issuerCertificate The Public certificate of the root CA above this used to sign it.
|
|
||||||
* @param issuerKeyPair The KeyPair of the root CA above this used to sign it.
|
|
||||||
* @param subject subject of the generated certificate.
|
|
||||||
* @param subjectPublicKey subject's public key.
|
|
||||||
* @param validityWindow The certificate's validity window. Default to [DEFAULT_VALIDITY_WINDOW] if not provided.
|
|
||||||
* @return A data class is returned containing the new intermediate CA Cert and its KeyPair for signing downstream certificates.
|
|
||||||
* Note the generated certificate tree is capped at max depth of 1 below this to be in line with commercially available certificates.
|
|
||||||
*/
|
|
||||||
@JvmStatic
|
|
||||||
fun createCertificate(certificateType: CertificateType,
|
|
||||||
issuerCertificate: X509CertificateHolder,
|
|
||||||
issuerKeyPair: KeyPair,
|
|
||||||
subject: CordaX500Name,
|
|
||||||
subjectPublicKey: PublicKey,
|
|
||||||
validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW,
|
|
||||||
nameConstraints: NameConstraints? = null): X509CertificateHolder {
|
|
||||||
return createCertificate(certificateType, issuerCertificate, issuerKeyPair, subject.x500Name, subjectPublicKey, validityWindow, nameConstraints)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a X509 v3 certificate for use as a CA or for TLS. This does not require a [CordaX500Name] because the
|
|
||||||
* constraints are inappropriate for TLS/CA usage, however as a result this is unsuitable for Corda identity
|
|
||||||
* certificate generation.
|
|
||||||
*
|
|
||||||
* @param issuerCertificate The Public certificate of the root CA above this used to sign it.
|
|
||||||
* @param issuerKeyPair The KeyPair of the root CA above this used to sign it.
|
|
||||||
* @param subject subject of the generated certificate.
|
|
||||||
* @param subjectPublicKey subject's public key.
|
|
||||||
* @param validityWindow The certificate's validity window. Default to [DEFAULT_VALIDITY_WINDOW] if not provided.
|
|
||||||
* @return A data class is returned containing the new intermediate CA Cert and its KeyPair for signing downstream certificates.
|
|
||||||
* Note the generated certificate tree is capped at max depth of 1 below this to be in line with commercially available certificates.
|
|
||||||
*/
|
|
||||||
@JvmStatic
|
|
||||||
fun createCertificate(certificateType: CertificateType,
|
|
||||||
issuerCertificate: X509CertificateHolder,
|
|
||||||
issuerKeyPair: KeyPair,
|
|
||||||
subject: X500Name,
|
|
||||||
subjectPublicKey: PublicKey,
|
|
||||||
validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW,
|
|
||||||
nameConstraints: NameConstraints? = null): X509CertificateHolder {
|
|
||||||
val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second, issuerCertificate)
|
|
||||||
return createCertificate(certificateType, issuerCertificate.subject, issuerKeyPair, subject, subjectPublicKey, window, nameConstraints)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(CertPathValidatorException::class)
|
@Throws(CertPathValidatorException::class)
|
||||||
@ -153,13 +107,13 @@ object X509Utilities {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper method to store a .pem/.cer format file copy of a certificate if required for import into a PC/Mac, or for inspection.
|
* Helper method to store a .pem/.cer format file copy of a certificate if required for import into a PC/Mac, or for inspection.
|
||||||
* @param x509Certificate certificate to save.
|
* @param certificate certificate to save.
|
||||||
* @param file Target file.
|
* @param file Target file.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun saveCertificateAsPEMFile(x509Certificate: X509Certificate, file: Path) {
|
fun saveCertificateAsPEMFile(certificate: X509Certificate, file: Path) {
|
||||||
JcaPEMWriter(file.writer()).use {
|
JcaPEMWriter(file.writer()).use {
|
||||||
it.writeObject(x509Certificate)
|
it.writeObject(certificate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,9 +126,10 @@ object X509Utilities {
|
|||||||
fun loadCertificateFromPEMFile(file: Path): X509Certificate {
|
fun loadCertificateFromPEMFile(file: Path): X509Certificate {
|
||||||
return file.reader().use {
|
return file.reader().use {
|
||||||
val pemObject = PemReader(it).readPemObject()
|
val pemObject = PemReader(it).readPemObject()
|
||||||
val certHolder = X509CertificateHolder(pemObject.content)
|
X509CertificateHolder(pemObject.content).run {
|
||||||
certHolder.isValidOn(Date())
|
isValidOn(Date())
|
||||||
certHolder.cert
|
toJca()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,38 +142,18 @@ object X509Utilities {
|
|||||||
* @param validityWindow the time period the certificate is valid for.
|
* @param validityWindow the time period the certificate is valid for.
|
||||||
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
|
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
|
||||||
*/
|
*/
|
||||||
fun createCertificate(certificateType: CertificateType,
|
fun createPartialCertificate(certificateType: CertificateType,
|
||||||
issuer: CordaX500Name,
|
issuer: X500Principal,
|
||||||
subject: CordaX500Name,
|
subject: X500Principal,
|
||||||
subjectPublicKey: PublicKey,
|
subjectPublicKey: PublicKey,
|
||||||
validityWindow: Pair<Date, Date>,
|
validityWindow: Pair<Date, Date>,
|
||||||
nameConstraints: NameConstraints? = null): X509v3CertificateBuilder {
|
nameConstraints: NameConstraints? = null): X509v3CertificateBuilder {
|
||||||
return createCertificate(certificateType, issuer.x500Name, subject.x500Name, subjectPublicKey, validityWindow, nameConstraints)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build a partial X.509 certificate ready for signing.
|
|
||||||
*
|
|
||||||
* @param issuer name of the issuing entity.
|
|
||||||
* @param subject name of the certificate subject.
|
|
||||||
* @param subjectPublicKey public key of the certificate subject.
|
|
||||||
* @param validityWindow the time period the certificate is valid for.
|
|
||||||
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
|
|
||||||
*/
|
|
||||||
internal fun createCertificate(certificateType: CertificateType,
|
|
||||||
issuer: X500Name,
|
|
||||||
subject: X500Name,
|
|
||||||
subjectPublicKey: PublicKey,
|
|
||||||
validityWindow: Pair<Date, Date>,
|
|
||||||
nameConstraints: NameConstraints? = null): X509v3CertificateBuilder {
|
|
||||||
|
|
||||||
val serial = BigInteger.valueOf(random63BitValue())
|
val serial = BigInteger.valueOf(random63BitValue())
|
||||||
val keyPurposes = DERSequence(ASN1EncodableVector().apply { certificateType.purposes.forEach { add(it) } })
|
val keyPurposes = DERSequence(ASN1EncodableVector().apply { certificateType.purposes.forEach { add(it) } })
|
||||||
val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(subjectPublicKey.encoded))
|
val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(subjectPublicKey.encoded))
|
||||||
val role = certificateType.role
|
val role = certificateType.role
|
||||||
|
|
||||||
val builder = JcaX509v3CertificateBuilder(issuer, serial, validityWindow.first, validityWindow.second,
|
val builder = JcaX509v3CertificateBuilder(issuer, serial, validityWindow.first, validityWindow.second, subject, subjectPublicKey)
|
||||||
subject, subjectPublicKey)
|
|
||||||
.addExtension(Extension.subjectKeyIdentifier, false, BcX509ExtensionUtils().createSubjectKeyIdentifier(subjectPublicKeyInfo))
|
.addExtension(Extension.subjectKeyIdentifier, false, BcX509ExtensionUtils().createSubjectKeyIdentifier(subjectPublicKeyInfo))
|
||||||
.addExtension(Extension.basicConstraints, certificateType.isCA, BasicConstraints(certificateType.isCA))
|
.addExtension(Extension.basicConstraints, certificateType.isCA, BasicConstraints(certificateType.isCA))
|
||||||
.addExtension(Extension.keyUsage, false, certificateType.keyUsage)
|
.addExtension(Extension.keyUsage, false, certificateType.keyUsage)
|
||||||
@ -234,6 +169,37 @@ object X509Utilities {
|
|||||||
return builder
|
return builder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a X509 v3 certificate using the given issuer certificate and key pair.
|
||||||
|
*
|
||||||
|
* @param issuerCertificate The Public certificate of the root CA above this used to sign it.
|
||||||
|
* @param issuerKeyPair The KeyPair of the root CA above this used to sign it.
|
||||||
|
* @param subject subject of the generated certificate.
|
||||||
|
* @param subjectPublicKey subject's public key.
|
||||||
|
* @param validityWindow The certificate's validity window. Default to [DEFAULT_VALIDITY_WINDOW] if not provided.
|
||||||
|
* @return A data class is returned containing the new intermediate CA Cert and its KeyPair for signing downstream certificates.
|
||||||
|
* Note the generated certificate tree is capped at max depth of 1 below this to be in line with commercially available certificates.
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun createCertificate(certificateType: CertificateType,
|
||||||
|
issuerCertificate: X509Certificate,
|
||||||
|
issuerKeyPair: KeyPair,
|
||||||
|
subject: X500Principal,
|
||||||
|
subjectPublicKey: PublicKey,
|
||||||
|
validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW,
|
||||||
|
nameConstraints: NameConstraints? = null): X509Certificate {
|
||||||
|
val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second, issuerCertificate)
|
||||||
|
return createCertificate(
|
||||||
|
certificateType,
|
||||||
|
issuerCertificate.subjectX500Principal,
|
||||||
|
issuerKeyPair,
|
||||||
|
subject,
|
||||||
|
subjectPublicKey,
|
||||||
|
window,
|
||||||
|
nameConstraints
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build and sign an X.509 certificate with the given signer.
|
* Build and sign an X.509 certificate with the given signer.
|
||||||
*
|
*
|
||||||
@ -245,15 +211,16 @@ object X509Utilities {
|
|||||||
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
|
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
|
||||||
*/
|
*/
|
||||||
fun createCertificate(certificateType: CertificateType,
|
fun createCertificate(certificateType: CertificateType,
|
||||||
issuer: X500Name,
|
issuer: X500Principal,
|
||||||
issuerSigner: ContentSigner,
|
issuerSigner: ContentSigner,
|
||||||
subject: CordaX500Name,
|
subject: X500Principal,
|
||||||
subjectPublicKey: PublicKey,
|
subjectPublicKey: PublicKey,
|
||||||
validityWindow: Pair<Date, Date>,
|
validityWindow: Pair<Date, Date>,
|
||||||
nameConstraints: NameConstraints? = null): X509CertificateHolder {
|
nameConstraints: NameConstraints? = null): X509Certificate {
|
||||||
val builder = createCertificate(certificateType, issuer, subject.x500Name, subjectPublicKey, validityWindow, nameConstraints)
|
val builder = createPartialCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints)
|
||||||
return builder.build(issuerSigner).apply {
|
return builder.build(issuerSigner).run {
|
||||||
require(isValidOn(Date()))
|
require(isValidOn(Date()))
|
||||||
|
toJca()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,39 +235,47 @@ object X509Utilities {
|
|||||||
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
|
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
|
||||||
*/
|
*/
|
||||||
fun createCertificate(certificateType: CertificateType,
|
fun createCertificate(certificateType: CertificateType,
|
||||||
issuer: X500Name,
|
issuer: X500Principal,
|
||||||
issuerKeyPair: KeyPair,
|
issuerKeyPair: KeyPair,
|
||||||
subject: X500Name,
|
subject: X500Principal,
|
||||||
subjectPublicKey: PublicKey,
|
subjectPublicKey: PublicKey,
|
||||||
validityWindow: Pair<Date, Date>,
|
validityWindow: Pair<Date, Date>,
|
||||||
nameConstraints: NameConstraints? = null): X509CertificateHolder {
|
nameConstraints: NameConstraints? = null): X509Certificate {
|
||||||
val signatureScheme = Crypto.findSignatureScheme(issuerKeyPair.private)
|
val signatureScheme = Crypto.findSignatureScheme(issuerKeyPair.private)
|
||||||
val provider = Crypto.findProvider(signatureScheme.providerName)
|
val provider = Crypto.findProvider(signatureScheme.providerName)
|
||||||
val builder = createCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints)
|
|
||||||
|
|
||||||
val signer = ContentSignerBuilder.build(signatureScheme, issuerKeyPair.private, provider)
|
val signer = ContentSignerBuilder.build(signatureScheme, issuerKeyPair.private, provider)
|
||||||
return builder.build(signer).apply {
|
val builder = createPartialCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints)
|
||||||
|
return builder.build(signer).run {
|
||||||
require(isValidOn(Date()))
|
require(isValidOn(Date()))
|
||||||
require(isSignatureValid(JcaContentVerifierProviderBuilder().build(issuerKeyPair.public)))
|
require(isSignatureValid(JcaContentVerifierProviderBuilder().build(issuerKeyPair.public)))
|
||||||
|
toJca()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create certificate signing request using provided information.
|
* Create certificate signing request using provided information.
|
||||||
*/
|
*/
|
||||||
private fun createCertificateSigningRequest(subject: CordaX500Name,
|
private fun createCertificateSigningRequest(subject: X500Principal,
|
||||||
email: String,
|
email: String,
|
||||||
keyPair: KeyPair,
|
keyPair: KeyPair,
|
||||||
signatureScheme: SignatureScheme): PKCS10CertificationRequest {
|
signatureScheme: SignatureScheme): PKCS10CertificationRequest {
|
||||||
val signer = ContentSignerBuilder.build(signatureScheme, keyPair.private, Crypto.findProvider(signatureScheme.providerName))
|
val signer = ContentSignerBuilder.build(signatureScheme, keyPair.private, Crypto.findProvider(signatureScheme.providerName))
|
||||||
return JcaPKCS10CertificationRequestBuilder(subject.x500Name, keyPair.public).addAttribute(BCStyle.E, DERUTF8String(email)).build(signer)
|
return JcaPKCS10CertificationRequestBuilder(subject, keyPair.public).addAttribute(BCStyle.E, DERUTF8String(email)).build(signer)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createCertificateSigningRequest(subject: CordaX500Name, email: String, keyPair: KeyPair): PKCS10CertificationRequest {
|
fun createCertificateSigningRequest(subject: X500Principal, email: String, keyPair: KeyPair): PKCS10CertificationRequest {
|
||||||
return createCertificateSigningRequest(subject, email, keyPair, DEFAULT_TLS_SIGNATURE_SCHEME)
|
return createCertificateSigningRequest(subject, email, keyPair, DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a [X509Certificate] into Bouncycastle's [X509CertificateHolder].
|
||||||
|
*
|
||||||
|
* NOTE: To avoid unnecessary copying use [X509Certificate] where possible.
|
||||||
|
*/
|
||||||
|
fun X509Certificate.toBc() = X509CertificateHolder(encoded)
|
||||||
|
fun X509CertificateHolder.toJca(): X509Certificate = X509CertificateFactory().generateCertificate(encoded.inputStream())
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps a [CertificateFactory] to remove boilerplate. It's unclear whether [CertificateFactory] is threadsafe so best
|
* Wraps a [CertificateFactory] to remove boilerplate. It's unclear whether [CertificateFactory] is threadsafe so best
|
||||||
* so assume this class is not.
|
* so assume this class is not.
|
||||||
@ -396,4 +371,4 @@ enum class CertificateType(val keyUsage: KeyUsage, vararg val purposes: KeyPurpo
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class CertificateAndKeyPair(val certificate: X509CertificateHolder, val keyPair: KeyPair)
|
data class CertificateAndKeyPair(val certificate: X509Certificate, val keyPair: KeyPair)
|
||||||
|
@ -83,16 +83,20 @@ class NetworkBootstrapper {
|
|||||||
for (confFile in confFiles) {
|
for (confFile in confFiles) {
|
||||||
val nodeName = confFile.fileName.toString().removeSuffix(".conf")
|
val nodeName = confFile.fileName.toString().removeSuffix(".conf")
|
||||||
println("Generating directory for $nodeName")
|
println("Generating directory for $nodeName")
|
||||||
val nodeDir = (directory / nodeName).createDirectory()
|
val nodeDir = (directory / nodeName)
|
||||||
confFile.moveTo(nodeDir / "node.conf")
|
if (!nodeDir.exists()) { nodeDir.createDirectory() }
|
||||||
Files.copy(cordaJar, (nodeDir / "corda.jar"))
|
confFile.moveTo(nodeDir / "node.conf", StandardCopyOption.REPLACE_EXISTING)
|
||||||
|
Files.copy(cordaJar, (nodeDir / "corda.jar"), StandardCopyOption.REPLACE_EXISTING)
|
||||||
}
|
}
|
||||||
Files.delete(cordaJar)
|
Files.delete(cordaJar)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun extractCordaJarTo(directory: Path): Path {
|
private fun extractCordaJarTo(directory: Path): Path {
|
||||||
val cordaJarPath = (directory / "corda.jar")
|
val cordaJarPath = (directory / "corda.jar")
|
||||||
Thread.currentThread().contextClassLoader.getResourceAsStream("corda.jar").copyTo(cordaJarPath)
|
if (!cordaJarPath.exists()) {
|
||||||
|
println("No corda jar found in root directory. Extracting from jar")
|
||||||
|
Thread.currentThread().contextClassLoader.getResourceAsStream("corda.jar").copyTo(cordaJarPath)
|
||||||
|
}
|
||||||
return cordaJarPath
|
return cordaJarPath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package net.corda.nodeapi.internal.serialization.amqp
|
package net.corda.nodeapi.internal.serialization.amqp
|
||||||
|
|
||||||
import net.corda.core.serialization.ClassWhitelist
|
|
||||||
import net.corda.core.serialization.CordaSerializable
|
|
||||||
import com.google.common.primitives.Primitives
|
import com.google.common.primitives.Primitives
|
||||||
import com.google.common.reflect.TypeToken
|
import com.google.common.reflect.TypeToken
|
||||||
|
import net.corda.core.serialization.ClassWhitelist
|
||||||
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import net.corda.core.serialization.SerializationContext
|
import net.corda.core.serialization.SerializationContext
|
||||||
import org.apache.qpid.proton.codec.Data
|
import org.apache.qpid.proton.codec.Data
|
||||||
import java.beans.IndexedPropertyDescriptor
|
import java.beans.IndexedPropertyDescriptor
|
||||||
@ -81,10 +81,17 @@ private fun <T : Any> propertiesForSerializationFromConstructor(kotlinConstructo
|
|||||||
val rc: MutableList<PropertySerializer> = ArrayList(kotlinConstructor.parameters.size)
|
val rc: MutableList<PropertySerializer> = ArrayList(kotlinConstructor.parameters.size)
|
||||||
for (param in kotlinConstructor.parameters) {
|
for (param in kotlinConstructor.parameters) {
|
||||||
val name = param.name ?: throw NotSerializableException("Constructor parameter of $clazz has no name.")
|
val name = param.name ?: throw NotSerializableException("Constructor parameter of $clazz has no name.")
|
||||||
|
|
||||||
val matchingProperty = properties[name] ?:
|
val matchingProperty = properties[name] ?:
|
||||||
throw NotSerializableException("No property matching constructor parameter named '$name' of '$clazz'. " +
|
try {
|
||||||
"If using Java, check that you have the -parameters option specified in the Java compiler. " +
|
clazz.getDeclaredField(param.name)
|
||||||
"Alternately, provide a proxy serializer (SerializationCustomSerializer) if recompiling isn't an option")
|
throw NotSerializableException("Property '$name' or its getter is non public, this renders class '$clazz' unserializable")
|
||||||
|
} catch (e: NoSuchFieldException) {
|
||||||
|
throw NotSerializableException("No property matching constructor parameter named '$name' of '$clazz'. " +
|
||||||
|
"If using Java, check that you have the -parameters option specified in the Java compiler. " +
|
||||||
|
"Alternately, provide a proxy serializer (SerializationCustomSerializer) if recompiling isn't an option")
|
||||||
|
}
|
||||||
|
|
||||||
// Check that the method has a getter in java.
|
// Check that the method has a getter in java.
|
||||||
val getter = matchingProperty.readMethod ?: throw NotSerializableException("Property has no getter method for $name of $clazz. " +
|
val getter = matchingProperty.readMethod ?: throw NotSerializableException("Property has no getter method for $name of $clazz. " +
|
||||||
"If using Java and the parameter name looks anonymous, check that you have the -parameters option specified in the Java compiler." +
|
"If using Java and the parameter name looks anonymous, check that you have the -parameters option specified in the Java compiler." +
|
||||||
|
@ -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.EDDSA_ED25519_SHA512
|
||||||
import net.corda.core.crypto.Crypto.generateKeyPair
|
import net.corda.core.crypto.Crypto.generateKeyPair
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.internal.toTypedArray
|
|
||||||
import net.corda.core.internal.x500Name
|
|
||||||
import net.corda.core.serialization.SerializationContext
|
import net.corda.core.serialization.SerializationContext
|
||||||
import net.corda.core.serialization.deserialize
|
import net.corda.core.serialization.deserialize
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
@ -23,12 +20,9 @@ import net.corda.testing.BOB_NAME
|
|||||||
import net.corda.testing.TestIdentity
|
import net.corda.testing.TestIdentity
|
||||||
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
|
||||||
import org.bouncycastle.asn1.x509.BasicConstraints
|
import org.bouncycastle.asn1.x509.BasicConstraints
|
||||||
import org.bouncycastle.asn1.x509.Extension
|
import org.bouncycastle.asn1.x509.Extension
|
||||||
import org.bouncycastle.asn1.x509.KeyUsage
|
import org.bouncycastle.asn1.x509.KeyUsage
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
|
||||||
import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder
|
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.rules.TemporaryFolder
|
import org.junit.rules.TemporaryFolder
|
||||||
@ -42,18 +36,16 @@ import java.security.SecureRandom
|
|||||||
import java.security.cert.CertPath
|
import java.security.cert.CertPath
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.stream.Stream
|
|
||||||
import javax.net.ssl.*
|
import javax.net.ssl.*
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
|
|
||||||
class X509UtilitiesTest {
|
class X509UtilitiesTest {
|
||||||
private companion object {
|
private companion object {
|
||||||
val ALICE = TestIdentity(ALICE_NAME, 70).party
|
val ALICE = TestIdentity(ALICE_NAME, 70).party
|
||||||
val bob = TestIdentity(BOB_NAME, 80)
|
val BOB = TestIdentity(BOB_NAME, 80)
|
||||||
val MEGA_CORP = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).party
|
val MEGA_CORP = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).party
|
||||||
val BOB get() = bob.party
|
|
||||||
val BOB_PUBKEY get() = bob.publicKey
|
|
||||||
val CIPHER_SUITES = arrayOf(
|
val CIPHER_SUITES = arrayOf(
|
||||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||||
@ -63,27 +55,30 @@ class X509UtilitiesTest {
|
|||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
@JvmField
|
@JvmField
|
||||||
val tempFolder: TemporaryFolder = TemporaryFolder()
|
val tempFolder = TemporaryFolder()
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `create valid self-signed CA certificate`() {
|
fun `create valid self-signed CA certificate`() {
|
||||||
val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val caCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Test Cert", organisation = "R3 Ltd", locality = "London", country = "GB"), caKey)
|
val subject = X500Principal("CN=Test Cert,O=R3 Ltd,L=London,C=GB")
|
||||||
assertEquals(X500Name("CN=Test Cert,O=R3 Ltd,L=London,C=GB"), caCert.subject) // using our subject common name
|
val caCert = X509Utilities.createSelfSignedCACertificate(subject, caKey)
|
||||||
assertEquals(caCert.issuer, caCert.subject) //self-signed
|
assertEquals(subject, caCert.subjectX500Principal) // using our subject common name
|
||||||
caCert.isValidOn(Date()) // throws on verification problems
|
assertEquals(caCert.issuerX500Principal, caCert.subjectX500Principal) //self-signed
|
||||||
caCert.isSignatureValid(JcaContentVerifierProviderBuilder().build(caKey.public)) // throws on verification problems
|
caCert.checkValidity(Date()) // throws on verification problems
|
||||||
val basicConstraints = BasicConstraints.getInstance(caCert.getExtension(Extension.basicConstraints).parsedValue)
|
caCert.verify(caKey.public) // throws on verification problems
|
||||||
val keyUsage = KeyUsage.getInstance(caCert.getExtension(Extension.keyUsage).parsedValue)
|
caCert.toBc().run {
|
||||||
assertFalse { keyUsage.hasUsages(5) } // Bit 5 == keyCertSign according to ASN.1 spec (see full comment on KeyUsage property)
|
val basicConstraints = BasicConstraints.getInstance(getExtension(Extension.basicConstraints).parsedValue)
|
||||||
assertNull(basicConstraints.pathLenConstraint) // No length constraint specified on this CA certificate
|
val keyUsage = KeyUsage.getInstance(getExtension(Extension.keyUsage).parsedValue)
|
||||||
|
assertFalse { keyUsage.hasUsages(5) } // Bit 5 == keyCertSign according to ASN.1 spec (see full comment on KeyUsage property)
|
||||||
|
assertNull(basicConstraints.pathLenConstraint) // No length constraint specified on this CA certificate
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `load and save a PEM file certificate`() {
|
fun `load and save a PEM file certificate`() {
|
||||||
val tmpCertificateFile = tempFile("cacert.pem")
|
val tmpCertificateFile = tempFile("cacert.pem")
|
||||||
val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val caCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Test Cert", organisation = "R3 Ltd", locality = "London", country = "GB"), caKey).cert
|
val caCert = X509Utilities.createSelfSignedCACertificate(X500Principal("CN=Test Cert,O=R3 Ltd,L=London,C=GB"), caKey)
|
||||||
X509Utilities.saveCertificateAsPEMFile(caCert, tmpCertificateFile)
|
X509Utilities.saveCertificateAsPEMFile(caCert, tmpCertificateFile)
|
||||||
val readCertificate = X509Utilities.loadCertificateFromPEMFile(tmpCertificateFile)
|
val readCertificate = X509Utilities.loadCertificateFromPEMFile(tmpCertificateFile)
|
||||||
assertEquals(caCert, readCertificate)
|
assertEquals(caCert, readCertificate)
|
||||||
@ -92,18 +87,20 @@ class X509UtilitiesTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `create valid server certificate chain`() {
|
fun `create valid server certificate chain`() {
|
||||||
val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val caCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Test CA Cert", organisation = "R3 Ltd", locality = "London", country = "GB"), caKey)
|
val caCert = X509Utilities.createSelfSignedCACertificate(X500Principal("CN=Test CA Cert,O=R3 Ltd,L=London,C=GB"), caKey)
|
||||||
val subject = CordaX500Name(commonName = "Server Cert", organisation = "R3 Ltd", locality = "London", country = "GB")
|
val subject = X500Principal("CN=Server Cert,O=R3 Ltd,L=London,C=GB")
|
||||||
val keyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val keyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val serverCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, caKey, subject, keyPair.public)
|
val serverCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, caKey, subject, keyPair.public)
|
||||||
assertEquals(X500Name("C=GB,L=London,O=R3 Ltd,CN=Server Cert"), serverCert.subject) // using our subject common name
|
assertEquals(subject, serverCert.subjectX500Principal) // using our subject common name
|
||||||
assertEquals(caCert.issuer, serverCert.issuer) // Issued by our CA cert
|
assertEquals(caCert.issuerX500Principal, serverCert.issuerX500Principal) // Issued by our CA cert
|
||||||
serverCert.isValidOn(Date()) // throws on verification problems
|
serverCert.checkValidity(Date()) // throws on verification problems
|
||||||
serverCert.isSignatureValid(JcaContentVerifierProviderBuilder().build(caKey.public)) // throws on verification problems
|
serverCert.verify(caKey.public) // throws on verification problems
|
||||||
val basicConstraints = BasicConstraints.getInstance(serverCert.getExtension(Extension.basicConstraints).parsedValue)
|
serverCert.toBc().run {
|
||||||
val keyUsage = KeyUsage.getInstance(serverCert.getExtension(Extension.keyUsage).parsedValue)
|
val basicConstraints = BasicConstraints.getInstance(getExtension(Extension.basicConstraints).parsedValue)
|
||||||
assertFalse { keyUsage.hasUsages(5) } // Bit 5 == keyCertSign according to ASN.1 spec (see full comment on KeyUsage property)
|
val keyUsage = KeyUsage.getInstance(getExtension(Extension.keyUsage).parsedValue)
|
||||||
assertNull(basicConstraints.pathLenConstraint) // Non-CA certificate
|
assertFalse { keyUsage.hasUsages(5) } // Bit 5 == keyCertSign according to ASN.1 spec (see full comment on KeyUsage property)
|
||||||
|
assertNull(basicConstraints.pathLenConstraint) // Non-CA certificate
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -111,15 +108,14 @@ class X509UtilitiesTest {
|
|||||||
val tmpKeyStore = tempFile("keystore.jks")
|
val tmpKeyStore = tempFile("keystore.jks")
|
||||||
|
|
||||||
val keyPair = generateKeyPair(EDDSA_ED25519_SHA512)
|
val keyPair = generateKeyPair(EDDSA_ED25519_SHA512)
|
||||||
val testName = CordaX500Name(commonName = "Test", organisation = "R3 Ltd", locality = "London", country = "GB")
|
val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB")
|
||||||
val selfSignCert = X509Utilities.createSelfSignedCACertificate(testName, keyPair)
|
val selfSignCert = X509Utilities.createSelfSignedCACertificate(testName, keyPair)
|
||||||
|
|
||||||
assertTrue(Arrays.equals(selfSignCert.subjectPublicKeyInfo.encoded, keyPair.public.encoded))
|
assertTrue(Arrays.equals(selfSignCert.publicKey.encoded, keyPair.public.encoded))
|
||||||
|
|
||||||
// Save the EdDSA private key with self sign cert in the keystore.
|
// Save the EdDSA private key with self sign cert in the keystore.
|
||||||
val keyStore = loadOrCreateKeyStore(tmpKeyStore, "keystorepass")
|
val keyStore = loadOrCreateKeyStore(tmpKeyStore, "keystorepass")
|
||||||
keyStore.setKeyEntry("Key", keyPair.private, "password".toCharArray(),
|
keyStore.setKeyEntry("Key", keyPair.private, "password".toCharArray(), arrayOf(selfSignCert))
|
||||||
Stream.of(selfSignCert).map { it.cert }.toTypedArray())
|
|
||||||
keyStore.save(tmpKeyStore, "keystorepass")
|
keyStore.save(tmpKeyStore, "keystorepass")
|
||||||
|
|
||||||
// Load the keystore from file and make sure keys are intact.
|
// Load the keystore from file and make sure keys are intact.
|
||||||
@ -137,15 +133,14 @@ class X509UtilitiesTest {
|
|||||||
fun `signing EdDSA key with EcDSA certificate`() {
|
fun `signing EdDSA key with EcDSA certificate`() {
|
||||||
val tmpKeyStore = tempFile("keystore.jks")
|
val tmpKeyStore = tempFile("keystore.jks")
|
||||||
val ecDSAKey = generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
|
val ecDSAKey = generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
|
||||||
val testName = CordaX500Name(commonName = "Test", organisation = "R3 Ltd", locality = "London", country = "GB")
|
val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB")
|
||||||
val ecDSACert = X509Utilities.createSelfSignedCACertificate(testName, ecDSAKey)
|
val ecDSACert = X509Utilities.createSelfSignedCACertificate(testName, ecDSAKey)
|
||||||
val edDSAKeypair = generateKeyPair(EDDSA_ED25519_SHA512)
|
val edDSAKeypair = generateKeyPair(EDDSA_ED25519_SHA512)
|
||||||
val edDSACert = X509Utilities.createCertificate(CertificateType.TLS, ecDSACert, ecDSAKey, X500Name("CN=TestEdDSA"), edDSAKeypair.public)
|
val edDSACert = X509Utilities.createCertificate(CertificateType.TLS, ecDSACert, ecDSAKey, BOB.name.x500Principal, edDSAKeypair.public)
|
||||||
|
|
||||||
// Save the EdDSA private key with cert chains.
|
// Save the EdDSA private key with cert chains.
|
||||||
val keyStore = loadOrCreateKeyStore(tmpKeyStore, "keystorepass")
|
val keyStore = loadOrCreateKeyStore(tmpKeyStore, "keystorepass")
|
||||||
keyStore.setKeyEntry("Key", edDSAKeypair.private, "password".toCharArray(),
|
keyStore.setKeyEntry("Key", edDSAKeypair.private, "password".toCharArray(), arrayOf(ecDSACert, edDSACert))
|
||||||
Stream.of(ecDSACert, edDSACert).map { it.cert }.toTypedArray())
|
|
||||||
keyStore.save(tmpKeyStore, "keystorepass")
|
keyStore.save(tmpKeyStore, "keystorepass")
|
||||||
|
|
||||||
// Load the keystore from file and make sure keys are intact.
|
// Load the keystore from file and make sure keys are intact.
|
||||||
@ -179,23 +174,22 @@ class X509UtilitiesTest {
|
|||||||
val serverKeyStore = loadKeyStore(sslConfig.nodeKeystore, sslConfig.keyStorePassword)
|
val serverKeyStore = loadKeyStore(sslConfig.nodeKeystore, sslConfig.keyStorePassword)
|
||||||
val (serverCert, serverKeyPair) = serverKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, sslConfig.keyStorePassword)
|
val (serverCert, serverKeyPair) = serverKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, sslConfig.keyStorePassword)
|
||||||
|
|
||||||
serverCert.cert.checkValidity()
|
serverCert.checkValidity()
|
||||||
serverCert.cert.verify(intermediateCa.certificate.cert.publicKey)
|
serverCert.verify(intermediateCa.certificate.publicKey)
|
||||||
assertThat(CordaX500Name.parse(serverCert.subject.toString())).isEqualTo(MEGA_CORP.name)
|
assertThat(CordaX500Name.build(serverCert.subjectX500Principal)).isEqualTo(MEGA_CORP.name)
|
||||||
|
|
||||||
// Load back SSL certificate
|
// Load back SSL certificate
|
||||||
val sslKeyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword)
|
val sslKeyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword)
|
||||||
val (sslCert) = sslKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_TLS, sslConfig.keyStorePassword)
|
val (sslCert) = sslKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_TLS, sslConfig.keyStorePassword)
|
||||||
|
|
||||||
sslCert.cert.checkValidity()
|
sslCert.checkValidity()
|
||||||
sslCert.cert.verify(serverCert.cert.publicKey)
|
sslCert.verify(serverCert.publicKey)
|
||||||
assertThat(CordaX500Name.parse(sslCert.subject.toString())).isEqualTo(MEGA_CORP.name)
|
assertThat(CordaX500Name.build(sslCert.subjectX500Principal)).isEqualTo(MEGA_CORP.name)
|
||||||
|
|
||||||
// Now sign something with private key and verify against certificate public key
|
// Now sign something with private key and verify against certificate public key
|
||||||
val testData = "123456".toByteArray()
|
val testData = "123456".toByteArray()
|
||||||
val signature = Crypto.doSign(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME, serverKeyPair.private, testData)
|
val signature = Crypto.doSign(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME, serverKeyPair.private, testData)
|
||||||
val publicKey = Crypto.toSupportedPublicKey(serverCert.subjectPublicKeyInfo)
|
assertTrue { Crypto.isValid(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME, serverCert.publicKey, signature, testData) }
|
||||||
assertTrue { Crypto.isValid(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME, publicKey, signature, testData) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -210,7 +204,7 @@ class X509UtilitiesTest {
|
|||||||
|
|
||||||
// Generate server cert and private key and populate another keystore suitable for SSL
|
// Generate server cert and private key and populate another keystore suitable for SSL
|
||||||
sslConfig.createDevKeyStores(rootCa.certificate, intermediateCa, MEGA_CORP.name)
|
sslConfig.createDevKeyStores(rootCa.certificate, intermediateCa, MEGA_CORP.name)
|
||||||
sslConfig.createTrustStore(rootCa.certificate.cert)
|
sslConfig.createTrustStore(rootCa.certificate)
|
||||||
|
|
||||||
val keyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword)
|
val keyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword)
|
||||||
val trustStore = loadKeyStore(sslConfig.trustStoreFile, sslConfig.trustStorePassword)
|
val trustStore = loadKeyStore(sslConfig.trustStoreFile, sslConfig.trustStorePassword)
|
||||||
@ -303,10 +297,10 @@ class X509UtilitiesTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `get correct private key type from Keystore`() {
|
fun `get correct private key type from Keystore`() {
|
||||||
val keyPair = generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
|
val keyPair = generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
|
||||||
val testName = CordaX500Name(commonName = "Test", organisation = "R3 Ltd", locality = "London", country = "GB")
|
val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB")
|
||||||
val selfSignCert = X509Utilities.createSelfSignedCACertificate(testName, keyPair)
|
val selfSignCert = X509Utilities.createSelfSignedCACertificate(testName, keyPair)
|
||||||
val keyStore = loadOrCreateKeyStore(tempFile("testKeystore.jks"), "keystorepassword")
|
val keyStore = loadOrCreateKeyStore(tempFile("testKeystore.jks"), "keystorepassword")
|
||||||
keyStore.setKeyEntry("Key", keyPair.private, "keypassword".toCharArray(), arrayOf(selfSignCert.cert))
|
keyStore.setKeyEntry("Key", keyPair.private, "keypassword".toCharArray(), arrayOf(selfSignCert))
|
||||||
|
|
||||||
val keyFromKeystore = keyStore.getKey("Key", "keypassword".toCharArray())
|
val keyFromKeystore = keyStore.getKey("Key", "keypassword".toCharArray())
|
||||||
val keyFromKeystoreCasted = keyStore.getSupportedKey("Key", "keypassword")
|
val keyFromKeystoreCasted = keyStore.getSupportedKey("Key", "keypassword")
|
||||||
@ -316,7 +310,7 @@ class X509UtilitiesTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `serialize - deserialize X509CertififcateHolder`() {
|
fun `serialize - deserialize X509Certififcate`() {
|
||||||
val factory = SerializationFactoryImpl().apply { registerScheme(KryoServerSerializationScheme()) }
|
val factory = SerializationFactoryImpl().apply { registerScheme(KryoServerSerializationScheme()) }
|
||||||
val context = SerializationContextImpl(KryoHeaderV0_1,
|
val context = SerializationContextImpl(KryoHeaderV0_1,
|
||||||
javaClass.classLoader,
|
javaClass.classLoader,
|
||||||
@ -324,9 +318,9 @@ class X509UtilitiesTest {
|
|||||||
emptyMap(),
|
emptyMap(),
|
||||||
true,
|
true,
|
||||||
SerializationContext.UseCase.P2P)
|
SerializationContext.UseCase.P2P)
|
||||||
val expected: X509CertificateHolder = X509Utilities.createSelfSignedCACertificate(ALICE.name, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
|
val expected = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Principal, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
|
||||||
val serialized = expected.serialize(factory, context).bytes
|
val serialized = expected.serialize(factory, context).bytes
|
||||||
val actual: X509CertificateHolder = serialized.deserialize(factory, context)
|
val actual = serialized.deserialize<X509Certificate>(factory, context)
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -340,9 +334,9 @@ class X509UtilitiesTest {
|
|||||||
true,
|
true,
|
||||||
SerializationContext.UseCase.P2P)
|
SerializationContext.UseCase.P2P)
|
||||||
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val rootCACert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootCAKey)
|
val rootCACert = X509Utilities.createSelfSignedCACertificate(ALICE_NAME.x500Principal, rootCAKey)
|
||||||
val certificate = X509Utilities.createCertificate(CertificateType.TLS, rootCACert, rootCAKey, BOB.name.x500Name, BOB_PUBKEY)
|
val certificate = X509Utilities.createCertificate(CertificateType.TLS, rootCACert, rootCAKey, BOB_NAME.x500Principal, BOB.publicKey)
|
||||||
val expected = X509CertificateFactory().generateCertPath(certificate.cert, rootCACert.cert)
|
val expected = X509CertificateFactory().generateCertPath(certificate, rootCACert)
|
||||||
val serialized = expected.serialize(factory, context).bytes
|
val serialized = expected.serialize(factory, context).bytes
|
||||||
val actual: CertPath = serialized.deserialize(factory, context)
|
val actual: CertPath = serialized.deserialize(factory, context)
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
|
@ -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 co.paralleluniverse.fibers.Suspendable
|
||||||
import net.corda.client.rpc.CordaRPCClient
|
import net.corda.client.rpc.CordaRPCClient
|
||||||
@ -11,33 +11,94 @@ import net.corda.core.messaging.startFlow
|
|||||||
import net.corda.finance.flows.CashIssueFlow
|
import net.corda.finance.flows.CashIssueFlow
|
||||||
import net.corda.node.internal.Node
|
import net.corda.node.internal.Node
|
||||||
import net.corda.node.internal.StartedNode
|
import net.corda.node.internal.StartedNode
|
||||||
import net.corda.node.services.config.AuthDataSourceType
|
import net.corda.node.services.Permissions
|
||||||
import net.corda.node.services.config.PasswordEncryption
|
import net.corda.node.services.config.PasswordEncryption
|
||||||
import net.corda.node.services.config.SecurityConfiguration
|
|
||||||
import net.corda.nodeapi.internal.config.User
|
|
||||||
import net.corda.nodeapi.internal.config.toConfig
|
|
||||||
import net.corda.testing.ALICE_NAME
|
import net.corda.testing.ALICE_NAME
|
||||||
import net.corda.testing.IntegrationTestSchemas
|
import net.corda.testing.internal.IntegrationTestSchemas
|
||||||
|
import net.corda.testing.internal.toDatabaseSchemaName
|
||||||
import net.corda.testing.node.internal.NodeBasedTest
|
import net.corda.testing.node.internal.NodeBasedTest
|
||||||
import net.corda.testing.toDatabaseSchemaName
|
|
||||||
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
|
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
|
||||||
|
import org.apache.shiro.authc.credential.DefaultPasswordService
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.ClassRule
|
import org.junit.ClassRule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.junit.runners.Parameterized
|
||||||
import java.sql.DriverManager
|
import java.sql.DriverManager
|
||||||
import java.sql.Statement
|
import java.sql.Statement
|
||||||
import java.util.*
|
|
||||||
import kotlin.test.assertFailsWith
|
import kotlin.test.assertFailsWith
|
||||||
|
|
||||||
abstract class UserAuthServiceTest : NodeBasedTest() {
|
/*
|
||||||
|
* Starts Node's instance configured to load clients credentials and permissions from an external DB, then
|
||||||
|
* check authentication/authorization of RPC connections.
|
||||||
|
*/
|
||||||
|
@RunWith(Parameterized::class)
|
||||||
|
class AuthDBTests : NodeBasedTest() {
|
||||||
companion object {
|
companion object {
|
||||||
@ClassRule @JvmField
|
@ClassRule @JvmField
|
||||||
val databaseSchemas = IntegrationTestSchemas(ALICE_NAME.toDatabaseSchemaName())
|
val databaseSchemas = IntegrationTestSchemas(ALICE_NAME.toDatabaseSchemaName())
|
||||||
|
|
||||||
|
private val cacheExpireAfterSecs: Long = 1
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
@Parameterized.Parameters(name = "password encryption format = {0}")
|
||||||
|
fun encFormats() = arrayOf(PasswordEncryption.NONE, PasswordEncryption.SHIRO_1_CRYPT)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected lateinit var node: StartedNode<Node>
|
private lateinit var node: StartedNode<Node>
|
||||||
protected lateinit var client: CordaRPCClient
|
private lateinit var client: CordaRPCClient
|
||||||
|
private lateinit var db: UsersDB
|
||||||
|
|
||||||
|
@Parameterized.Parameter
|
||||||
|
lateinit var passwordEncryption: PasswordEncryption
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setup() {
|
||||||
|
db = UsersDB(
|
||||||
|
name = "SecurityDataSourceTestDB",
|
||||||
|
users = listOf(UserAndRoles(username = "user",
|
||||||
|
password = encodePassword("foo", passwordEncryption),
|
||||||
|
roles = listOf("default"))),
|
||||||
|
roleAndPermissions = listOf(
|
||||||
|
RoleAndPermissions(
|
||||||
|
role = "default",
|
||||||
|
permissions = listOf(
|
||||||
|
Permissions.startFlow<DummyFlow>(),
|
||||||
|
Permissions.invokeRpc("vaultQueryBy"),
|
||||||
|
Permissions.invokeRpc(CordaRPCOps::stateMachinesFeed),
|
||||||
|
Permissions.invokeRpc("vaultQueryByCriteria"))),
|
||||||
|
RoleAndPermissions(
|
||||||
|
role = "admin",
|
||||||
|
permissions = listOf("ALL")
|
||||||
|
)))
|
||||||
|
|
||||||
|
val securityConfig = mapOf(
|
||||||
|
"security" to mapOf(
|
||||||
|
"authService" to mapOf(
|
||||||
|
"dataSource" to mapOf(
|
||||||
|
"type" to "DB",
|
||||||
|
"passwordEncryption" to passwordEncryption.toString(),
|
||||||
|
"connection" to mapOf(
|
||||||
|
"jdbcUrl" to db.jdbcUrl,
|
||||||
|
"username" to "",
|
||||||
|
"password" to "",
|
||||||
|
"driverClassName" to "org.h2.Driver"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"options" to mapOf(
|
||||||
|
"cache" to mapOf(
|
||||||
|
"expireAfterSecs" to cacheExpireAfterSecs,
|
||||||
|
"maxEntries" to 50
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
node = startNode(ALICE_NAME, rpcUsers = emptyList(), configOverrides = securityConfig)
|
||||||
|
client = CordaRPCClient(node.internals.configuration.rpcAddress!!)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `login with correct credentials`() {
|
fun `login with correct credentials`() {
|
||||||
@ -65,7 +126,7 @@ abstract class UserAuthServiceTest : NodeBasedTest() {
|
|||||||
val proxy = it.proxy
|
val proxy = it.proxy
|
||||||
proxy.startFlowDynamic(DummyFlow::class.java)
|
proxy.startFlowDynamic(DummyFlow::class.java)
|
||||||
proxy.startTrackedFlowDynamic(DummyFlow::class.java)
|
proxy.startTrackedFlowDynamic(DummyFlow::class.java)
|
||||||
proxy.startFlow(::DummyFlow)
|
proxy.startFlow(AuthDBTests::DummyFlow)
|
||||||
assertFailsWith(
|
assertFailsWith(
|
||||||
PermissionException::class,
|
PermissionException::class,
|
||||||
"This user should not be authorized to start flow `CashIssueFlow`") {
|
"This user should not be authorized to start flow `CashIssueFlow`") {
|
||||||
@ -92,77 +153,8 @@ abstract class UserAuthServiceTest : NodeBasedTest() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@StartableByRPC
|
|
||||||
@InitiatingFlow
|
|
||||||
class DummyFlow : FlowLogic<Unit>() {
|
|
||||||
@Suspendable
|
|
||||||
override fun call() = Unit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class UserAuthServiceEmbedded : UserAuthServiceTest() {
|
|
||||||
|
|
||||||
private val rpcUser = User("user", "foo", permissions = setOf(
|
|
||||||
Permissions.startFlow<DummyFlow>(),
|
|
||||||
Permissions.invokeRpc("vaultQueryBy"),
|
|
||||||
Permissions.invokeRpc(CordaRPCOps::stateMachinesFeed),
|
|
||||||
Permissions.invokeRpc("vaultQueryByCriteria")))
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun setup() {
|
|
||||||
val securityConfig = SecurityConfiguration(
|
|
||||||
authService = SecurityConfiguration.AuthService.fromUsers(listOf(rpcUser)))
|
|
||||||
|
|
||||||
val configOverrides = mapOf("security" to securityConfig.toConfig().root().unwrapped())
|
|
||||||
node = startNode(ALICE_NAME, rpcUsers = emptyList(), configOverrides = configOverrides)
|
|
||||||
client = CordaRPCClient(node.internals.configuration.rpcAddress!!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class UserAuthServiceTestsJDBC : UserAuthServiceTest() {
|
|
||||||
|
|
||||||
private val db = UsersDB(
|
|
||||||
name = "SecurityDataSourceTestDB",
|
|
||||||
users = listOf(UserAndRoles(username = "user",
|
|
||||||
password = "foo",
|
|
||||||
roles = listOf("default"))),
|
|
||||||
roleAndPermissions = listOf(
|
|
||||||
RoleAndPermissions(
|
|
||||||
role = "default",
|
|
||||||
permissions = listOf(
|
|
||||||
Permissions.startFlow<DummyFlow>(),
|
|
||||||
Permissions.invokeRpc("vaultQueryBy"),
|
|
||||||
Permissions.invokeRpc(CordaRPCOps::stateMachinesFeed),
|
|
||||||
Permissions.invokeRpc("vaultQueryByCriteria"))),
|
|
||||||
RoleAndPermissions(
|
|
||||||
role = "admin",
|
|
||||||
permissions = listOf("ALL")
|
|
||||||
)))
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun setup() {
|
|
||||||
val securityConfig = SecurityConfiguration(
|
|
||||||
authService = SecurityConfiguration.AuthService(
|
|
||||||
dataSource = SecurityConfiguration.AuthService.DataSource(
|
|
||||||
type = AuthDataSourceType.DB,
|
|
||||||
passwordEncryption = PasswordEncryption.NONE,
|
|
||||||
connection = Properties().apply {
|
|
||||||
setProperty("jdbcUrl", db.jdbcUrl)
|
|
||||||
setProperty("username", "")
|
|
||||||
setProperty("password", "")
|
|
||||||
setProperty("driverClassName", "org.h2.Driver")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
val configOverrides = mapOf("security" to securityConfig.toConfig().root().unwrapped())
|
|
||||||
node = startNode(ALICE_NAME, rpcUsers = emptyList(), configOverrides = configOverrides)
|
|
||||||
client = CordaRPCClient(node.internals.configuration.rpcAddress!!)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Add new users on-the-fly`() {
|
fun `Add new users dynamically`() {
|
||||||
assertFailsWith(
|
assertFailsWith(
|
||||||
ActiveMQSecurityException::class,
|
ActiveMQSecurityException::class,
|
||||||
"Login with incorrect password should fail") {
|
"Login with incorrect password should fail") {
|
||||||
@ -171,7 +163,7 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() {
|
|||||||
|
|
||||||
db.insert(UserAndRoles(
|
db.insert(UserAndRoles(
|
||||||
username = "user2",
|
username = "user2",
|
||||||
password = "bar",
|
password = encodePassword("bar"),
|
||||||
roles = listOf("default")))
|
roles = listOf("default")))
|
||||||
|
|
||||||
client.start("user2", "bar")
|
client.start("user2", "bar")
|
||||||
@ -181,10 +173,9 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() {
|
|||||||
fun `Modify user permissions during RPC session`() {
|
fun `Modify user permissions during RPC session`() {
|
||||||
db.insert(UserAndRoles(
|
db.insert(UserAndRoles(
|
||||||
username = "user3",
|
username = "user3",
|
||||||
password = "bar",
|
password = encodePassword("bar"),
|
||||||
roles = emptyList()))
|
roles = emptyList()))
|
||||||
|
|
||||||
|
|
||||||
client.start("user3", "bar").use {
|
client.start("user3", "bar").use {
|
||||||
val proxy = it.proxy
|
val proxy = it.proxy
|
||||||
assertFailsWith(
|
assertFailsWith(
|
||||||
@ -193,6 +184,7 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() {
|
|||||||
proxy.stateMachinesFeed()
|
proxy.stateMachinesFeed()
|
||||||
}
|
}
|
||||||
db.addRoleToUser("user3", "default")
|
db.addRoleToUser("user3", "default")
|
||||||
|
Thread.sleep(1500)
|
||||||
proxy.stateMachinesFeed()
|
proxy.stateMachinesFeed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -201,13 +193,14 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() {
|
|||||||
fun `Revoke user permissions during RPC session`() {
|
fun `Revoke user permissions during RPC session`() {
|
||||||
db.insert(UserAndRoles(
|
db.insert(UserAndRoles(
|
||||||
username = "user4",
|
username = "user4",
|
||||||
password = "test",
|
password = encodePassword("test"),
|
||||||
roles = listOf("default")))
|
roles = listOf("default")))
|
||||||
|
|
||||||
client.start("user4", "test").use {
|
client.start("user4", "test").use {
|
||||||
val proxy = it.proxy
|
val proxy = it.proxy
|
||||||
proxy.stateMachinesFeed()
|
proxy.stateMachinesFeed()
|
||||||
db.deleteUser("user4")
|
db.deleteUser("user4")
|
||||||
|
Thread.sleep(1500)
|
||||||
assertFailsWith(
|
assertFailsWith(
|
||||||
PermissionException::class,
|
PermissionException::class,
|
||||||
"This user should not be authorized to call 'nodeInfo'") {
|
"This user should not be authorized to call 'nodeInfo'") {
|
||||||
@ -216,15 +209,27 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@StartableByRPC
|
||||||
|
@InitiatingFlow
|
||||||
|
class DummyFlow : FlowLogic<Unit>() {
|
||||||
|
@Suspendable
|
||||||
|
override fun call() = Unit
|
||||||
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
override fun tearDown() {
|
override fun tearDown() {
|
||||||
db.close()
|
db.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun encodePassword(s: String) = encodePassword(s, passwordEncryption)
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class UserAndRoles(val username: String, val password: String, val roles: List<String>)
|
private data class UserAndRoles(val username: String, val password: String, val roles: List<String>)
|
||||||
private data class RoleAndPermissions(val role: String, val permissions: List<String>)
|
private data class RoleAndPermissions(val role: String, val permissions: List<String>)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Manage in-memory DB mocking a users database with the schema expected by Node's security manager
|
||||||
|
*/
|
||||||
private class UsersDB : AutoCloseable {
|
private class UsersDB : AutoCloseable {
|
||||||
|
|
||||||
val jdbcUrl: String
|
val jdbcUrl: String
|
||||||
@ -261,12 +266,6 @@ private class UsersDB : AutoCloseable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteRole(role: String) {
|
|
||||||
session {
|
|
||||||
it.execute("DELETE FROM role_permissions WHERE role_name = '$role'")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun deleteUser(username: String) {
|
fun deleteUser(username: String) {
|
||||||
session {
|
session {
|
||||||
it.execute("DELETE FROM users WHERE username = '$username'")
|
it.execute("DELETE FROM users WHERE username = '$username'")
|
||||||
@ -307,4 +306,22 @@ private class UsersDB : AutoCloseable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sample of hardcoded hashes to watch for format backward compatibility
|
||||||
|
*/
|
||||||
|
private val hashedPasswords = mapOf(
|
||||||
|
PasswordEncryption.SHIRO_1_CRYPT to mapOf(
|
||||||
|
"foo" to "\$shiro1\$SHA-256$500000\$WSiEVj6q8d02sFcCk1dkoA==\$MBkU/ghdD9ovoDerdzNfkXdP9Bdhmok7tidvVIqGzcA=",
|
||||||
|
"bar" to "\$shiro1\$SHA-256$500000\$Q6dmdY1uVMm0LYAWaOHtCA==\$u7NbFaj9tHf2RTW54jedLPiOiGjJv0RVEPIjVquJuYY=",
|
||||||
|
"test" to "\$shiro1\$SHA-256$500000\$F6CWSFDDxGTlzvREwih8Gw==\$DQhyAPoUw3RdvNYJ1aubCnzEIXm+szGQ3HplaG+euz8="))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A functional object for producing password encoded according to the given scheme.
|
||||||
|
*/
|
||||||
|
private fun encodePassword(s: String, format: PasswordEncryption) = when (format) {
|
||||||
|
PasswordEncryption.NONE -> s
|
||||||
|
PasswordEncryption.SHIRO_1_CRYPT -> hashedPasswords[format]!![s] ?:
|
||||||
|
DefaultPasswordService().encryptPassword(s.toCharArray())
|
||||||
|
}
|
@ -12,6 +12,9 @@ import net.corda.nodeapi.internal.config.User
|
|||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
import net.corda.testing.common.internal.ProjectStructure.projectRootDir
|
import net.corda.testing.common.internal.ProjectStructure.projectRootDir
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
|
import net.corda.testing.internal.IntegrationTest
|
||||||
|
import net.corda.testing.internal.IntegrationTestSchemas
|
||||||
|
import net.corda.testing.internal.toDatabaseSchemaNames
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||||
import org.junit.ClassRule
|
import org.junit.ClassRule
|
||||||
@ -24,7 +27,7 @@ class BootTests : IntegrationTest() {
|
|||||||
companion object {
|
companion object {
|
||||||
@ClassRule @JvmField
|
@ClassRule @JvmField
|
||||||
val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, DUMMY_BANK_A_NAME)
|
val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, DUMMY_BANK_A_NAME)
|
||||||
.map { it.toDatabaseSchemaNames("", "_10000","_10003") }.flatten().toTypedArray())
|
.map { it.toDatabaseSchemaNames("", "_10000", "_10003") }.flatten().toTypedArray())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -11,6 +11,9 @@ import net.corda.node.services.Permissions.Companion.startFlow
|
|||||||
import net.corda.nodeapi.internal.config.User
|
import net.corda.nodeapi.internal.config.User
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
|
import net.corda.testing.internal.IntegrationTest
|
||||||
|
import net.corda.testing.internal.IntegrationTestSchemas
|
||||||
|
import net.corda.testing.internal.toDatabaseSchemaName
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.ClassRule
|
import org.junit.ClassRule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
@ -1,22 +1,21 @@
|
|||||||
package net.corda.node
|
package net.corda.node
|
||||||
|
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.identity.CordaX500Name
|
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.node.services.config.configureDevKeyAndTrustStores
|
import net.corda.node.services.config.configureDevKeyAndTrustStores
|
||||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
import net.corda.nodeapi.internal.config.SSLConfiguration
|
||||||
import net.corda.nodeapi.internal.crypto.*
|
import net.corda.nodeapi.internal.crypto.*
|
||||||
import net.corda.testing.ALICE_NAME
|
import net.corda.testing.ALICE_NAME
|
||||||
import net.corda.testing.IntegrationTest
|
import net.corda.testing.internal.IntegrationTest
|
||||||
import net.corda.testing.IntegrationTestSchemas
|
import net.corda.testing.internal.IntegrationTestSchemas
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
import net.corda.testing.toDatabaseSchemaName
|
import net.corda.testing.internal.toDatabaseSchemaName
|
||||||
import org.junit.ClassRule
|
import org.junit.ClassRule
|
||||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
|
|
||||||
class NodeKeystoreCheckTest : IntegrationTest() {
|
class NodeKeystoreCheckTest : IntegrationTest() {
|
||||||
companion object {
|
companion object {
|
||||||
@ -59,10 +58,10 @@ class NodeKeystoreCheckTest : IntegrationTest() {
|
|||||||
|
|
||||||
// Self signed root
|
// Self signed root
|
||||||
val badRootKeyPair = Crypto.generateKeyPair()
|
val badRootKeyPair = Crypto.generateKeyPair()
|
||||||
val badRoot = X509Utilities.createSelfSignedCACertificate(CordaX500Name("Bad Root", "Lodnon", "GB"), badRootKeyPair)
|
val badRoot = X509Utilities.createSelfSignedCACertificate(X500Principal("O=Bad Root,L=Lodnon,C=GB"), badRootKeyPair)
|
||||||
val nodeCA = keystore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, config.keyStorePassword)
|
val nodeCA = keystore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, config.keyStorePassword)
|
||||||
val badNodeCACert = X509Utilities.createCertificate(CertificateType.NODE_CA, badRoot, badRootKeyPair, ALICE_NAME, nodeCA.keyPair.public)
|
val badNodeCACert = X509Utilities.createCertificate(CertificateType.NODE_CA, badRoot, badRootKeyPair, ALICE_NAME.x500Principal, nodeCA.keyPair.public)
|
||||||
keystore.setKeyEntry(X509Utilities.CORDA_CLIENT_CA, nodeCA.keyPair.private, config.keyStorePassword.toCharArray(), arrayOf(badNodeCACert.cert, badRoot.cert))
|
keystore.setKeyEntry(X509Utilities.CORDA_CLIENT_CA, nodeCA.keyPair.private, config.keyStorePassword.toCharArray(), arrayOf(badNodeCACert, badRoot))
|
||||||
keystore.save(config.nodeKeystore, config.keyStorePassword)
|
keystore.save(config.nodeKeystore, config.keyStorePassword)
|
||||||
|
|
||||||
assertThatThrownBy {
|
assertThatThrownBy {
|
||||||
|
@ -18,7 +18,11 @@ import net.corda.testing.*
|
|||||||
import net.corda.testing.driver.NodeHandle
|
import net.corda.testing.driver.NodeHandle
|
||||||
import net.corda.testing.driver.PortAllocation
|
import net.corda.testing.driver.PortAllocation
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
|
import net.corda.testing.internal.IntegrationTest
|
||||||
|
import net.corda.testing.internal.IntegrationTestSchemas
|
||||||
import net.corda.testing.internal.performance.div
|
import net.corda.testing.internal.performance.div
|
||||||
|
import net.corda.testing.internal.toDatabaseSchemaName
|
||||||
|
import net.corda.testing.internal.toDatabaseSchemaNames
|
||||||
import net.corda.testing.node.NotarySpec
|
import net.corda.testing.node.NotarySpec
|
||||||
import net.corda.testing.node.internal.InternalDriverDSL
|
import net.corda.testing.node.internal.InternalDriverDSL
|
||||||
import net.corda.testing.node.internal.performance.startPublishingFixedRateInjector
|
import net.corda.testing.node.internal.performance.startPublishingFixedRateInjector
|
||||||
|
@ -3,6 +3,9 @@ package net.corda.node
|
|||||||
import com.google.common.base.Stopwatch
|
import com.google.common.base.Stopwatch
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
|
import net.corda.testing.internal.IntegrationTest
|
||||||
|
import net.corda.testing.internal.IntegrationTestSchemas
|
||||||
|
import net.corda.testing.internal.toDatabaseSchemaName
|
||||||
import org.junit.ClassRule
|
import org.junit.ClassRule
|
||||||
import org.junit.Ignore
|
import org.junit.Ignore
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
@ -14,13 +14,14 @@ import net.corda.core.utilities.unwrap
|
|||||||
import net.corda.node.services.Permissions.Companion.startFlow
|
import net.corda.node.services.Permissions.Companion.startFlow
|
||||||
import net.corda.nodeapi.internal.config.User
|
import net.corda.nodeapi.internal.config.User
|
||||||
import net.corda.testing.ALICE_NAME
|
import net.corda.testing.ALICE_NAME
|
||||||
import net.corda.testing.IntegrationTest
|
|
||||||
import net.corda.testing.IntegrationTestSchemas
|
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
import net.corda.testing.toDatabaseSchemaName
|
import net.corda.testing.internal.IntegrationTest
|
||||||
|
import net.corda.testing.internal.IntegrationTestSchemas
|
||||||
|
import net.corda.testing.internal.toDatabaseSchemaName
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.bouncycastle.util.io.Streams
|
import org.bouncycastle.util.io.Streams
|
||||||
import org.junit.ClassRule
|
import org.junit.ClassRule
|
||||||
|
import org.junit.Ignore
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.net.ConnectException
|
import java.net.ConnectException
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
@ -33,6 +34,7 @@ class SSHServerTest : IntegrationTest() {
|
|||||||
val databaseSchemas = IntegrationTestSchemas(ALICE_NAME.toDatabaseSchemaName())
|
val databaseSchemas = IntegrationTestSchemas(ALICE_NAME.toDatabaseSchemaName())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Ignore("Test has undeterministic capacity to hang, ignore till fixed")
|
||||||
@Test()
|
@Test()
|
||||||
fun `ssh server does not start be default`() {
|
fun `ssh server does not start be default`() {
|
||||||
val user = User("u", "p", setOf())
|
val user = User("u", "p", setOf())
|
||||||
@ -54,6 +56,7 @@ class SSHServerTest : IntegrationTest() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Ignore("Test has undeterministic capacity to hang, ignore till fixed")
|
||||||
@Test
|
@Test
|
||||||
fun `ssh server starts when configured`() {
|
fun `ssh server starts when configured`() {
|
||||||
val user = User("u", "p", setOf())
|
val user = User("u", "p", setOf())
|
||||||
@ -74,6 +77,7 @@ class SSHServerTest : IntegrationTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Ignore("Test has undeterministic capacity to hang, ignore till fixed")
|
||||||
@Test
|
@Test
|
||||||
fun `ssh server verify credentials`() {
|
fun `ssh server verify credentials`() {
|
||||||
val user = User("u", "p", setOf())
|
val user = User("u", "p", setOf())
|
||||||
@ -97,6 +101,7 @@ class SSHServerTest : IntegrationTest() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Ignore("Test has undeterministic capacity to hang, ignore till fixed")
|
||||||
@Test
|
@Test
|
||||||
fun `ssh respects permissions`() {
|
fun `ssh respects permissions`() {
|
||||||
val user = User("u", "p", setOf(startFlow<FlowICanRun>()))
|
val user = User("u", "p", setOf(startFlow<FlowICanRun>()))
|
||||||
@ -127,6 +132,7 @@ class SSHServerTest : IntegrationTest() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Ignore("Test has undeterministic capacity to hang, ignore till fixed")
|
||||||
@Test
|
@Test
|
||||||
fun `ssh runs flows`() {
|
fun `ssh runs flows`() {
|
||||||
val user = User("u", "p", setOf(startFlow<FlowICanRun>()))
|
val user = User("u", "p", setOf(startFlow<FlowICanRun>()))
|
||||||
|
@ -59,10 +59,10 @@ class ProtonWrapperTests {
|
|||||||
amqpClient.start()
|
amqpClient.start()
|
||||||
val serverConnect = serverConnected.get()
|
val serverConnect = serverConnected.get()
|
||||||
assertEquals(true, serverConnect.connected)
|
assertEquals(true, serverConnect.connected)
|
||||||
assertEquals(BOB_NAME, CordaX500Name.parse(serverConnect.remoteCert!!.subject.toString()))
|
assertEquals(BOB_NAME, CordaX500Name.build(serverConnect.remoteCert!!.subjectX500Principal))
|
||||||
val clientConnect = clientConnected.get()
|
val clientConnect = clientConnected.get()
|
||||||
assertEquals(true, clientConnect.connected)
|
assertEquals(true, clientConnect.connected)
|
||||||
assertEquals(ALICE_NAME, CordaX500Name.parse(clientConnect.remoteCert!!.subject.toString()))
|
assertEquals(ALICE_NAME, CordaX500Name.build(clientConnect.remoteCert!!.subjectX500Principal))
|
||||||
val msg = amqpClient.createMessage("Test".toByteArray(),
|
val msg = amqpClient.createMessage("Test".toByteArray(),
|
||||||
"p2p.inbound",
|
"p2p.inbound",
|
||||||
ALICE_NAME.toString(),
|
ALICE_NAME.toString(),
|
||||||
@ -102,10 +102,10 @@ class ProtonWrapperTests {
|
|||||||
amqpClient.start()
|
amqpClient.start()
|
||||||
val serverConn1 = serverConnected.get()
|
val serverConn1 = serverConnected.get()
|
||||||
assertEquals(true, serverConn1.connected)
|
assertEquals(true, serverConn1.connected)
|
||||||
assertEquals(BOB_NAME, CordaX500Name.parse(serverConn1.remoteCert!!.subject.toString()))
|
assertEquals(BOB_NAME, CordaX500Name.build(serverConn1.remoteCert!!.subjectX500Principal))
|
||||||
val connState1 = clientConnected.next()
|
val connState1 = clientConnected.next()
|
||||||
assertEquals(true, connState1.connected)
|
assertEquals(true, connState1.connected)
|
||||||
assertEquals(ALICE_NAME, CordaX500Name.parse(connState1.remoteCert!!.subject.toString()))
|
assertEquals(ALICE_NAME, CordaX500Name.build(connState1.remoteCert!!.subjectX500Principal))
|
||||||
assertEquals(serverPort, connState1.remoteAddress.port)
|
assertEquals(serverPort, connState1.remoteAddress.port)
|
||||||
|
|
||||||
// Fail over
|
// Fail over
|
||||||
@ -116,10 +116,10 @@ class ProtonWrapperTests {
|
|||||||
assertEquals(serverPort, connState2.remoteAddress.port)
|
assertEquals(serverPort, connState2.remoteAddress.port)
|
||||||
val serverConn2 = serverConnected2.get()
|
val serverConn2 = serverConnected2.get()
|
||||||
assertEquals(true, serverConn2.connected)
|
assertEquals(true, serverConn2.connected)
|
||||||
assertEquals(BOB_NAME, CordaX500Name.parse(serverConn2.remoteCert!!.subject.toString()))
|
assertEquals(BOB_NAME, CordaX500Name.build(serverConn2.remoteCert!!.subjectX500Principal))
|
||||||
val connState3 = clientConnected.next()
|
val connState3 = clientConnected.next()
|
||||||
assertEquals(true, connState3.connected)
|
assertEquals(true, connState3.connected)
|
||||||
assertEquals(ALICE_NAME, CordaX500Name.parse(connState3.remoteCert!!.subject.toString()))
|
assertEquals(ALICE_NAME, CordaX500Name.build(connState3.remoteCert!!.subjectX500Principal))
|
||||||
assertEquals(serverPort2, connState3.remoteAddress.port)
|
assertEquals(serverPort2, connState3.remoteAddress.port)
|
||||||
|
|
||||||
// Fail back
|
// Fail back
|
||||||
@ -130,10 +130,10 @@ class ProtonWrapperTests {
|
|||||||
assertEquals(serverPort2, connState4.remoteAddress.port)
|
assertEquals(serverPort2, connState4.remoteAddress.port)
|
||||||
val serverConn3 = serverConnected.get()
|
val serverConn3 = serverConnected.get()
|
||||||
assertEquals(true, serverConn3.connected)
|
assertEquals(true, serverConn3.connected)
|
||||||
assertEquals(BOB_NAME, CordaX500Name.parse(serverConn3.remoteCert!!.subject.toString()))
|
assertEquals(BOB_NAME, CordaX500Name.build(serverConn3.remoteCert!!.subjectX500Principal))
|
||||||
val connState5 = clientConnected.next()
|
val connState5 = clientConnected.next()
|
||||||
assertEquals(true, connState5.connected)
|
assertEquals(true, connState5.connected)
|
||||||
assertEquals(ALICE_NAME, CordaX500Name.parse(connState5.remoteCert!!.subject.toString()))
|
assertEquals(ALICE_NAME, CordaX500Name.build(connState5.remoteCert!!.subjectX500Principal))
|
||||||
assertEquals(serverPort, connState5.remoteAddress.port)
|
assertEquals(serverPort, connState5.remoteAddress.port)
|
||||||
} finally {
|
} finally {
|
||||||
amqpClient.close()
|
amqpClient.close()
|
||||||
@ -149,7 +149,7 @@ class ProtonWrapperTests {
|
|||||||
val clientConnected = amqpClient.onConnection.toFuture()
|
val clientConnected = amqpClient.onConnection.toFuture()
|
||||||
amqpClient.start()
|
amqpClient.start()
|
||||||
assertEquals(true, clientConnected.get().connected)
|
assertEquals(true, clientConnected.get().connected)
|
||||||
assertEquals(CHARLIE_NAME, CordaX500Name.parse(clientConnected.get().remoteCert!!.subject.toString()))
|
assertEquals(CHARLIE_NAME, CordaX500Name.build(clientConnected.get().remoteCert!!.subjectX500Principal))
|
||||||
val artemis = artemisClient.started!!
|
val artemis = artemisClient.started!!
|
||||||
val sendAddress = "p2p.inbound"
|
val sendAddress = "p2p.inbound"
|
||||||
artemis.session.createQueue(sendAddress, RoutingType.MULTICAST, "queue", true)
|
artemis.session.createQueue(sendAddress, RoutingType.MULTICAST, "queue", true)
|
||||||
@ -180,13 +180,13 @@ class ProtonWrapperTests {
|
|||||||
amqpClient1.start()
|
amqpClient1.start()
|
||||||
val connection1 = connectionEvents.next()
|
val connection1 = connectionEvents.next()
|
||||||
assertEquals(true, connection1.connected)
|
assertEquals(true, connection1.connected)
|
||||||
val connection1ID = CordaX500Name.parse(connection1.remoteCert!!.subject.toString())
|
val connection1ID = CordaX500Name.build(connection1.remoteCert!!.subjectX500Principal)
|
||||||
assertEquals("client 0", connection1ID.organisationUnit)
|
assertEquals("client 0", connection1ID.organisationUnit)
|
||||||
val source1 = connection1.remoteAddress
|
val source1 = connection1.remoteAddress
|
||||||
amqpClient2.start()
|
amqpClient2.start()
|
||||||
val connection2 = connectionEvents.next()
|
val connection2 = connectionEvents.next()
|
||||||
assertEquals(true, connection2.connected)
|
assertEquals(true, connection2.connected)
|
||||||
val connection2ID = CordaX500Name.parse(connection2.remoteCert!!.subject.toString())
|
val connection2ID = CordaX500Name.build(connection2.remoteCert!!.subjectX500Principal)
|
||||||
assertEquals("client 1", connection2ID.organisationUnit)
|
assertEquals("client 1", connection2ID.organisationUnit)
|
||||||
val source2 = connection2.remoteAddress
|
val source2 = connection2.remoteAddress
|
||||||
// Stopping one shouldn't disconnect the other
|
// Stopping one shouldn't disconnect the other
|
||||||
@ -207,7 +207,7 @@ class ProtonWrapperTests {
|
|||||||
amqpClient1.start()
|
amqpClient1.start()
|
||||||
val connection5 = connectionEvents.next()
|
val connection5 = connectionEvents.next()
|
||||||
assertEquals(true, connection5.connected)
|
assertEquals(true, connection5.connected)
|
||||||
val connection5ID = CordaX500Name.parse(connection5.remoteCert!!.subject.toString())
|
val connection5ID = CordaX500Name.build(connection5.remoteCert!!.subjectX500Principal)
|
||||||
assertEquals("client 0", connection5ID.organisationUnit)
|
assertEquals("client 0", connection5ID.organisationUnit)
|
||||||
assertEquals(true, amqpClient1.connected)
|
assertEquals(true, amqpClient1.connected)
|
||||||
assertEquals(false, amqpClient2.connected)
|
assertEquals(false, amqpClient2.connected)
|
||||||
@ -252,7 +252,8 @@ class ProtonWrapperTests {
|
|||||||
|
|
||||||
val clientTruststore = loadKeyStore(clientConfig.trustStoreFile, clientConfig.trustStorePassword)
|
val clientTruststore = loadKeyStore(clientConfig.trustStoreFile, clientConfig.trustStorePassword)
|
||||||
val clientKeystore = loadKeyStore(clientConfig.sslKeystore, clientConfig.keyStorePassword)
|
val clientKeystore = loadKeyStore(clientConfig.sslKeystore, clientConfig.keyStorePassword)
|
||||||
val amqpClient = AMQPClient(listOf(NetworkHostAndPort("localhost", serverPort),
|
return AMQPClient(
|
||||||
|
listOf(NetworkHostAndPort("localhost", serverPort),
|
||||||
NetworkHostAndPort("localhost", serverPort2),
|
NetworkHostAndPort("localhost", serverPort2),
|
||||||
NetworkHostAndPort("localhost", artemisPort)),
|
NetworkHostAndPort("localhost", artemisPort)),
|
||||||
setOf(ALICE_NAME, CHARLIE_NAME),
|
setOf(ALICE_NAME, CHARLIE_NAME),
|
||||||
@ -261,7 +262,6 @@ class ProtonWrapperTests {
|
|||||||
clientKeystore,
|
clientKeystore,
|
||||||
clientConfig.keyStorePassword,
|
clientConfig.keyStorePassword,
|
||||||
clientTruststore, true)
|
clientTruststore, true)
|
||||||
return amqpClient
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createSharedThreadsClient(sharedEventGroup: EventLoopGroup, id: Int): AMQPClient {
|
private fun createSharedThreadsClient(sharedEventGroup: EventLoopGroup, id: Int): AMQPClient {
|
||||||
@ -275,14 +275,14 @@ class ProtonWrapperTests {
|
|||||||
|
|
||||||
val clientTruststore = loadKeyStore(clientConfig.trustStoreFile, clientConfig.trustStorePassword)
|
val clientTruststore = loadKeyStore(clientConfig.trustStoreFile, clientConfig.trustStorePassword)
|
||||||
val clientKeystore = loadKeyStore(clientConfig.sslKeystore, clientConfig.keyStorePassword)
|
val clientKeystore = loadKeyStore(clientConfig.sslKeystore, clientConfig.keyStorePassword)
|
||||||
val amqpClient = AMQPClient(listOf(NetworkHostAndPort("localhost", serverPort)),
|
return AMQPClient(
|
||||||
|
listOf(NetworkHostAndPort("localhost", serverPort)),
|
||||||
setOf(ALICE_NAME),
|
setOf(ALICE_NAME),
|
||||||
PEER_USER,
|
PEER_USER,
|
||||||
PEER_USER,
|
PEER_USER,
|
||||||
clientKeystore,
|
clientKeystore,
|
||||||
clientConfig.keyStorePassword,
|
clientConfig.keyStorePassword,
|
||||||
clientTruststore, true, sharedEventGroup)
|
clientTruststore, true, sharedEventGroup)
|
||||||
return amqpClient
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createServer(port: Int, name: CordaX500Name = ALICE_NAME): AMQPServer {
|
private fun createServer(port: Int, name: CordaX500Name = ALICE_NAME): AMQPServer {
|
||||||
@ -296,14 +296,13 @@ class ProtonWrapperTests {
|
|||||||
|
|
||||||
val serverTruststore = loadKeyStore(serverConfig.trustStoreFile, serverConfig.trustStorePassword)
|
val serverTruststore = loadKeyStore(serverConfig.trustStoreFile, serverConfig.trustStorePassword)
|
||||||
val serverKeystore = loadKeyStore(serverConfig.sslKeystore, serverConfig.keyStorePassword)
|
val serverKeystore = loadKeyStore(serverConfig.sslKeystore, serverConfig.keyStorePassword)
|
||||||
val amqpServer = AMQPServer("0.0.0.0",
|
return AMQPServer(
|
||||||
|
"0.0.0.0",
|
||||||
port,
|
port,
|
||||||
PEER_USER,
|
PEER_USER,
|
||||||
PEER_USER,
|
PEER_USER,
|
||||||
serverKeystore,
|
serverKeystore,
|
||||||
serverConfig.keyStorePassword,
|
serverConfig.keyStorePassword,
|
||||||
serverTruststore)
|
serverTruststore)
|
||||||
return amqpServer
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
@ -24,8 +24,7 @@ import net.corda.testing.*
|
|||||||
import net.corda.testing.driver.DriverDSL
|
import net.corda.testing.driver.DriverDSL
|
||||||
import net.corda.testing.driver.NodeHandle
|
import net.corda.testing.driver.NodeHandle
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
import net.corda.testing.internal.rigorousMock
|
import net.corda.testing.internal.*
|
||||||
import net.corda.testing.internal.withoutTestSerialization
|
|
||||||
import net.corda.testing.services.MockAttachmentStorage
|
import net.corda.testing.services.MockAttachmentStorage
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.ClassRule
|
import org.junit.ClassRule
|
||||||
|
@ -26,8 +26,8 @@ import net.corda.node.services.transactions.minCorrectReplicas
|
|||||||
import net.corda.nodeapi.internal.DevIdentityGenerator
|
import net.corda.nodeapi.internal.DevIdentityGenerator
|
||||||
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||||
import net.corda.nodeapi.internal.network.NotaryInfo
|
import net.corda.nodeapi.internal.network.NotaryInfo
|
||||||
import net.corda.testing.IntegrationTest
|
import net.corda.testing.internal.IntegrationTest
|
||||||
import net.corda.testing.IntegrationTestSchemas
|
import net.corda.testing.internal.IntegrationTestSchemas
|
||||||
import net.corda.testing.chooseIdentity
|
import net.corda.testing.chooseIdentity
|
||||||
import net.corda.testing.common.internal.testNetworkParameters
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
import net.corda.testing.contracts.DummyContract
|
import net.corda.testing.contracts.DummyContract
|
||||||
|
@ -11,13 +11,16 @@ import net.corda.core.utilities.getOrThrow
|
|||||||
import net.corda.finance.POUNDS
|
import net.corda.finance.POUNDS
|
||||||
import net.corda.finance.flows.CashIssueFlow
|
import net.corda.finance.flows.CashIssueFlow
|
||||||
import net.corda.finance.flows.CashPaymentFlow
|
import net.corda.finance.flows.CashPaymentFlow
|
||||||
import net.corda.finance.schemas.CashSchemaV1
|
|
||||||
import net.corda.node.services.Permissions.Companion.invokeRpc
|
import net.corda.node.services.Permissions.Companion.invokeRpc
|
||||||
import net.corda.node.services.Permissions.Companion.startFlow
|
import net.corda.node.services.Permissions.Companion.startFlow
|
||||||
import net.corda.nodeapi.internal.config.User
|
import net.corda.nodeapi.internal.config.User
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
import net.corda.testing.driver.NodeHandle
|
import net.corda.testing.driver.NodeHandle
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
|
import net.corda.testing.internal.IntegrationTest
|
||||||
|
import net.corda.testing.internal.IntegrationTestSchemas
|
||||||
|
import net.corda.testing.internal.toDatabaseSchemaName
|
||||||
|
import net.corda.testing.internal.toDatabaseSchemaNames
|
||||||
import net.corda.testing.node.ClusterSpec
|
import net.corda.testing.node.ClusterSpec
|
||||||
import net.corda.testing.node.NotarySpec
|
import net.corda.testing.node.NotarySpec
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
@ -20,6 +20,8 @@ import net.corda.nodeapi.internal.network.NotaryInfo
|
|||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
import net.corda.testing.common.internal.testNetworkParameters
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
import net.corda.testing.contracts.DummyContract
|
import net.corda.testing.contracts.DummyContract
|
||||||
|
import net.corda.testing.internal.IntegrationTest
|
||||||
|
import net.corda.testing.internal.IntegrationTestSchemas
|
||||||
import net.corda.testing.node.MockNetwork
|
import net.corda.testing.node.MockNetwork
|
||||||
import net.corda.testing.node.MockNodeParameters
|
import net.corda.testing.node.MockNodeParameters
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||||
|
@ -15,6 +15,9 @@ import net.corda.testing.*
|
|||||||
import net.corda.testing.contracts.DummyContract
|
import net.corda.testing.contracts.DummyContract
|
||||||
import net.corda.testing.driver.NodeHandle
|
import net.corda.testing.driver.NodeHandle
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
|
import net.corda.testing.internal.IntegrationTest
|
||||||
|
import net.corda.testing.internal.IntegrationTestSchemas
|
||||||
|
import net.corda.testing.internal.toDatabaseSchemaName
|
||||||
import net.corda.testing.node.ClusterSpec
|
import net.corda.testing.node.ClusterSpec
|
||||||
import net.corda.testing.node.NotarySpec
|
import net.corda.testing.node.NotarySpec
|
||||||
import net.corda.testing.node.startFlow
|
import net.corda.testing.node.startFlow
|
||||||
@ -27,7 +30,7 @@ import kotlin.test.assertFailsWith
|
|||||||
class RaftNotaryServiceTests : IntegrationTest() {
|
class RaftNotaryServiceTests : IntegrationTest() {
|
||||||
companion object {
|
companion object {
|
||||||
@ClassRule @JvmField
|
@ClassRule @JvmField
|
||||||
val databaseSchemas = IntegrationTestSchemas( "RAFTNotaryService_0", "RAFTNotaryService_1", "RAFTNotaryService_2",
|
val databaseSchemas = IntegrationTestSchemas("RAFTNotaryService_0", "RAFTNotaryService_1", "RAFTNotaryService_2",
|
||||||
DUMMY_BANK_A_NAME.toDatabaseSchemaName())
|
DUMMY_BANK_A_NAME.toDatabaseSchemaName())
|
||||||
}
|
}
|
||||||
private val notaryName = CordaX500Name("RAFT Notary Service", "London", "GB")
|
private val notaryName = CordaX500Name("RAFT Notary Service", "London", "GB")
|
||||||
|
@ -21,6 +21,9 @@ import net.corda.testing.SerializationEnvironmentRule
|
|||||||
import net.corda.testing.common.internal.testNetworkParameters
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
import net.corda.testing.driver.NodeHandle
|
import net.corda.testing.driver.NodeHandle
|
||||||
import net.corda.testing.driver.PortAllocation
|
import net.corda.testing.driver.PortAllocation
|
||||||
|
import net.corda.testing.internal.IntegrationTest
|
||||||
|
import net.corda.testing.internal.IntegrationTestSchemas
|
||||||
|
import net.corda.testing.internal.toDatabaseSchemaName
|
||||||
import net.corda.testing.node.internal.CompatibilityZoneParams
|
import net.corda.testing.node.internal.CompatibilityZoneParams
|
||||||
import net.corda.testing.node.internal.internalDriver
|
import net.corda.testing.node.internal.internalDriver
|
||||||
import net.corda.testing.node.internal.network.NetworkMapServer
|
import net.corda.testing.node.internal.network.NetworkMapServer
|
||||||
|
@ -7,6 +7,8 @@ import net.corda.core.utilities.NetworkHostAndPort
|
|||||||
import net.corda.node.internal.Node
|
import net.corda.node.internal.Node
|
||||||
import net.corda.node.internal.StartedNode
|
import net.corda.node.internal.StartedNode
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
|
import net.corda.testing.internal.IntegrationTestSchemas
|
||||||
|
import net.corda.testing.internal.toDatabaseSchemaName
|
||||||
import net.corda.testing.node.internal.NodeBasedTest
|
import net.corda.testing.node.internal.NodeBasedTest
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.ClassRule
|
import org.junit.ClassRule
|
||||||
|
@ -8,6 +8,8 @@ import net.corda.core.identity.Party
|
|||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.core.utilities.unwrap
|
import net.corda.core.utilities.unwrap
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
|
import net.corda.testing.internal.IntegrationTestSchemas
|
||||||
|
import net.corda.testing.internal.toDatabaseSchemaName
|
||||||
import net.corda.testing.node.internal.NodeBasedTest
|
import net.corda.testing.node.internal.NodeBasedTest
|
||||||
import net.corda.testing.node.startFlow
|
import net.corda.testing.node.startFlow
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
@ -13,6 +13,9 @@ import net.corda.testing.*
|
|||||||
import net.corda.testing.contracts.DummyContract
|
import net.corda.testing.contracts.DummyContract
|
||||||
import net.corda.testing.contracts.DummyState
|
import net.corda.testing.contracts.DummyState
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
|
import net.corda.testing.internal.IntegrationTest
|
||||||
|
import net.corda.testing.internal.IntegrationTestSchemas
|
||||||
|
import net.corda.testing.internal.toDatabaseSchemaName
|
||||||
import org.junit.ClassRule
|
import org.junit.ClassRule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
@ -3,6 +3,9 @@ package net.corda.node.services.vault
|
|||||||
|
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
|
import net.corda.testing.internal.IntegrationTest
|
||||||
|
import net.corda.testing.internal.IntegrationTestSchemas
|
||||||
|
import net.corda.testing.internal.toDatabaseSchemaName
|
||||||
import org.junit.*
|
import org.junit.*
|
||||||
|
|
||||||
class VaultQueryIntegrationTests : VaultQueryTests() {
|
class VaultQueryIntegrationTests : VaultQueryTests() {
|
||||||
|
@ -2,9 +2,7 @@ package net.corda.node.utilities.registration
|
|||||||
|
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.internal.concurrent.transpose
|
import net.corda.core.internal.concurrent.transpose
|
||||||
import net.corda.core.internal.toX509CertHolder
|
|
||||||
import net.corda.core.messaging.startFlow
|
import net.corda.core.messaging.startFlow
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.core.utilities.OpaqueBytes
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
@ -22,6 +20,8 @@ import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA
|
|||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
import net.corda.testing.common.internal.testNetworkParameters
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
import net.corda.testing.driver.PortAllocation
|
import net.corda.testing.driver.PortAllocation
|
||||||
|
import net.corda.testing.internal.IntegrationTest
|
||||||
|
import net.corda.testing.internal.IntegrationTestSchemas
|
||||||
import net.corda.testing.node.NotarySpec
|
import net.corda.testing.node.NotarySpec
|
||||||
import net.corda.testing.node.internal.CompatibilityZoneParams
|
import net.corda.testing.node.internal.CompatibilityZoneParams
|
||||||
import net.corda.testing.node.internal.internalDriver
|
import net.corda.testing.node.internal.internalDriver
|
||||||
@ -38,8 +38,10 @@ import java.security.KeyPair
|
|||||||
import java.security.cert.CertPath
|
import java.security.cert.CertPath
|
||||||
import java.security.cert.CertPathValidatorException
|
import java.security.cert.CertPathValidatorException
|
||||||
import java.security.cert.Certificate
|
import java.security.cert.Certificate
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
import java.util.zip.ZipEntry
|
import java.util.zip.ZipEntry
|
||||||
import java.util.zip.ZipOutputStream
|
import java.util.zip.ZipOutputStream
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
import javax.ws.rs.*
|
import javax.ws.rs.*
|
||||||
import javax.ws.rs.core.MediaType
|
import javax.ws.rs.core.MediaType
|
||||||
import javax.ws.rs.core.Response
|
import javax.ws.rs.core.Response
|
||||||
@ -59,14 +61,13 @@ class NodeRegistrationTest : IntegrationTest() {
|
|||||||
val testSerialization = SerializationEnvironmentRule(true)
|
val testSerialization = SerializationEnvironmentRule(true)
|
||||||
|
|
||||||
private val portAllocation = PortAllocation.Incremental(13000)
|
private val portAllocation = PortAllocation.Incremental(13000)
|
||||||
private val registrationHandler = RegistrationHandler(ROOT_CA)
|
private val registrationHandler = RegistrationHandler(DEV_ROOT_CA)
|
||||||
|
|
||||||
private lateinit var server: NetworkMapServer
|
private lateinit var server: NetworkMapServer
|
||||||
private lateinit var serverHostAndPort: NetworkHostAndPort
|
private lateinit var serverHostAndPort: NetworkHostAndPort
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun startServer() {
|
fun startServer() {
|
||||||
server = NetworkMapServer(1.minutes, portAllocation.nextHostAndPort(), ROOT_CA, "localhost", registrationHandler)
|
server = NetworkMapServer(1.minutes, portAllocation.nextHostAndPort(), DEV_ROOT_CA, "localhost", registrationHandler)
|
||||||
serverHostAndPort = server.start()
|
serverHostAndPort = server.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,7 +81,7 @@ class NodeRegistrationTest : IntegrationTest() {
|
|||||||
val compatibilityZone = CompatibilityZoneParams(
|
val compatibilityZone = CompatibilityZoneParams(
|
||||||
URL("http://$serverHostAndPort"),
|
URL("http://$serverHostAndPort"),
|
||||||
publishNotaries = { server.networkParameters = testNetworkParameters(it) },
|
publishNotaries = { server.networkParameters = testNetworkParameters(it) },
|
||||||
rootCert = ROOT_CA.certificate.cert)
|
rootCert = DEV_ROOT_CA.certificate)
|
||||||
internalDriver(
|
internalDriver(
|
||||||
portAllocation = portAllocation,
|
portAllocation = portAllocation,
|
||||||
compatibilityZone = compatibilityZone,
|
compatibilityZone = compatibilityZone,
|
||||||
@ -116,12 +117,12 @@ class NodeRegistrationTest : IntegrationTest() {
|
|||||||
@Test
|
@Test
|
||||||
fun `node registration wrong root cert`() {
|
fun `node registration wrong root cert`() {
|
||||||
val someRootCert = X509Utilities.createSelfSignedCACertificate(
|
val someRootCert = X509Utilities.createSelfSignedCACertificate(
|
||||||
CordaX500Name("Integration Test Corda Node Root CA", "R3 Ltd", "London", "GB"),
|
X500Principal("CN=Integration Test Corda Node Root CA,O=R3 Ltd,L=London,C=GB"),
|
||||||
Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
|
Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
|
||||||
val compatibilityZone = CompatibilityZoneParams(
|
val compatibilityZone = CompatibilityZoneParams(
|
||||||
URL("http://$serverHostAndPort"),
|
URL("http://$serverHostAndPort"),
|
||||||
publishNotaries = { server.networkParameters = testNetworkParameters(it) },
|
publishNotaries = { server.networkParameters = testNetworkParameters(it) },
|
||||||
rootCert = someRootCert.cert)
|
rootCert = someRootCert)
|
||||||
internalDriver(
|
internalDriver(
|
||||||
portAllocation = portAllocation,
|
portAllocation = portAllocation,
|
||||||
compatibilityZone = compatibilityZone,
|
compatibilityZone = compatibilityZone,
|
||||||
@ -149,7 +150,7 @@ class RegistrationHandler(private val rootCertAndKeyPair: CertificateAndKeyPair)
|
|||||||
val (certPath, name) = createSignedClientCertificate(
|
val (certPath, name) = createSignedClientCertificate(
|
||||||
certificationRequest,
|
certificationRequest,
|
||||||
rootCertAndKeyPair.keyPair,
|
rootCertAndKeyPair.keyPair,
|
||||||
arrayOf(rootCertAndKeyPair.certificate.cert))
|
arrayOf(rootCertAndKeyPair.certificate))
|
||||||
require(!name.organisation.contains("\\s".toRegex())) { "Whitespace in the organisation name not supported" }
|
require(!name.organisation.contains("\\s".toRegex())) { "Whitespace in the organisation name not supported" }
|
||||||
certPaths[name.organisation] = certPath
|
certPaths[name.organisation] = certPath
|
||||||
return Response.ok(name.organisation).build()
|
return Response.ok(name.organisation).build()
|
||||||
@ -183,12 +184,12 @@ class RegistrationHandler(private val rootCertAndKeyPair: CertificateAndKeyPair)
|
|||||||
val name = CordaX500Name.parse(request.subject.toString())
|
val name = CordaX500Name.parse(request.subject.toString())
|
||||||
val nodeCaCert = X509Utilities.createCertificate(
|
val nodeCaCert = X509Utilities.createCertificate(
|
||||||
CertificateType.NODE_CA,
|
CertificateType.NODE_CA,
|
||||||
caCertPath.first().toX509CertHolder(),
|
caCertPath[0] as X509Certificate ,
|
||||||
caKeyPair,
|
caKeyPair,
|
||||||
name,
|
name.x500Principal,
|
||||||
request.publicKey,
|
request.publicKey,
|
||||||
nameConstraints = null)
|
nameConstraints = null)
|
||||||
val certPath = X509CertificateFactory().generateCertPath(nodeCaCert.cert, *caCertPath)
|
val certPath = X509CertificateFactory().generateCertPath(nodeCaCert, *caCertPath)
|
||||||
return Pair(certPath, name)
|
return Pair(certPath, name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,22 +94,34 @@ class MQSecurityAsNodeTest : MQSecurityTest() {
|
|||||||
javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"),
|
javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"),
|
||||||
"cordacadevpass")
|
"cordacadevpass")
|
||||||
|
|
||||||
val rootCACert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA).toX509CertHolder()
|
val rootCACert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA)
|
||||||
val intermediateCA = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
|
val intermediateCA = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
|
||||||
val clientKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
|
||||||
|
|
||||||
|
val clientKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
// Set name constrain to the legal name.
|
// Set name constrain to the legal name.
|
||||||
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.x500Name))), arrayOf())
|
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.x500Name))), arrayOf())
|
||||||
val clientCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, intermediateCA.certificate,
|
val clientCACert = X509Utilities.createCertificate(
|
||||||
intermediateCA.keyPair, legalName, clientKey.public, nameConstraints = nameConstraints)
|
CertificateType.INTERMEDIATE_CA,
|
||||||
val tlsKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
intermediateCA.certificate,
|
||||||
|
intermediateCA.keyPair,
|
||||||
|
legalName.x500Principal,
|
||||||
|
clientKeyPair.public,
|
||||||
|
nameConstraints = nameConstraints)
|
||||||
|
|
||||||
|
val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
// Using different x500 name in the TLS cert which is not allowed in the name constraints.
|
// Using different x500 name in the TLS cert which is not allowed in the name constraints.
|
||||||
val clientTLSCert = X509Utilities.createCertificate(CertificateType.TLS, clientCACert, clientKey, CordaX500Name("MiniCorp", "London", "GB"), tlsKey.public)
|
val clientTLSCert = X509Utilities.createCertificate(
|
||||||
|
CertificateType.TLS,
|
||||||
|
clientCACert,
|
||||||
|
clientKeyPair,
|
||||||
|
CordaX500Name("MiniCorp", "London", "GB").x500Principal,
|
||||||
|
tlsKeyPair.public)
|
||||||
|
|
||||||
val keyPass = keyStorePassword.toCharArray()
|
val keyPass = keyStorePassword.toCharArray()
|
||||||
val clientCAKeystore = loadOrCreateKeyStore(nodeKeystore, keyStorePassword)
|
val clientCAKeystore = loadOrCreateKeyStore(nodeKeystore, keyStorePassword)
|
||||||
clientCAKeystore.addOrReplaceKey(
|
clientCAKeystore.addOrReplaceKey(
|
||||||
X509Utilities.CORDA_CLIENT_CA,
|
X509Utilities.CORDA_CLIENT_CA,
|
||||||
clientKey.private,
|
clientKeyPair.private,
|
||||||
keyPass,
|
keyPass,
|
||||||
arrayOf(clientCACert, intermediateCA.certificate, rootCACert))
|
arrayOf(clientCACert, intermediateCA.certificate, rootCACert))
|
||||||
clientCAKeystore.save(nodeKeystore, keyStorePassword)
|
clientCAKeystore.save(nodeKeystore, keyStorePassword)
|
||||||
@ -117,7 +129,7 @@ class MQSecurityAsNodeTest : MQSecurityTest() {
|
|||||||
val tlsKeystore = loadOrCreateKeyStore(sslKeystore, keyStorePassword)
|
val tlsKeystore = loadOrCreateKeyStore(sslKeystore, keyStorePassword)
|
||||||
tlsKeystore.addOrReplaceKey(
|
tlsKeystore.addOrReplaceKey(
|
||||||
X509Utilities.CORDA_CLIENT_TLS,
|
X509Utilities.CORDA_CLIENT_TLS,
|
||||||
tlsKey.private,
|
tlsKeyPair.private,
|
||||||
keyPass,
|
keyPass,
|
||||||
arrayOf(clientTLSCert, clientCACert, intermediateCA.certificate, rootCACert))
|
arrayOf(clientTLSCert, clientCACert, intermediateCA.certificate, rootCACert))
|
||||||
tlsKeystore.save(sslKeystore, keyStorePassword)
|
tlsKeystore.save(sslKeystore, keyStorePassword)
|
||||||
|
@ -26,7 +26,9 @@ import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEERS_PREF
|
|||||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
import net.corda.nodeapi.internal.config.SSLConfiguration
|
||||||
import net.corda.nodeapi.internal.config.User
|
import net.corda.nodeapi.internal.config.User
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
|
import net.corda.testing.internal.IntegrationTestSchemas
|
||||||
import net.corda.testing.internal.configureTestSSL
|
import net.corda.testing.internal.configureTestSSL
|
||||||
|
import net.corda.testing.internal.toDatabaseSchemaName
|
||||||
import net.corda.testing.node.internal.NodeBasedTest
|
import net.corda.testing.node.internal.NodeBasedTest
|
||||||
import net.corda.testing.node.startFlow
|
import net.corda.testing.node.startFlow
|
||||||
import org.apache.activemq.artemis.api.core.ActiveMQNonExistentQueueException
|
import org.apache.activemq.artemis.api.core.ActiveMQNonExistentQueueException
|
||||||
|
@ -17,14 +17,14 @@ import net.corda.node.internal.StartedNode
|
|||||||
import net.corda.node.services.messaging.MessagingService
|
import net.corda.node.services.messaging.MessagingService
|
||||||
import net.corda.node.services.messaging.ReceivedMessage
|
import net.corda.node.services.messaging.ReceivedMessage
|
||||||
import net.corda.node.services.messaging.send
|
import net.corda.node.services.messaging.send
|
||||||
import net.corda.node.services.transactions.RaftValidatingNotaryService
|
|
||||||
import net.corda.testing.*
|
|
||||||
import net.corda.node.services.messaging.*
|
|
||||||
import net.corda.testing.ALICE_NAME
|
import net.corda.testing.ALICE_NAME
|
||||||
import net.corda.testing.chooseIdentity
|
import net.corda.testing.chooseIdentity
|
||||||
import net.corda.testing.driver.DriverDSL
|
import net.corda.testing.driver.DriverDSL
|
||||||
import net.corda.testing.driver.NodeHandle
|
import net.corda.testing.driver.NodeHandle
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
|
import net.corda.testing.internal.IntegrationTest
|
||||||
|
import net.corda.testing.internal.IntegrationTestSchemas
|
||||||
|
import net.corda.testing.internal.toDatabaseSchemaName
|
||||||
import net.corda.testing.node.ClusterSpec
|
import net.corda.testing.node.ClusterSpec
|
||||||
import net.corda.testing.node.NotarySpec
|
import net.corda.testing.node.NotarySpec
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
@ -22,6 +22,10 @@ import net.corda.node.services.Permissions.Companion.startFlow
|
|||||||
import net.corda.nodeapi.internal.config.User
|
import net.corda.nodeapi.internal.config.User
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
|
import net.corda.testing.internal.IntegrationTest
|
||||||
|
import net.corda.testing.internal.IntegrationTestSchemas
|
||||||
|
import net.corda.testing.internal.toDatabaseSchemaName
|
||||||
|
import net.corda.testing.internal.toDatabaseSchemaNames
|
||||||
import org.junit.Assume.assumeFalse
|
import org.junit.Assume.assumeFalse
|
||||||
import org.junit.ClassRule
|
import org.junit.ClassRule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@ -36,7 +40,7 @@ class NodeStatePersistenceTests : IntegrationTest() {
|
|||||||
companion object {
|
companion object {
|
||||||
@ClassRule @JvmField
|
@ClassRule @JvmField
|
||||||
val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, DUMMY_BANK_A_NAME)
|
val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, DUMMY_BANK_A_NAME)
|
||||||
.map { it.toDatabaseSchemaNames("", "_10000","_10003","_10006") }.flatten().toTypedArray(),
|
.map { it.toDatabaseSchemaNames("", "_10000", "_10003", "_10006") }.flatten().toTypedArray(),
|
||||||
DUMMY_NOTARY_NAME.toDatabaseSchemaName())
|
DUMMY_NOTARY_NAME.toDatabaseSchemaName())
|
||||||
}
|
}
|
||||||
@Test
|
@Test
|
||||||
|
@ -726,7 +726,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
|||||||
val caKeyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword)
|
val caKeyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword)
|
||||||
val trustRoot = trustStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA)
|
val trustRoot = trustStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA)
|
||||||
val clientCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)
|
val clientCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)
|
||||||
val caCertificates = arrayOf(identityCert, clientCa.certificate.cert)
|
val caCertificates = arrayOf(identityCert, clientCa.certificate)
|
||||||
return PersistentIdentityService(trustRoot, *caCertificates)
|
return PersistentIdentityService(trustRoot, *caCertificates)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -784,7 +784,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
|||||||
listOf(certificate) + keyStore.getCertificateChain(privateKeyAlias).drop(1)
|
listOf(certificate) + keyStore.getCertificateChain(privateKeyAlias).drop(1)
|
||||||
} else {
|
} else {
|
||||||
keyStore.getCertificateChain(privateKeyAlias).let {
|
keyStore.getCertificateChain(privateKeyAlias).let {
|
||||||
check(it[0].toX509CertHolder() == x509Cert) { "Certificates from key store do not line up!" }
|
check(it[0] == x509Cert) { "Certificates from key store do not line up!" }
|
||||||
it.asList()
|
it.asList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ import io.netty.handler.ssl.SslHandler
|
|||||||
import io.netty.handler.ssl.SslHandshakeCompletionEvent
|
import io.netty.handler.ssl.SslHandshakeCompletionEvent
|
||||||
import io.netty.util.ReferenceCountUtil
|
import io.netty.util.ReferenceCountUtil
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.toX509CertHolder
|
|
||||||
import net.corda.core.utilities.debug
|
import net.corda.core.utilities.debug
|
||||||
import net.corda.node.internal.protonwrapper.engine.EventProcessor
|
import net.corda.node.internal.protonwrapper.engine.EventProcessor
|
||||||
import net.corda.node.internal.protonwrapper.messages.ReceivedMessage
|
import net.corda.node.internal.protonwrapper.messages.ReceivedMessage
|
||||||
@ -19,9 +18,9 @@ import org.apache.qpid.proton.engine.ProtonJTransport
|
|||||||
import org.apache.qpid.proton.engine.Transport
|
import org.apache.qpid.proton.engine.Transport
|
||||||
import org.apache.qpid.proton.engine.impl.ProtocolTracer
|
import org.apache.qpid.proton.engine.impl.ProtocolTracer
|
||||||
import org.apache.qpid.proton.framing.TransportFrame
|
import org.apache.qpid.proton.framing.TransportFrame
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import java.net.InetSocketAddress
|
import java.net.InetSocketAddress
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An instance of AMQPChannelHandler sits inside the netty pipeline and controls the socket level lifecycle.
|
* An instance of AMQPChannelHandler sits inside the netty pipeline and controls the socket level lifecycle.
|
||||||
@ -38,20 +37,20 @@ internal class AMQPChannelHandler(private val serverMode: Boolean,
|
|||||||
private val onReceive: (ReceivedMessage) -> Unit) : ChannelDuplexHandler() {
|
private val onReceive: (ReceivedMessage) -> Unit) : ChannelDuplexHandler() {
|
||||||
private val log = LoggerFactory.getLogger(allowedRemoteLegalNames?.firstOrNull()?.toString() ?: "AMQPChannelHandler")
|
private val log = LoggerFactory.getLogger(allowedRemoteLegalNames?.firstOrNull()?.toString() ?: "AMQPChannelHandler")
|
||||||
private lateinit var remoteAddress: InetSocketAddress
|
private lateinit var remoteAddress: InetSocketAddress
|
||||||
private lateinit var localCert: X509CertificateHolder
|
private lateinit var localCert: X509Certificate
|
||||||
private lateinit var remoteCert: X509CertificateHolder
|
private lateinit var remoteCert: X509Certificate
|
||||||
private var eventProcessor: EventProcessor? = null
|
private var eventProcessor: EventProcessor? = null
|
||||||
|
|
||||||
override fun channelActive(ctx: ChannelHandlerContext) {
|
override fun channelActive(ctx: ChannelHandlerContext) {
|
||||||
val ch = ctx.channel()
|
val ch = ctx.channel()
|
||||||
remoteAddress = ch.remoteAddress() as InetSocketAddress
|
remoteAddress = ch.remoteAddress() as InetSocketAddress
|
||||||
val localAddress = ch.localAddress() as InetSocketAddress
|
val localAddress = ch.localAddress() as InetSocketAddress
|
||||||
log.info("New client connection ${ch.id()} from ${remoteAddress} to ${localAddress}")
|
log.info("New client connection ${ch.id()} from $remoteAddress to $localAddress")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createAMQPEngine(ctx: ChannelHandlerContext) {
|
private fun createAMQPEngine(ctx: ChannelHandlerContext) {
|
||||||
val ch = ctx.channel()
|
val ch = ctx.channel()
|
||||||
eventProcessor = EventProcessor(ch, serverMode, localCert.subject.toString(), remoteCert.subject.toString(), userName, password)
|
eventProcessor = EventProcessor(ch, serverMode, localCert.subjectX500Principal.toString(), remoteCert.subjectX500Principal.toString(), userName, password)
|
||||||
val connection = eventProcessor!!.connection
|
val connection = eventProcessor!!.connection
|
||||||
val transport = connection.transport as ProtonJTransport
|
val transport = connection.transport as ProtonJTransport
|
||||||
if (trace) {
|
if (trace) {
|
||||||
@ -71,7 +70,7 @@ internal class AMQPChannelHandler(private val serverMode: Boolean,
|
|||||||
|
|
||||||
override fun channelInactive(ctx: ChannelHandlerContext) {
|
override fun channelInactive(ctx: ChannelHandlerContext) {
|
||||||
val ch = ctx.channel()
|
val ch = ctx.channel()
|
||||||
log.info("Closed client connection ${ch.id()} from ${remoteAddress} to ${ch.localAddress()}")
|
log.info("Closed client connection ${ch.id()} from $remoteAddress to ${ch.localAddress()}")
|
||||||
onClose(Pair(ch as SocketChannel, ConnectionChange(remoteAddress, null, false)))
|
onClose(Pair(ch as SocketChannel, ConnectionChange(remoteAddress, null, false)))
|
||||||
eventProcessor?.close()
|
eventProcessor?.close()
|
||||||
ctx.fireChannelInactive()
|
ctx.fireChannelInactive()
|
||||||
@ -81,12 +80,12 @@ internal class AMQPChannelHandler(private val serverMode: Boolean,
|
|||||||
if (evt is SslHandshakeCompletionEvent) {
|
if (evt is SslHandshakeCompletionEvent) {
|
||||||
if (evt.isSuccess) {
|
if (evt.isSuccess) {
|
||||||
val sslHandler = ctx.pipeline().get(SslHandler::class.java)
|
val sslHandler = ctx.pipeline().get(SslHandler::class.java)
|
||||||
localCert = sslHandler.engine().session.localCertificates.first().toX509CertHolder()
|
localCert = sslHandler.engine().session.localCertificates[0] as X509Certificate
|
||||||
remoteCert = sslHandler.engine().session.peerCertificates.first().toX509CertHolder()
|
remoteCert = sslHandler.engine().session.peerCertificates[0] as X509Certificate
|
||||||
try {
|
try {
|
||||||
val remoteX500Name = CordaX500Name.parse(remoteCert.subject.toString())
|
val remoteX500Name = CordaX500Name.build(remoteCert.subjectX500Principal)
|
||||||
require(allowedRemoteLegalNames == null || remoteX500Name in allowedRemoteLegalNames)
|
require(allowedRemoteLegalNames == null || remoteX500Name in allowedRemoteLegalNames)
|
||||||
log.info("handshake completed subject: ${remoteX500Name}")
|
log.info("handshake completed subject: $remoteX500Name")
|
||||||
} catch (ex: IllegalArgumentException) {
|
} catch (ex: IllegalArgumentException) {
|
||||||
log.error("Invalid certificate subject", ex)
|
log.error("Invalid certificate subject", ex)
|
||||||
ctx.close()
|
ctx.close()
|
||||||
@ -124,7 +123,7 @@ internal class AMQPChannelHandler(private val serverMode: Boolean,
|
|||||||
require(inetAddress == remoteAddress) {
|
require(inetAddress == remoteAddress) {
|
||||||
"Message for incorrect endpoint"
|
"Message for incorrect endpoint"
|
||||||
}
|
}
|
||||||
require(CordaX500Name.parse(msg.destinationLegalName) == CordaX500Name.parse(remoteCert.subject.toString())) {
|
require(CordaX500Name.parse(msg.destinationLegalName) == CordaX500Name.build(remoteCert.subjectX500Principal)) {
|
||||||
"Message for incorrect legal identity"
|
"Message for incorrect legal identity"
|
||||||
}
|
}
|
||||||
log.debug { "channel write ${msg.applicationProperties["_AMQ_DUPL_ID"]}" }
|
log.debug { "channel write ${msg.applicationProperties["_AMQ_DUPL_ID"]}" }
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package net.corda.node.internal.protonwrapper.netty
|
package net.corda.node.internal.protonwrapper.netty
|
||||||
|
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
|
||||||
import java.net.InetSocketAddress
|
import java.net.InetSocketAddress
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
|
|
||||||
data class ConnectionChange(val remoteAddress: InetSocketAddress, val remoteCert: X509CertificateHolder?, val connected: Boolean)
|
data class ConnectionChange(val remoteAddress: InetSocketAddress, val remoteCert: X509Certificate?, val connected: Boolean)
|
@ -11,7 +11,6 @@ import net.corda.core.identity.CordaX500Name
|
|||||||
import net.corda.core.internal.createDirectories
|
import net.corda.core.internal.createDirectories
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.internal.exists
|
import net.corda.core.internal.exists
|
||||||
import net.corda.core.internal.toX509CertHolder
|
|
||||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
import net.corda.nodeapi.internal.config.SSLConfiguration
|
||||||
import net.corda.nodeapi.internal.config.toProperties
|
import net.corda.nodeapi.internal.config.toProperties
|
||||||
import net.corda.nodeapi.internal.createDevKeyStores
|
import net.corda.nodeapi.internal.createDevKeyStores
|
||||||
@ -73,7 +72,7 @@ fun SSLConfiguration.configureDevKeyAndTrustStores(myLegalName: CordaX500Name) {
|
|||||||
}
|
}
|
||||||
if (!sslKeystore.exists() || !nodeKeystore.exists()) {
|
if (!sslKeystore.exists() || !nodeKeystore.exists()) {
|
||||||
val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
|
val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
|
||||||
val rootCert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA).toX509CertHolder()
|
val rootCert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA)
|
||||||
val intermediateCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
|
val intermediateCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
|
||||||
createDevKeyStores(rootCert, intermediateCa, myLegalName)
|
createDevKeyStores(rootCert, intermediateCa, myLegalName)
|
||||||
|
|
||||||
|
@ -203,8 +203,16 @@ data class SecurityConfiguration(val authService: SecurityConfiguration.AuthServ
|
|||||||
data class Options(val cache: Options.Cache?) {
|
data class Options(val cache: Options.Cache?) {
|
||||||
|
|
||||||
// Cache parameters
|
// Cache parameters
|
||||||
data class Cache(val expireAfterSecs: Long, val maxEntries: Long)
|
data class Cache(val expireAfterSecs: Long, val maxEntries: Long) {
|
||||||
|
init {
|
||||||
|
require(expireAfterSecs >= 0) {
|
||||||
|
"Expected positive value for 'cache.expireAfterSecs'"
|
||||||
|
}
|
||||||
|
require(maxEntries > 0) {
|
||||||
|
"Expected positive value for 'cache.maxEntries'"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Provider of users credentials and permissions data
|
// Provider of users credentials and permissions data
|
||||||
@ -228,12 +236,13 @@ data class SecurityConfiguration(val authService: SecurityConfiguration.AuthServ
|
|||||||
AuthDataSourceType.DB -> AuthServiceId("REMOTE_DATABASE")
|
AuthDataSourceType.DB -> AuthServiceId("REMOTE_DATABASE")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fromUsers(users: List<User>) = AuthService(
|
fun fromUsers(users: List<User>, encryption: PasswordEncryption = PasswordEncryption.NONE) =
|
||||||
dataSource = DataSource(
|
AuthService(
|
||||||
type = AuthDataSourceType.INMEMORY,
|
dataSource = DataSource(
|
||||||
users = users,
|
type = AuthDataSourceType.INMEMORY,
|
||||||
passwordEncryption = PasswordEncryption.NONE),
|
users = users,
|
||||||
id = AuthServiceId("NODE_CONFIG"))
|
passwordEncryption = encryption),
|
||||||
|
id = AuthServiceId("NODE_CONFIG"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,15 +4,12 @@ import net.corda.core.contracts.PartyAndReference
|
|||||||
import net.corda.core.crypto.toStringShort
|
import net.corda.core.crypto.toStringShort
|
||||||
import net.corda.core.identity.*
|
import net.corda.core.identity.*
|
||||||
import net.corda.core.internal.CertRole
|
import net.corda.core.internal.CertRole
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.internal.toX509CertHolder
|
|
||||||
import net.corda.core.node.services.UnknownAnonymousPartyException
|
import net.corda.core.node.services.UnknownAnonymousPartyException
|
||||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||||
import net.corda.core.utilities.contextLogger
|
import net.corda.core.utilities.contextLogger
|
||||||
import net.corda.core.utilities.trace
|
import net.corda.core.utilities.trace
|
||||||
import net.corda.node.services.api.IdentityServiceInternal
|
import net.corda.node.services.api.IdentityServiceInternal
|
||||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
|
||||||
import java.security.InvalidAlgorithmParameterException
|
import java.security.InvalidAlgorithmParameterException
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.security.cert.*
|
import java.security.cert.*
|
||||||
@ -27,7 +24,7 @@ import javax.annotation.concurrent.ThreadSafe
|
|||||||
// TODO There is duplicated logic between this and PersistentIdentityService
|
// TODO There is duplicated logic between this and PersistentIdentityService
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
class InMemoryIdentityService(identities: Array<out PartyAndCertificate>,
|
class InMemoryIdentityService(identities: Array<out PartyAndCertificate>,
|
||||||
trustRoot: X509CertificateHolder) : SingletonSerializeAsToken(), IdentityServiceInternal {
|
override val trustRoot: X509Certificate) : SingletonSerializeAsToken(), IdentityServiceInternal {
|
||||||
companion object {
|
companion object {
|
||||||
private val log = contextLogger()
|
private val log = contextLogger()
|
||||||
}
|
}
|
||||||
@ -35,14 +32,12 @@ class InMemoryIdentityService(identities: Array<out PartyAndCertificate>,
|
|||||||
/**
|
/**
|
||||||
* Certificate store for certificate authority and intermediary certificates.
|
* Certificate store for certificate authority and intermediary certificates.
|
||||||
*/
|
*/
|
||||||
override val caCertStore: CertStore
|
override val caCertStore: CertStore = CertStore.getInstance("Collection", CollectionCertStoreParameters(setOf(trustRoot)))
|
||||||
override val trustRoot = trustRoot.cert
|
override val trustAnchor: TrustAnchor = TrustAnchor(trustRoot, null)
|
||||||
override val trustAnchor: TrustAnchor = TrustAnchor(this.trustRoot, null)
|
|
||||||
private val keyToParties = ConcurrentHashMap<PublicKey, PartyAndCertificate>()
|
private val keyToParties = ConcurrentHashMap<PublicKey, PartyAndCertificate>()
|
||||||
private val principalToParties = ConcurrentHashMap<CordaX500Name, PartyAndCertificate>()
|
private val principalToParties = ConcurrentHashMap<CordaX500Name, PartyAndCertificate>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
caCertStore = CertStore.getInstance("Collection", CollectionCertStoreParameters(setOf(this.trustRoot)))
|
|
||||||
keyToParties.putAll(identities.associateBy { it.owningKey })
|
keyToParties.putAll(identities.associateBy { it.owningKey })
|
||||||
principalToParties.putAll(identities.associateBy { it.name })
|
principalToParties.putAll(identities.associateBy { it.name })
|
||||||
}
|
}
|
||||||
@ -57,7 +52,7 @@ class InMemoryIdentityService(identities: Array<out PartyAndCertificate>,
|
|||||||
log.warn("Certificate path :")
|
log.warn("Certificate path :")
|
||||||
identity.certPath.certificates.reversed().forEachIndexed { index, certificate ->
|
identity.certPath.certificates.reversed().forEachIndexed { index, certificate ->
|
||||||
val space = (0 until index).joinToString("") { " " }
|
val space = (0 until index).joinToString("") { " " }
|
||||||
log.warn("$space${certificate.toX509CertHolder().subject}")
|
log.warn("$space${(certificate as X509Certificate).subjectX500Principal}")
|
||||||
}
|
}
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,6 @@ import net.corda.core.crypto.SecureHash
|
|||||||
import net.corda.core.crypto.toStringShort
|
import net.corda.core.crypto.toStringShort
|
||||||
import net.corda.core.identity.*
|
import net.corda.core.identity.*
|
||||||
import net.corda.core.internal.CertRole
|
import net.corda.core.internal.CertRole
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.internal.toX509CertHolder
|
|
||||||
import net.corda.core.node.services.UnknownAnonymousPartyException
|
import net.corda.core.node.services.UnknownAnonymousPartyException
|
||||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||||
import net.corda.core.utilities.MAX_HASH_HEX_SIZE
|
import net.corda.core.utilities.MAX_HASH_HEX_SIZE
|
||||||
@ -16,7 +14,6 @@ import net.corda.node.services.api.IdentityServiceInternal
|
|||||||
import net.corda.node.utilities.AppendOnlyPersistentMap
|
import net.corda.node.utilities.AppendOnlyPersistentMap
|
||||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||||
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
|
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
|
||||||
import java.security.InvalidAlgorithmParameterException
|
import java.security.InvalidAlgorithmParameterException
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.security.cert.*
|
import java.security.cert.*
|
||||||
@ -30,7 +27,6 @@ import javax.persistence.Lob
|
|||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
class PersistentIdentityService(override val trustRoot: X509Certificate,
|
class PersistentIdentityService(override val trustRoot: X509Certificate,
|
||||||
vararg caCertificates: X509Certificate) : SingletonSerializeAsToken(), IdentityServiceInternal {
|
vararg caCertificates: X509Certificate) : SingletonSerializeAsToken(), IdentityServiceInternal {
|
||||||
constructor(trustRoot: X509CertificateHolder) : this(trustRoot.cert)
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val log = contextLogger()
|
private val log = contextLogger()
|
||||||
@ -121,7 +117,7 @@ class PersistentIdentityService(override val trustRoot: X509Certificate,
|
|||||||
log.warn(e.localizedMessage)
|
log.warn(e.localizedMessage)
|
||||||
log.warn("Path = ")
|
log.warn("Path = ")
|
||||||
identity.certPath.certificates.reversed().forEach {
|
identity.certPath.certificates.reversed().forEach {
|
||||||
log.warn(it.toX509CertHolder().subject.toString())
|
log.warn((it as X509Certificate).subjectX500Principal.toString())
|
||||||
}
|
}
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,6 @@ package net.corda.node.services.keys
|
|||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.identity.PartyAndCertificate
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
import net.corda.core.internal.CertRole
|
import net.corda.core.internal.CertRole
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.internal.toX509CertHolder
|
|
||||||
import net.corda.core.utilities.days
|
import net.corda.core.utilities.days
|
||||||
import net.corda.node.services.api.IdentityServiceInternal
|
import net.corda.node.services.api.IdentityServiceInternal
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
@ -36,11 +34,16 @@ fun freshCertificate(identityService: IdentityServiceInternal,
|
|||||||
revocationEnabled: Boolean = false): PartyAndCertificate {
|
revocationEnabled: Boolean = false): PartyAndCertificate {
|
||||||
val issuerRole = CertRole.extract(issuer.certificate)
|
val issuerRole = CertRole.extract(issuer.certificate)
|
||||||
require(issuerRole == CertRole.LEGAL_IDENTITY) { "Confidential identities can only be issued from well known identities, provided issuer ${issuer.name} has role $issuerRole" }
|
require(issuerRole == CertRole.LEGAL_IDENTITY) { "Confidential identities can only be issued from well known identities, provided issuer ${issuer.name} has role $issuerRole" }
|
||||||
val issuerCert = issuer.certificate.toX509CertHolder()
|
val issuerCert = issuer.certificate
|
||||||
val window = X509Utilities.getCertificateValidityWindow(Duration.ZERO, 3650.days, issuerCert)
|
val window = X509Utilities.getCertificateValidityWindow(Duration.ZERO, 3650.days, issuerCert)
|
||||||
val ourCertificate = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuerCert.subject,
|
val ourCertificate = X509Utilities.createCertificate(
|
||||||
issuerSigner, issuer.name, subjectPublicKey, window)
|
CertificateType.CONFIDENTIAL_LEGAL_IDENTITY,
|
||||||
val ourCertPath = X509CertificateFactory().generateCertPath(listOf(ourCertificate.cert) + issuer.certPath.certificates)
|
issuerCert.subjectX500Principal,
|
||||||
|
issuerSigner,
|
||||||
|
issuer.name.x500Principal,
|
||||||
|
subjectPublicKey,
|
||||||
|
window)
|
||||||
|
val ourCertPath = X509CertificateFactory().generateCertPath(listOf(ourCertificate) + issuer.certPath.certificates)
|
||||||
val anonymisedIdentity = PartyAndCertificate(ourCertPath)
|
val anonymisedIdentity = PartyAndCertificate(ourCertPath)
|
||||||
identityService.justVerifyAndRegisterIdentity(anonymisedIdentity)
|
identityService.justVerifyAndRegisterIdentity(anonymisedIdentity)
|
||||||
return anonymisedIdentity
|
return anonymisedIdentity
|
||||||
|
@ -40,6 +40,18 @@ class FlowLogicRefFactoryImpl(private val classloader: ClassLoader) : SingletonS
|
|||||||
return createForRPC(flowClass, *args)
|
return createForRPC(flowClass, *args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun create(flowClassName: String, vararg args: Any?): FlowLogicRef {
|
||||||
|
val flowClass = Class.forName(flowClassName, true, classloader).asSubclass(FlowLogic::class.java)
|
||||||
|
if (flowClass == null) {
|
||||||
|
throw IllegalArgumentException("The class $flowClassName is not a subclass of FlowLogic.")
|
||||||
|
} else {
|
||||||
|
if (!flowClass.isAnnotationPresent(SchedulableFlow::class.java)) {
|
||||||
|
throw IllegalFlowLogicException(flowClass, "because it's not a schedulable flow")
|
||||||
|
}
|
||||||
|
return createForRPC(flowClass, *args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun createForRPC(flowClass: Class<out FlowLogic<*>>, vararg args: Any?): FlowLogicRef {
|
override fun createForRPC(flowClass: Class<out FlowLogic<*>>, vararg args: Any?): FlowLogicRef {
|
||||||
// TODO: This is used via RPC but it's probably better if we pass in argument names and values explicitly
|
// TODO: This is used via RPC but it's probably better if we pass in argument names and values explicitly
|
||||||
// to avoid requiring only a single constructor.
|
// to avoid requiring only a single constructor.
|
||||||
|
@ -22,9 +22,9 @@ import java.security.cert.X509Certificate
|
|||||||
* needed.
|
* needed.
|
||||||
*/
|
*/
|
||||||
class NetworkRegistrationHelper(private val config: NodeConfiguration, private val certService: NetworkRegistrationService) {
|
class NetworkRegistrationHelper(private val config: NodeConfiguration, private val certService: NetworkRegistrationService) {
|
||||||
companion object {
|
private companion object {
|
||||||
val pollInterval = 10.seconds
|
val pollInterval = 10.seconds
|
||||||
val SELF_SIGNED_PRIVATE_KEY = "Self Signed Private Key"
|
const val SELF_SIGNED_PRIVATE_KEY = "Self Signed Private Key"
|
||||||
}
|
}
|
||||||
|
|
||||||
private val requestIdStore = config.certificatesDirectory / "certificate-request-id.txt"
|
private val requestIdStore = config.certificatesDirectory / "certificate-request-id.txt"
|
||||||
@ -62,54 +62,81 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v
|
|||||||
*/
|
*/
|
||||||
fun buildKeystore() {
|
fun buildKeystore() {
|
||||||
config.certificatesDirectory.createDirectories()
|
config.certificatesDirectory.createDirectories()
|
||||||
val caKeyStore = loadOrCreateKeyStore(config.nodeKeystore, keystorePassword)
|
val nodeKeyStore = loadOrCreateKeyStore(config.nodeKeystore, keystorePassword)
|
||||||
if (!caKeyStore.containsAlias(CORDA_CLIENT_CA)) {
|
if (nodeKeyStore.containsAlias(CORDA_CLIENT_CA)) {
|
||||||
// Create or load self signed keypair from the key store.
|
|
||||||
// We use the self sign certificate to store the key temporarily in the keystore while waiting for the request approval.
|
|
||||||
if (!caKeyStore.containsAlias(SELF_SIGNED_PRIVATE_KEY)) {
|
|
||||||
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
|
||||||
val selfSignCert = X509Utilities.createSelfSignedCACertificate(config.myLegalName, keyPair)
|
|
||||||
// Save to the key store.
|
|
||||||
caKeyStore.addOrReplaceKey(SELF_SIGNED_PRIVATE_KEY, keyPair.private, privateKeyPassword.toCharArray(),
|
|
||||||
arrayOf(selfSignCert))
|
|
||||||
caKeyStore.save(config.nodeKeystore, keystorePassword)
|
|
||||||
}
|
|
||||||
val keyPair = caKeyStore.getKeyPair(SELF_SIGNED_PRIVATE_KEY, privateKeyPassword)
|
|
||||||
val requestId = submitOrResumeCertificateSigningRequest(keyPair)
|
|
||||||
|
|
||||||
val certificates = try {
|
|
||||||
pollServerForCertificates(requestId)
|
|
||||||
} catch (certificateRequestException: CertificateRequestException) {
|
|
||||||
System.err.println(certificateRequestException.message)
|
|
||||||
System.err.println("Please make sure the details in configuration file are correct and try again.")
|
|
||||||
System.err.println("Corda node will now terminate.")
|
|
||||||
requestIdStore.deleteIfExists()
|
|
||||||
throw certificateRequestException
|
|
||||||
}
|
|
||||||
|
|
||||||
println("Certificate signing request approved, storing private key with the certificate chain.")
|
|
||||||
// Save private key and certificate chain to the key store.
|
|
||||||
caKeyStore.addOrReplaceKey(CORDA_CLIENT_CA, keyPair.private, privateKeyPassword.toCharArray(), certificates)
|
|
||||||
caKeyStore.deleteEntry(SELF_SIGNED_PRIVATE_KEY)
|
|
||||||
caKeyStore.save(config.nodeKeystore, keystorePassword)
|
|
||||||
println("Node private key and certificate stored in ${config.nodeKeystore}.")
|
|
||||||
|
|
||||||
println("Checking root of the certificate path is what we expect.")
|
|
||||||
X509Utilities.validateCertificateChain(rootCert, *certificates)
|
|
||||||
|
|
||||||
println("Generating SSL certificate for node messaging service.")
|
|
||||||
val sslKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
|
||||||
val caCert = caKeyStore.getX509Certificate(CORDA_CLIENT_CA).toX509CertHolder()
|
|
||||||
val sslCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, keyPair, CordaX500Name.build(caCert.cert.subjectX500Principal), sslKey.public)
|
|
||||||
val sslKeyStore = loadOrCreateKeyStore(config.sslKeystore, keystorePassword)
|
|
||||||
sslKeyStore.addOrReplaceKey(CORDA_CLIENT_TLS, sslKey.private, privateKeyPassword.toCharArray(), arrayOf(sslCert.cert, *certificates))
|
|
||||||
sslKeyStore.save(config.sslKeystore, config.keyStorePassword)
|
|
||||||
println("SSL private key and certificate stored in ${config.sslKeystore}.")
|
|
||||||
// All done, clean up temp files.
|
|
||||||
requestIdStore.deleteIfExists()
|
|
||||||
} else {
|
|
||||||
println("Certificate already exists, Corda node will now terminate...")
|
println("Certificate already exists, Corda node will now terminate...")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create or load self signed keypair from the key store.
|
||||||
|
// We use the self sign certificate to store the key temporarily in the keystore while waiting for the request approval.
|
||||||
|
if (!nodeKeyStore.containsAlias(SELF_SIGNED_PRIVATE_KEY)) {
|
||||||
|
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
|
val selfSignCert = X509Utilities.createSelfSignedCACertificate(config.myLegalName.x500Principal, keyPair)
|
||||||
|
// Save to the key store.
|
||||||
|
nodeKeyStore.addOrReplaceKey(SELF_SIGNED_PRIVATE_KEY, keyPair.private, privateKeyPassword.toCharArray(),
|
||||||
|
arrayOf(selfSignCert))
|
||||||
|
nodeKeyStore.save(config.nodeKeystore, keystorePassword)
|
||||||
|
}
|
||||||
|
|
||||||
|
val keyPair = nodeKeyStore.getKeyPair(SELF_SIGNED_PRIVATE_KEY, privateKeyPassword)
|
||||||
|
val requestId = submitOrResumeCertificateSigningRequest(keyPair)
|
||||||
|
|
||||||
|
val certificates = try {
|
||||||
|
pollServerForCertificates(requestId)
|
||||||
|
} catch (certificateRequestException: CertificateRequestException) {
|
||||||
|
System.err.println(certificateRequestException.message)
|
||||||
|
System.err.println("Please make sure the details in configuration file are correct and try again.")
|
||||||
|
System.err.println("Corda node will now terminate.")
|
||||||
|
requestIdStore.deleteIfExists()
|
||||||
|
throw certificateRequestException
|
||||||
|
}
|
||||||
|
|
||||||
|
val nodeCaCert = certificates[0] as X509Certificate
|
||||||
|
|
||||||
|
val nodeCaSubject = try {
|
||||||
|
CordaX500Name.build(nodeCaCert.subjectX500Principal)
|
||||||
|
} catch (e: IllegalArgumentException) {
|
||||||
|
throw CertificateRequestException("Received node CA cert has invalid subject name: ${e.message}")
|
||||||
|
}
|
||||||
|
if (nodeCaSubject != config.myLegalName) {
|
||||||
|
throw CertificateRequestException("Subject of received node CA cert doesn't match with node legal name: $nodeCaSubject")
|
||||||
|
}
|
||||||
|
|
||||||
|
val nodeCaCertRole = try {
|
||||||
|
CertRole.extract(nodeCaCert)
|
||||||
|
} catch (e: IllegalArgumentException) {
|
||||||
|
throw CertificateRequestException("Unable to extract cert role from received node CA cert: ${e.message}")
|
||||||
|
}
|
||||||
|
if (nodeCaCertRole != CertRole.NODE_CA) {
|
||||||
|
throw CertificateRequestException("Received node CA cert has invalid role: $nodeCaCertRole")
|
||||||
|
}
|
||||||
|
|
||||||
|
println("Checking root of the certificate path is what we expect.")
|
||||||
|
X509Utilities.validateCertificateChain(rootCert, *certificates)
|
||||||
|
|
||||||
|
println("Certificate signing request approved, storing private key with the certificate chain.")
|
||||||
|
// Save private key and certificate chain to the key store.
|
||||||
|
nodeKeyStore.addOrReplaceKey(CORDA_CLIENT_CA, keyPair.private, privateKeyPassword.toCharArray(), certificates)
|
||||||
|
nodeKeyStore.deleteEntry(SELF_SIGNED_PRIVATE_KEY)
|
||||||
|
nodeKeyStore.save(config.nodeKeystore, keystorePassword)
|
||||||
|
println("Node private key and certificate stored in ${config.nodeKeystore}.")
|
||||||
|
|
||||||
|
println("Generating SSL certificate for node messaging service.")
|
||||||
|
val sslKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
|
val sslCert = X509Utilities.createCertificate(
|
||||||
|
CertificateType.TLS,
|
||||||
|
nodeCaCert,
|
||||||
|
keyPair,
|
||||||
|
config.myLegalName.x500Principal,
|
||||||
|
sslKeyPair.public)
|
||||||
|
val sslKeyStore = loadOrCreateKeyStore(config.sslKeystore, keystorePassword)
|
||||||
|
sslKeyStore.addOrReplaceKey(CORDA_CLIENT_TLS, sslKeyPair.private, privateKeyPassword.toCharArray(), arrayOf(sslCert, *certificates))
|
||||||
|
sslKeyStore.save(config.sslKeystore, config.keyStorePassword)
|
||||||
|
println("SSL private key and certificate stored in ${config.sslKeystore}.")
|
||||||
|
|
||||||
|
// All done, clean up temp files.
|
||||||
|
requestIdStore.deleteIfExists()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -138,7 +165,7 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v
|
|||||||
private fun submitOrResumeCertificateSigningRequest(keyPair: KeyPair): String {
|
private fun submitOrResumeCertificateSigningRequest(keyPair: KeyPair): String {
|
||||||
// Retrieve request id from file if exists, else post a request to server.
|
// Retrieve request id from file if exists, else post a request to server.
|
||||||
return if (!requestIdStore.exists()) {
|
return if (!requestIdStore.exists()) {
|
||||||
val request = X509Utilities.createCertificateSigningRequest(config.myLegalName, config.emailAddress, keyPair)
|
val request = X509Utilities.createCertificateSigningRequest(config.myLegalName.x500Principal, config.emailAddress, keyPair)
|
||||||
val writer = StringWriter()
|
val writer = StringWriter()
|
||||||
JcaPEMWriter(writer).use {
|
JcaPEMWriter(writer).use {
|
||||||
it.writeObject(PemObject("CERTIFICATE REQUEST", request.encoded))
|
it.writeObject(PemObject("CERTIFICATE REQUEST", request.encoded))
|
||||||
|
@ -7,6 +7,7 @@ import net.corda.node.internal.security.Password
|
|||||||
import net.corda.node.internal.security.RPCSecurityManagerImpl
|
import net.corda.node.internal.security.RPCSecurityManagerImpl
|
||||||
import net.corda.node.internal.security.tryAuthenticate
|
import net.corda.node.internal.security.tryAuthenticate
|
||||||
import net.corda.nodeapi.internal.config.User
|
import net.corda.nodeapi.internal.config.User
|
||||||
|
import net.corda.node.services.config.SecurityConfiguration
|
||||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import javax.security.auth.login.FailedLoginException
|
import javax.security.auth.login.FailedLoginException
|
||||||
@ -26,7 +27,7 @@ class RPCSecurityManagerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Generic RPC call authorization`() {
|
fun `Generic RPC call authorization`() {
|
||||||
checkUserPermissions(
|
checkUserActions(
|
||||||
permitted = setOf(arrayListOf("nodeInfo"), arrayListOf("notaryIdentities")),
|
permitted = setOf(arrayListOf("nodeInfo"), arrayListOf("notaryIdentities")),
|
||||||
permissions = setOf(
|
permissions = setOf(
|
||||||
Permissions.invokeRpc(CordaRPCOps::nodeInfo),
|
Permissions.invokeRpc(CordaRPCOps::nodeInfo),
|
||||||
@ -35,7 +36,7 @@ class RPCSecurityManagerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Flow invocation authorization`() {
|
fun `Flow invocation authorization`() {
|
||||||
checkUserPermissions(
|
checkUserActions(
|
||||||
permissions = setOf(Permissions.startFlow<DummyFlow>()),
|
permissions = setOf(Permissions.startFlow<DummyFlow>()),
|
||||||
permitted = setOf(
|
permitted = setOf(
|
||||||
arrayListOf("startTrackedFlowDynamic", "net.corda.node.services.RPCSecurityManagerTest\$DummyFlow"),
|
arrayListOf("startTrackedFlowDynamic", "net.corda.node.services.RPCSecurityManagerTest\$DummyFlow"),
|
||||||
@ -44,21 +45,21 @@ class RPCSecurityManagerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Check startFlow RPC permission implies startFlowDynamic`() {
|
fun `Check startFlow RPC permission implies startFlowDynamic`() {
|
||||||
checkUserPermissions(
|
checkUserActions(
|
||||||
permissions = setOf(Permissions.invokeRpc("startFlow")),
|
permissions = setOf(Permissions.invokeRpc("startFlow")),
|
||||||
permitted = setOf(arrayListOf("startFlow"), arrayListOf("startFlowDynamic")))
|
permitted = setOf(arrayListOf("startFlow"), arrayListOf("startFlowDynamic")))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Check startTrackedFlow RPC permission implies startTrackedFlowDynamic`() {
|
fun `Check startTrackedFlow RPC permission implies startTrackedFlowDynamic`() {
|
||||||
checkUserPermissions(
|
checkUserActions(
|
||||||
permitted = setOf(arrayListOf("startTrackedFlow"), arrayListOf("startTrackedFlowDynamic")),
|
permitted = setOf(arrayListOf("startTrackedFlow"), arrayListOf("startTrackedFlowDynamic")),
|
||||||
permissions = setOf(Permissions.invokeRpc("startTrackedFlow")))
|
permissions = setOf(Permissions.invokeRpc("startTrackedFlow")))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Admin authorization`() {
|
fun `Admin authorization`() {
|
||||||
checkUserPermissions(
|
checkUserActions(
|
||||||
permissions = setOf("all"),
|
permissions = setOf("all"),
|
||||||
permitted = allActions.map { arrayListOf(it) }.toSet())
|
permitted = allActions.map { arrayListOf(it) }.toSet())
|
||||||
}
|
}
|
||||||
@ -118,9 +119,9 @@ class RPCSecurityManagerTest {
|
|||||||
users = listOf(User(username, "password", setOf())), id = AuthServiceId("TEST"))
|
users = listOf(User(username, "password", setOf())), id = AuthServiceId("TEST"))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkUserPermissions(permissions: Set<String>, permitted: Set<ArrayList<String>>) {
|
private fun checkUserActions(permissions: Set<String>, permitted: Set<ArrayList<String>>) {
|
||||||
val user = User(username = "user", password = "password", permissions = permissions)
|
val user = User(username = "user", password = "password", permissions = permissions)
|
||||||
val userRealms = RPCSecurityManagerImpl.fromUserList(users = listOf(user), id = AuthServiceId("TEST"))
|
val userRealms = RPCSecurityManagerImpl(SecurityConfiguration.AuthService.fromUsers(listOf(user)))
|
||||||
val disabled = allActions.filter { !permitted.contains(listOf(it)) }
|
val disabled = allActions.filter { !permitted.contains(listOf(it)) }
|
||||||
for (subject in listOf(
|
for (subject in listOf(
|
||||||
userRealms.authenticate("user", Password("password")),
|
userRealms.authenticate("user", Password("password")),
|
||||||
|
@ -30,6 +30,7 @@ import org.junit.Assert.*
|
|||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
import kotlin.reflect.jvm.jvmName
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class ScheduledFlowTests {
|
class ScheduledFlowTests {
|
||||||
@ -52,7 +53,7 @@ class ScheduledFlowTests {
|
|||||||
override val linearId: UniqueIdentifier = UniqueIdentifier()) : SchedulableState, LinearState {
|
override val linearId: UniqueIdentifier = UniqueIdentifier()) : SchedulableState, LinearState {
|
||||||
override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? {
|
override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? {
|
||||||
return if (!processed) {
|
return if (!processed) {
|
||||||
val logicRef = flowLogicRefFactory.create(ScheduledFlow::class.java, thisStateRef)
|
val logicRef = flowLogicRefFactory.create(ScheduledFlow::class.jvmName, thisStateRef)
|
||||||
ScheduledActivity(logicRef, creationTime)
|
ScheduledActivity(logicRef, creationTime)
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
|
@ -6,8 +6,6 @@ import net.corda.core.identity.AnonymousParty
|
|||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.identity.PartyAndCertificate
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.internal.toX509CertHolder
|
|
||||||
import net.corda.core.node.services.UnknownAnonymousPartyException
|
import net.corda.core.node.services.UnknownAnonymousPartyException
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||||
@ -32,7 +30,7 @@ class InMemoryIdentityServiceTests {
|
|||||||
val BOB get() = bob.party
|
val BOB get() = bob.party
|
||||||
val BOB_IDENTITY get() = bob.identity
|
val BOB_IDENTITY get() = bob.identity
|
||||||
val BOB_PUBKEY get() = bob.publicKey
|
val BOB_PUBKEY get() = bob.publicKey
|
||||||
fun createService(vararg identities: PartyAndCertificate) = InMemoryIdentityService(identities, DEV_TRUST_ROOT)
|
fun createService(vararg identities: PartyAndCertificate) = InMemoryIdentityService(identities, DEV_ROOT_CA.certificate)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
@ -100,11 +98,11 @@ class InMemoryIdentityServiceTests {
|
|||||||
@Test
|
@Test
|
||||||
fun `assert unknown anonymous key is unrecognised`() {
|
fun `assert unknown anonymous key is unrecognised`() {
|
||||||
val rootKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val rootKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootKey)
|
val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Principal, rootKey)
|
||||||
val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val service = createService()
|
val service = createService()
|
||||||
// TODO: Generate certificate with an EdDSA key rather than ECDSA
|
// TODO: Generate certificate with an EdDSA key rather than ECDSA
|
||||||
val identity = Party(rootCert.cert)
|
val identity = Party(rootCert)
|
||||||
val txIdentity = AnonymousParty(txKey.public)
|
val txIdentity = AnonymousParty(txKey.public)
|
||||||
|
|
||||||
assertFailsWith<UnknownAnonymousPartyException> {
|
assertFailsWith<UnknownAnonymousPartyException> {
|
||||||
@ -159,8 +157,8 @@ class InMemoryIdentityServiceTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
assertFailsWith<IllegalArgumentException> {
|
assertFailsWith<IllegalArgumentException> {
|
||||||
val owningKey = Crypto.decodePublicKey(DEV_CA.certificate.subjectPublicKeyInfo.encoded)
|
val owningKey = DEV_INTERMEDIATE_CA.certificate.publicKey
|
||||||
val subject = CordaX500Name.build(DEV_CA.certificate.cert.subjectX500Principal)
|
val subject = CordaX500Name.build(DEV_INTERMEDIATE_CA.certificate.subjectX500Principal)
|
||||||
service.assertOwnership(Party(subject, owningKey), anonymousAlice.party.anonymise())
|
service.assertOwnership(Party(subject, owningKey), anonymousAlice.party.anonymise())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -168,9 +166,14 @@ class InMemoryIdentityServiceTests {
|
|||||||
private fun createConfidentialIdentity(x500Name: CordaX500Name): Pair<PartyAndCertificate, PartyAndCertificate> {
|
private fun createConfidentialIdentity(x500Name: CordaX500Name): Pair<PartyAndCertificate, PartyAndCertificate> {
|
||||||
val issuerKeyPair = generateKeyPair()
|
val issuerKeyPair = generateKeyPair()
|
||||||
val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public)
|
val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public)
|
||||||
val txKey = Crypto.generateKeyPair()
|
val txKeyPair = Crypto.generateKeyPair()
|
||||||
val txCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuer.certificate.toX509CertHolder(), issuerKeyPair, x500Name, txKey.public)
|
val txCert = X509Utilities.createCertificate(
|
||||||
val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates)
|
CertificateType.CONFIDENTIAL_LEGAL_IDENTITY,
|
||||||
|
issuer.certificate,
|
||||||
|
issuerKeyPair,
|
||||||
|
x500Name.x500Principal,
|
||||||
|
txKeyPair.public)
|
||||||
|
val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert) + issuer.certPath.certificates)
|
||||||
return Pair(issuer, PartyAndCertificate(txCertPath))
|
return Pair(issuer, PartyAndCertificate(txCertPath))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,8 +6,6 @@ import net.corda.core.identity.AnonymousParty
|
|||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.identity.PartyAndCertificate
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.internal.toX509CertHolder
|
|
||||||
import net.corda.core.node.services.IdentityService
|
import net.corda.core.node.services.IdentityService
|
||||||
import net.corda.core.node.services.UnknownAnonymousPartyException
|
import net.corda.core.node.services.UnknownAnonymousPartyException
|
||||||
import net.corda.node.internal.configureDatabase
|
import net.corda.node.internal.configureDatabase
|
||||||
@ -50,7 +48,7 @@ class PersistentIdentityServiceTests {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
identityService = PersistentIdentityService(DEV_TRUST_ROOT)
|
identityService = PersistentIdentityService(DEV_ROOT_CA.certificate)
|
||||||
database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), identityService)
|
database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), identityService)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,9 +141,9 @@ class PersistentIdentityServiceTests {
|
|||||||
@Test
|
@Test
|
||||||
fun `assert unknown anonymous key is unrecognised`() {
|
fun `assert unknown anonymous key is unrecognised`() {
|
||||||
val rootKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val rootKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootKey)
|
val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Principal, rootKey)
|
||||||
val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_IDENTITY_SIGNATURE_SCHEME)
|
val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_IDENTITY_SIGNATURE_SCHEME)
|
||||||
val identity = Party(rootCert.cert)
|
val identity = Party(rootCert)
|
||||||
val txIdentity = AnonymousParty(txKey.public)
|
val txIdentity = AnonymousParty(txKey.public)
|
||||||
|
|
||||||
assertFailsWith<UnknownAnonymousPartyException> {
|
assertFailsWith<UnknownAnonymousPartyException> {
|
||||||
@ -219,9 +217,9 @@ class PersistentIdentityServiceTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
assertFailsWith<IllegalArgumentException> {
|
assertFailsWith<IllegalArgumentException> {
|
||||||
val owningKey = Crypto.decodePublicKey(DEV_CA.certificate.subjectPublicKeyInfo.encoded)
|
val owningKey = DEV_INTERMEDIATE_CA.certificate.publicKey
|
||||||
database.transaction {
|
database.transaction {
|
||||||
val subject = CordaX500Name.build(DEV_CA.certificate.cert.subjectX500Principal)
|
val subject = CordaX500Name.build(DEV_INTERMEDIATE_CA.certificate.subjectX500Principal)
|
||||||
identityService.assertOwnership(Party(subject, owningKey), anonymousAlice.party.anonymise())
|
identityService.assertOwnership(Party(subject, owningKey), anonymousAlice.party.anonymise())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -243,7 +241,7 @@ class PersistentIdentityServiceTests {
|
|||||||
|
|
||||||
// Create new identity service mounted onto same DB
|
// Create new identity service mounted onto same DB
|
||||||
val newPersistentIdentityService = database.transaction {
|
val newPersistentIdentityService = database.transaction {
|
||||||
PersistentIdentityService(DEV_TRUST_ROOT)
|
PersistentIdentityService(DEV_ROOT_CA.certificate)
|
||||||
}
|
}
|
||||||
|
|
||||||
database.transaction {
|
database.transaction {
|
||||||
@ -266,8 +264,8 @@ class PersistentIdentityServiceTests {
|
|||||||
val issuerKeyPair = generateKeyPair()
|
val issuerKeyPair = generateKeyPair()
|
||||||
val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public)
|
val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public)
|
||||||
val txKey = Crypto.generateKeyPair()
|
val txKey = Crypto.generateKeyPair()
|
||||||
val txCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuer.certificate.toX509CertHolder(), issuerKeyPair, x500Name, txKey.public)
|
val txCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuer.certificate, issuerKeyPair, x500Name.x500Principal, txKey.public)
|
||||||
val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates)
|
val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert) + issuer.certPath.certificates)
|
||||||
return Pair(issuer, PartyAndCertificate(txCertPath))
|
return Pair(issuer, PartyAndCertificate(txCertPath))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,12 +2,11 @@ package net.corda.node.services.network
|
|||||||
|
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.sha256
|
import net.corda.core.crypto.sha256
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.utilities.seconds
|
import net.corda.core.utilities.seconds
|
||||||
import net.corda.testing.ALICE_NAME
|
import net.corda.testing.ALICE_NAME
|
||||||
import net.corda.testing.BOB_NAME
|
import net.corda.testing.BOB_NAME
|
||||||
import net.corda.testing.DEV_TRUST_ROOT
|
import net.corda.testing.DEV_ROOT_CA
|
||||||
import net.corda.testing.SerializationEnvironmentRule
|
import net.corda.testing.SerializationEnvironmentRule
|
||||||
import net.corda.testing.driver.PortAllocation
|
import net.corda.testing.driver.PortAllocation
|
||||||
import net.corda.testing.internal.createNodeInfoAndSigned
|
import net.corda.testing.internal.createNodeInfoAndSigned
|
||||||
@ -35,7 +34,7 @@ class NetworkMapClientTest {
|
|||||||
fun setUp() {
|
fun setUp() {
|
||||||
server = NetworkMapServer(cacheTimeout, PortAllocation.Incremental(10000).nextHostAndPort())
|
server = NetworkMapServer(cacheTimeout, PortAllocation.Incremental(10000).nextHostAndPort())
|
||||||
val hostAndPort = server.start()
|
val hostAndPort = server.start()
|
||||||
networkMapClient = NetworkMapClient(URL("http://${hostAndPort.host}:${hostAndPort.port}"), DEV_TRUST_ROOT.cert)
|
networkMapClient = NetworkMapClient(URL("http://${hostAndPort.host}:${hostAndPort.port}"), DEV_ROOT_CA.certificate)
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
@ -1,17 +1,20 @@
|
|||||||
package net.corda.node.services.events
|
package net.corda.node.services.statemachine
|
||||||
|
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.flows.IllegalFlowLogicException
|
import net.corda.core.flows.IllegalFlowLogicException
|
||||||
import net.corda.node.services.statemachine.FlowLogicRefFactoryImpl
|
import net.corda.core.flows.SchedulableFlow
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
|
import kotlin.reflect.jvm.jvmName
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class FlowLogicRefTest {
|
class FlowLogicRefFactoryImplTest {
|
||||||
|
|
||||||
data class ParamType1(val value: Int)
|
data class ParamType1(val value: Int)
|
||||||
data class ParamType2(val value: String)
|
data class ParamType2(val value: String)
|
||||||
|
|
||||||
@Suppress("UNUSED_PARAMETER", "unused") // Things are used via reflection.
|
@Suppress("UNUSED_PARAMETER", "unused") // Things are used via reflection.
|
||||||
|
@SchedulableFlow
|
||||||
class KotlinFlowLogic(A: ParamType1, b: ParamType2) : FlowLogic<Unit>() {
|
class KotlinFlowLogic(A: ParamType1, b: ParamType2) : FlowLogic<Unit>() {
|
||||||
constructor() : this(ParamType1(1), ParamType2("2"))
|
constructor() : this(ParamType1(1), ParamType2("2"))
|
||||||
|
|
||||||
@ -26,6 +29,7 @@ class FlowLogicRefTest {
|
|||||||
override fun call() = Unit
|
override fun call() = Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SchedulableFlow
|
||||||
class KotlinNoArgFlowLogic : FlowLogic<Unit>() {
|
class KotlinNoArgFlowLogic : FlowLogic<Unit>() {
|
||||||
override fun call() = Unit
|
override fun call() = Unit
|
||||||
}
|
}
|
||||||
@ -37,18 +41,18 @@ class FlowLogicRefTest {
|
|||||||
private val flowLogicRefFactory = FlowLogicRefFactoryImpl(FlowLogicRefFactoryImpl::class.java.classLoader)
|
private val flowLogicRefFactory = FlowLogicRefFactoryImpl(FlowLogicRefFactoryImpl::class.java.classLoader)
|
||||||
@Test
|
@Test
|
||||||
fun `create kotlin no arg`() {
|
fun `create kotlin no arg`() {
|
||||||
flowLogicRefFactory.createForRPC(KotlinNoArgFlowLogic::class.java)
|
flowLogicRefFactory.create(KotlinNoArgFlowLogic::class.jvmName)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `create kotlin`() {
|
fun `should create kotlin types`() {
|
||||||
val args = mapOf(Pair("A", ParamType1(1)), Pair("b", ParamType2("Hello Jack")))
|
val args = mapOf(Pair("A", ParamType1(1)), Pair("b", ParamType2("Hello Jack")))
|
||||||
flowLogicRefFactory.createKotlin(KotlinFlowLogic::class.java, args)
|
flowLogicRefFactory.createKotlin(KotlinFlowLogic::class.java, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `create primary`() {
|
fun `create primary`() {
|
||||||
flowLogicRefFactory.createForRPC(KotlinFlowLogic::class.java, ParamType1(1), ParamType2("Hello Jack"))
|
flowLogicRefFactory.create(KotlinFlowLogic::class.jvmName, ParamType1(1), ParamType2("Hello Jack"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -76,6 +80,6 @@ class FlowLogicRefTest {
|
|||||||
|
|
||||||
@Test(expected = IllegalFlowLogicException::class)
|
@Test(expected = IllegalFlowLogicException::class)
|
||||||
fun `create for non-schedulable flow logic`() {
|
fun `create for non-schedulable flow logic`() {
|
||||||
flowLogicRefFactory.create(NonSchedulableFlow::class.java)
|
flowLogicRefFactory.create(NonSchedulableFlow::class.jvmName)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -24,6 +24,7 @@ import net.corda.testing.node.startFlow
|
|||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
|
import org.junit.Ignore
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -99,6 +100,7 @@ class NotaryServiceTests {
|
|||||||
assertThat(ex.error).isInstanceOf(NotaryError.TimeWindowInvalid::class.java)
|
assertThat(ex.error).isInstanceOf(NotaryError.TimeWindowInvalid::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Ignore("Only applies to deterministic signature schemes (e.g. EdDSA) and when deterministic metadata is attached (no timestamps or nonces)")
|
||||||
@Test
|
@Test
|
||||||
fun `should sign identical transaction multiple times (signing is idempotent)`() {
|
fun `should sign identical transaction multiple times (signing is idempotent)`() {
|
||||||
val stx = run {
|
val stx = run {
|
||||||
|
@ -18,6 +18,7 @@ import java.net.ServerSocket
|
|||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.security.KeyStore
|
import java.security.KeyStore
|
||||||
import javax.net.ssl.*
|
import javax.net.ssl.*
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
|
|
||||||
@ -51,9 +52,9 @@ class TLSAuthenticationTests {
|
|||||||
val tempFolder: TemporaryFolder = TemporaryFolder()
|
val tempFolder: TemporaryFolder = TemporaryFolder()
|
||||||
|
|
||||||
// Root CA.
|
// Root CA.
|
||||||
private val ROOT_X500 = CordaX500Name(commonName = "Root_CA_1", organisation = "R3CEV", locality = "London", country = "GB")
|
private val ROOT_X500 = X500Principal("CN=Root_CA_1,O=R3CEV,L=London,C=GB")
|
||||||
// Intermediate CA.
|
// Intermediate CA.
|
||||||
private val INTERMEDIATE_X500 = CordaX500Name(commonName = "Intermediate_CA_1", organisation = "R3CEV", locality = "London", country = "GB")
|
private val INTERMEDIATE_X500 = X500Principal("CN=Intermediate_CA_1,O=R3CEV,L=London,C=GB")
|
||||||
// TLS server (client1).
|
// TLS server (client1).
|
||||||
private val CLIENT_1_X500 = CordaX500Name(commonName = "Client_1", organisation = "R3CEV", locality = "London", country = "GB")
|
private val CLIENT_1_X500 = CordaX500Name(commonName = "Client_1", organisation = "R3CEV", locality = "London", country = "GB")
|
||||||
// TLS client (client2).
|
// TLS client (client2).
|
||||||
@ -274,7 +275,7 @@ class TLSAuthenticationTests {
|
|||||||
CertificateType.NODE_CA,
|
CertificateType.NODE_CA,
|
||||||
intermediateCACert,
|
intermediateCACert,
|
||||||
intermediateCAKeyPair,
|
intermediateCAKeyPair,
|
||||||
CLIENT_1_X500,
|
CLIENT_1_X500.x500Principal,
|
||||||
client1CAKeyPair.public
|
client1CAKeyPair.public
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -283,7 +284,7 @@ class TLSAuthenticationTests {
|
|||||||
CertificateType.TLS,
|
CertificateType.TLS,
|
||||||
client1CACert,
|
client1CACert,
|
||||||
client1CAKeyPair,
|
client1CAKeyPair,
|
||||||
CLIENT_1_X500,
|
CLIENT_1_X500.x500Principal,
|
||||||
client1TLSKeyPair.public
|
client1TLSKeyPair.public
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -301,7 +302,7 @@ class TLSAuthenticationTests {
|
|||||||
CertificateType.NODE_CA,
|
CertificateType.NODE_CA,
|
||||||
intermediateCACert,
|
intermediateCACert,
|
||||||
intermediateCAKeyPair,
|
intermediateCAKeyPair,
|
||||||
CLIENT_2_X500,
|
CLIENT_2_X500.x500Principal,
|
||||||
client2CAKeyPair.public
|
client2CAKeyPair.public
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -310,7 +311,7 @@ class TLSAuthenticationTests {
|
|||||||
CertificateType.TLS,
|
CertificateType.TLS,
|
||||||
client2CACert,
|
client2CACert,
|
||||||
client2CAKeyPair,
|
client2CAKeyPair,
|
||||||
CLIENT_2_X500,
|
CLIENT_2_X500.x500Principal,
|
||||||
client2TLSKeyPair.public
|
client2TLSKeyPair.public
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -323,8 +324,8 @@ class TLSAuthenticationTests {
|
|||||||
// client2TLSKeyStore.save(client2TLSKeyStorePath, PASSWORD)
|
// client2TLSKeyStore.save(client2TLSKeyStorePath, PASSWORD)
|
||||||
|
|
||||||
val trustStore = loadOrCreateKeyStore(trustStorePath, PASSWORD)
|
val trustStore = loadOrCreateKeyStore(trustStorePath, PASSWORD)
|
||||||
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCACert.cert)
|
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCACert)
|
||||||
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_INTERMEDIATE_CA, intermediateCACert.cert)
|
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_INTERMEDIATE_CA, intermediateCACert)
|
||||||
// trustStore.save(trustStorePath, PASSWORD)
|
// trustStore.save(trustStorePath, PASSWORD)
|
||||||
|
|
||||||
val client1SSLContext = sslContext(client1TLSKeyStore, PASSWORD, trustStore)
|
val client1SSLContext = sslContext(client1TLSKeyStore, PASSWORD, trustStore)
|
||||||
|
@ -9,21 +9,23 @@ import com.nhaarman.mockito_kotlin.whenever
|
|||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.internal.createDirectories
|
import net.corda.core.internal.createDirectories
|
||||||
|
import net.corda.core.internal.x500Name
|
||||||
import net.corda.node.services.config.NodeConfiguration
|
import net.corda.node.services.config.NodeConfiguration
|
||||||
import net.corda.nodeapi.internal.crypto.*
|
import net.corda.nodeapi.internal.crypto.*
|
||||||
import net.corda.testing.ALICE_NAME
|
import net.corda.testing.ALICE_NAME
|
||||||
import net.corda.testing.internal.createDevNodeCaCertPath
|
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
||||||
import net.corda.testing.internal.rigorousMock
|
import net.corda.testing.internal.rigorousMock
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.*
|
||||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
import org.bouncycastle.asn1.x509.GeneralName
|
||||||
|
import org.bouncycastle.asn1.x509.GeneralSubtree
|
||||||
|
import org.bouncycastle.asn1.x509.NameConstraints
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.security.cert.CertPathValidatorException
|
import java.security.cert.CertPathValidatorException
|
||||||
import java.security.cert.Certificate
|
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
import kotlin.test.assertFalse
|
import kotlin.test.assertFalse
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
@ -32,18 +34,10 @@ class NetworkRegistrationHelperTest {
|
|||||||
private val requestId = SecureHash.randomSHA256().toString()
|
private val requestId = SecureHash.randomSHA256().toString()
|
||||||
private val nodeLegalName = ALICE_NAME
|
private val nodeLegalName = ALICE_NAME
|
||||||
|
|
||||||
private lateinit var rootCaCert: X509Certificate
|
|
||||||
private lateinit var intermediateCaCert: X509Certificate
|
|
||||||
private lateinit var nodeCaCert: X509Certificate
|
|
||||||
private lateinit var config: NodeConfiguration
|
private lateinit var config: NodeConfiguration
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun init() {
|
fun init() {
|
||||||
val (rootCa, intermediateCa, nodeCa) = createDevNodeCaCertPath(nodeLegalName)
|
|
||||||
this.rootCaCert = rootCa.certificate.cert
|
|
||||||
this.intermediateCaCert = intermediateCa.certificate.cert
|
|
||||||
this.nodeCaCert = nodeCa.certificate.cert
|
|
||||||
|
|
||||||
val baseDirectory = fs.getPath("/baseDir").createDirectories()
|
val baseDirectory = fs.getPath("/baseDir").createDirectories()
|
||||||
abstract class AbstractNodeConfiguration : NodeConfiguration
|
abstract class AbstractNodeConfiguration : NodeConfiguration
|
||||||
config = rigorousMock<AbstractNodeConfiguration>().also {
|
config = rigorousMock<AbstractNodeConfiguration>().also {
|
||||||
@ -66,9 +60,10 @@ class NetworkRegistrationHelperTest {
|
|||||||
assertThat(config.sslKeystore).doesNotExist()
|
assertThat(config.sslKeystore).doesNotExist()
|
||||||
assertThat(config.trustStoreFile).doesNotExist()
|
assertThat(config.trustStoreFile).doesNotExist()
|
||||||
|
|
||||||
saveTrustStoreWithRootCa(rootCaCert)
|
val nodeCaCertPath = createNodeCaCertPath()
|
||||||
|
|
||||||
createRegistrationHelper().buildKeystore()
|
saveTrustStoreWithRootCa(nodeCaCertPath.last())
|
||||||
|
createRegistrationHelper(nodeCaCertPath).buildKeystore()
|
||||||
|
|
||||||
val nodeKeystore = loadKeyStore(config.nodeKeystore, config.keyStorePassword)
|
val nodeKeystore = loadKeyStore(config.nodeKeystore, config.keyStorePassword)
|
||||||
val sslKeystore = loadKeyStore(config.sslKeystore, config.keyStorePassword)
|
val sslKeystore = loadKeyStore(config.sslKeystore, config.keyStorePassword)
|
||||||
@ -79,8 +74,7 @@ class NetworkRegistrationHelperTest {
|
|||||||
assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA))
|
assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA))
|
||||||
assertFalse(containsAlias(X509Utilities.CORDA_ROOT_CA))
|
assertFalse(containsAlias(X509Utilities.CORDA_ROOT_CA))
|
||||||
assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_TLS))
|
assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_TLS))
|
||||||
val nodeCaCertChain = getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
|
assertThat(getCertificateChain(X509Utilities.CORDA_CLIENT_CA)).containsExactly(*nodeCaCertPath)
|
||||||
assertThat(nodeCaCertChain).containsExactly(nodeCaCert, intermediateCaCert, rootCaCert)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sslKeystore.run {
|
sslKeystore.run {
|
||||||
@ -92,40 +86,77 @@ class NetworkRegistrationHelperTest {
|
|||||||
assertThat(nodeTlsCertChain).hasSize(4)
|
assertThat(nodeTlsCertChain).hasSize(4)
|
||||||
// The TLS cert has the same subject as the node CA cert
|
// The TLS cert has the same subject as the node CA cert
|
||||||
assertThat(CordaX500Name.build((nodeTlsCertChain[0] as X509Certificate).subjectX500Principal)).isEqualTo(nodeLegalName)
|
assertThat(CordaX500Name.build((nodeTlsCertChain[0] as X509Certificate).subjectX500Principal)).isEqualTo(nodeLegalName)
|
||||||
assertThat(nodeTlsCertChain.drop(1)).containsExactly(nodeCaCert, intermediateCaCert, rootCaCert)
|
assertThat(nodeTlsCertChain.drop(1)).containsExactly(*nodeCaCertPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
trustStore.run {
|
trustStore.run {
|
||||||
assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_CA))
|
assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_CA))
|
||||||
assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA))
|
assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA))
|
||||||
assertTrue(containsAlias(X509Utilities.CORDA_ROOT_CA))
|
assertTrue(containsAlias(X509Utilities.CORDA_ROOT_CA))
|
||||||
val trustStoreRootCaCert = getCertificate(X509Utilities.CORDA_ROOT_CA)
|
assertThat(getCertificate(X509Utilities.CORDA_ROOT_CA)).isEqualTo(nodeCaCertPath.last())
|
||||||
assertThat(trustStoreRootCaCert).isEqualTo(rootCaCert)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `missing truststore`() {
|
fun `missing truststore`() {
|
||||||
|
val nodeCaCertPath = createNodeCaCertPath()
|
||||||
assertThatThrownBy {
|
assertThatThrownBy {
|
||||||
createRegistrationHelper()
|
createRegistrationHelper(nodeCaCertPath)
|
||||||
}.hasMessageContaining("This file must contain the root CA cert of your compatibility zone. Please contact your CZ operator.")
|
}.hasMessageContaining("This file must contain the root CA cert of your compatibility zone. Please contact your CZ operator.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `node CA with incorrect cert role`() {
|
||||||
|
val nodeCaCertPath = createNodeCaCertPath(type = CertificateType.TLS)
|
||||||
|
saveTrustStoreWithRootCa(nodeCaCertPath.last())
|
||||||
|
val registrationHelper = createRegistrationHelper(nodeCaCertPath)
|
||||||
|
assertThatExceptionOfType(CertificateRequestException::class.java)
|
||||||
|
.isThrownBy { registrationHelper.buildKeystore() }
|
||||||
|
.withMessageContaining(CertificateType.TLS.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `node CA with incorrect subject`() {
|
||||||
|
val invalidName = CordaX500Name("Foo", "MU", "GB")
|
||||||
|
val nodeCaCertPath = createNodeCaCertPath(legalName = invalidName)
|
||||||
|
saveTrustStoreWithRootCa(nodeCaCertPath.last())
|
||||||
|
val registrationHelper = createRegistrationHelper(nodeCaCertPath)
|
||||||
|
assertThatExceptionOfType(CertificateRequestException::class.java)
|
||||||
|
.isThrownBy { registrationHelper.buildKeystore() }
|
||||||
|
.withMessageContaining(invalidName.toString())
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `wrong root cert in truststore`() {
|
fun `wrong root cert in truststore`() {
|
||||||
val rootKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val wrongRootCert = X509Utilities.createSelfSignedCACertificate(
|
||||||
val rootCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name("Foo", "MU", "GB"), rootKeyPair)
|
X500Principal("O=Foo,L=MU,C=GB"),
|
||||||
saveTrustStoreWithRootCa(rootCert.cert)
|
Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
|
||||||
val registrationHelper = createRegistrationHelper()
|
saveTrustStoreWithRootCa(wrongRootCert)
|
||||||
|
val registrationHelper = createRegistrationHelper(createNodeCaCertPath())
|
||||||
assertThatThrownBy {
|
assertThatThrownBy {
|
||||||
registrationHelper.buildKeystore()
|
registrationHelper.buildKeystore()
|
||||||
}.isInstanceOf(CertPathValidatorException::class.java)
|
}.isInstanceOf(CertPathValidatorException::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createRegistrationHelper(): NetworkRegistrationHelper {
|
private fun createNodeCaCertPath(type: CertificateType = CertificateType.NODE_CA,
|
||||||
|
legalName: CordaX500Name = nodeLegalName): Array<X509Certificate> {
|
||||||
|
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
|
||||||
|
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
|
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.x500Name))), arrayOf())
|
||||||
|
val nodeCaCert = X509Utilities.createCertificate(
|
||||||
|
type,
|
||||||
|
intermediateCa.certificate,
|
||||||
|
intermediateCa.keyPair,
|
||||||
|
legalName.x500Principal,
|
||||||
|
keyPair.public,
|
||||||
|
nameConstraints = nameConstraints)
|
||||||
|
return arrayOf(nodeCaCert, intermediateCa.certificate, rootCa.certificate)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createRegistrationHelper(response: Array<X509Certificate>): NetworkRegistrationHelper {
|
||||||
val certService = rigorousMock<NetworkRegistrationService>().also {
|
val certService = rigorousMock<NetworkRegistrationService>().also {
|
||||||
doReturn(requestId).whenever(it).submitRequest(any())
|
doReturn(requestId).whenever(it).submitRequest(any())
|
||||||
doReturn(arrayOf<Certificate>(nodeCaCert, intermediateCaCert, rootCaCert)).whenever(it).retrieveCertificates(eq(requestId))
|
doReturn(response).whenever(it).retrieveCertificates(eq(requestId))
|
||||||
}
|
}
|
||||||
return NetworkRegistrationHelper(config, certService)
|
return NetworkRegistrationHelper(config, certService)
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,9 @@ import net.corda.nodeapi.internal.config.User
|
|||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
import net.corda.testing.driver.PortAllocation
|
import net.corda.testing.driver.PortAllocation
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
|
import net.corda.testing.internal.IntegrationTest
|
||||||
|
import net.corda.testing.internal.IntegrationTestSchemas
|
||||||
|
import net.corda.testing.internal.toDatabaseSchemaName
|
||||||
import org.junit.ClassRule
|
import org.junit.ClassRule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.util.concurrent.CompletableFuture.supplyAsync
|
import java.util.concurrent.CompletableFuture.supplyAsync
|
||||||
|
@ -2,7 +2,8 @@ package net.corda.bank
|
|||||||
|
|
||||||
import net.corda.finance.DOLLARS
|
import net.corda.finance.DOLLARS
|
||||||
import net.corda.finance.POUNDS
|
import net.corda.finance.POUNDS
|
||||||
import net.corda.testing.*
|
import net.corda.testing.internal.IntegrationTest
|
||||||
|
import net.corda.testing.internal.IntegrationTestSchemas
|
||||||
import net.corda.testing.node.internal.demorun.deployNodesThen
|
import net.corda.testing.node.internal.demorun.deployNodesThen
|
||||||
import org.junit.ClassRule
|
import org.junit.ClassRule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
@ -14,6 +14,9 @@ import net.corda.node.services.Permissions.Companion.startFlow
|
|||||||
import net.corda.nodeapi.internal.config.User
|
import net.corda.nodeapi.internal.config.User
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
|
import net.corda.testing.internal.IntegrationTest
|
||||||
|
import net.corda.testing.internal.IntegrationTestSchemas
|
||||||
|
import net.corda.testing.internal.toDatabaseSchemaName
|
||||||
import org.junit.ClassRule
|
import org.junit.ClassRule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
|
@ -9,11 +9,12 @@ webapp which provides REST API and web frontend. Application communicate using C
|
|||||||
|
|
||||||
To run from the command line in Unix:
|
To run from the command line in Unix:
|
||||||
1. Run ``./gradlew samples:irs-demo:cordapp:deployNodes`` to install configs and a command line tool under
|
1. Run ``./gradlew samples:irs-demo:cordapp:deployNodes`` to install configs and a command line tool under
|
||||||
``samples/irs-demo/build``
|
``samples/irs-demo/cordapp/build``
|
||||||
2. Run ``./gradlew samples:irs-demo:web:deployWebapps`` to install configs and tools for running webservers
|
2. Run ``./gradlew samples:irs-demo:web:deployWebapps`` to install configs and tools for running webservers
|
||||||
3. Move to the ``samples/irs-demo/`` directory
|
3. Move to the ``samples/irs-demo/`` directory
|
||||||
4. Run ``./cordapp/build/nodes/runnodes`` to open up three new terminals with the three nodes (you may have to install xterm)
|
4. Run ``./cordapp/build/nodes/runnodes`` to open up three new terminals with the three nodes (you may have to install xterm)
|
||||||
5. Run ``./web/build/webapps/runwebapps`` to open three more terminals for associated webserver
|
5. On Linux, run ``./web/build/webapps/runwebapps`` to open three more terminals for associated webservers. On macOS,
|
||||||
|
use the following command instead: ``osascript ./web/build/webapps/runwebapps.scpt``
|
||||||
|
|
||||||
To run from the command line in Windows:
|
To run from the command line in Windows:
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user