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

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

View File

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

View File

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

View File

@ -12,8 +12,8 @@ import net.corda.node.internal.Node;
import net.corda.node.internal.StartedNode;
import net.corda.nodeapi.internal.config.User;
import net.corda.testing.CoreTestUtils;
import net.corda.testing.IntegrationTestKt;
import net.corda.testing.IntegrationTestSchemas;
import net.corda.testing.internal.IntegrationTestKt;
import net.corda.testing.internal.IntegrationTestSchemas;
import net.corda.testing.node.internal.NodeBasedTest;
import org.junit.After;
import org.junit.Before;
@ -32,7 +32,6 @@ import static net.corda.finance.contracts.GetBalances.getCashBalance;
import static net.corda.node.services.Permissions.invokeRpc;
import static net.corda.node.services.Permissions.startFlow;
import static net.corda.testing.TestConstants.ALICE_NAME;
import static net.corda.testing.TestConstants.DUMMY_NOTARY_NAME;
public class CordaRPCJavaClientTest extends NodeBasedTest {
public CordaRPCJavaClientTest() {

View File

@ -9,6 +9,9 @@ import net.corda.core.serialization.CordaSerializable
import net.corda.core.utilities.getOrThrow
import net.corda.testing.*
import net.corda.testing.driver.driver
import net.corda.testing.internal.IntegrationTest
import net.corda.testing.internal.IntegrationTestSchemas
import net.corda.testing.internal.toDatabaseSchemaName
import org.assertj.core.api.Assertions.assertThatExceptionOfType
import org.junit.ClassRule
import org.junit.Test

View File

@ -22,6 +22,8 @@ import net.corda.node.services.Permissions.Companion.invokeRpc
import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.internal.config.User
import net.corda.testing.*
import net.corda.testing.internal.IntegrationTestSchemas
import net.corda.testing.internal.toDatabaseSchemaName
import net.corda.testing.node.internal.NodeBasedTest
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
import org.assertj.core.api.Assertions.assertThat

View File

@ -13,7 +13,10 @@ import net.corda.core.utilities.*
import net.corda.node.services.messaging.RPCServerConfiguration
import net.corda.nodeapi.RPCApi
import net.corda.testing.*
import net.corda.testing.internal.IntegrationTest
import net.corda.testing.internal.IntegrationTestSchemas
import net.corda.testing.internal.testThreadFactory
import net.corda.testing.internal.toDatabaseSchemaNames
import net.corda.testing.node.internal.*
import org.apache.activemq.artemis.api.core.SimpleString
import org.junit.After
@ -45,7 +48,7 @@ class RPCStabilityTests : IntegrationTest() {
companion object {
@ClassRule @JvmField
val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, DUMMY_BANK_A_NAME)
.map { it.toDatabaseSchemaNames("","_10000","_10003","_10012") }.flatten().toTypedArray())
.map { it.toDatabaseSchemaNames("", "_10000", "_10003", "_10012") }.flatten().toTypedArray())
}
object DummyOps : RPCOps {

View File

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

View File

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

View File

@ -30,6 +30,7 @@ import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPublicKey
import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter
import org.bouncycastle.jce.ECNamedCurveTable
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec
import org.bouncycastle.jce.spec.ECParameterSpec
import org.bouncycastle.jce.spec.ECPrivateKeySpec
import org.bouncycastle.jce.spec.ECPublicKeySpec
@ -808,7 +809,7 @@ object Crypto {
/**
* Returns a key pair derived from the given [BigInteger] entropy. This is useful for unit tests
* and other cases where you want hard-coded private keys.
* Currently, [EDDSA_ED25519_SHA512] is the sole scheme supported for this operation.
* Currently, the following schemes are supported: [EDDSA_ED25519_SHA512], [ECDSA_SECP256R1_SHA256] and [ECDSA_SECP256K1_SHA256].
* @param signatureScheme a supported [SignatureScheme], see [Crypto].
* @param entropy a [BigInteger] value.
* @return a new [KeyPair] from an entropy input.
@ -818,6 +819,7 @@ object Crypto {
fun deriveKeyPairFromEntropy(signatureScheme: SignatureScheme, entropy: BigInteger): KeyPair {
return when (signatureScheme) {
EDDSA_ED25519_SHA512 -> deriveEdDSAKeyPairFromEntropy(entropy)
ECDSA_SECP256R1_SHA256, ECDSA_SECP256K1_SHA256 -> deriveECDSAKeyPairFromEntropy(signatureScheme, entropy)
else -> throw IllegalArgumentException("Unsupported signature scheme for fixed entropy-based key pair " +
"generation: ${signatureScheme.schemeCodeName}")
}
@ -832,6 +834,9 @@ object Crypto {
fun deriveKeyPairFromEntropy(entropy: BigInteger): KeyPair = deriveKeyPairFromEntropy(DEFAULT_SIGNATURE_SCHEME, entropy)
// Custom key pair generator from entropy.
// The BigIntenger.toByteArray() uses the two's-complement representation.
// The entropy is transformed to a byte array in big-endian byte-order and
// only the first ed25519.field.getb() / 8 bytes are used.
private fun deriveEdDSAKeyPairFromEntropy(entropy: BigInteger): KeyPair {
val params = EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec
val bytes = entropy.toByteArray().copyOf(params.curve.field.getb() / 8) // Need to pad the entropy to the valid seed length.
@ -840,6 +845,32 @@ object Crypto {
return KeyPair(EdDSAPublicKey(pub), EdDSAPrivateKey(priv))
}
// Custom key pair generator from an entropy required for various tests. It is similar to deriveKeyPairECDSA,
// but the accepted range of the input entropy is more relaxed:
// 2 <= entropy < N, where N is the order of base-point G.
private fun deriveECDSAKeyPairFromEntropy(signatureScheme: SignatureScheme, entropy: BigInteger): KeyPair {
val parameterSpec = signatureScheme.algSpec as ECNamedCurveParameterSpec
// The entropy might be a negative number and/or out of range (e.g. PRNG output).
// In such cases we retry with hash(currentEntropy).
while (entropy < ECConstants.TWO || entropy >= parameterSpec.n) {
return deriveECDSAKeyPairFromEntropy(signatureScheme, BigInteger(1, entropy.toByteArray().sha256().bytes))
}
val privateKeySpec = ECPrivateKeySpec(entropy, parameterSpec)
val priv = BCECPrivateKey("EC", privateKeySpec, BouncyCastleProvider.CONFIGURATION)
val pointQ = FixedPointCombMultiplier().multiply(parameterSpec.g, entropy)
while (pointQ.isInfinity) {
// Instead of throwing an exception, we retry with hash(entropy).
return deriveECDSAKeyPairFromEntropy(signatureScheme, BigInteger(1, entropy.toByteArray().sha256().bytes))
}
val publicKeySpec = ECPublicKeySpec(pointQ, parameterSpec)
val pub = BCECPublicKey("EC", publicKeySpec, BouncyCastleProvider.CONFIGURATION)
return KeyPair(pub, priv)
}
// Compute the HMAC-SHA512 using a privateKey as the MAC_key and a seed ByteArray.
private fun deriveHMAC(privateKey: PrivateKey, seed: ByteArray): ByteArray {
// Compute hmac(privateKey, seed).

View File

@ -118,18 +118,19 @@ fun Iterable<TransactionSignature>.byKeys() = map { it.by }.toSet()
// Allow Kotlin destructuring:
// val (private, public) = keyPair
/* The [PrivateKey] of this [KeyPair] .*/
/* The [PrivateKey] of this [KeyPair]. */
operator fun KeyPair.component1(): PrivateKey = this.private
/* The [PublicKey] of this [KeyPair] .*/
/* The [PublicKey] of this [KeyPair]. */
operator fun KeyPair.component2(): PublicKey = this.public
/** A simple wrapper that will make it easier to swap out the EC algorithm we use in future. */
/** A simple wrapper that will make it easier to swap out the signature algorithm we use in future. */
fun generateKeyPair(): KeyPair = Crypto.generateKeyPair()
/**
* Returns a key pair derived from the given private key entropy. This is useful for unit tests and other cases where
* you want hard-coded private keys.
* This currently works for the default signature scheme EdDSA ed25519 only.
* @param entropy a [BigInteger] value.
* @return a deterministically generated [KeyPair] for the [Crypto.DEFAULT_SIGNATURE_SCHEME].
*/
fun entropyToKeyPair(entropy: BigInteger): KeyPair = Crypto.deriveKeyPairFromEntropy(entropy)

View File

@ -10,7 +10,17 @@ import net.corda.core.serialization.CordaSerializable
*/
@DoNotImplement
interface FlowLogicRefFactory {
/**
* Construct a FlowLogicRef. This is intended for cases where the calling code has the relevant class already
* and can provide it directly.
*/
@Deprecated("This should be avoided, and the version which takes a class name used instead to avoid requiring the class on the classpath to deserialize calling code")
fun create(flowClass: Class<out FlowLogic<*>>, vararg args: Any?): FlowLogicRef
/**
* Construct a FlowLogicRef. This is intended for cases where the calling code does not want to require the flow
* class on the classpath for all cases where the calling code is loaded.
*/
fun create(flowClassName: String, vararg args: Any?): FlowLogicRef
fun createForRPC(flowClass: Class<out FlowLogic<*>>, vararg args: Any?): FlowLogicRef
fun toFlowLogic(ref: FlowLogicRef): FlowLogic<*>
}

View File

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

View File

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

View File

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

View File

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

View File

@ -14,21 +14,22 @@ import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey
import org.bouncycastle.jce.ECNamedCurveTable
import org.bouncycastle.jce.interfaces.ECKey
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec
import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PrivateKey
import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PublicKey
import org.junit.Assert.assertNotEquals
import org.junit.Test
import java.math.BigInteger
import java.security.KeyPairGenerator
import java.util.*
import kotlin.test.*
/**
* Run tests for cryptographic algorithms
* Run tests for cryptographic algorithms.
*/
class CryptoUtilsTest {
private val testString = "Hello World"
private val testBytes = testString.toByteArray()
private val testBytes = "Hello World".toByteArray()
// key generation test
@Test
@ -781,4 +782,113 @@ class CryptoUtilsTest {
assertEquals(dpriv2, dpriv_2)
assertEquals(dpub2, dpub_2)
}
@Test
fun `EdDSA ed25519 keyPair from entropy`() {
val keyPairPositive = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("10"))
assertEquals("DLBL3iHCp9uRReWhhCGfCsrxZZpfAm9h9GLbfN8ijqXTq", keyPairPositive.public.toStringShort())
val keyPairNegative = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("-10"))
assertEquals("DLC5HXnYsJAFqmM9hgPj5G8whQ4TpyE9WMBssqCayLBwA2", keyPairNegative.public.toStringShort())
val keyPairZero = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("0"))
assertEquals("DL4UVhGh4tqu1G86UVoGNaDDNCMsBtNHzE6BSZuNNJN7W2", keyPairZero.public.toStringShort())
val keyPairOne = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("1"))
assertEquals("DL8EZUdHixovcCynKMQzrMWBnXQAcbVDHi6ArPphqwJVzq", keyPairOne.public.toStringShort())
val keyPairBiggerThan256bits = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("2").pow(258).minus(BigInteger.TEN))
assertEquals("DLB9K1UiBrWonn481z6NzkqoWHjMBXpfDeaet3wiwRNWSU", keyPairBiggerThan256bits.public.toStringShort())
// The underlying implementation uses the first 256 bytes of the entropy. Thus, 2^258-10 and 2^258-50 and 2^514-10 have the same impact.
val keyPairBiggerThan256bitsV2 = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("2").pow(258).minus(BigInteger("50")))
assertEquals("DLB9K1UiBrWonn481z6NzkqoWHjMBXpfDeaet3wiwRNWSU", keyPairBiggerThan256bitsV2.public.toStringShort())
val keyPairBiggerThan512bits = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("2").pow(514).minus(BigInteger.TEN))
assertEquals("DLB9K1UiBrWonn481z6NzkqoWHjMBXpfDeaet3wiwRNWSU", keyPairBiggerThan512bits.public.toStringShort())
// Try another big number.
val keyPairBiggerThan258bits = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("2").pow(259).plus(BigInteger.ONE))
assertEquals("DL5tEFVMXMGrzwjfCAW34JjkhsRkPfFyJ38iEnmpB6L2Z9", keyPairBiggerThan258bits.public.toStringShort())
}
@Test
fun `ECDSA R1 keyPair from entropy`() {
val keyPairPositive = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("10"))
assertEquals("DLHDcxuSt9J3cbjd2Dsx4rAgYYA7BAP7A8VLrFiq1tH9yy", keyPairPositive.public.toStringShort())
// The underlying implementation uses the hash of entropy if it is out of range 2 < entropy < N, where N the order of the group.
val keyPairNegative = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("-10"))
assertEquals("DLBASmjiMZuu1g3EtdHJxfSueXE8PRoUWbkdU61Qcnpamt", keyPairNegative.public.toStringShort())
val keyPairZero = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("0"))
assertEquals("DLH2FEHEnsT3MpCJt2gfyNjpqRqcBxeupK4YRPXvDsVEkb", keyPairZero.public.toStringShort())
// BigIntenger.Zero is out or range, so 1 and hash(1.toByteArray) would have the same impact.
val zeroHashed = BigInteger(1, BigInteger("0").toByteArray().sha256().bytes)
// Check oneHashed < N (order of the group), otherwise we would need an extra hash.
assertEquals(-1, zeroHashed.compareTo((Crypto.ECDSA_SECP256R1_SHA256.algSpec as ECNamedCurveParameterSpec).n))
val keyPairZeroHashed = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, zeroHashed)
assertEquals("DLH2FEHEnsT3MpCJt2gfyNjpqRqcBxeupK4YRPXvDsVEkb", keyPairZeroHashed.public.toStringShort())
val keyPairOne = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("1"))
assertEquals("DLHrtKwjv6onq9HcrQDJPs8Cgtai5mZU5ZU6sb1ivJjx3z", keyPairOne.public.toStringShort())
// BigIntenger.ONE is out or range, so 1 and hash(1.toByteArray) would have the same impact.
val oneHashed = BigInteger(1, BigInteger("1").toByteArray().sha256().bytes)
// Check oneHashed < N (order of the group), otherwise we would need an extra hash.
assertEquals(-1, oneHashed.compareTo((Crypto.ECDSA_SECP256R1_SHA256.algSpec as ECNamedCurveParameterSpec).n))
val keyPairOneHashed = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, oneHashed)
assertEquals("DLHrtKwjv6onq9HcrQDJPs8Cgtai5mZU5ZU6sb1ivJjx3z", keyPairOneHashed.public.toStringShort())
// 2 is in the range.
val keyPairTwo = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2"))
assertEquals("DLFoz6txJ3vHcKNSM1vFxHJUoEQ69PorBwW64dHsAnEoZB", keyPairTwo.public.toStringShort())
// Try big numbers that are out of range.
val keyPairBiggerThan256bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2").pow(258).minus(BigInteger.TEN))
assertEquals("DLBv6fZqaCTbE4L7sgjbt19biXHMgU9CzR5s8g8XBJjZ11", keyPairBiggerThan256bits.public.toStringShort())
val keyPairBiggerThan256bitsV2 = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2").pow(258).minus(BigInteger("50")))
assertEquals("DLANmjhGSVdLyghxcPHrn3KuGatscf6LtvqifUDxw7SGU8", keyPairBiggerThan256bitsV2.public.toStringShort())
val keyPairBiggerThan512bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2").pow(514).minus(BigInteger.TEN))
assertEquals("DL9sKwMExBTD3MnJN6LWGqo496Erkebs9fxZtXLVJUBY9Z", keyPairBiggerThan512bits.public.toStringShort())
val keyPairBiggerThan258bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2").pow(259).plus(BigInteger.ONE))
assertEquals("DLBwjWwPJSF9E7b1NWaSbEJ4oK8CF7RDGWd648TiBhZoL1", keyPairBiggerThan258bits.public.toStringShort())
}
@Test
fun `ECDSA K1 keyPair from entropy`() {
val keyPairPositive = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("10"))
assertEquals("DL6pYKUgH17az8MLdonvvUtUPN8TqwpCGcdgLr7vg3skCU", keyPairPositive.public.toStringShort())
// The underlying implementation uses the hash of entropy if it is out of range 2 <= entropy < N, where N the order of the group.
val keyPairNegative = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("-10"))
assertEquals("DLnpXhxece69Nyqgm3pPt3yV7ESQYDJKoYxs1hKgfBAEu", keyPairNegative.public.toStringShort())
val keyPairZero = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("0"))
assertEquals("DLBC28e18T6KsYwjTFfUWJfhvHjvYVapyVf6antnqUkbgd", keyPairZero.public.toStringShort())
// BigIntenger.Zero is out or range, so 1 and hash(1.toByteArray) would have the same impact.
val zeroHashed = BigInteger(1, BigInteger("0").toByteArray().sha256().bytes)
// Check oneHashed < N (order of the group), otherwise we would need an extra hash.
assertEquals(-1, zeroHashed.compareTo((Crypto.ECDSA_SECP256K1_SHA256.algSpec as ECNamedCurveParameterSpec).n))
val keyPairZeroHashed = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, zeroHashed)
assertEquals("DLBC28e18T6KsYwjTFfUWJfhvHjvYVapyVf6antnqUkbgd", keyPairZeroHashed.public.toStringShort())
val keyPairOne = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("1"))
assertEquals("DLBimRXdEQhJUTpL6f9ri9woNdsze6mwkRrhsML13Eh7ET", keyPairOne.public.toStringShort())
// BigIntenger.ONE is out or range, so 1 and hash(1.toByteArray) would have the same impact.
val oneHashed = BigInteger(1, BigInteger("1").toByteArray().sha256().bytes)
// Check oneHashed < N (order of the group), otherwise we would need an extra hash.
assertEquals(-1, oneHashed.compareTo((Crypto.ECDSA_SECP256K1_SHA256.algSpec as ECNamedCurveParameterSpec).n))
val keyPairOneHashed = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, oneHashed)
assertEquals("DLBimRXdEQhJUTpL6f9ri9woNdsze6mwkRrhsML13Eh7ET", keyPairOneHashed.public.toStringShort())
// 2 is in the range.
val keyPairTwo = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2"))
assertEquals("DLG32UWaevGw9YY7w1Rf9mmK88biavgpDnJA9bG4GapVPs", keyPairTwo.public.toStringShort())
// Try big numbers that are out of range.
val keyPairBiggerThan256bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2").pow(258).minus(BigInteger.TEN))
assertEquals("DLGHsdv2xeAuM7n3sBc6mFfiphXe6VSf3YxqvviKDU6Vbd", keyPairBiggerThan256bits.public.toStringShort())
val keyPairBiggerThan256bitsV2 = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2").pow(258).minus(BigInteger("50")))
assertEquals("DL9yJfiNGqteRrKPjGUkRQkeqzuQ4kwcYQWMCi5YKuUHrk", keyPairBiggerThan256bitsV2.public.toStringShort())
val keyPairBiggerThan512bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2").pow(514).minus(BigInteger.TEN))
assertEquals("DL3Wr5EQGrMTaKBy5XMvG8rvSfKX1AYZLCRU8kixGbxt1E", keyPairBiggerThan512bits.public.toStringShort())
val keyPairBiggerThan258bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2").pow(259).plus(BigInteger.ONE))
assertEquals("DL7NbssqvuuJ4cqFkkaVYu9j1MsVswESGgCfbqBS9ULwuM", keyPairBiggerThan258bits.public.toStringShort())
}
}

View File

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

View File

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

View File

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

View File

@ -16,6 +16,9 @@ import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.internal.config.User
import net.corda.testing.*
import net.corda.testing.driver.driver
import net.corda.testing.internal.IntegrationTest
import net.corda.testing.internal.IntegrationTestSchemas
import net.corda.testing.internal.toDatabaseSchemaName
import org.junit.ClassRule
import org.junit.Test
import kotlin.test.assertEquals

View File

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

View File

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

View File

@ -6,6 +6,9 @@ import net.corda.finance.EUR
import net.corda.finance.USD
import net.corda.testing.*
import net.corda.testing.driver.driver
import net.corda.testing.internal.IntegrationTest
import net.corda.testing.internal.IntegrationTestSchemas
import net.corda.testing.internal.toDatabaseSchemaNames
import org.assertj.core.api.Assertions.assertThat
import org.junit.ClassRule
import org.junit.Test
@ -14,7 +17,7 @@ class CashConfigDataFlowTest : IntegrationTest() {
companion object {
@ClassRule @JvmField
val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, DUMMY_BANK_A_NAME)
.map { it.toDatabaseSchemaNames("","_10000","_10003") }.flatten().toTypedArray())
.map { it.toDatabaseSchemaNames("", "_10000", "_10003") }.flatten().toTypedArray())
}
@Test
fun `issuable currencies are read in from node config`() {

View File

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

View File

@ -6,7 +6,7 @@ Generates a text summary of Corda's public API that we can check for API-breakin
$ gradlew generateApi
```
See [here](../../docs/source/api-index.rst) for Corda's public API strategy. We will need to
See [here](../../docs/source/corda-api.rst) for Corda's public API strategy. We will need to
apply this plugin to other modules in future Corda releases as those modules' APIs stabilise.
Basically, this plugin will document a module's `public` and `protected` classes/methods/fields,

View File

@ -22,6 +22,7 @@ import java.util.jar.JarInputStream
@Suppress("unused")
open class Cordform : DefaultTask() {
private companion object {
val nodeJarName = "corda.jar"
private val defaultDirectory: Path = Paths.get("build", "nodes")
}
@ -132,9 +133,26 @@ open class Cordform : DefaultTask() {
fun build() {
project.logger.info("Running Cordform task")
initializeConfiguration()
nodes.forEach(Node::installConfig)
installCordaJar()
installRunScript()
nodes.forEach(Node::build)
bootstrapNetwork()
nodes.forEach(Node::build)
}
/**
* Installs the corda fat JAR to the root directory, for the network bootstrapper to use.
*/
private fun installCordaJar() {
val cordaJar = Cordformation.verifyAndGetRuntimeJar(project, "corda")
project.copy {
it.apply {
from(cordaJar)
into(directory)
rename(cordaJar.name, nodeJarName)
fileMode = Cordformation.executableFileMode
}
}
}
private fun initializeConfiguration() {
@ -150,8 +168,8 @@ open class Cordform : DefaultTask() {
cd.nodeConfigurers.forEach {
val node = node { }
it.accept(node)
node.additionalCordapps.addAll(cordapps)
node.rootDir(directory)
node.installCordapps(cordapps)
}
cd.setup { nodeName -> project.projectDir.toPath().resolve(getNodeByName(nodeName)!!.nodeDir.toPath()) }
} else {

View File

@ -20,14 +20,35 @@ class Cordformation : Plugin<Project> {
* @return A file handle to the file in the JAR.
*/
fun getPluginFile(project: Project, filePathInJar: String): File {
val archive: File? = project.rootProject.buildscript.configurations
val archive = project.rootProject.buildscript.configurations
.single { it.name == "classpath" }
.find { it.name.contains("cordformation") }
.first { it.name.contains("cordformation") }
return project.rootProject.resources.text
.fromArchiveEntry(archive, filePathInJar)
.asFile()
}
/**
* Gets a current built corda jar file
*
* @param project The project environment this plugin executes in.
* @param jarName The name of the JAR you wish to access.
* @return A file handle to the file in the JAR.
*/
fun verifyAndGetRuntimeJar(project: Project, jarName: String): File {
val releaseVersion = project.rootProject.ext<String>("corda_release_version")
val maybeJar = project.configuration("runtime").filter {
"$jarName-$releaseVersion.jar" in it.toString() || "$jarName-enterprise-$releaseVersion.jar" in it.toString()
}
if (maybeJar.isEmpty) {
throw IllegalStateException("No $jarName JAR found. Have you deployed the Corda project to Maven? Looked for \"$jarName-$releaseVersion.jar\"")
} else {
val jar = maybeJar.singleFile
require(jar.isFile)
return jar
}
}
val executableFileMode = "0755".toInt(8)
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,20 +4,18 @@ import com.nhaarman.mockito_kotlin.*
import com.r3.corda.networkmanage.TestBase
import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage
import com.r3.corda.networkmanage.common.utils.withCert
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.sha256
import net.corda.core.crypto.sign
import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.cert
import net.corda.core.serialization.serialize
import net.corda.nodeapi.internal.crypto.CertificateType
import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
import net.corda.nodeapi.internal.network.NetworkMap
import net.corda.nodeapi.internal.network.SignedNetworkMap
import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.internal.createDevIntermediateCaCertPath
import org.junit.Before
import org.junit.Test
import java.security.cert.X509Certificate
import kotlin.test.assertEquals
import kotlin.test.assertTrue
@ -25,12 +23,15 @@ class NetworkMapSignerTest : TestBase() {
private lateinit var signer: Signer
private lateinit var networkMapStorage: NetworkMapStorage
private lateinit var networkMapSigner: NetworkMapSigner
private val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
private val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 LTD", country = "GB"), rootCAKey)
private val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
private val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, CordaX500Name(commonName = "Corda Node Intermediate CA", locality = "London", organisation = "R3 LTD", country = "GB"), intermediateCAKey.public)
private lateinit var rootCaCert: X509Certificate
private lateinit var intermediateCa: CertificateAndKeyPair
@Before
fun setUp() {
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
rootCaCert = rootCa.certificate
this.intermediateCa = intermediateCa
signer = mock()
networkMapStorage = mock()
networkMapSigner = NetworkMapSigner(networkMapStorage, signer)
@ -43,11 +44,11 @@ class NetworkMapSignerTest : TestBase() {
val networkParameters = testNetworkParameters(emptyList())
val serializedNetworkMap = NetworkMap(signedNodeInfoHashes, SecureHash.randomSHA256()).serialize()
whenever(networkMapStorage.getCurrentNetworkMap())
.thenReturn(SignedNetworkMap(serializedNetworkMap, intermediateCAKey.sign(serializedNetworkMap).withCert(intermediateCACert.cert)))
.thenReturn(SignedNetworkMap(serializedNetworkMap, intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate)))
whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(signedNodeInfoHashes)
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters)
whenever(signer.sign(any())).then {
intermediateCAKey.sign(it.arguments.first() as ByteArray).withCert(intermediateCACert.cert)
intermediateCa.keyPair.sign(it.arguments[0] as ByteArray).withCert(intermediateCa.certificate)
}
// when
@ -59,7 +60,7 @@ class NetworkMapSignerTest : TestBase() {
verify(networkMapStorage).getLatestNetworkParameters()
argumentCaptor<SignedNetworkMap>().apply {
verify(networkMapStorage).saveNetworkMap(capture())
val networkMap = firstValue.verified(rootCACert.cert)
val networkMap = firstValue.verified(rootCaCert)
assertEquals(networkParameters.serialize().hash, networkMap.networkParameterHash)
assertEquals(signedNodeInfoHashes.size, networkMap.nodeInfoHashes.size)
assertTrue(networkMap.nodeInfoHashes.containsAll(signedNodeInfoHashes))
@ -73,7 +74,7 @@ class NetworkMapSignerTest : TestBase() {
val networkMapParametersHash = networkParameters.serialize().bytes.sha256()
val networkMap = NetworkMap(emptyList(), networkMapParametersHash)
val serializedNetworkMap = networkMap.serialize()
val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, intermediateCAKey.sign(serializedNetworkMap).withCert(intermediateCACert.cert))
val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate))
whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(signedNetworkMap)
whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList())
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters)
@ -94,7 +95,7 @@ class NetworkMapSignerTest : TestBase() {
whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList())
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters)
whenever(signer.sign(any())).then {
intermediateCAKey.sign(it.arguments.first() as ByteArray).withCert(intermediateCACert.cert)
intermediateCa.keyPair.sign(it.arguments[0] as ByteArray).withCert(intermediateCa.certificate)
}
// when
networkMapSigner.signNetworkMap()
@ -105,7 +106,7 @@ class NetworkMapSignerTest : TestBase() {
verify(networkMapStorage).getLatestNetworkParameters()
argumentCaptor<SignedNetworkMap>().apply {
verify(networkMapStorage).saveNetworkMap(capture())
val networkMap = firstValue.verified(rootCACert.cert)
val networkMap = firstValue.verified(rootCaCert)
assertEquals(networkParameters.serialize().hash, networkMap.networkParameterHash)
}
}

View File

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

View File

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

View File

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

View File

@ -39,7 +39,10 @@ class JiraCsrHandlerTest {
private lateinit var certificateResponse: CertificateResponse.Ready
private val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
private val pkcS10CertificationRequest = X509Utilities.createCertificateSigningRequest(CordaX500Name(locality = "London", organisation = "LegalName", country = "GB"), "my@mail.com", keyPair)
private val pkcS10CertificationRequest = X509Utilities.createCertificateSigningRequest(
CordaX500Name(locality = "London", organisation = "LegalName", country = "GB").x500Principal,
"my@mail.com",
keyPair)
@Before
fun setup() {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -83,16 +83,20 @@ class NetworkBootstrapper {
for (confFile in confFiles) {
val nodeName = confFile.fileName.toString().removeSuffix(".conf")
println("Generating directory for $nodeName")
val nodeDir = (directory / nodeName).createDirectory()
confFile.moveTo(nodeDir / "node.conf")
Files.copy(cordaJar, (nodeDir / "corda.jar"))
val nodeDir = (directory / nodeName)
if (!nodeDir.exists()) { nodeDir.createDirectory() }
confFile.moveTo(nodeDir / "node.conf", StandardCopyOption.REPLACE_EXISTING)
Files.copy(cordaJar, (nodeDir / "corda.jar"), StandardCopyOption.REPLACE_EXISTING)
}
Files.delete(cordaJar)
}
private fun extractCordaJarTo(directory: Path): Path {
val cordaJarPath = (directory / "corda.jar")
Thread.currentThread().contextClassLoader.getResourceAsStream("corda.jar").copyTo(cordaJarPath)
if (!cordaJarPath.exists()) {
println("No corda jar found in root directory. Extracting from jar")
Thread.currentThread().contextClassLoader.getResourceAsStream("corda.jar").copyTo(cordaJarPath)
}
return cordaJarPath
}

View File

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

View File

@ -0,0 +1,40 @@
package net.corda.nodeapi.internal.serialization.amqp;
import net.corda.nodeapi.internal.serialization.AllWhitelist;
import org.assertj.core.api.Assertions;
import org.junit.Test;
import java.io.NotSerializableException;
public class ErrorMessageTests {
private String errMsg(String property, String testname) {
return "Property '"
+ property
+ "' or its getter is non public, this renders class 'class "
+ testname
+ "$C' unserializable -> class "
+ testname
+ "$C";
}
static class C {
public Integer a;
public C(Integer a) {
this.a = a;
}
private Integer getA() { return this.a; }
}
@Test
public void testJavaConstructorAnnotations() {
SerializerFactory factory1 = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader());
SerializationOutput ser = new SerializationOutput(factory1);
Assertions.assertThatThrownBy(() -> ser.serialize(new C(1)))
.isInstanceOf(NotSerializableException.class)
.hasMessage(errMsg("a", getClass().getName()));
}
}

View File

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

View File

@ -0,0 +1,69 @@
package net.corda.nodeapi.internal.serialization.amqp
import org.assertj.core.api.Assertions
import org.junit.Test
import java.io.NotSerializableException
class ErrorMessagesTests {
companion object {
val VERBOSE get() = false
}
private fun errMsg(property:String, testname: String) =
"Property '$property' or its getter is non public, this renders class 'class $testname\$C' unserializable -> class $testname\$C"
@Test
fun privateProperty() {
data class C(private val a: Int)
val sf = testDefaultFactory()
val testname = "${javaClass.name}\$${testName()}"
Assertions.assertThatThrownBy {
TestSerializationOutput(VERBOSE, sf).serialize(C(1))
}.isInstanceOf(NotSerializableException::class.java).hasMessage(errMsg("a", testname))
}
@Test
fun privateProperty2() {
data class C(val a: Int, private val b: Int)
val sf = testDefaultFactory()
val testname = "${javaClass.name}\$${testName()}"
Assertions.assertThatThrownBy {
TestSerializationOutput(VERBOSE, sf).serialize(C(1, 2))
}.isInstanceOf(NotSerializableException::class.java).hasMessage(errMsg("b", testname))
}
@Test
fun privateProperty3() {
// despite b being private, the getter we've added is public and thus allows for the serialisation
// of the object
data class C(val a: Int, private val b: Int) {
public fun getB() = b
}
val sf = testDefaultFactory()
val testname = "${javaClass.name}\$${testName()}"
val bytes = TestSerializationOutput(VERBOSE, sf).serialize(C(1, 2))
val c = DeserializationInput(sf).deserialize(bytes)
}
@Test
fun protectedProperty() {
data class C(protected val a: Int)
val sf = testDefaultFactory()
val testname = "${javaClass.name}\$${testName()}"
Assertions.assertThatThrownBy {
TestSerializationOutput(VERBOSE, sf).serialize(C(1))
}.isInstanceOf(NotSerializableException::class.java).hasMessage(errMsg("a", testname))
}
}

View File

@ -1,4 +1,4 @@
package net.corda.node.services
package net.corda.node
import co.paralleluniverse.fibers.Suspendable
import net.corda.client.rpc.CordaRPCClient
@ -11,33 +11,94 @@ import net.corda.core.messaging.startFlow
import net.corda.finance.flows.CashIssueFlow
import net.corda.node.internal.Node
import net.corda.node.internal.StartedNode
import net.corda.node.services.config.AuthDataSourceType
import net.corda.node.services.Permissions
import net.corda.node.services.config.PasswordEncryption
import net.corda.node.services.config.SecurityConfiguration
import net.corda.nodeapi.internal.config.User
import net.corda.nodeapi.internal.config.toConfig
import net.corda.testing.ALICE_NAME
import net.corda.testing.IntegrationTestSchemas
import net.corda.testing.internal.IntegrationTestSchemas
import net.corda.testing.internal.toDatabaseSchemaName
import net.corda.testing.node.internal.NodeBasedTest
import net.corda.testing.toDatabaseSchemaName
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
import org.apache.shiro.authc.credential.DefaultPasswordService
import org.junit.After
import org.junit.Before
import org.junit.ClassRule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import java.sql.DriverManager
import java.sql.Statement
import java.util.*
import kotlin.test.assertFailsWith
abstract class UserAuthServiceTest : NodeBasedTest() {
/*
* Starts Node's instance configured to load clients credentials and permissions from an external DB, then
* check authentication/authorization of RPC connections.
*/
@RunWith(Parameterized::class)
class AuthDBTests : NodeBasedTest() {
companion object {
@ClassRule @JvmField
val databaseSchemas = IntegrationTestSchemas(ALICE_NAME.toDatabaseSchemaName())
private val cacheExpireAfterSecs: Long = 1
@JvmStatic
@Parameterized.Parameters(name = "password encryption format = {0}")
fun encFormats() = arrayOf(PasswordEncryption.NONE, PasswordEncryption.SHIRO_1_CRYPT)
}
protected lateinit var node: StartedNode<Node>
protected lateinit var client: CordaRPCClient
private lateinit var node: StartedNode<Node>
private lateinit var client: CordaRPCClient
private lateinit var db: UsersDB
@Parameterized.Parameter
lateinit var passwordEncryption: PasswordEncryption
@Before
fun setup() {
db = UsersDB(
name = "SecurityDataSourceTestDB",
users = listOf(UserAndRoles(username = "user",
password = encodePassword("foo", passwordEncryption),
roles = listOf("default"))),
roleAndPermissions = listOf(
RoleAndPermissions(
role = "default",
permissions = listOf(
Permissions.startFlow<DummyFlow>(),
Permissions.invokeRpc("vaultQueryBy"),
Permissions.invokeRpc(CordaRPCOps::stateMachinesFeed),
Permissions.invokeRpc("vaultQueryByCriteria"))),
RoleAndPermissions(
role = "admin",
permissions = listOf("ALL")
)))
val securityConfig = mapOf(
"security" to mapOf(
"authService" to mapOf(
"dataSource" to mapOf(
"type" to "DB",
"passwordEncryption" to passwordEncryption.toString(),
"connection" to mapOf(
"jdbcUrl" to db.jdbcUrl,
"username" to "",
"password" to "",
"driverClassName" to "org.h2.Driver"
)
)
),
"options" to mapOf(
"cache" to mapOf(
"expireAfterSecs" to cacheExpireAfterSecs,
"maxEntries" to 50
)
)
)
)
node = startNode(ALICE_NAME, rpcUsers = emptyList(), configOverrides = securityConfig)
client = CordaRPCClient(node.internals.configuration.rpcAddress!!)
}
@Test
fun `login with correct credentials`() {
@ -65,7 +126,7 @@ abstract class UserAuthServiceTest : NodeBasedTest() {
val proxy = it.proxy
proxy.startFlowDynamic(DummyFlow::class.java)
proxy.startTrackedFlowDynamic(DummyFlow::class.java)
proxy.startFlow(::DummyFlow)
proxy.startFlow(AuthDBTests::DummyFlow)
assertFailsWith(
PermissionException::class,
"This user should not be authorized to start flow `CashIssueFlow`") {
@ -92,77 +153,8 @@ abstract class UserAuthServiceTest : NodeBasedTest() {
}
}
@StartableByRPC
@InitiatingFlow
class DummyFlow : FlowLogic<Unit>() {
@Suspendable
override fun call() = Unit
}
}
class UserAuthServiceEmbedded : UserAuthServiceTest() {
private val rpcUser = User("user", "foo", permissions = setOf(
Permissions.startFlow<DummyFlow>(),
Permissions.invokeRpc("vaultQueryBy"),
Permissions.invokeRpc(CordaRPCOps::stateMachinesFeed),
Permissions.invokeRpc("vaultQueryByCriteria")))
@Before
fun setup() {
val securityConfig = SecurityConfiguration(
authService = SecurityConfiguration.AuthService.fromUsers(listOf(rpcUser)))
val configOverrides = mapOf("security" to securityConfig.toConfig().root().unwrapped())
node = startNode(ALICE_NAME, rpcUsers = emptyList(), configOverrides = configOverrides)
client = CordaRPCClient(node.internals.configuration.rpcAddress!!)
}
}
class UserAuthServiceTestsJDBC : UserAuthServiceTest() {
private val db = UsersDB(
name = "SecurityDataSourceTestDB",
users = listOf(UserAndRoles(username = "user",
password = "foo",
roles = listOf("default"))),
roleAndPermissions = listOf(
RoleAndPermissions(
role = "default",
permissions = listOf(
Permissions.startFlow<DummyFlow>(),
Permissions.invokeRpc("vaultQueryBy"),
Permissions.invokeRpc(CordaRPCOps::stateMachinesFeed),
Permissions.invokeRpc("vaultQueryByCriteria"))),
RoleAndPermissions(
role = "admin",
permissions = listOf("ALL")
)))
@Before
fun setup() {
val securityConfig = SecurityConfiguration(
authService = SecurityConfiguration.AuthService(
dataSource = SecurityConfiguration.AuthService.DataSource(
type = AuthDataSourceType.DB,
passwordEncryption = PasswordEncryption.NONE,
connection = Properties().apply {
setProperty("jdbcUrl", db.jdbcUrl)
setProperty("username", "")
setProperty("password", "")
setProperty("driverClassName", "org.h2.Driver")
}
)
)
)
val configOverrides = mapOf("security" to securityConfig.toConfig().root().unwrapped())
node = startNode(ALICE_NAME, rpcUsers = emptyList(), configOverrides = configOverrides)
client = CordaRPCClient(node.internals.configuration.rpcAddress!!)
}
@Test
fun `Add new users on-the-fly`() {
fun `Add new users dynamically`() {
assertFailsWith(
ActiveMQSecurityException::class,
"Login with incorrect password should fail") {
@ -171,7 +163,7 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() {
db.insert(UserAndRoles(
username = "user2",
password = "bar",
password = encodePassword("bar"),
roles = listOf("default")))
client.start("user2", "bar")
@ -181,10 +173,9 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() {
fun `Modify user permissions during RPC session`() {
db.insert(UserAndRoles(
username = "user3",
password = "bar",
password = encodePassword("bar"),
roles = emptyList()))
client.start("user3", "bar").use {
val proxy = it.proxy
assertFailsWith(
@ -193,6 +184,7 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() {
proxy.stateMachinesFeed()
}
db.addRoleToUser("user3", "default")
Thread.sleep(1500)
proxy.stateMachinesFeed()
}
}
@ -201,13 +193,14 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() {
fun `Revoke user permissions during RPC session`() {
db.insert(UserAndRoles(
username = "user4",
password = "test",
password = encodePassword("test"),
roles = listOf("default")))
client.start("user4", "test").use {
val proxy = it.proxy
proxy.stateMachinesFeed()
db.deleteUser("user4")
Thread.sleep(1500)
assertFailsWith(
PermissionException::class,
"This user should not be authorized to call 'nodeInfo'") {
@ -216,15 +209,27 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() {
}
}
@StartableByRPC
@InitiatingFlow
class DummyFlow : FlowLogic<Unit>() {
@Suspendable
override fun call() = Unit
}
@After
override fun tearDown() {
db.close()
db.close()
}
private fun encodePassword(s: String) = encodePassword(s, passwordEncryption)
}
private data class UserAndRoles(val username: String, val password: String, val roles: List<String>)
private data class RoleAndPermissions(val role: String, val permissions: List<String>)
/*
* Manage in-memory DB mocking a users database with the schema expected by Node's security manager
*/
private class UsersDB : AutoCloseable {
val jdbcUrl: String
@ -261,12 +266,6 @@ private class UsersDB : AutoCloseable {
}
}
fun deleteRole(role: String) {
session {
it.execute("DELETE FROM role_permissions WHERE role_name = '$role'")
}
}
fun deleteUser(username: String) {
session {
it.execute("DELETE FROM users WHERE username = '$username'")
@ -307,4 +306,22 @@ private class UsersDB : AutoCloseable {
}
}
}
}
}
/*
* Sample of hardcoded hashes to watch for format backward compatibility
*/
private val hashedPasswords = mapOf(
PasswordEncryption.SHIRO_1_CRYPT to mapOf(
"foo" to "\$shiro1\$SHA-256$500000\$WSiEVj6q8d02sFcCk1dkoA==\$MBkU/ghdD9ovoDerdzNfkXdP9Bdhmok7tidvVIqGzcA=",
"bar" to "\$shiro1\$SHA-256$500000\$Q6dmdY1uVMm0LYAWaOHtCA==\$u7NbFaj9tHf2RTW54jedLPiOiGjJv0RVEPIjVquJuYY=",
"test" to "\$shiro1\$SHA-256$500000\$F6CWSFDDxGTlzvREwih8Gw==\$DQhyAPoUw3RdvNYJ1aubCnzEIXm+szGQ3HplaG+euz8="))
/*
* A functional object for producing password encoded according to the given scheme.
*/
private fun encodePassword(s: String, format: PasswordEncryption) = when (format) {
PasswordEncryption.NONE -> s
PasswordEncryption.SHIRO_1_CRYPT -> hashedPasswords[format]!![s] ?:
DefaultPasswordService().encryptPassword(s.toCharArray())
}

View File

@ -12,6 +12,9 @@ import net.corda.nodeapi.internal.config.User
import net.corda.testing.*
import net.corda.testing.common.internal.ProjectStructure.projectRootDir
import net.corda.testing.driver.driver
import net.corda.testing.internal.IntegrationTest
import net.corda.testing.internal.IntegrationTestSchemas
import net.corda.testing.internal.toDatabaseSchemaNames
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.ClassRule
@ -24,7 +27,7 @@ class BootTests : IntegrationTest() {
companion object {
@ClassRule @JvmField
val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, DUMMY_BANK_A_NAME)
.map { it.toDatabaseSchemaNames("", "_10000","_10003") }.flatten().toTypedArray())
.map { it.toDatabaseSchemaNames("", "_10000", "_10003") }.flatten().toTypedArray())
}
@Test

View File

@ -11,6 +11,9 @@ import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.internal.config.User
import net.corda.testing.*
import net.corda.testing.driver.driver
import net.corda.testing.internal.IntegrationTest
import net.corda.testing.internal.IntegrationTestSchemas
import net.corda.testing.internal.toDatabaseSchemaName
import org.assertj.core.api.Assertions.assertThat
import org.junit.ClassRule
import org.junit.Test

View File

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

View File

@ -18,7 +18,11 @@ import net.corda.testing.*
import net.corda.testing.driver.NodeHandle
import net.corda.testing.driver.PortAllocation
import net.corda.testing.driver.driver
import net.corda.testing.internal.IntegrationTest
import net.corda.testing.internal.IntegrationTestSchemas
import net.corda.testing.internal.performance.div
import net.corda.testing.internal.toDatabaseSchemaName
import net.corda.testing.internal.toDatabaseSchemaNames
import net.corda.testing.node.NotarySpec
import net.corda.testing.node.internal.InternalDriverDSL
import net.corda.testing.node.internal.performance.startPublishingFixedRateInjector

View File

@ -3,6 +3,9 @@ package net.corda.node
import com.google.common.base.Stopwatch
import net.corda.testing.*
import net.corda.testing.driver.driver
import net.corda.testing.internal.IntegrationTest
import net.corda.testing.internal.IntegrationTestSchemas
import net.corda.testing.internal.toDatabaseSchemaName
import org.junit.ClassRule
import org.junit.Ignore
import org.junit.Test

View File

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

View File

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

View File

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

View File

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

View File

@ -11,13 +11,16 @@ import net.corda.core.utilities.getOrThrow
import net.corda.finance.POUNDS
import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.flows.CashPaymentFlow
import net.corda.finance.schemas.CashSchemaV1
import net.corda.node.services.Permissions.Companion.invokeRpc
import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.internal.config.User
import net.corda.testing.*
import net.corda.testing.driver.NodeHandle
import net.corda.testing.driver.driver
import net.corda.testing.internal.IntegrationTest
import net.corda.testing.internal.IntegrationTestSchemas
import net.corda.testing.internal.toDatabaseSchemaName
import net.corda.testing.internal.toDatabaseSchemaNames
import net.corda.testing.node.ClusterSpec
import net.corda.testing.node.NotarySpec
import org.assertj.core.api.Assertions.assertThat

View File

@ -20,6 +20,8 @@ import net.corda.nodeapi.internal.network.NotaryInfo
import net.corda.testing.*
import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.contracts.DummyContract
import net.corda.testing.internal.IntegrationTest
import net.corda.testing.internal.IntegrationTestSchemas
import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockNodeParameters
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties

View File

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

View File

@ -21,6 +21,9 @@ import net.corda.testing.SerializationEnvironmentRule
import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.driver.NodeHandle
import net.corda.testing.driver.PortAllocation
import net.corda.testing.internal.IntegrationTest
import net.corda.testing.internal.IntegrationTestSchemas
import net.corda.testing.internal.toDatabaseSchemaName
import net.corda.testing.node.internal.CompatibilityZoneParams
import net.corda.testing.node.internal.internalDriver
import net.corda.testing.node.internal.network.NetworkMapServer

View File

@ -7,6 +7,8 @@ import net.corda.core.utilities.NetworkHostAndPort
import net.corda.node.internal.Node
import net.corda.node.internal.StartedNode
import net.corda.testing.*
import net.corda.testing.internal.IntegrationTestSchemas
import net.corda.testing.internal.toDatabaseSchemaName
import net.corda.testing.node.internal.NodeBasedTest
import org.junit.Before
import org.junit.ClassRule

View File

@ -8,6 +8,8 @@ import net.corda.core.identity.Party
import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.unwrap
import net.corda.testing.*
import net.corda.testing.internal.IntegrationTestSchemas
import net.corda.testing.internal.toDatabaseSchemaName
import net.corda.testing.node.internal.NodeBasedTest
import net.corda.testing.node.startFlow
import org.assertj.core.api.Assertions.assertThat

View File

@ -13,6 +13,9 @@ import net.corda.testing.*
import net.corda.testing.contracts.DummyContract
import net.corda.testing.contracts.DummyState
import net.corda.testing.driver.driver
import net.corda.testing.internal.IntegrationTest
import net.corda.testing.internal.IntegrationTestSchemas
import net.corda.testing.internal.toDatabaseSchemaName
import org.junit.ClassRule
import org.junit.Test
import kotlin.test.assertEquals

View File

@ -3,6 +3,9 @@ package net.corda.node.services.vault
import net.corda.core.identity.CordaX500Name
import net.corda.testing.*
import net.corda.testing.internal.IntegrationTest
import net.corda.testing.internal.IntegrationTestSchemas
import net.corda.testing.internal.toDatabaseSchemaName
import org.junit.*
class VaultQueryIntegrationTests : VaultQueryTests() {

View File

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

View File

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

View File

@ -26,7 +26,9 @@ import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEERS_PREF
import net.corda.nodeapi.internal.config.SSLConfiguration
import net.corda.nodeapi.internal.config.User
import net.corda.testing.*
import net.corda.testing.internal.IntegrationTestSchemas
import net.corda.testing.internal.configureTestSSL
import net.corda.testing.internal.toDatabaseSchemaName
import net.corda.testing.node.internal.NodeBasedTest
import net.corda.testing.node.startFlow
import org.apache.activemq.artemis.api.core.ActiveMQNonExistentQueueException

View File

@ -17,14 +17,14 @@ import net.corda.node.internal.StartedNode
import net.corda.node.services.messaging.MessagingService
import net.corda.node.services.messaging.ReceivedMessage
import net.corda.node.services.messaging.send
import net.corda.node.services.transactions.RaftValidatingNotaryService
import net.corda.testing.*
import net.corda.node.services.messaging.*
import net.corda.testing.ALICE_NAME
import net.corda.testing.chooseIdentity
import net.corda.testing.driver.DriverDSL
import net.corda.testing.driver.NodeHandle
import net.corda.testing.driver.driver
import net.corda.testing.internal.IntegrationTest
import net.corda.testing.internal.IntegrationTestSchemas
import net.corda.testing.internal.toDatabaseSchemaName
import net.corda.testing.node.ClusterSpec
import net.corda.testing.node.NotarySpec
import org.assertj.core.api.Assertions.assertThat

View File

@ -22,6 +22,10 @@ import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.internal.config.User
import net.corda.testing.*
import net.corda.testing.driver.driver
import net.corda.testing.internal.IntegrationTest
import net.corda.testing.internal.IntegrationTestSchemas
import net.corda.testing.internal.toDatabaseSchemaName
import net.corda.testing.internal.toDatabaseSchemaNames
import org.junit.Assume.assumeFalse
import org.junit.ClassRule
import org.junit.Test
@ -36,7 +40,7 @@ class NodeStatePersistenceTests : IntegrationTest() {
companion object {
@ClassRule @JvmField
val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, DUMMY_BANK_A_NAME)
.map { it.toDatabaseSchemaNames("", "_10000","_10003","_10006") }.flatten().toTypedArray(),
.map { it.toDatabaseSchemaNames("", "_10000", "_10003", "_10006") }.flatten().toTypedArray(),
DUMMY_NOTARY_NAME.toDatabaseSchemaName())
}
@Test

View File

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

View File

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

View File

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

View File

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

View File

@ -203,8 +203,16 @@ data class SecurityConfiguration(val authService: SecurityConfiguration.AuthServ
data class Options(val cache: Options.Cache?) {
// Cache parameters
data class Cache(val expireAfterSecs: Long, val maxEntries: Long)
data class Cache(val expireAfterSecs: Long, val maxEntries: Long) {
init {
require(expireAfterSecs >= 0) {
"Expected positive value for 'cache.expireAfterSecs'"
}
require(maxEntries > 0) {
"Expected positive value for 'cache.maxEntries'"
}
}
}
}
// Provider of users credentials and permissions data
@ -228,12 +236,13 @@ data class SecurityConfiguration(val authService: SecurityConfiguration.AuthServ
AuthDataSourceType.DB -> AuthServiceId("REMOTE_DATABASE")
}
fun fromUsers(users: List<User>) = AuthService(
dataSource = DataSource(
type = AuthDataSourceType.INMEMORY,
users = users,
passwordEncryption = PasswordEncryption.NONE),
id = AuthServiceId("NODE_CONFIG"))
fun fromUsers(users: List<User>, encryption: PasswordEncryption = PasswordEncryption.NONE) =
AuthService(
dataSource = DataSource(
type = AuthDataSourceType.INMEMORY,
users = users,
passwordEncryption = encryption),
id = AuthServiceId("NODE_CONFIG"))
}
}
}

View File

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

View File

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

View File

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

View File

@ -40,6 +40,18 @@ class FlowLogicRefFactoryImpl(private val classloader: ClassLoader) : SingletonS
return createForRPC(flowClass, *args)
}
override fun create(flowClassName: String, vararg args: Any?): FlowLogicRef {
val flowClass = Class.forName(flowClassName, true, classloader).asSubclass(FlowLogic::class.java)
if (flowClass == null) {
throw IllegalArgumentException("The class $flowClassName is not a subclass of FlowLogic.")
} else {
if (!flowClass.isAnnotationPresent(SchedulableFlow::class.java)) {
throw IllegalFlowLogicException(flowClass, "because it's not a schedulable flow")
}
return createForRPC(flowClass, *args)
}
}
override fun createForRPC(flowClass: Class<out FlowLogic<*>>, vararg args: Any?): FlowLogicRef {
// TODO: This is used via RPC but it's probably better if we pass in argument names and values explicitly
// to avoid requiring only a single constructor.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -24,6 +24,7 @@ import net.corda.testing.node.startFlow
import org.assertj.core.api.Assertions.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Ignore
import org.junit.Test
import java.time.Instant
import java.util.*
@ -99,6 +100,7 @@ class NotaryServiceTests {
assertThat(ex.error).isInstanceOf(NotaryError.TimeWindowInvalid::class.java)
}
@Ignore("Only applies to deterministic signature schemes (e.g. EdDSA) and when deterministic metadata is attached (no timestamps or nonces)")
@Test
fun `should sign identical transaction multiple times (signing is idempotent)`() {
val stx = run {

View File

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

View File

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

View File

@ -8,6 +8,9 @@ import net.corda.nodeapi.internal.config.User
import net.corda.testing.*
import net.corda.testing.driver.PortAllocation
import net.corda.testing.driver.driver
import net.corda.testing.internal.IntegrationTest
import net.corda.testing.internal.IntegrationTestSchemas
import net.corda.testing.internal.toDatabaseSchemaName
import org.junit.ClassRule
import org.junit.Test
import java.util.concurrent.CompletableFuture.supplyAsync

View File

@ -2,7 +2,8 @@ package net.corda.bank
import net.corda.finance.DOLLARS
import net.corda.finance.POUNDS
import net.corda.testing.*
import net.corda.testing.internal.IntegrationTest
import net.corda.testing.internal.IntegrationTestSchemas
import net.corda.testing.node.internal.demorun.deployNodesThen
import org.junit.ClassRule
import org.junit.Test

View File

@ -14,6 +14,9 @@ import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.internal.config.User
import net.corda.testing.*
import net.corda.testing.driver.driver
import net.corda.testing.internal.IntegrationTest
import net.corda.testing.internal.IntegrationTestSchemas
import net.corda.testing.internal.toDatabaseSchemaName
import org.junit.ClassRule
import org.junit.Test

View File

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

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