mirror of
https://github.com/corda/corda.git
synced 2024-12-28 16:58:55 +00:00
parent
d31f27571b
commit
d48bc41a7c
@ -1862,7 +1862,7 @@ public @interface net.corda.core.node.services.CordaService
|
|||||||
@org.jetbrains.annotations.NotNull public static final String ID_PREFIX = "corda.notary."
|
@org.jetbrains.annotations.NotNull public static final String ID_PREFIX = "corda.notary."
|
||||||
##
|
##
|
||||||
public static final class net.corda.core.node.services.NotaryService$Companion extends java.lang.Object
|
public static final class net.corda.core.node.services.NotaryService$Companion extends java.lang.Object
|
||||||
@kotlin.Deprecated @org.jetbrains.annotations.NotNull public final String constructId(boolean, boolean, boolean, boolean)
|
@org.jetbrains.annotations.NotNull public final String constructId(boolean, boolean, boolean, boolean)
|
||||||
##
|
##
|
||||||
public abstract class net.corda.core.node.services.PartyInfo extends java.lang.Object
|
public abstract class net.corda.core.node.services.PartyInfo extends java.lang.Object
|
||||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.identity.Party getParty()
|
@org.jetbrains.annotations.NotNull public abstract net.corda.core.identity.Party getParty()
|
||||||
|
@ -2,7 +2,7 @@ package net.corda.core.identity
|
|||||||
|
|
||||||
import com.google.common.collect.ImmutableSet
|
import com.google.common.collect.ImmutableSet
|
||||||
import net.corda.core.internal.LegalNameValidator
|
import net.corda.core.internal.LegalNameValidator
|
||||||
import net.corda.core.internal.unspecifiedCountry
|
import net.corda.core.internal.VisibleForTesting
|
||||||
import net.corda.core.internal.x500Name
|
import net.corda.core.internal.x500Name
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import org.bouncycastle.asn1.ASN1Encodable
|
import org.bouncycastle.asn1.ASN1Encodable
|
||||||
@ -36,9 +36,7 @@ data class CordaX500Name(val commonName: String?,
|
|||||||
val locality: String,
|
val locality: String,
|
||||||
val state: String?,
|
val state: String?,
|
||||||
val country: String) {
|
val country: String) {
|
||||||
constructor(commonName: String, organisation: String, locality: String, country: String) :
|
constructor(commonName: String, organisation: String, locality: String, country: String) : this(commonName = commonName, organisationUnit = null, organisation = organisation, locality = locality, state = null, country = country)
|
||||||
this(commonName = commonName, organisationUnit = null, organisation = organisation, locality = locality, state = null, country = country)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param organisation name of the organisation.
|
* @param organisation name of the organisation.
|
||||||
* @param locality locality of the organisation, typically nearest major city.
|
* @param locality locality of the organisation, typically nearest major city.
|
||||||
@ -81,6 +79,8 @@ data class CordaX500Name(val commonName: String?,
|
|||||||
const val MAX_LENGTH_ORGANISATION_UNIT = 64
|
const val MAX_LENGTH_ORGANISATION_UNIT = 64
|
||||||
const val MAX_LENGTH_COMMON_NAME = 64
|
const val MAX_LENGTH_COMMON_NAME = 64
|
||||||
private val supportedAttributes = setOf(BCStyle.O, BCStyle.C, BCStyle.L, BCStyle.CN, BCStyle.ST, BCStyle.OU)
|
private val supportedAttributes = setOf(BCStyle.O, BCStyle.C, BCStyle.L, BCStyle.CN, BCStyle.ST, BCStyle.OU)
|
||||||
|
@VisibleForTesting
|
||||||
|
val unspecifiedCountry = "ZZ"
|
||||||
private val countryCodes: Set<String> = ImmutableSet.copyOf(Locale.getISOCountries() + unspecifiedCountry)
|
private val countryCodes: Set<String> = ImmutableSet.copyOf(Locale.getISOCountries() + unspecifiedCountry)
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun build(principal: X500Principal): CordaX500Name {
|
fun build(principal: X500Principal): CordaX500Name {
|
||||||
|
@ -5,7 +5,6 @@ package net.corda.core.internal
|
|||||||
import net.corda.core.cordapp.CordappProvider
|
import net.corda.core.cordapp.CordappProvider
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.sha256
|
import net.corda.core.crypto.sha256
|
||||||
import net.corda.core.identity.CordaX500Name
|
|
||||||
import net.corda.core.node.ServicesForResolution
|
import net.corda.core.node.ServicesForResolution
|
||||||
import net.corda.core.serialization.SerializationContext
|
import net.corda.core.serialization.SerializationContext
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
@ -119,8 +118,6 @@ fun Path.isDirectory(vararg options: LinkOption): Boolean = Files.isDirectory(th
|
|||||||
inline val Path.size: Long get() = Files.size(this)
|
inline val Path.size: Long get() = Files.size(this)
|
||||||
inline fun <R> Path.list(block: (Stream<Path>) -> R): R = Files.list(this).use(block)
|
inline fun <R> Path.list(block: (Stream<Path>) -> R): R = Files.list(this).use(block)
|
||||||
fun Path.deleteIfExists(): Boolean = Files.deleteIfExists(this)
|
fun Path.deleteIfExists(): Boolean = Files.deleteIfExists(this)
|
||||||
fun Path.reader(charset: Charset = UTF_8): BufferedReader = Files.newBufferedReader(this, charset)
|
|
||||||
fun Path.writer(charset: Charset = UTF_8, vararg options: OpenOption): BufferedWriter = Files.newBufferedWriter(this, charset, *options)
|
|
||||||
fun Path.readAll(): ByteArray = Files.readAllBytes(this)
|
fun Path.readAll(): ByteArray = Files.readAllBytes(this)
|
||||||
inline fun <R> Path.read(vararg options: OpenOption, block: (InputStream) -> R): R = Files.newInputStream(this, *options).use(block)
|
inline fun <R> Path.read(vararg options: OpenOption, block: (InputStream) -> R): R = Files.newInputStream(this, *options).use(block)
|
||||||
inline fun Path.write(createDirs: Boolean = false, vararg options: OpenOption = emptyArray(), block: (OutputStream) -> Unit) {
|
inline fun Path.write(createDirs: Boolean = false, vararg options: OpenOption = emptyArray(), block: (OutputStream) -> Unit) {
|
||||||
@ -319,8 +316,3 @@ fun ExecutorService.join() {
|
|||||||
// Try forever. Do not give up, tests use this method to assert the executor has no more tasks.
|
// Try forever. Do not give up, tests use this method to assert the executor has no more tasks.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
@VisibleForTesting
|
|
||||||
val CordaX500Name.Companion.unspecifiedCountry
|
|
||||||
get() = "ZZ"
|
|
||||||
|
@ -15,16 +15,22 @@ import java.security.PublicKey
|
|||||||
|
|
||||||
abstract class NotaryService : SingletonSerializeAsToken() {
|
abstract class NotaryService : SingletonSerializeAsToken() {
|
||||||
companion object {
|
companion object {
|
||||||
@Deprecated("No longer used")
|
|
||||||
const val ID_PREFIX = "corda.notary."
|
const val ID_PREFIX = "corda.notary."
|
||||||
@Deprecated("No longer used")
|
@JvmOverloads
|
||||||
fun constructId(validating: Boolean, raft: Boolean = false, bft: Boolean = false, custom: Boolean = false): String {
|
fun constructId(
|
||||||
require(Booleans.countTrue(raft, bft, custom) <= 1) { "At most one of raft, bft or custom may be true" }
|
validating: Boolean,
|
||||||
|
raft: Boolean = false,
|
||||||
|
bft: Boolean = false,
|
||||||
|
custom: Boolean = false,
|
||||||
|
mysql: Boolean = false
|
||||||
|
): String {
|
||||||
|
require(Booleans.countTrue(raft, bft, custom, mysql) <= 1) { "At most one of raft, bft, mysql or custom may be true" }
|
||||||
return StringBuffer(ID_PREFIX).apply {
|
return StringBuffer(ID_PREFIX).apply {
|
||||||
append(if (validating) "validating" else "simple")
|
append(if (validating) "validating" else "simple")
|
||||||
if (raft) append(".raft")
|
if (raft) append(".raft")
|
||||||
if (bft) append(".bft")
|
if (bft) append(".bft")
|
||||||
if (custom) append(".custom")
|
if (custom) append(".custom")
|
||||||
|
if (mysql) append(".mysql")
|
||||||
}.toString()
|
}.toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,8 @@ import net.corda.core.serialization.serialize
|
|||||||
import net.corda.core.utilities.OpaqueBytes
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
import net.corda.core.utilities.toBase58String
|
import net.corda.core.utilities.toBase58String
|
||||||
import net.corda.nodeapi.internal.crypto.*
|
import net.corda.nodeapi.internal.crypto.*
|
||||||
import net.corda.testing.SerializationEnvironmentRule
|
|
||||||
import net.corda.testing.internal.kryoSpecific
|
import net.corda.testing.internal.kryoSpecific
|
||||||
|
import net.corda.testing.SerializationEnvironmentRule
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.rules.TemporaryFolder
|
import org.junit.rules.TemporaryFolder
|
||||||
@ -24,7 +24,6 @@ class CompositeKeyTests {
|
|||||||
@Rule
|
@Rule
|
||||||
@JvmField
|
@JvmField
|
||||||
val testSerialization = SerializationEnvironmentRule()
|
val testSerialization = SerializationEnvironmentRule()
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
@JvmField
|
@JvmField
|
||||||
val tempFolder: TemporaryFolder = TemporaryFolder()
|
val tempFolder: TemporaryFolder = TemporaryFolder()
|
||||||
@ -41,9 +40,9 @@ class CompositeKeyTests {
|
|||||||
private val secureHash = message.sha256()
|
private val secureHash = message.sha256()
|
||||||
|
|
||||||
// By lazy is required so that the serialisers are configured before vals initialisation takes place (they internally invoke serialise).
|
// By lazy is required so that the serialisers are configured before vals initialisation takes place (they internally invoke serialise).
|
||||||
private val aliceSignature by lazy { aliceKey.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(alicePublicKey).schemeNumberID))) }
|
val aliceSignature by lazy { aliceKey.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(alicePublicKey).schemeNumberID))) }
|
||||||
private val bobSignature by lazy { bobKey.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(bobPublicKey).schemeNumberID))) }
|
val bobSignature by lazy { bobKey.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(bobPublicKey).schemeNumberID))) }
|
||||||
private val charlieSignature by lazy { charlieKey.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(charliePublicKey).schemeNumberID))) }
|
val charlieSignature by lazy { charlieKey.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(charliePublicKey).schemeNumberID))) }
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `(Alice) fulfilled by Alice signature`() {
|
fun `(Alice) fulfilled by Alice signature`() {
|
||||||
@ -338,7 +337,7 @@ class CompositeKeyTests {
|
|||||||
val ca = X509Utilities.createSelfSignedCACertificate(caName, caKeyPair)
|
val ca = X509Utilities.createSelfSignedCACertificate(caName, caKeyPair)
|
||||||
|
|
||||||
// Sign the composite key with the self sign CA.
|
// Sign the composite key with the self sign CA.
|
||||||
val compositeKeyCert = X509Utilities.createCertificate(CertificateType.LEGAL_IDENTITY, ca, caKeyPair, caName, compositeKey)
|
val compositeKeyCert = X509Utilities.createCertificate(CertificateType.LEGAL_IDENTITY, ca, caKeyPair, caName.copy(commonName = "CompositeKey"), compositeKey)
|
||||||
|
|
||||||
// Store certificate to keystore.
|
// Store certificate to keystore.
|
||||||
val keystorePath = tempFolder.root.toPath() / "keystore.jks"
|
val keystorePath = tempFolder.root.toPath() / "keystore.jks"
|
||||||
|
@ -159,7 +159,7 @@ class AttachmentSerializationTest {
|
|||||||
|
|
||||||
private fun rebootClientAndGetAttachmentContent(checkAttachmentsOnLoad: Boolean = true): String {
|
private fun rebootClientAndGetAttachmentContent(checkAttachmentsOnLoad: Boolean = true): String {
|
||||||
client.dispose()
|
client.dispose()
|
||||||
client = mockNet.createNode(MockNodeParameters(client.internals.id, client.internals.configuration.myLegalName), { args ->
|
client = mockNet.createNode(MockNodeParameters(client.internals.id), { args ->
|
||||||
object : MockNetwork.MockNode(args) {
|
object : MockNetwork.MockNode(args) {
|
||||||
override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = checkAttachmentsOnLoad }
|
override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = checkAttachmentsOnLoad }
|
||||||
}
|
}
|
||||||
|
@ -92,6 +92,7 @@ nodes. Here is an example ``Cordform`` task called ``deployNodes`` that creates
|
|||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=PartyA,L=London,C=GB"
|
name "O=PartyA,L=London,C=GB"
|
||||||
|
advertisedServices = []
|
||||||
p2pPort 10005
|
p2pPort 10005
|
||||||
rpcPort 10006
|
rpcPort 10006
|
||||||
webPort 10007
|
webPort 10007
|
||||||
@ -102,6 +103,7 @@ nodes. Here is an example ``Cordform`` task called ``deployNodes`` that creates
|
|||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=PartyB,L=New York,C=US"
|
name "O=PartyB,L=New York,C=US"
|
||||||
|
advertisedServices = []
|
||||||
p2pPort 10009
|
p2pPort 10009
|
||||||
rpcPort 10010
|
rpcPort 10010
|
||||||
webPort 10011
|
webPort 10011
|
||||||
|
@ -22,7 +22,7 @@ service.
|
|||||||
directory "./build/nodes"
|
directory "./build/nodes"
|
||||||
node {
|
node {
|
||||||
name "O=Controller,L=London,C=GB"
|
name "O=Controller,L=London,C=GB"
|
||||||
notary = [validating : true]
|
advertisedServices = ["corda.notary.validating"]
|
||||||
p2pPort 10002
|
p2pPort 10002
|
||||||
rpcPort 10003
|
rpcPort 10003
|
||||||
cordapps = ["net.corda:corda-finance:$corda_release_version"]
|
cordapps = ["net.corda:corda-finance:$corda_release_version"]
|
||||||
|
@ -3,6 +3,7 @@ package com.r3.corda.networkmanage.doorman
|
|||||||
import com.nhaarman.mockito_kotlin.doReturn
|
import com.nhaarman.mockito_kotlin.doReturn
|
||||||
import com.nhaarman.mockito_kotlin.whenever
|
import com.nhaarman.mockito_kotlin.whenever
|
||||||
import com.r3.corda.networkmanage.common.persistence.configureDatabase
|
import com.r3.corda.networkmanage.common.persistence.configureDatabase
|
||||||
|
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
||||||
import com.r3.corda.networkmanage.common.utils.toX509Certificate
|
import com.r3.corda.networkmanage.common.utils.toX509Certificate
|
||||||
import com.r3.corda.networkmanage.doorman.signer.LocalSigner
|
import com.r3.corda.networkmanage.doorman.signer.LocalSigner
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
@ -29,6 +30,7 @@ import net.corda.testing.SerializationEnvironmentRule
|
|||||||
import net.corda.testing.common.internal.testNetworkParameters
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
import net.corda.testing.internal.rigorousMock
|
import net.corda.testing.internal.rigorousMock
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
import org.bouncycastle.cert.X509CertificateHolder
|
||||||
|
import org.junit.Ignore
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.rules.TemporaryFolder
|
import org.junit.rules.TemporaryFolder
|
||||||
@ -76,7 +78,7 @@ class DoormanIntegrationTest {
|
|||||||
|
|
||||||
loadKeyStore(config.nodeKeystore, config.keyStorePassword).apply {
|
loadKeyStore(config.nodeKeystore, config.keyStorePassword).apply {
|
||||||
assert(containsAlias(X509Utilities.CORDA_CLIENT_CA))
|
assert(containsAlias(X509Utilities.CORDA_CLIENT_CA))
|
||||||
assertEquals(ALICE_NAME.x500Principal, getX509Certificate(X509Utilities.CORDA_CLIENT_CA).subjectX500Principal)
|
assertEquals(ALICE_NAME.copy(commonName = X509Utilities.CORDA_CLIENT_CA_CN).x500Principal, getX509Certificate(X509Utilities.CORDA_CLIENT_CA).subjectX500Principal)
|
||||||
assertEquals(listOf(intermediateCACert.cert, rootCACert.cert), getCertificateChain(X509Utilities.CORDA_CLIENT_CA).drop(1).toList())
|
assertEquals(listOf(intermediateCACert.cert, rootCACert.cert), getCertificateChain(X509Utilities.CORDA_CLIENT_CA).drop(1).toList())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,18 +120,13 @@ class DoormanIntegrationTest {
|
|||||||
|
|
||||||
// Publish NodeInfo
|
// Publish NodeInfo
|
||||||
val networkMapClient = NetworkMapClient(config.compatibilityZoneURL!!, rootCertAndKey.certificate.cert)
|
val networkMapClient = NetworkMapClient(config.compatibilityZoneURL!!, rootCertAndKey.certificate.cert)
|
||||||
|
val certs = loadKeyStore(config.nodeKeystore, config.keyStorePassword).getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
|
||||||
val keyStore = loadKeyStore(config.nodeKeystore, config.keyStorePassword)
|
val keyPair = loadKeyStore(config.nodeKeystore, config.keyStorePassword).getKeyPair(X509Utilities.CORDA_CLIENT_CA, config.keyStorePassword)
|
||||||
val clientCertPath = keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
|
val nodeInfo = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(buildCertPath(*certs))), 1, serial = 1L)
|
||||||
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 nodeInfo = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(certPath)), 1, serial = 1L)
|
|
||||||
val nodeInfoBytes = nodeInfo.serialize()
|
val nodeInfoBytes = nodeInfo.serialize()
|
||||||
|
|
||||||
// When
|
// When
|
||||||
val signedNodeInfo = SignedNodeInfo(nodeInfoBytes, listOf(identityKeyPair.private.sign(nodeInfoBytes.bytes)))
|
val signedNodeInfo = SignedNodeInfo(nodeInfoBytes, listOf(keyPair.sign(nodeInfoBytes)))
|
||||||
networkMapClient.publish(signedNodeInfo)
|
networkMapClient.publish(signedNodeInfo)
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
@ -140,7 +137,7 @@ class DoormanIntegrationTest {
|
|||||||
doorman.close()
|
doorman.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createConfig(): NodeConfiguration {
|
fun createConfig(): NodeConfiguration {
|
||||||
return rigorousMock<NodeConfiguration>().also {
|
return rigorousMock<NodeConfiguration>().also {
|
||||||
doReturn(tempFolder.root.toPath()).whenever(it).baseDirectory
|
doReturn(tempFolder.root.toPath()).whenever(it).baseDirectory
|
||||||
doReturn(ALICE_NAME).whenever(it).myLegalName
|
doReturn(ALICE_NAME).whenever(it).myLegalName
|
||||||
|
@ -1,28 +1,30 @@
|
|||||||
package com.r3.corda.networkmanage.common.persistence
|
package com.r3.corda.networkmanage.common.persistence
|
||||||
|
|
||||||
import com.r3.corda.networkmanage.common.persistence.entity.CertificateDataEntity
|
import com.r3.corda.networkmanage.common.persistence.entity.*
|
||||||
import com.r3.corda.networkmanage.common.persistence.entity.CertificateSigningRequestEntity
|
|
||||||
import com.r3.corda.networkmanage.common.persistence.entity.NodeInfoEntity
|
|
||||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.sha256
|
import net.corda.core.crypto.sha256
|
||||||
import net.corda.core.internal.CertRole
|
import net.corda.core.identity.CordaX500Name
|
||||||
|
import net.corda.core.serialization.SerializedBytes
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.nodeapi.internal.SignedNodeInfo
|
import net.corda.nodeapi.internal.SignedNodeInfo
|
||||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||||
import net.corda.nodeapi.internal.persistence.TransactionIsolationLevel
|
import net.corda.nodeapi.internal.persistence.TransactionIsolationLevel
|
||||||
import java.security.cert.CertPath
|
import java.security.cert.CertPath
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Database implementation of the [NetworkMapStorage] interface
|
* Database implementation of the [NetworkMapStorage] interface
|
||||||
*/
|
*/
|
||||||
class PersistentNodeInfoStorage(private val database: CordaPersistence) : NodeInfoStorage {
|
class PersistentNodeInfoStorage(private val database: CordaPersistence) : NodeInfoStorage {
|
||||||
override fun putNodeInfo(signedNodeInfo: SignedNodeInfo): SecureHash {
|
override fun putNodeInfo(signedNodeInfo: SignedNodeInfo): SecureHash = database.transaction(TransactionIsolationLevel.SERIALIZABLE) {
|
||||||
return database.transaction(TransactionIsolationLevel.SERIALIZABLE) {
|
|
||||||
val nodeInfo = signedNodeInfo.verified()
|
val nodeInfo = signedNodeInfo.verified()
|
||||||
val nodeCaCert = nodeInfo.legalIdentitiesAndCerts[0].certPath.certificates.find { CertRole.extract(it) == CertRole.NODE_CA }
|
val orgName = nodeInfo.legalIdentities.first().name.organisation
|
||||||
|
// TODO: use cert extension to identify NodeCA cert when Ross's work is in.
|
||||||
|
val nodeCACert = nodeInfo.legalIdentitiesAndCerts.first().certPath.certificates.map { it as X509Certificate }
|
||||||
|
.find { CordaX500Name.build(it.issuerX500Principal).organisation != orgName && CordaX500Name.build(it.subjectX500Principal).organisation == orgName }
|
||||||
|
|
||||||
val request = nodeCaCert?.let {
|
val request = nodeCACert?.let {
|
||||||
singleRequestWhere(CertificateDataEntity::class.java) { builder, path ->
|
singleRequestWhere(CertificateDataEntity::class.java) { builder, path ->
|
||||||
val certPublicKeyHashEq = builder.equal(path.get<String>(CertificateDataEntity::publicKeyHash.name), it.publicKey.encoded.sha256().toString())
|
val certPublicKeyHashEq = builder.equal(path.get<String>(CertificateDataEntity::publicKeyHash.name), it.publicKey.encoded.sha256().toString())
|
||||||
val certStatusValid = builder.equal(path.get<CertificateStatus>(CertificateDataEntity::certificateStatus.name), CertificateStatus.VALID)
|
val certStatusValid = builder.equal(path.get<CertificateStatus>(CertificateDataEntity::certificateStatus.name), CertificateStatus.VALID)
|
||||||
@ -30,7 +32,6 @@ class PersistentNodeInfoStorage(private val database: CordaPersistence) : NodeIn
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
request ?: throw IllegalArgumentException("Unknown node info, this public key is not registered with the network management service.")
|
request ?: throw IllegalArgumentException("Unknown node info, this public key is not registered with the network management service.")
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Delete any previous [NodeInfoEntity] instance for this CSR
|
* Delete any previous [NodeInfoEntity] instance for this CSR
|
||||||
* Possibly it should be moved at the network signing process at the network signing process
|
* Possibly it should be moved at the network signing process at the network signing process
|
||||||
@ -50,11 +51,15 @@ class PersistentNodeInfoStorage(private val database: CordaPersistence) : NodeIn
|
|||||||
session.save(hashedNodeInfo)
|
session.save(hashedNodeInfo)
|
||||||
hash
|
hash
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
override fun getNodeInfo(nodeInfoHash: SecureHash): SignedNodeInfo? {
|
override fun getNodeInfo(nodeInfoHash: SecureHash): SignedNodeInfo? {
|
||||||
return database.transaction {
|
return database.transaction {
|
||||||
session.find(NodeInfoEntity::class.java, nodeInfoHash.toString())?.signedNodeInfo()
|
val nodeInfoEntity = session.find(NodeInfoEntity::class.java, nodeInfoHash.toString())
|
||||||
|
if (nodeInfoEntity == null) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
nodeInfoEntity.signedNodeInfo()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,10 +2,10 @@ package com.r3.corda.networkmanage.doorman.signer
|
|||||||
|
|
||||||
import com.r3.corda.networkmanage.common.signer.Signer
|
import com.r3.corda.networkmanage.common.signer.Signer
|
||||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
||||||
|
import com.r3.corda.networkmanage.common.utils.toX509Certificate
|
||||||
import com.r3.corda.networkmanage.common.utils.withCert
|
import com.r3.corda.networkmanage.common.utils.withCert
|
||||||
import net.corda.core.crypto.sign
|
import net.corda.core.crypto.sign
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.cert
|
|
||||||
import net.corda.core.internal.toX509CertHolder
|
import net.corda.core.internal.toX509CertHolder
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
@ -33,14 +33,13 @@ class LocalSigner(private val caKeyPair: KeyPair, private val caCertPath: Array<
|
|||||||
val nameConstraints = NameConstraints(
|
val nameConstraints = NameConstraints(
|
||||||
arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, request.subject))),
|
arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, request.subject))),
|
||||||
arrayOf())
|
arrayOf())
|
||||||
val nodeCaCert = X509Utilities.createCertificate(
|
val clientCertificate = X509Utilities.createCertificate(CertificateType.NODE_CA,
|
||||||
CertificateType.NODE_CA,
|
|
||||||
caCertPath.first().toX509CertHolder(),
|
caCertPath.first().toX509CertHolder(),
|
||||||
caKeyPair,
|
caKeyPair,
|
||||||
CordaX500Name.parse(request.subject.toString()),
|
CordaX500Name.parse(request.subject.toString()).copy(commonName = X509Utilities.CORDA_CLIENT_CA_CN),
|
||||||
request.publicKey,
|
request.publicKey,
|
||||||
nameConstraints = nameConstraints)
|
nameConstraints = nameConstraints).toX509Certificate()
|
||||||
return buildCertPath(nodeCaCert.cert, *caCertPath)
|
return buildCertPath(clientCertificate, *caCertPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun sign(data: ByteArray): DigitalSignatureWithCert {
|
override fun sign(data: ByteArray): DigitalSignatureWithCert {
|
||||||
|
@ -7,6 +7,7 @@ import net.corda.core.internal.toX509CertHolder
|
|||||||
import net.corda.core.internal.x500Name
|
import net.corda.core.internal.x500Name
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
import net.corda.nodeapi.internal.crypto.getX509Certificate
|
import net.corda.nodeapi.internal.crypto.getX509Certificate
|
||||||
import org.bouncycastle.asn1.ASN1EncodableVector
|
import org.bouncycastle.asn1.ASN1EncodableVector
|
||||||
import org.bouncycastle.asn1.ASN1Sequence
|
import org.bouncycastle.asn1.ASN1Sequence
|
||||||
@ -183,7 +184,7 @@ object X509Utilities {
|
|||||||
val certificateType = CertificateType.NODE_CA
|
val certificateType = CertificateType.NODE_CA
|
||||||
val validityWindow = getCertificateValidityWindow(0, validDays, issuerCertificate.notBefore, issuerCertificate.notAfter)
|
val validityWindow = getCertificateValidityWindow(0, validDays, issuerCertificate.notBefore, issuerCertificate.notAfter)
|
||||||
val serial = BigInteger.valueOf(random63BitValue(provider))
|
val serial = BigInteger.valueOf(random63BitValue(provider))
|
||||||
val subject = CordaX500Name.parse(jcaRequest.subject.toString()).x500Name
|
val subject = CordaX500Name.parse(jcaRequest.subject.toString()).copy(commonName = X509Utilities.CORDA_CLIENT_CA_CN).x500Name
|
||||||
val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(jcaRequest.publicKey.encoded))
|
val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(jcaRequest.publicKey.encoded))
|
||||||
val keyPurposes = DERSequence(ASN1EncodableVector().apply { certificateType.purposes.forEach { add(it) } })
|
val keyPurposes = DERSequence(ASN1EncodableVector().apply { certificateType.purposes.forEach { add(it) } })
|
||||||
val builder = JcaX509v3CertificateBuilder(issuerCertificate.subject, serial, validityWindow.first, validityWindow.second, subject, jcaRequest.publicKey)
|
val builder = JcaX509v3CertificateBuilder(issuerCertificate.subject, serial, validityWindow.first, validityWindow.second, subject, jcaRequest.publicKey)
|
||||||
|
@ -1,21 +1,24 @@
|
|||||||
package com.r3.corda.networkmanage.common.persistence
|
package com.r3.corda.networkmanage.common.persistence
|
||||||
|
|
||||||
import com.r3.corda.networkmanage.TestBase
|
import com.r3.corda.networkmanage.TestBase
|
||||||
|
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
||||||
|
import com.r3.corda.networkmanage.common.utils.toX509Certificate
|
||||||
import com.r3.corda.networkmanage.common.utils.withCert
|
import com.r3.corda.networkmanage.common.utils.withCert
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.crypto.sign
|
import net.corda.core.crypto.sign
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
import net.corda.core.internal.cert
|
import net.corda.core.internal.cert
|
||||||
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.nodeapi.internal.SignedNodeInfo
|
import net.corda.nodeapi.internal.SignedNodeInfo
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
import net.corda.nodeapi.internal.network.NetworkMap
|
import net.corda.nodeapi.internal.network.NetworkMap
|
||||||
import net.corda.nodeapi.internal.network.SignedNetworkMap
|
import net.corda.nodeapi.internal.network.SignedNetworkMap
|
||||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||||
import net.corda.testing.common.internal.testNetworkParameters
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
import net.corda.testing.internal.TestNodeInfoBuilder
|
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
@ -28,7 +31,6 @@ class DBNetworkMapStorageTest : TestBase() {
|
|||||||
private lateinit var requestStorage: CertificationRequestStorage
|
private lateinit var requestStorage: CertificationRequestStorage
|
||||||
private lateinit var nodeInfoStorage: NodeInfoStorage
|
private lateinit var nodeInfoStorage: NodeInfoStorage
|
||||||
private lateinit var persistence: CordaPersistence
|
private lateinit var persistence: CordaPersistence
|
||||||
|
|
||||||
private val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
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 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 intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
@ -51,8 +53,18 @@ class DBNetworkMapStorageTest : TestBase() {
|
|||||||
fun `signNetworkMap creates current network map`() {
|
fun `signNetworkMap creates current network map`() {
|
||||||
// given
|
// given
|
||||||
// Create node info.
|
// Create node info.
|
||||||
val signedNodeInfo = createValidSignedNodeInfo("Test")
|
val organisation = "Test"
|
||||||
val nodeInfoHash = nodeInfoStorage.putNodeInfo(signedNodeInfo)
|
val requestId = requestStorage.saveRequest(createRequest(organisation).first)
|
||||||
|
requestStorage.markRequestTicketCreated(requestId)
|
||||||
|
requestStorage.approveRequest(requestId, "TestUser")
|
||||||
|
val keyPair = Crypto.generateKeyPair()
|
||||||
|
val clientCert = X509Utilities.createCertificate(CertificateType.NODE_CA, intermediateCACert, intermediateCAKey, CordaX500Name(organisation = organisation, locality = "London", country = "GB"), keyPair.public)
|
||||||
|
val certPath = buildCertPath(clientCert.toX509Certificate(), intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
|
||||||
|
requestStorage.putCertificatePath(requestId, certPath, emptyList())
|
||||||
|
val nodeInfo = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(certPath)), 1, serial = 1L)
|
||||||
|
// Put signed node info data
|
||||||
|
val nodeInfoBytes = nodeInfo.serialize()
|
||||||
|
val nodeInfoHash = nodeInfoStorage.putNodeInfo(SignedNodeInfo(nodeInfoBytes, listOf(keyPair.sign(nodeInfoBytes))))
|
||||||
|
|
||||||
// Create network parameters
|
// Create network parameters
|
||||||
val networkParametersHash = networkMapStorage.saveNetworkParameters(testNetworkParameters(emptyList()))
|
val networkParametersHash = networkMapStorage.saveNetworkParameters(testNetworkParameters(emptyList()))
|
||||||
@ -91,11 +103,13 @@ class DBNetworkMapStorageTest : TestBase() {
|
|||||||
// Create network parameters
|
// Create network parameters
|
||||||
val networkMapParametersHash = networkMapStorage.saveNetworkParameters(createNetworkParameters(1))
|
val networkMapParametersHash = networkMapStorage.saveNetworkParameters(createNetworkParameters(1))
|
||||||
// Create empty network map
|
// Create empty network map
|
||||||
|
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
|
val intermediateCert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, intermediateCACert, intermediateCAKey, CordaX500Name(organisation = "Corda", locality = "London", country = "GB"), keyPair.public)
|
||||||
|
|
||||||
// Sign network map making it current network map
|
// Sign network map making it current network map
|
||||||
val networkMap = NetworkMap(emptyList(), networkMapParametersHash)
|
val networkMap = NetworkMap(emptyList(), networkMapParametersHash)
|
||||||
val serializedNetworkMap = networkMap.serialize()
|
val serializedNetworkMap = networkMap.serialize()
|
||||||
val signatureData = intermediateCAKey.sign(serializedNetworkMap).withCert(intermediateCACert.cert)
|
val signatureData = keyPair.sign(serializedNetworkMap).withCert(intermediateCert.cert)
|
||||||
val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, signatureData)
|
val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, signatureData)
|
||||||
networkMapStorage.saveNetworkMap(signedNetworkMap)
|
networkMapStorage.saveNetworkMap(signedNetworkMap)
|
||||||
|
|
||||||
@ -112,19 +126,36 @@ class DBNetworkMapStorageTest : TestBase() {
|
|||||||
@Test
|
@Test
|
||||||
fun `getValidNodeInfoHashes returns only valid and signed node info hashes`() {
|
fun `getValidNodeInfoHashes returns only valid and signed node info hashes`() {
|
||||||
// given
|
// given
|
||||||
// Create node infos.
|
// Create node info.
|
||||||
val signedNodeInfoA = createValidSignedNodeInfo("TestA")
|
val organisationA = "TestA"
|
||||||
val signedNodeInfoB = createValidSignedNodeInfo("TestB")
|
val requestIdA = requestStorage.saveRequest(createRequest(organisationA).first)
|
||||||
|
requestStorage.markRequestTicketCreated(requestIdA)
|
||||||
|
requestStorage.approveRequest(requestIdA, "TestUser")
|
||||||
|
val keyPairA = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
|
val clientCertA = X509Utilities.createCertificate(CertificateType.NODE_CA, intermediateCACert, intermediateCAKey, CordaX500Name(organisation = organisationA, locality = "London", country = "GB"), keyPairA.public)
|
||||||
|
val certPathA = buildCertPath(clientCertA.toX509Certificate(), intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
|
||||||
|
requestStorage.putCertificatePath(requestIdA, certPathA, emptyList())
|
||||||
|
val organisationB = "TestB"
|
||||||
|
val requestIdB = requestStorage.saveRequest(createRequest(organisationB).first)
|
||||||
|
requestStorage.markRequestTicketCreated(requestIdB)
|
||||||
|
requestStorage.approveRequest(requestIdB, "TestUser")
|
||||||
|
val keyPairB = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
|
val clientCertB = X509Utilities.createCertificate(CertificateType.NODE_CA, intermediateCACert, intermediateCAKey, CordaX500Name(organisation = organisationB, locality = "London", country = "GB"), keyPairB.public)
|
||||||
|
val certPathB = buildCertPath(clientCertB.toX509Certificate(), intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
|
||||||
|
requestStorage.putCertificatePath(requestIdB, certPathB, emptyList())
|
||||||
|
val nodeInfoA = NodeInfo(listOf(NetworkHostAndPort("my.companyA.com", 1234)), listOf(PartyAndCertificate(certPathA)), 1, serial = 1L)
|
||||||
|
val nodeInfoB = NodeInfo(listOf(NetworkHostAndPort("my.companyB.com", 1234)), listOf(PartyAndCertificate(certPathB)), 1, serial = 1L)
|
||||||
// Put signed node info data
|
// Put signed node info data
|
||||||
val nodeInfoHashA = nodeInfoStorage.putNodeInfo(signedNodeInfoA)
|
val nodeInfoABytes = nodeInfoA.serialize()
|
||||||
val nodeInfoHashB = nodeInfoStorage.putNodeInfo(signedNodeInfoB)
|
val nodeInfoBBytes = nodeInfoB.serialize()
|
||||||
|
val nodeInfoHashA = nodeInfoStorage.putNodeInfo(SignedNodeInfo(nodeInfoABytes, listOf(keyPairA.sign(nodeInfoABytes))))
|
||||||
|
val nodeInfoHashB = nodeInfoStorage.putNodeInfo(SignedNodeInfo(nodeInfoBBytes, listOf(keyPairB.sign(nodeInfoBBytes))))
|
||||||
|
|
||||||
// Create network parameters
|
// Create network parameters
|
||||||
val networkParametersHash = networkMapStorage.saveNetworkParameters(createNetworkParameters())
|
val networkParametersHash = networkMapStorage.saveNetworkParameters(createNetworkParameters())
|
||||||
val networkMap = NetworkMap(listOf(nodeInfoHashA), networkParametersHash)
|
val networkMap = NetworkMap(listOf(nodeInfoHashA), networkParametersHash)
|
||||||
val serializedNetworkMap = networkMap.serialize()
|
val serializedNetworkMap = networkMap.serialize()
|
||||||
val signatureData = intermediateCAKey.sign(serializedNetworkMap).withCert(intermediateCACert.cert)
|
val signatureData = keyPairA.sign(serializedNetworkMap).withCert(clientCertA.cert)
|
||||||
val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, signatureData)
|
val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, signatureData)
|
||||||
|
|
||||||
// Sign network map
|
// Sign network map
|
||||||
@ -136,15 +167,4 @@ class DBNetworkMapStorageTest : TestBase() {
|
|||||||
// then
|
// then
|
||||||
assertThat(validNodeInfoHash).containsOnly(nodeInfoHashA, nodeInfoHashB)
|
assertThat(validNodeInfoHash).containsOnly(nodeInfoHashA, nodeInfoHashB)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createValidSignedNodeInfo(organisation: String): SignedNodeInfo {
|
|
||||||
val nodeInfoBuilder = TestNodeInfoBuilder()
|
|
||||||
val requestId = requestStorage.saveRequest(createRequest(organisation).first)
|
|
||||||
requestStorage.markRequestTicketCreated(requestId)
|
|
||||||
requestStorage.approveRequest(requestId, "TestUser")
|
|
||||||
val (identity) = nodeInfoBuilder.addIdentity(CordaX500Name(organisation, "London", "GB"))
|
|
||||||
val nodeCaCertPath = X509CertificateFactory().generateCertPath(identity.certPath.certificates.drop(1))
|
|
||||||
requestStorage.putCertificatePath(requestId, nodeCaCertPath, emptyList())
|
|
||||||
return nodeInfoBuilder.buildWithSigned().second
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -3,34 +3,31 @@ package com.r3.corda.networkmanage.common.persistence
|
|||||||
import com.r3.corda.networkmanage.TestBase
|
import com.r3.corda.networkmanage.TestBase
|
||||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
||||||
import com.r3.corda.networkmanage.common.utils.hashString
|
import com.r3.corda.networkmanage.common.utils.hashString
|
||||||
|
import com.r3.corda.networkmanage.common.utils.toX509Certificate
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.crypto.sign
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.cert
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.nodeapi.internal.SignedNodeInfo
|
import net.corda.nodeapi.internal.SignedNodeInfo
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||||
import net.corda.testing.internal.TestNodeInfoBuilder
|
|
||||||
import net.corda.testing.internal.signWith
|
|
||||||
import net.corda.testing.node.MockServices
|
import net.corda.testing.node.MockServices
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.security.PrivateKey
|
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertNotNull
|
import kotlin.test.assertNotNull
|
||||||
import kotlin.test.assertNull
|
import kotlin.test.assertNull
|
||||||
|
|
||||||
class PersitenceNodeInfoStorageTest : TestBase() {
|
class PersitenceNodeInfoStorageTest : TestBase() {
|
||||||
private lateinit var requestStorage: CertificationRequestStorage
|
private lateinit var requestStorage: CertificationRequestStorage
|
||||||
private lateinit var nodeInfoStorage: PersistentNodeInfoStorage
|
private lateinit var nodeInfoStorage: NodeInfoStorage
|
||||||
private lateinit var persistence: CordaPersistence
|
private lateinit var persistence: CordaPersistence
|
||||||
|
|
||||||
private val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
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 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 intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
@ -49,13 +46,14 @@ class PersitenceNodeInfoStorageTest : TestBase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `test getCertificatePath`() {
|
fun `test get CertificatePath`() {
|
||||||
// Create node info.
|
// Create node info.
|
||||||
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val name = CordaX500Name(organisation = "Test", locality = "London", country = "GB")
|
val clientCert = X509Utilities.createCertificate(CertificateType.NODE_CA, intermediateCACert, intermediateCAKey, CordaX500Name(organisation = "Test", locality = "London", country = "GB"), keyPair.public)
|
||||||
val nodeCaCert = X509Utilities.createCertificate(CertificateType.NODE_CA, intermediateCACert, intermediateCAKey, name, keyPair.public)
|
val certPath = buildCertPath(clientCert.toX509Certificate(), intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
|
||||||
|
val nodeInfo = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(certPath)), 1, serial = 1L)
|
||||||
|
|
||||||
val request = X509Utilities.createCertificateSigningRequest(name, "my@mail.com", keyPair)
|
val request = X509Utilities.createCertificateSigningRequest(nodeInfo.legalIdentities.first().name, "my@mail.com", keyPair)
|
||||||
|
|
||||||
val requestId = requestStorage.saveRequest(request)
|
val requestId = requestStorage.saveRequest(request)
|
||||||
requestStorage.markRequestTicketCreated(requestId)
|
requestStorage.markRequestTicketCreated(requestId)
|
||||||
@ -63,73 +61,104 @@ class PersitenceNodeInfoStorageTest : TestBase() {
|
|||||||
|
|
||||||
assertNull(nodeInfoStorage.getCertificatePath(SecureHash.parse(keyPair.public.hashString())))
|
assertNull(nodeInfoStorage.getCertificatePath(SecureHash.parse(keyPair.public.hashString())))
|
||||||
|
|
||||||
requestStorage.putCertificatePath(requestId, buildCertPath(nodeCaCert.cert, intermediateCACert.cert, rootCACert.cert), listOf(CertificationRequestStorage.DOORMAN_SIGNATURE))
|
requestStorage.putCertificatePath(requestId, buildCertPath(clientCert.toX509Certificate(), intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate()), listOf(CertificationRequestStorage.DOORMAN_SIGNATURE))
|
||||||
|
|
||||||
val storedCertPath = nodeInfoStorage.getCertificatePath(SecureHash.parse(keyPair.public.hashString()))
|
val storedCertPath = nodeInfoStorage.getCertificatePath(SecureHash.parse(keyPair.public.hashString()))
|
||||||
assertNotNull(storedCertPath)
|
assertNotNull(storedCertPath)
|
||||||
|
|
||||||
assertEquals(nodeCaCert.cert, storedCertPath!!.certificates.first())
|
assertEquals(clientCert.toX509Certificate(), storedCertPath!!.certificates.first())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `getNodeInfo returns persisted SignedNodeInfo using the hash of just the NodeInfo`() {
|
fun `test getNodeInfoHash returns correct data`() {
|
||||||
// given
|
// given
|
||||||
val (nodeInfoA, signedNodeInfoA) = createValidSignedNodeInfo("TestA")
|
val organisationA = "TestA"
|
||||||
val (nodeInfoB, signedNodeInfoB) = createValidSignedNodeInfo("TestB")
|
val requestIdA = requestStorage.saveRequest(createRequest(organisationA).first)
|
||||||
|
requestStorage.markRequestTicketCreated(requestIdA)
|
||||||
|
requestStorage.approveRequest(requestIdA, "TestUser")
|
||||||
|
val keyPairA = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
|
val clientCertA = X509Utilities.createCertificate(CertificateType.NODE_CA, intermediateCACert, intermediateCAKey, CordaX500Name(organisation = organisationA, locality = "London", country = "GB"), keyPairA.public)
|
||||||
|
val certPathA = buildCertPath(clientCertA.toX509Certificate(), intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
|
||||||
|
requestStorage.putCertificatePath(requestIdA, certPathA, emptyList())
|
||||||
|
val organisationB = "TestB"
|
||||||
|
val requestIdB = requestStorage.saveRequest(createRequest(organisationB).first)
|
||||||
|
requestStorage.markRequestTicketCreated(requestIdB)
|
||||||
|
requestStorage.approveRequest(requestIdB, "TestUser")
|
||||||
|
val keyPairB = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
|
val clientCertB = X509Utilities.createCertificate(CertificateType.NODE_CA, intermediateCACert, intermediateCAKey, CordaX500Name(organisation = organisationB, locality = "London", country = "GB"), keyPairB.public)
|
||||||
|
val certPathB = buildCertPath(clientCertB.toX509Certificate(), intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
|
||||||
|
requestStorage.putCertificatePath(requestIdB, certPathB, emptyList())
|
||||||
|
val nodeInfoA = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(certPathA)), 1, serial = 1L)
|
||||||
|
val nodeInfoB = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(certPathB)), 1, serial = 1L)
|
||||||
|
|
||||||
// Put signed node info data
|
// Put signed node info data
|
||||||
nodeInfoStorage.putNodeInfo(signedNodeInfoA)
|
val nodeInfoABytes = nodeInfoA.serialize()
|
||||||
nodeInfoStorage.putNodeInfo(signedNodeInfoB)
|
val nodeInfoBBytes = nodeInfoB.serialize()
|
||||||
|
nodeInfoStorage.putNodeInfo(SignedNodeInfo(nodeInfoABytes, listOf(keyPairA.sign(nodeInfoABytes))))
|
||||||
|
nodeInfoStorage.putNodeInfo(SignedNodeInfo(nodeInfoBBytes, listOf(keyPairB.sign(nodeInfoBBytes))))
|
||||||
|
|
||||||
// when
|
// when
|
||||||
val persistedSignedNodeInfoA = nodeInfoStorage.getNodeInfo(nodeInfoA.serialize().hash)
|
val persistedNodeInfoA = nodeInfoStorage.getNodeInfo(nodeInfoABytes.hash)
|
||||||
val persistedSignedNodeInfoB = nodeInfoStorage.getNodeInfo(nodeInfoB.serialize().hash)
|
val persistedNodeInfoB = nodeInfoStorage.getNodeInfo(nodeInfoBBytes.hash)
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(persistedSignedNodeInfoA?.verified(), nodeInfoA)
|
assertNotNull(persistedNodeInfoA)
|
||||||
assertEquals(persistedSignedNodeInfoB?.verified(), nodeInfoB)
|
assertNotNull(persistedNodeInfoB)
|
||||||
|
assertEquals(persistedNodeInfoA!!.verified(), nodeInfoA)
|
||||||
|
assertEquals(persistedNodeInfoB!!.verified(), nodeInfoB)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `same public key with different node info`() {
|
fun `same pub key with different node info`() {
|
||||||
// Create node info.
|
// Create node info.
|
||||||
val (nodeInfo1, signedNodeInfo1, key) = createValidSignedNodeInfo("Test", serial = 1)
|
val organisation = "Test"
|
||||||
val nodeInfo2 = nodeInfo1.copy(serial = 2)
|
|
||||||
val signedNodeInfo2 = nodeInfo2.signWith(listOf(key))
|
|
||||||
|
|
||||||
val nodeInfo1Hash = nodeInfoStorage.putNodeInfo(signedNodeInfo1)
|
|
||||||
assertEquals(nodeInfo1, nodeInfoStorage.getNodeInfo(nodeInfo1Hash)?.verified())
|
|
||||||
|
|
||||||
// This should replace the node info.
|
|
||||||
nodeInfoStorage.putNodeInfo(signedNodeInfo2)
|
|
||||||
|
|
||||||
// Old node info should be removed.
|
|
||||||
assertNull(nodeInfoStorage.getNodeInfo(nodeInfo1Hash))
|
|
||||||
assertEquals(nodeInfo2, nodeInfoStorage.getNodeInfo(nodeInfo2.serialize().hash)?.verified())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `putNodeInfo persists SignedNodeInfo with its signature`() {
|
|
||||||
// given
|
|
||||||
val (_, signedNodeInfo) = createValidSignedNodeInfo("Test")
|
|
||||||
|
|
||||||
// when
|
|
||||||
val nodeInfoHash = nodeInfoStorage.putNodeInfo(signedNodeInfo)
|
|
||||||
|
|
||||||
// then
|
|
||||||
val persistedSignedNodeInfo = nodeInfoStorage.getNodeInfo(nodeInfoHash)
|
|
||||||
assertThat(persistedSignedNodeInfo?.signatures).isEqualTo(signedNodeInfo.signatures)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createValidSignedNodeInfo(organisation: String, serial: Long = 1): Triple<NodeInfo, SignedNodeInfo, PrivateKey> {
|
|
||||||
val nodeInfoBuilder = TestNodeInfoBuilder()
|
|
||||||
val requestId = requestStorage.saveRequest(createRequest(organisation).first)
|
val requestId = requestStorage.saveRequest(createRequest(organisation).first)
|
||||||
requestStorage.markRequestTicketCreated(requestId)
|
requestStorage.markRequestTicketCreated(requestId)
|
||||||
requestStorage.approveRequest(requestId, "TestUser")
|
requestStorage.approveRequest(requestId, "TestUser")
|
||||||
val (identity, key) = nodeInfoBuilder.addIdentity(CordaX500Name(organisation, "London", "GB"))
|
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val nodeCaCertPath = X509CertificateFactory().generateCertPath(identity.certPath.certificates.drop(1))
|
val clientCert = X509Utilities.createCertificate(CertificateType.NODE_CA, intermediateCACert, intermediateCAKey, CordaX500Name(organisation = organisation, locality = "London", country = "GB"), keyPair.public)
|
||||||
requestStorage.putCertificatePath(requestId, nodeCaCertPath, emptyList())
|
val certPath = buildCertPath(clientCert.toX509Certificate(), intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
|
||||||
val (nodeInfo, signedNodeInfo) = nodeInfoBuilder.buildWithSigned(serial)
|
requestStorage.putCertificatePath(requestId, certPath, emptyList())
|
||||||
return Triple(nodeInfo, signedNodeInfo, key)
|
|
||||||
|
val nodeInfo = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(certPath)), 1, serial = 1L)
|
||||||
|
val nodeInfoSamePubKey = NodeInfo(listOf(NetworkHostAndPort("my.company2.com", 1234)), listOf(PartyAndCertificate(certPath)), 1, serial = 1L)
|
||||||
|
val nodeInfoBytes = nodeInfo.serialize()
|
||||||
|
val nodeInfoHash = nodeInfoStorage.putNodeInfo(SignedNodeInfo(nodeInfoBytes, listOf(keyPair.sign(nodeInfoBytes))))
|
||||||
|
assertEquals(nodeInfo, nodeInfoStorage.getNodeInfo(nodeInfoHash)?.verified())
|
||||||
|
|
||||||
|
val nodeInfoSamePubKeyBytes = nodeInfoSamePubKey.serialize()
|
||||||
|
// This should replace the node info.
|
||||||
|
nodeInfoStorage.putNodeInfo(SignedNodeInfo(nodeInfoSamePubKeyBytes, listOf(keyPair.sign(nodeInfoSamePubKeyBytes))))
|
||||||
|
|
||||||
|
// Old node info should be removed.
|
||||||
|
assertNull(nodeInfoStorage.getNodeInfo(nodeInfoHash))
|
||||||
|
assertEquals(nodeInfoSamePubKey, nodeInfoStorage.getNodeInfo(nodeInfoSamePubKeyBytes.hash)?.verified())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `putNodeInfo persists node info data with its signature`() {
|
||||||
|
// given
|
||||||
|
// Create node info.
|
||||||
|
val organisation = "Test"
|
||||||
|
val requestId = requestStorage.saveRequest(createRequest(organisation).first)
|
||||||
|
requestStorage.markRequestTicketCreated(requestId)
|
||||||
|
requestStorage.approveRequest(requestId, "TestUser")
|
||||||
|
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
|
val clientCert = X509Utilities.createCertificate(CertificateType.NODE_CA, intermediateCACert, intermediateCAKey, CordaX500Name(organisation = organisation, locality = "London", country = "GB"), keyPair.public)
|
||||||
|
val certPath = buildCertPath(clientCert.toX509Certificate(), intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
|
||||||
|
requestStorage.putCertificatePath(requestId, certPath, emptyList())
|
||||||
|
|
||||||
|
val nodeInfo = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(certPath)), 1, serial = 1L)
|
||||||
|
val nodeInfoBytes = nodeInfo.serialize()
|
||||||
|
val signature = keyPair.sign(nodeInfoBytes)
|
||||||
|
|
||||||
|
// when
|
||||||
|
val nodeInfoHash = nodeInfoStorage.putNodeInfo(SignedNodeInfo(nodeInfoBytes, listOf(signature)))
|
||||||
|
|
||||||
|
// then
|
||||||
|
val persistedNodeInfo = nodeInfoStorage.getNodeInfo(nodeInfoHash)
|
||||||
|
assertNotNull(persistedNodeInfo)
|
||||||
|
assertEquals(nodeInfo, persistedNodeInfo!!.verified())
|
||||||
|
assertEquals(signature, persistedNodeInfo.signatures.firstOrNull())
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,18 +1,20 @@
|
|||||||
package com.r3.corda.networkmanage.doorman
|
package com.r3.corda.networkmanage.doorman
|
||||||
|
|
||||||
|
import com.nhaarman.mockito_kotlin.any
|
||||||
import com.nhaarman.mockito_kotlin.mock
|
import com.nhaarman.mockito_kotlin.mock
|
||||||
import com.nhaarman.mockito_kotlin.times
|
import com.nhaarman.mockito_kotlin.times
|
||||||
import com.nhaarman.mockito_kotlin.verify
|
import com.nhaarman.mockito_kotlin.verify
|
||||||
import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage
|
import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage
|
||||||
import com.r3.corda.networkmanage.common.persistence.NodeInfoStorage
|
import com.r3.corda.networkmanage.common.persistence.NodeInfoStorage
|
||||||
|
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
||||||
|
import com.r3.corda.networkmanage.common.utils.toX509Certificate
|
||||||
import com.r3.corda.networkmanage.common.utils.withCert
|
import com.r3.corda.networkmanage.common.utils.withCert
|
||||||
import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService
|
import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.*
|
||||||
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.identity.CordaX500Name
|
||||||
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
import net.corda.core.internal.cert
|
import net.corda.core.internal.cert
|
||||||
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.serialization.deserialize
|
import net.corda.core.serialization.deserialize
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
@ -23,7 +25,6 @@ import net.corda.nodeapi.internal.crypto.X509Utilities
|
|||||||
import net.corda.nodeapi.internal.network.NetworkMap
|
import net.corda.nodeapi.internal.network.NetworkMap
|
||||||
import net.corda.nodeapi.internal.network.SignedNetworkMap
|
import net.corda.nodeapi.internal.network.SignedNetworkMap
|
||||||
import net.corda.testing.SerializationEnvironmentRule
|
import net.corda.testing.SerializationEnvironmentRule
|
||||||
import net.corda.testing.internal.createNodeInfoAndSigned
|
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@ -40,17 +41,31 @@ class NodeInfoWebServiceTest {
|
|||||||
@JvmField
|
@JvmField
|
||||||
val testSerialization = SerializationEnvironmentRule(true)
|
val testSerialization = SerializationEnvironmentRule(true)
|
||||||
|
|
||||||
private val testNetwotkMapConfig = NetworkMapConfig(10.seconds.toMillis(), 10.seconds.toMillis())
|
private val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
|
private val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(locality = "London", organisation = "R3 LTD", country = "GB", commonName = "Corda Node Root CA"), 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 val testNetwotkMapConfig = NetworkMapConfig(10.seconds.toMillis(), 10.seconds.toMillis())
|
||||||
@Test
|
@Test
|
||||||
fun `submit nodeInfo`() {
|
fun `submit nodeInfo`() {
|
||||||
// Create node info.
|
// Create node info.
|
||||||
val (_, signedNodeInfo) = createNodeInfoAndSigned(CordaX500Name("Test", "London", "GB"))
|
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
|
val clientCert = X509Utilities.createCertificate(CertificateType.NODE_CA, intermediateCACert, intermediateCAKey, CordaX500Name(organisation = "Test", locality = "London", country = "GB"), keyPair.public)
|
||||||
|
val certPath = buildCertPath(clientCert.toX509Certificate(), intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
|
||||||
|
val nodeInfo = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(certPath)), 1, serial = 1L)
|
||||||
|
|
||||||
NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), NodeInfoWebService(mock(), mock(), testNetwotkMapConfig)).use {
|
// Create digital signature.
|
||||||
|
val digitalSignature = DigitalSignature.WithKey(keyPair.public, Crypto.doSign(keyPair.private, nodeInfo.serialize().bytes))
|
||||||
|
|
||||||
|
val nodeInfoStorage: NodeInfoStorage = mock {
|
||||||
|
on { getCertificatePath(any()) }.thenReturn(certPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), NodeInfoWebService(nodeInfoStorage, mock(), testNetwotkMapConfig)).use {
|
||||||
it.start()
|
it.start()
|
||||||
val registerURL = URL("http://${it.hostAndPort}/${NodeInfoWebService.NETWORK_MAP_PATH}/publish")
|
val registerURL = URL("http://${it.hostAndPort}/${NodeInfoWebService.NETWORK_MAP_PATH}/publish")
|
||||||
val nodeInfoAndSignature = signedNodeInfo.serialize().bytes
|
val nodeInfoAndSignature = SignedNodeInfo(nodeInfo.serialize(), listOf(digitalSignature)).serialize().bytes
|
||||||
// Post node info and signature to doorman, this should pass without any exception.
|
// Post node info and signature to doorman, this should pass without any exception.
|
||||||
doPost(registerURL, nodeInfoAndSignature)
|
doPost(registerURL, nodeInfoAndSignature)
|
||||||
}
|
}
|
||||||
@ -58,11 +73,6 @@ class NodeInfoWebServiceTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `get network map`() {
|
fun `get network map`() {
|
||||||
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
|
||||||
val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(locality = "London", organisation = "R3 LTD", country = "GB", commonName = "Corda Node Root CA"), 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 networkMap = NetworkMap(listOf(SecureHash.randomSHA256(), SecureHash.randomSHA256()), SecureHash.randomSHA256())
|
val networkMap = NetworkMap(listOf(SecureHash.randomSHA256(), SecureHash.randomSHA256()), SecureHash.randomSHA256())
|
||||||
val serializedNetworkMap = networkMap.serialize()
|
val serializedNetworkMap = networkMap.serialize()
|
||||||
val networkMapStorage: NetworkMapStorage = mock {
|
val networkMapStorage: NetworkMapStorage = mock {
|
||||||
@ -79,12 +89,16 @@ class NodeInfoWebServiceTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `get node info`() {
|
fun `get node info`() {
|
||||||
val (nodeInfo, signedNodeInfo) = createNodeInfoAndSigned(CordaX500Name("Test", "London", "GB"))
|
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
|
val clientCert = X509Utilities.createCertificate(CertificateType.NODE_CA, intermediateCACert, intermediateCAKey, CordaX500Name(organisation = "Test", locality = "London", country = "GB"), keyPair.public)
|
||||||
|
val certPath = buildCertPath(clientCert.toX509Certificate(), intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
|
||||||
|
val nodeInfo = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(certPath)), 1, serial = 1L)
|
||||||
|
|
||||||
val nodeInfoHash = nodeInfo.serialize().sha256()
|
val nodeInfoHash = nodeInfo.serialize().sha256()
|
||||||
|
|
||||||
val nodeInfoStorage: NodeInfoStorage = mock {
|
val nodeInfoStorage: NodeInfoStorage = mock {
|
||||||
on { getNodeInfo(nodeInfoHash) }.thenReturn(signedNodeInfo)
|
val serializedNodeInfo = nodeInfo.serialize()
|
||||||
|
on { getNodeInfo(nodeInfoHash) }.thenReturn(SignedNodeInfo(serializedNodeInfo, listOf(keyPair.sign(serializedNodeInfo))))
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), NodeInfoWebService(nodeInfoStorage, mock(), testNetwotkMapConfig)).use {
|
NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), NodeInfoWebService(nodeInfoStorage, mock(), testNetwotkMapConfig)).use {
|
||||||
|
@ -13,54 +13,42 @@ import org.slf4j.LoggerFactory
|
|||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
|
|
||||||
object IdentityGenerator {
|
object ServiceIdentityGenerator {
|
||||||
private val log = LoggerFactory.getLogger(javaClass)
|
private val log = LoggerFactory.getLogger(javaClass)
|
||||||
|
|
||||||
const val NODE_IDENTITY_ALIAS_PREFIX = "identity"
|
|
||||||
const val DISTRIBUTED_NOTARY_ALIAS_PREFIX = "distributed-notary"
|
|
||||||
|
|
||||||
fun generateNodeIdentity(dir: Path, legalName: CordaX500Name, customRootCert: X509Certificate? = null): Party {
|
|
||||||
return generateToDisk(listOf(dir), legalName, NODE_IDENTITY_ALIAS_PREFIX, threshold = 1, customRootCert = customRootCert)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun generateDistributedNotaryIdentity(dirs: List<Path>, notaryName: CordaX500Name, threshold: Int = 1, customRootCert: X509Certificate? = null): Party {
|
|
||||||
return generateToDisk(dirs, notaryName, DISTRIBUTED_NOTARY_ALIAS_PREFIX, threshold, customRootCert)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates signing key pairs and a common distributed service identity for a set of nodes.
|
* Generates signing key pairs and a common distributed service identity for a set of nodes.
|
||||||
* The key pairs and the group identity get serialized to disk in the corresponding node directories.
|
* The key pairs and the group identity get serialized to disk in the corresponding node directories.
|
||||||
* This method should be called *before* any of the nodes are started.
|
* This method should be called *before* any of the nodes are started.
|
||||||
*
|
*
|
||||||
* @param dirs List of node directories to place the generated identity and key pairs in.
|
* @param dirs List of node directories to place the generated identity and key pairs in.
|
||||||
* @param name The name of the identity.
|
* @param serviceName The legal name of the distributed service.
|
||||||
* @param threshold The threshold for the generated group [CompositeKey].
|
* @param threshold The threshold for the generated group [CompositeKey].
|
||||||
* @param customRootCert the certificate to use as the Corda root CA. If not specified the one in
|
* @param customRootCert the certificate to use a Corda root CA. If not specified the one in
|
||||||
* internal/certificates/cordadevcakeys.jks is used.
|
* certificates/cordadevcakeys.jks is used.
|
||||||
*/
|
*/
|
||||||
private fun generateToDisk(dirs: List<Path>,
|
fun generateToDisk(dirs: List<Path>,
|
||||||
name: CordaX500Name,
|
serviceName: CordaX500Name,
|
||||||
aliasPrefix: String,
|
serviceId: String,
|
||||||
threshold: Int,
|
threshold: Int = 1,
|
||||||
customRootCert: X509Certificate?): Party {
|
customRootCert: X509Certificate? = null): Party {
|
||||||
log.trace { "Generating identity \"$name\" for nodes: ${dirs.joinToString()}" }
|
log.trace { "Generating a group identity \"serviceName\" for nodes: ${dirs.joinToString()}" }
|
||||||
val keyPairs = (1..dirs.size).map { generateKeyPair() }
|
val keyPairs = (1..dirs.size).map { generateKeyPair() }
|
||||||
val key = CompositeKey.Builder().addKeys(keyPairs.map { it.public }).build(threshold)
|
val notaryKey = CompositeKey.Builder().addKeys(keyPairs.map { it.public }).build(threshold)
|
||||||
|
|
||||||
val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
|
val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
|
||||||
val intermediateCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
|
val intermediateCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
|
||||||
val rootCert = customRootCert ?: caKeyStore.getCertificate(X509Utilities.CORDA_ROOT_CA)
|
val rootCert = customRootCert ?: caKeyStore.getCertificate(X509Utilities.CORDA_ROOT_CA)
|
||||||
|
|
||||||
keyPairs.zip(dirs) { keyPair, dir ->
|
keyPairs.zip(dirs) { keyPair, dir ->
|
||||||
val serviceKeyCert = X509Utilities.createCertificate(CertificateType.SERVICE_IDENTITY, intermediateCa.certificate, intermediateCa.keyPair, name, keyPair.public)
|
val serviceKeyCert = X509Utilities.createCertificate(CertificateType.SERVICE_IDENTITY, intermediateCa.certificate, intermediateCa.keyPair, serviceName, keyPair.public)
|
||||||
val compositeKeyCert = X509Utilities.createCertificate(CertificateType.SERVICE_IDENTITY, intermediateCa.certificate, intermediateCa.keyPair, name, key)
|
val compositeKeyCert = X509Utilities.createCertificate(CertificateType.SERVICE_IDENTITY, intermediateCa.certificate, intermediateCa.keyPair, serviceName, notaryKey)
|
||||||
val certPath = (dir / "certificates").createDirectories() / "distributedService.jks"
|
val certPath = (dir / "certificates").createDirectories() / "distributedService.jks"
|
||||||
val keystore = loadOrCreateKeyStore(certPath, "cordacadevpass")
|
val keystore = loadOrCreateKeyStore(certPath, "cordacadevpass")
|
||||||
keystore.setCertificateEntry("$aliasPrefix-composite-key", compositeKeyCert.cert)
|
keystore.setCertificateEntry("$serviceId-composite-key", compositeKeyCert.cert)
|
||||||
keystore.setKeyEntry("$aliasPrefix-private-key", keyPair.private, "cordacadevkeypass".toCharArray(), arrayOf(serviceKeyCert.cert, intermediateCa.certificate.cert, rootCert))
|
keystore.setKeyEntry("$serviceId-private-key", keyPair.private, "cordacadevkeypass".toCharArray(), arrayOf(serviceKeyCert.cert, intermediateCa.certificate.cert, rootCert))
|
||||||
keystore.save(certPath, "cordacadevpass")
|
keystore.save(certPath, "cordacadevpass")
|
||||||
}
|
}
|
||||||
|
return Party(serviceName, notaryKey)
|
||||||
return Party(name, key)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,8 +7,7 @@ import net.corda.core.crypto.random63BitValue
|
|||||||
import net.corda.core.internal.CertRole
|
import net.corda.core.internal.CertRole
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.cert
|
import net.corda.core.internal.cert
|
||||||
import net.corda.core.internal.reader
|
import net.corda.core.internal.read
|
||||||
import net.corda.core.internal.writer
|
|
||||||
import net.corda.core.internal.x500Name
|
import net.corda.core.internal.x500Name
|
||||||
import net.corda.core.utilities.days
|
import net.corda.core.utilities.days
|
||||||
import net.corda.core.utilities.millis
|
import net.corda.core.utilities.millis
|
||||||
@ -49,6 +48,8 @@ object X509Utilities {
|
|||||||
const val CORDA_CLIENT_TLS = "cordaclienttls"
|
const val CORDA_CLIENT_TLS = "cordaclienttls"
|
||||||
const val CORDA_CLIENT_CA = "cordaclientca"
|
const val CORDA_CLIENT_CA = "cordaclientca"
|
||||||
|
|
||||||
|
const val CORDA_CLIENT_CA_CN = "Corda Client CA Certificate"
|
||||||
|
|
||||||
private val DEFAULT_VALIDITY_WINDOW = Pair(0.millis, 3650.days)
|
private val DEFAULT_VALIDITY_WINDOW = Pair(0.millis, 3650.days)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -161,7 +162,7 @@ object X509Utilities {
|
|||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun saveCertificateAsPEMFile(x509Certificate: X509Certificate, file: Path) {
|
fun saveCertificateAsPEMFile(x509Certificate: X509Certificate, file: Path) {
|
||||||
JcaPEMWriter(file.writer()).use {
|
JcaPEMWriter(file.toFile().writer()).use {
|
||||||
it.writeObject(x509Certificate)
|
it.writeObject(x509Certificate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -173,8 +174,9 @@ object X509Utilities {
|
|||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun loadCertificateFromPEMFile(file: Path): X509Certificate {
|
fun loadCertificateFromPEMFile(file: Path): X509Certificate {
|
||||||
return file.reader().use {
|
return file.read {
|
||||||
val pemObject = PemReader(it).readPemObject()
|
val reader = PemReader(it.reader())
|
||||||
|
val pemObject = reader.readPemObject()
|
||||||
val certHolder = X509CertificateHolder(pemObject.content)
|
val certHolder = X509CertificateHolder(pemObject.content)
|
||||||
certHolder.isValidOn(Date())
|
certHolder.isValidOn(Date())
|
||||||
certHolder.cert
|
certHolder.cert
|
||||||
|
@ -13,6 +13,7 @@ import net.corda.core.identity.CordaX500Name
|
|||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.deleteIfExists
|
import net.corda.core.internal.deleteIfExists
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
|
import net.corda.core.node.services.NotaryService
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
@ -23,7 +24,7 @@ import net.corda.node.services.config.BFTSMaRtConfiguration
|
|||||||
import net.corda.node.services.config.NotaryConfig
|
import net.corda.node.services.config.NotaryConfig
|
||||||
import net.corda.node.services.transactions.minClusterSize
|
import net.corda.node.services.transactions.minClusterSize
|
||||||
import net.corda.node.services.transactions.minCorrectReplicas
|
import net.corda.node.services.transactions.minCorrectReplicas
|
||||||
import net.corda.nodeapi.internal.IdentityGenerator
|
import net.corda.nodeapi.internal.ServiceIdentityGenerator
|
||||||
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||||
import net.corda.nodeapi.internal.network.NotaryInfo
|
import net.corda.nodeapi.internal.network.NotaryInfo
|
||||||
import net.corda.testing.IntegrationTest
|
import net.corda.testing.IntegrationTest
|
||||||
@ -67,9 +68,10 @@ class BFTNotaryServiceTests : IntegrationTest() {
|
|||||||
(Paths.get("config") / "currentView").deleteIfExists() // XXX: Make config object warn if this exists?
|
(Paths.get("config") / "currentView").deleteIfExists() // XXX: Make config object warn if this exists?
|
||||||
val replicaIds = (0 until clusterSize)
|
val replicaIds = (0 until clusterSize)
|
||||||
|
|
||||||
notary = IdentityGenerator.generateDistributedNotaryIdentity(
|
notary = ServiceIdentityGenerator.generateToDisk(
|
||||||
replicaIds.map { mockNet.baseDirectory(mockNet.nextNodeId + it) },
|
replicaIds.map { mockNet.baseDirectory(mockNet.nextNodeId + it) },
|
||||||
CordaX500Name("BFT", "Zurich", "CH"))
|
CordaX500Name("BFT", "Zurich", "CH"),
|
||||||
|
NotaryService.constructId(validating = false, bft = true))
|
||||||
|
|
||||||
val networkParameters = NetworkParametersCopier(testNetworkParameters(listOf(NotaryInfo(notary, false))))
|
val networkParameters = NetworkParametersCopier(testNetworkParameters(listOf(NotaryInfo(notary, false))))
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ import net.corda.core.transactions.TransactionBuilder
|
|||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.node.internal.StartedNode
|
import net.corda.node.internal.StartedNode
|
||||||
import net.corda.node.services.config.NotaryConfig
|
import net.corda.node.services.config.NotaryConfig
|
||||||
import net.corda.nodeapi.internal.IdentityGenerator
|
import net.corda.nodeapi.internal.ServiceIdentityGenerator
|
||||||
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||||
import net.corda.nodeapi.internal.network.NotaryInfo
|
import net.corda.nodeapi.internal.network.NotaryInfo
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
@ -49,7 +49,11 @@ class MySQLNotaryServiceTests : IntegrationTest() {
|
|||||||
@Before
|
@Before
|
||||||
fun before() {
|
fun before() {
|
||||||
mockNet = MockNetwork(cordappPackages = listOf("net.corda.testing.contracts"))
|
mockNet = MockNetwork(cordappPackages = listOf("net.corda.testing.contracts"))
|
||||||
notaryParty = IdentityGenerator.generateNodeIdentity(mockNet.baseDirectory(mockNet.nextNodeId), notaryName)
|
notaryParty = ServiceIdentityGenerator.generateToDisk(
|
||||||
|
listOf(mockNet.baseDirectory(mockNet.nextNodeId)),
|
||||||
|
notaryName,
|
||||||
|
"identity"
|
||||||
|
)
|
||||||
val networkParameters = NetworkParametersCopier(testNetworkParameters(listOf(NotaryInfo(notaryParty, false))))
|
val networkParameters = NetworkParametersCopier(testNetworkParameters(listOf(NotaryInfo(notaryParty, false))))
|
||||||
val notaryNodeUnstarted = createNotaryNode()
|
val notaryNodeUnstarted = createNotaryNode()
|
||||||
val nodeUnstarted = mockNet.createUnstartedNode()
|
val nodeUnstarted = mockNet.createUnstartedNode()
|
||||||
|
@ -11,6 +11,7 @@ import net.corda.core.internal.concurrent.map
|
|||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.node.internal.StartedNode
|
import net.corda.node.internal.StartedNode
|
||||||
|
import net.corda.node.services.transactions.RaftValidatingNotaryService
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
import net.corda.testing.contracts.DummyContract
|
import net.corda.testing.contracts.DummyContract
|
||||||
import net.corda.testing.driver.NodeHandle
|
import net.corda.testing.driver.NodeHandle
|
||||||
@ -30,15 +31,15 @@ class RaftNotaryServiceTests : IntegrationTest() {
|
|||||||
val databaseSchemas = IntegrationTestSchemas( "RAFTNotaryService_0", "RAFTNotaryService_1", "RAFTNotaryService_2",
|
val databaseSchemas = IntegrationTestSchemas( "RAFTNotaryService_0", "RAFTNotaryService_1", "RAFTNotaryService_2",
|
||||||
DUMMY_BANK_A_NAME.toDatabaseSchemaName())
|
DUMMY_BANK_A_NAME.toDatabaseSchemaName())
|
||||||
}
|
}
|
||||||
private val notaryName = CordaX500Name("RAFT Notary Service", "London", "GB")
|
private val notaryName = CordaX500Name(RaftValidatingNotaryService.id, "RAFT Notary Service", "London", "GB")
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `detect double spend`() {
|
fun `detect double spend`() {
|
||||||
driver(
|
driver(
|
||||||
startNodesInProcess = true,
|
startNodesInProcess = true,
|
||||||
extraCordappPackagesToScan = listOf("net.corda.testing.contracts"),
|
extraCordappPackagesToScan = listOf("net.corda.testing.contracts"),
|
||||||
notarySpecs = listOf(NotarySpec(notaryName, cluster = ClusterSpec.Raft(clusterSize = 3)))
|
notarySpecs = listOf(NotarySpec(notaryName, cluster = ClusterSpec.Raft(clusterSize = 3))))
|
||||||
) {
|
{
|
||||||
val bankA = startNode(providedName = DUMMY_BANK_A_NAME).map { (it as NodeHandle.InProcess).node }.getOrThrow()
|
val bankA = startNode(providedName = DUMMY_BANK_A_NAME).map { (it as NodeHandle.InProcess).node }.getOrThrow()
|
||||||
val inputState = issueState(bankA, defaultNotaryIdentity)
|
val inputState = issueState(bankA, defaultNotaryIdentity)
|
||||||
|
|
||||||
|
@ -19,9 +19,6 @@ import net.corda.node.services.messaging.ReceivedMessage
|
|||||||
import net.corda.node.services.messaging.send
|
import net.corda.node.services.messaging.send
|
||||||
import net.corda.node.services.transactions.RaftValidatingNotaryService
|
import net.corda.node.services.transactions.RaftValidatingNotaryService
|
||||||
import net.corda.testing.*
|
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.DriverDSL
|
||||||
import net.corda.testing.driver.NodeHandle
|
import net.corda.testing.driver.NodeHandle
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
@ -41,7 +38,7 @@ class P2PMessagingTest : IntegrationTest() {
|
|||||||
@ClassRule @JvmField
|
@ClassRule @JvmField
|
||||||
val databaseSchemas = IntegrationTestSchemas(ALICE_NAME.toDatabaseSchemaName(), "DistributedService_0", "DistributedService_1")
|
val databaseSchemas = IntegrationTestSchemas(ALICE_NAME.toDatabaseSchemaName(), "DistributedService_0", "DistributedService_1")
|
||||||
|
|
||||||
val DISTRIBUTED_SERVICE_NAME = CordaX500Name("DistributedService", "London", "GB")
|
val DISTRIBUTED_SERVICE_NAME = CordaX500Name(RaftValidatingNotaryService.id, "DistributedService", "London", "GB")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -60,12 +60,8 @@ import net.corda.node.services.vault.NodeVaultService
|
|||||||
import net.corda.node.services.vault.VaultSoftLockManager
|
import net.corda.node.services.vault.VaultSoftLockManager
|
||||||
import net.corda.node.shell.InteractiveShell
|
import net.corda.node.shell.InteractiveShell
|
||||||
import net.corda.node.utilities.AffinityExecutor
|
import net.corda.node.utilities.AffinityExecutor
|
||||||
import net.corda.nodeapi.internal.IdentityGenerator
|
|
||||||
import net.corda.nodeapi.internal.SignedNodeInfo
|
import net.corda.nodeapi.internal.SignedNodeInfo
|
||||||
import net.corda.nodeapi.internal.crypto.KeyStoreWrapper
|
import net.corda.nodeapi.internal.crypto.*
|
||||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
|
||||||
import net.corda.nodeapi.internal.crypto.loadKeyStore
|
|
||||||
import net.corda.nodeapi.internal.network.NETWORK_PARAMS_FILE_NAME
|
import net.corda.nodeapi.internal.network.NETWORK_PARAMS_FILE_NAME
|
||||||
import net.corda.nodeapi.internal.network.NetworkParameters
|
import net.corda.nodeapi.internal.network.NetworkParameters
|
||||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||||
@ -141,19 +137,25 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
|||||||
protected val services: ServiceHubInternal get() = _services
|
protected val services: ServiceHubInternal get() = _services
|
||||||
private lateinit var _services: ServiceHubInternalImpl
|
private lateinit var _services: ServiceHubInternalImpl
|
||||||
protected var myNotaryIdentity: PartyAndCertificate? = null
|
protected var myNotaryIdentity: PartyAndCertificate? = null
|
||||||
private lateinit var checkpointStorage: CheckpointStorage
|
protected lateinit var checkpointStorage: CheckpointStorage
|
||||||
private lateinit var tokenizableServices: List<Any>
|
private lateinit var tokenizableServices: List<Any>
|
||||||
protected lateinit var attachments: NodeAttachmentService
|
protected lateinit var attachments: NodeAttachmentService
|
||||||
protected lateinit var network: MessagingService
|
protected lateinit var network: MessagingService
|
||||||
protected val runOnStop = ArrayList<() -> Any?>()
|
protected val runOnStop = ArrayList<() -> Any?>()
|
||||||
private val _nodeReadyFuture = openFuture<Unit>()
|
protected val _nodeReadyFuture = openFuture<Unit>()
|
||||||
protected var networkMapClient: NetworkMapClient? = null
|
protected var networkMapClient: NetworkMapClient? = null
|
||||||
|
|
||||||
lateinit var securityManager: RPCSecurityManager get
|
lateinit var securityManager: RPCSecurityManager get
|
||||||
|
|
||||||
/** Completes once the node has successfully registered with the network map service
|
/** Completes once the node has successfully registered with the network map service
|
||||||
* or has loaded network map data from local database */
|
* or has loaded network map data from local database */
|
||||||
val nodeReadyFuture: CordaFuture<Unit> get() = _nodeReadyFuture
|
val nodeReadyFuture: CordaFuture<Unit>
|
||||||
|
get() = _nodeReadyFuture
|
||||||
|
/** A [CordaX500Name] with null common name. */
|
||||||
|
protected val myLegalName: CordaX500Name by lazy {
|
||||||
|
val cert = loadKeyStore(configuration.nodeKeystore, configuration.keyStorePassword).getX509Certificate(X509Utilities.CORDA_CLIENT_CA)
|
||||||
|
CordaX500Name.build(cert.subjectX500Principal).copy(commonName = null)
|
||||||
|
}
|
||||||
|
|
||||||
open val serializationWhitelists: List<SerializationWhitelist> by lazy {
|
open val serializationWhitelists: List<SerializationWhitelist> by lazy {
|
||||||
cordappLoader.cordapps.flatMap { it.serializationWhitelists }
|
cordappLoader.cordapps.flatMap { it.serializationWhitelists }
|
||||||
@ -328,7 +330,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
|||||||
)
|
)
|
||||||
// Check if we have already stored a version of 'our own' NodeInfo, this is to avoid regenerating it with
|
// Check if we have already stored a version of 'our own' NodeInfo, this is to avoid regenerating it with
|
||||||
// a different timestamp.
|
// a different timestamp.
|
||||||
networkMapCache.getNodesByLegalName(configuration.myLegalName).firstOrNull()?.let {
|
networkMapCache.getNodesByLegalName(myLegalName).firstOrNull()?.let {
|
||||||
if (info.copy(serial = it.serial) == it) {
|
if (info.copy(serial = it.serial) == it) {
|
||||||
info = it
|
info = it
|
||||||
}
|
}
|
||||||
@ -754,10 +756,13 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
|||||||
|
|
||||||
val (id, singleName) = if (notaryConfig == null || !notaryConfig.isClusterConfig) {
|
val (id, singleName) = if (notaryConfig == null || !notaryConfig.isClusterConfig) {
|
||||||
// Node's main identity or if it's a single node notary
|
// Node's main identity or if it's a single node notary
|
||||||
Pair(IdentityGenerator.NODE_IDENTITY_ALIAS_PREFIX, configuration.myLegalName)
|
Pair("identity", myLegalName)
|
||||||
} else {
|
} else {
|
||||||
|
val notaryId = notaryConfig.run {
|
||||||
|
NotaryService.constructId(validating, raft != null, bftSMaRt != null, custom, mysql != null)
|
||||||
|
}
|
||||||
// The node is part of a distributed notary whose identity must already be generated beforehand.
|
// The node is part of a distributed notary whose identity must already be generated beforehand.
|
||||||
Pair(IdentityGenerator.DISTRIBUTED_NOTARY_ALIAS_PREFIX, null)
|
Pair(notaryId, null)
|
||||||
}
|
}
|
||||||
// TODO: Integrate with Key management service?
|
// TODO: Integrate with Key management service?
|
||||||
val privateKeyAlias = "$id-private-key"
|
val privateKeyAlias = "$id-private-key"
|
||||||
@ -765,7 +770,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
|||||||
if (!keyStore.containsAlias(privateKeyAlias)) {
|
if (!keyStore.containsAlias(privateKeyAlias)) {
|
||||||
singleName ?: throw IllegalArgumentException(
|
singleName ?: throw IllegalArgumentException(
|
||||||
"Unable to find in the key store the identity of the distributed notary ($id) the node is part of")
|
"Unable to find in the key store the identity of the distributed notary ($id) the node is part of")
|
||||||
// TODO: Remove use of [IdentityGenerator.generateToDisk].
|
// TODO: Remove use of [ServiceIdentityGenerator.generateToDisk].
|
||||||
log.info("$privateKeyAlias not found in key store ${configuration.nodeKeystore}, generating fresh key!")
|
log.info("$privateKeyAlias not found in key store ${configuration.nodeKeystore}, generating fresh key!")
|
||||||
keyStore.signAndSaveNewKeyPair(singleName, privateKeyAlias, generateKeyPair())
|
keyStore.signAndSaveNewKeyPair(singleName, privateKeyAlias, generateKeyPair())
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ fun SSLConfiguration.configureDevKeyAndTrustStores(myLegalName: CordaX500Name) {
|
|||||||
val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
|
val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
|
||||||
createKeystoreForCordaNode(sslKeystore, nodeKeystore, keyStorePassword, keyStorePassword, caKeyStore, "cordacadevkeypass", myLegalName)
|
createKeystoreForCordaNode(sslKeystore, nodeKeystore, keyStorePassword, keyStorePassword, caKeyStore, "cordacadevkeypass", myLegalName)
|
||||||
|
|
||||||
// Move distributed service composite key (generated by IdentityGenerator.generateToDisk) to keystore if exists.
|
// Move distributed service composite key (generated by ServiceIdentityGenerator.generateToDisk) to keystore if exists.
|
||||||
val distributedServiceKeystore = certificatesDirectory / "distributedService.jks"
|
val distributedServiceKeystore = certificatesDirectory / "distributedService.jks"
|
||||||
if (distributedServiceKeystore.exists()) {
|
if (distributedServiceKeystore.exists()) {
|
||||||
val serviceKeystore = loadKeyStore(distributedServiceKeystore, "cordacadevpass")
|
val serviceKeystore = loadKeyStore(distributedServiceKeystore, "cordacadevpass")
|
||||||
@ -111,17 +111,18 @@ fun createKeystoreForCordaNode(sslKeyStorePath: Path,
|
|||||||
val (intermediateCACert, intermediateCAKeyPair) = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, caKeyPassword)
|
val (intermediateCACert, intermediateCAKeyPair) = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, caKeyPassword)
|
||||||
|
|
||||||
val clientKey = Crypto.generateKeyPair(signatureScheme)
|
val clientKey = Crypto.generateKeyPair(signatureScheme)
|
||||||
|
val clientName = legalName.copy(commonName = null)
|
||||||
|
|
||||||
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.x500Name))), arrayOf())
|
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, clientName.x500Name))), arrayOf())
|
||||||
val clientCACert = X509Utilities.createCertificate(CertificateType.NODE_CA,
|
val clientCACert = X509Utilities.createCertificate(CertificateType.NODE_CA,
|
||||||
intermediateCACert,
|
intermediateCACert,
|
||||||
intermediateCAKeyPair,
|
intermediateCAKeyPair,
|
||||||
legalName,
|
clientName.copy(commonName = X509Utilities.CORDA_CLIENT_CA_CN),
|
||||||
clientKey.public,
|
clientKey.public,
|
||||||
nameConstraints = nameConstraints)
|
nameConstraints = nameConstraints)
|
||||||
|
|
||||||
val tlsKey = Crypto.generateKeyPair(signatureScheme)
|
val tlsKey = Crypto.generateKeyPair(signatureScheme)
|
||||||
val clientTLSCert = X509Utilities.createCertificate(CertificateType.TLS, clientCACert, clientKey, legalName, tlsKey.public)
|
val clientTLSCert = X509Utilities.createCertificate(CertificateType.TLS, clientCACert, clientKey, clientName, tlsKey.public)
|
||||||
|
|
||||||
val keyPass = keyPassword.toCharArray()
|
val keyPass = keyPassword.toCharArray()
|
||||||
|
|
||||||
|
@ -24,7 +24,6 @@ import javax.annotation.concurrent.ThreadSafe
|
|||||||
*
|
*
|
||||||
* @param identities initial set of identities for the service, typically only used for unit tests.
|
* @param identities initial set of identities for the service, typically only used for unit tests.
|
||||||
*/
|
*/
|
||||||
// TODO There is duplicated logic between this and PersistentIdentityService
|
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
class InMemoryIdentityService(identities: Array<out PartyAndCertificate>,
|
class InMemoryIdentityService(identities: Array<out PartyAndCertificate>,
|
||||||
trustRoot: X509CertificateHolder) : SingletonSerializeAsToken(), IdentityServiceInternal {
|
trustRoot: X509CertificateHolder) : SingletonSerializeAsToken(), IdentityServiceInternal {
|
||||||
|
@ -26,7 +26,6 @@ import javax.persistence.Entity
|
|||||||
import javax.persistence.Id
|
import javax.persistence.Id
|
||||||
import javax.persistence.Lob
|
import javax.persistence.Lob
|
||||||
|
|
||||||
// TODO There is duplicated logic between this and InMemoryIdentityService
|
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
class PersistentIdentityService(override val trustRoot: X509Certificate,
|
class PersistentIdentityService(override val trustRoot: X509Certificate,
|
||||||
vararg caCertificates: X509Certificate) : SingletonSerializeAsToken(), IdentityServiceInternal {
|
vararg caCertificates: X509Certificate) : SingletonSerializeAsToken(), IdentityServiceInternal {
|
||||||
|
@ -34,13 +34,12 @@ import kotlin.concurrent.thread
|
|||||||
*
|
*
|
||||||
* A transaction is notarised when the consensus is reached by the cluster on its uniqueness, and time-window validity.
|
* A transaction is notarised when the consensus is reached by the cluster on its uniqueness, and time-window validity.
|
||||||
*/
|
*/
|
||||||
class BFTNonValidatingNotaryService(
|
class BFTNonValidatingNotaryService(override val services: ServiceHubInternal,
|
||||||
override val services: ServiceHubInternal,
|
|
||||||
override val notaryIdentityKey: PublicKey,
|
override val notaryIdentityKey: PublicKey,
|
||||||
private val bftSMaRtConfig: BFTSMaRtConfiguration,
|
private val bftSMaRtConfig: BFTSMaRtConfiguration,
|
||||||
cluster: BFTSMaRt.Cluster
|
cluster: BFTSMaRt.Cluster) : NotaryService() {
|
||||||
) : NotaryService() {
|
|
||||||
companion object {
|
companion object {
|
||||||
|
val id = constructId(validating = false, bft = true)
|
||||||
private val log = contextLogger()
|
private val log = contextLogger()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,6 +35,9 @@ class MySQLNonValidatingNotaryService(services: ServiceHubInternal,
|
|||||||
notaryIdentityKey: PublicKey,
|
notaryIdentityKey: PublicKey,
|
||||||
dataSourceProperties: Properties,
|
dataSourceProperties: Properties,
|
||||||
devMode: Boolean = false) : MySQLNotaryService(services, notaryIdentityKey, dataSourceProperties, devMode) {
|
devMode: Boolean = false) : MySQLNotaryService(services, notaryIdentityKey, dataSourceProperties, devMode) {
|
||||||
|
companion object {
|
||||||
|
val id = constructId(validating = false, mysql = true)
|
||||||
|
}
|
||||||
override fun createServiceFlow(otherPartySession: FlowSession): FlowLogic<Void?> = NonValidatingNotaryFlow(otherPartySession, this)
|
override fun createServiceFlow(otherPartySession: FlowSession): FlowLogic<Void?> = NonValidatingNotaryFlow(otherPartySession, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,5 +45,8 @@ class MySQLValidatingNotaryService(services: ServiceHubInternal,
|
|||||||
notaryIdentityKey: PublicKey,
|
notaryIdentityKey: PublicKey,
|
||||||
dataSourceProperties: Properties,
|
dataSourceProperties: Properties,
|
||||||
devMode: Boolean = false) : MySQLNotaryService(services, notaryIdentityKey, dataSourceProperties, devMode) {
|
devMode: Boolean = false) : MySQLNotaryService(services, notaryIdentityKey, dataSourceProperties, devMode) {
|
||||||
|
companion object {
|
||||||
|
val id = constructId(validating = true, mysql = true)
|
||||||
|
}
|
||||||
override fun createServiceFlow(otherPartySession: FlowSession): FlowLogic<Void?> = ValidatingNotaryFlow(otherPartySession, this)
|
override fun createServiceFlow(otherPartySession: FlowSession): FlowLogic<Void?> = ValidatingNotaryFlow(otherPartySession, this)
|
||||||
}
|
}
|
@ -8,13 +8,14 @@ import net.corda.core.node.services.TrustedAuthorityNotaryService
|
|||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
|
|
||||||
/** A non-validating notary service operated by a group of mutually trusting parties, uses the Raft algorithm to achieve consensus. */
|
/** A non-validating notary service operated by a group of mutually trusting parties, uses the Raft algorithm to achieve consensus. */
|
||||||
class RaftNonValidatingNotaryService(
|
class RaftNonValidatingNotaryService(override val services: ServiceHub,
|
||||||
override val services: ServiceHub,
|
|
||||||
override val notaryIdentityKey: PublicKey,
|
override val notaryIdentityKey: PublicKey,
|
||||||
override val uniquenessProvider: RaftUniquenessProvider
|
override val uniquenessProvider: RaftUniquenessProvider) : TrustedAuthorityNotaryService() {
|
||||||
) : TrustedAuthorityNotaryService() {
|
companion object {
|
||||||
override val timeWindowChecker: TimeWindowChecker = TimeWindowChecker(services.clock)
|
val id = constructId(validating = false, raft = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val timeWindowChecker: TimeWindowChecker = TimeWindowChecker(services.clock)
|
||||||
override fun createServiceFlow(otherPartySession: FlowSession): NotaryFlow.Service {
|
override fun createServiceFlow(otherPartySession: FlowSession): NotaryFlow.Service {
|
||||||
return NonValidatingNotaryFlow(otherPartySession, this)
|
return NonValidatingNotaryFlow(otherPartySession, this)
|
||||||
}
|
}
|
||||||
|
@ -8,13 +8,14 @@ import net.corda.core.node.services.TrustedAuthorityNotaryService
|
|||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
|
|
||||||
/** A validating notary service operated by a group of mutually trusting parties, uses the Raft algorithm to achieve consensus. */
|
/** A validating notary service operated by a group of mutually trusting parties, uses the Raft algorithm to achieve consensus. */
|
||||||
class RaftValidatingNotaryService(
|
class RaftValidatingNotaryService(override val services: ServiceHub,
|
||||||
override val services: ServiceHub,
|
|
||||||
override val notaryIdentityKey: PublicKey,
|
override val notaryIdentityKey: PublicKey,
|
||||||
override val uniquenessProvider: RaftUniquenessProvider
|
override val uniquenessProvider: RaftUniquenessProvider) : TrustedAuthorityNotaryService() {
|
||||||
) : TrustedAuthorityNotaryService() {
|
companion object {
|
||||||
override val timeWindowChecker: TimeWindowChecker = TimeWindowChecker(services.clock)
|
val id = constructId(validating = true, raft = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val timeWindowChecker: TimeWindowChecker = TimeWindowChecker(services.clock)
|
||||||
override fun createServiceFlow(otherPartySession: FlowSession): NotaryFlow.Service {
|
override fun createServiceFlow(otherPartySession: FlowSession): NotaryFlow.Service {
|
||||||
return ValidatingNotaryFlow(otherPartySession, this)
|
return ValidatingNotaryFlow(otherPartySession, this)
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,10 @@ import java.security.PublicKey
|
|||||||
|
|
||||||
/** A Notary service that validates the transaction chain of the submitted transaction before committing it */
|
/** A Notary service that validates the transaction chain of the submitted transaction before committing it */
|
||||||
class ValidatingNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() {
|
class ValidatingNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() {
|
||||||
|
companion object {
|
||||||
|
val id = constructId(validating = true)
|
||||||
|
}
|
||||||
override val timeWindowChecker = TimeWindowChecker(services.clock)
|
override val timeWindowChecker = TimeWindowChecker(services.clock)
|
||||||
|
|
||||||
override val uniquenessProvider = PersistentUniquenessProvider()
|
override val uniquenessProvider = PersistentUniquenessProvider()
|
||||||
|
|
||||||
override fun createServiceFlow(otherPartySession: FlowSession): NotaryFlow.Service = ValidatingNotaryFlow(otherPartySession, this)
|
override fun createServiceFlow(otherPartySession: FlowSession): NotaryFlow.Service = ValidatingNotaryFlow(otherPartySession, this)
|
||||||
|
@ -103,9 +103,10 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v
|
|||||||
println("Generating SSL certificate for node messaging service.")
|
println("Generating SSL certificate for node messaging service.")
|
||||||
val sslKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val sslKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val caCert = caKeyStore.getX509Certificate(CORDA_CLIENT_CA).toX509CertHolder()
|
val caCert = caKeyStore.getX509Certificate(CORDA_CLIENT_CA).toX509CertHolder()
|
||||||
val sslCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, keyPair, CordaX500Name.build(caCert.cert.subjectX500Principal), sslKey.public)
|
val sslCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, keyPair, CordaX500Name.build(caCert.cert.subjectX500Principal).copy(commonName = null), sslKey.public)
|
||||||
val sslKeyStore = loadOrCreateKeyStore(config.sslKeystore, keystorePassword)
|
val sslKeyStore = loadOrCreateKeyStore(config.sslKeystore, keystorePassword)
|
||||||
sslKeyStore.addOrReplaceKey(CORDA_CLIENT_TLS, sslKey.private, privateKeyPassword.toCharArray(), arrayOf(sslCert.cert, *certificates))
|
sslKeyStore.addOrReplaceKey(CORDA_CLIENT_TLS, sslKey.private, privateKeyPassword.toCharArray(),
|
||||||
|
arrayOf(sslCert.cert, *certificates))
|
||||||
sslKeyStore.save(config.sslKeystore, config.keyStorePassword)
|
sslKeyStore.save(config.sslKeystore, config.keyStorePassword)
|
||||||
println("SSL private key and certificate stored in ${config.sslKeystore}.")
|
println("SSL private key and certificate stored in ${config.sslKeystore}.")
|
||||||
// All done, clean up temp files.
|
// All done, clean up temp files.
|
||||||
|
@ -656,7 +656,7 @@ class FlowFrameworkTests {
|
|||||||
private inline fun <reified P : FlowLogic<*>> StartedNode<MockNode>.restartAndGetRestoredFlow() = internals.run {
|
private inline fun <reified P : FlowLogic<*>> StartedNode<MockNode>.restartAndGetRestoredFlow() = internals.run {
|
||||||
disableDBCloseOnStop() // Handover DB to new node copy
|
disableDBCloseOnStop() // Handover DB to new node copy
|
||||||
stop()
|
stop()
|
||||||
val newNode = mockNet.createNode(MockNodeParameters(id, configuration.myLegalName))
|
val newNode = mockNet.createNode(MockNodeParameters(id))
|
||||||
newNode.internals.acceptableLiveFiberCountOnStop = 1
|
newNode.internals.acceptableLiveFiberCountOnStop = 1
|
||||||
manuallyCloseDB()
|
manuallyCloseDB()
|
||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package net.corda.node.utilities.registration
|
package net.corda.node.utilities.registration
|
||||||
|
|
||||||
import com.google.common.jimfs.Configuration.unix
|
|
||||||
import com.google.common.jimfs.Jimfs
|
|
||||||
import com.nhaarman.mockito_kotlin.any
|
import com.nhaarman.mockito_kotlin.any
|
||||||
import com.nhaarman.mockito_kotlin.doReturn
|
import com.nhaarman.mockito_kotlin.doReturn
|
||||||
import com.nhaarman.mockito_kotlin.eq
|
import com.nhaarman.mockito_kotlin.eq
|
||||||
@ -9,61 +7,68 @@ import com.nhaarman.mockito_kotlin.whenever
|
|||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.cert
|
import net.corda.core.internal.*
|
||||||
import net.corda.core.internal.createDirectories
|
|
||||||
import net.corda.node.services.config.NodeConfiguration
|
import net.corda.node.services.config.NodeConfiguration
|
||||||
import net.corda.nodeapi.internal.crypto.*
|
import net.corda.nodeapi.internal.crypto.*
|
||||||
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
|
import net.corda.nodeapi.internal.crypto.getX509Certificate
|
||||||
|
import net.corda.nodeapi.internal.crypto.loadKeyStore
|
||||||
import net.corda.testing.ALICE_NAME
|
import net.corda.testing.ALICE_NAME
|
||||||
import net.corda.testing.internal.rigorousMock
|
import net.corda.testing.internal.rigorousMock
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
|
||||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||||
import org.junit.After
|
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import org.junit.rules.TemporaryFolder
|
||||||
import java.security.cert.Certificate
|
import java.security.cert.Certificate
|
||||||
import java.security.cert.X509Certificate
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertFalse
|
import kotlin.test.assertFalse
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
class NetworkRegistrationHelperTest {
|
class NetworkRegistrationHelperTest {
|
||||||
private val fs = Jimfs.newFileSystem(unix())
|
@Rule
|
||||||
private val requestId = SecureHash.randomSHA256().toString()
|
@JvmField
|
||||||
private val nodeLegalName = ALICE_NAME
|
val tempFolder = TemporaryFolder()
|
||||||
private val intermediateCaName = CordaX500Name("CORDA_INTERMEDIATE_CA", "R3 Ltd", "London", "GB")
|
|
||||||
private val rootCaName = CordaX500Name("CORDA_ROOT_CA", "R3 Ltd", "London", "GB")
|
|
||||||
private val nodeCaCert = createCaCert(nodeLegalName)
|
|
||||||
private val intermediateCaCert = createCaCert(intermediateCaName)
|
|
||||||
private val rootCaCert = createCaCert(rootCaName)
|
|
||||||
|
|
||||||
|
private val requestId = SecureHash.randomSHA256().toString()
|
||||||
private lateinit var config: NodeConfiguration
|
private lateinit var config: NodeConfiguration
|
||||||
|
|
||||||
|
private val identities = listOf("CORDA_CLIENT_CA",
|
||||||
|
"CORDA_INTERMEDIATE_CA",
|
||||||
|
"CORDA_ROOT_CA")
|
||||||
|
.map { CordaX500Name(commonName = it, organisation = "R3 Ltd", locality = "London", country = "GB") }
|
||||||
|
private val certs = identities.map { X509Utilities.createSelfSignedCACertificate(it, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) }
|
||||||
|
.map { it.cert }.toTypedArray()
|
||||||
|
|
||||||
|
private val certService = mockRegistrationResponse(*certs)
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun init() {
|
fun init() {
|
||||||
val baseDirectory = fs.getPath("/baseDir").createDirectories()
|
|
||||||
abstract class AbstractNodeConfiguration : NodeConfiguration
|
abstract class AbstractNodeConfiguration : NodeConfiguration
|
||||||
config = rigorousMock<AbstractNodeConfiguration>().also {
|
config = rigorousMock<AbstractNodeConfiguration>().also {
|
||||||
doReturn(baseDirectory).whenever(it).baseDirectory
|
doReturn(tempFolder.root.toPath()).whenever(it).baseDirectory
|
||||||
doReturn("trustpass").whenever(it).trustStorePassword
|
doReturn("trustpass").whenever(it).trustStorePassword
|
||||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||||
doReturn(nodeLegalName).whenever(it).myLegalName
|
doReturn(ALICE_NAME).whenever(it).myLegalName
|
||||||
doReturn("").whenever(it).emailAddress
|
doReturn("").whenever(it).emailAddress
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
|
||||||
fun cleanUp() {
|
|
||||||
fs.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `successful registration`() {
|
fun `successful registration`() {
|
||||||
assertThat(config.nodeKeystore).doesNotExist()
|
assertFalse(config.nodeKeystore.exists())
|
||||||
assertThat(config.sslKeystore).doesNotExist()
|
assertFalse(config.sslKeystore.exists())
|
||||||
assertThat(config.trustStoreFile).doesNotExist()
|
config.trustStoreFile.parent.createDirectories()
|
||||||
|
loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also {
|
||||||
|
it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, certs.last())
|
||||||
|
it.save(config.trustStoreFile, config.trustStorePassword)
|
||||||
|
}
|
||||||
|
|
||||||
saveTrustStoreWithRootCa(rootCaCert)
|
NetworkRegistrationHelper(config, certService).buildKeystore()
|
||||||
|
|
||||||
createRegistrationHelper().buildKeystore()
|
assertTrue(config.nodeKeystore.exists())
|
||||||
|
assertTrue(config.sslKeystore.exists())
|
||||||
|
assertTrue(config.trustStoreFile.exists())
|
||||||
|
|
||||||
val nodeKeystore = loadKeyStore(config.nodeKeystore, config.keyStorePassword)
|
val nodeKeystore = loadKeyStore(config.nodeKeystore, config.keyStorePassword)
|
||||||
val sslKeystore = loadKeyStore(config.sslKeystore, config.keyStorePassword)
|
val sslKeystore = loadKeyStore(config.sslKeystore, config.keyStorePassword)
|
||||||
@ -74,8 +79,9 @@ class NetworkRegistrationHelperTest {
|
|||||||
assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA))
|
assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA))
|
||||||
assertFalse(containsAlias(X509Utilities.CORDA_ROOT_CA))
|
assertFalse(containsAlias(X509Utilities.CORDA_ROOT_CA))
|
||||||
assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_TLS))
|
assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_TLS))
|
||||||
val nodeCaCertChain = getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
|
val certificateChain = getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
|
||||||
assertThat(nodeCaCertChain).containsExactly(nodeCaCert, intermediateCaCert, rootCaCert)
|
assertEquals(3, certificateChain.size)
|
||||||
|
assertEquals(listOf("CORDA_CLIENT_CA", "CORDA_INTERMEDIATE_CA", "CORDA_ROOT_CA"), certificateChain.map { it.toX509CertHolder().subject.commonName })
|
||||||
}
|
}
|
||||||
|
|
||||||
sslKeystore.run {
|
sslKeystore.run {
|
||||||
@ -83,55 +89,46 @@ class NetworkRegistrationHelperTest {
|
|||||||
assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA))
|
assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA))
|
||||||
assertFalse(containsAlias(X509Utilities.CORDA_ROOT_CA))
|
assertFalse(containsAlias(X509Utilities.CORDA_ROOT_CA))
|
||||||
assertTrue(containsAlias(X509Utilities.CORDA_CLIENT_TLS))
|
assertTrue(containsAlias(X509Utilities.CORDA_CLIENT_TLS))
|
||||||
val nodeTlsCertChain = getCertificateChain(X509Utilities.CORDA_CLIENT_TLS)
|
val certificateChain = getCertificateChain(X509Utilities.CORDA_CLIENT_TLS)
|
||||||
assertThat(nodeTlsCertChain).hasSize(4)
|
assertEquals(4, certificateChain.size)
|
||||||
// The TLS cert has the same subject as the node CA cert
|
assertEquals(listOf(CordaX500Name(organisation = "R3 Ltd", locality = "London", country = "GB").x500Name) + identities.map { it.x500Name },
|
||||||
assertThat(CordaX500Name.build((nodeTlsCertChain[0] as X509Certificate).subjectX500Principal)).isEqualTo(nodeLegalName)
|
certificateChain.map { it.toX509CertHolder().subject })
|
||||||
assertThat(nodeTlsCertChain.drop(1)).containsExactly(nodeCaCert, intermediateCaCert, rootCaCert)
|
assertEquals(CordaX500Name(organisation = "R3 Ltd", locality = "London", country = "GB").x500Principal,
|
||||||
|
getX509Certificate(X509Utilities.CORDA_CLIENT_TLS).subjectX500Principal)
|
||||||
}
|
}
|
||||||
|
|
||||||
trustStore.run {
|
trustStore.run {
|
||||||
assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_CA))
|
assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_CA))
|
||||||
assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA))
|
assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA))
|
||||||
assertTrue(containsAlias(X509Utilities.CORDA_ROOT_CA))
|
assertTrue(containsAlias(X509Utilities.CORDA_ROOT_CA))
|
||||||
val trustStoreRootCaCert = getCertificate(X509Utilities.CORDA_ROOT_CA)
|
|
||||||
assertThat(trustStoreRootCaCert).isEqualTo(rootCaCert)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `missing truststore`() {
|
fun `missing truststore`() {
|
||||||
assertThatThrownBy {
|
assertThatThrownBy {
|
||||||
createRegistrationHelper()
|
NetworkRegistrationHelper(config, certService).buildKeystore()
|
||||||
}.hasMessageContaining("This file must contain the root CA cert of your compatibility zone. Please contact your CZ operator.")
|
}.hasMessageContaining("This file must contain the root CA cert of your compatibility zone. Please contact your CZ operator.")
|
||||||
|
.isInstanceOf(IllegalArgumentException::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `wrong root cert in truststore`() {
|
fun `wrong root cert in truststore`() {
|
||||||
saveTrustStoreWithRootCa(createCaCert(CordaX500Name("Foo", "MU", "GB")))
|
val someCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name("Foo", "MU", "GB"), Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)).cert
|
||||||
val registrationHelper = createRegistrationHelper()
|
config.trustStoreFile.parent.createDirectories()
|
||||||
|
loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also {
|
||||||
|
it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, someCert)
|
||||||
|
it.save(config.trustStoreFile, config.trustStorePassword)
|
||||||
|
}
|
||||||
assertThatThrownBy {
|
assertThatThrownBy {
|
||||||
registrationHelper.buildKeystore()
|
NetworkRegistrationHelper(config, certService).buildKeystore()
|
||||||
}.isInstanceOf(WrongRootCertException::class.java)
|
}.isInstanceOf(WrongRootCertException::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createRegistrationHelper(): NetworkRegistrationHelper {
|
private fun mockRegistrationResponse(vararg response: Certificate): NetworkRegistrationService {
|
||||||
val certService = rigorousMock<NetworkRegistrationService>().also {
|
return rigorousMock<NetworkRegistrationService>().also {
|
||||||
doReturn(requestId).whenever(it).submitRequest(any())
|
doReturn(requestId).whenever(it).submitRequest(any())
|
||||||
doReturn(arrayOf<Certificate>(nodeCaCert, intermediateCaCert, rootCaCert)).whenever(it).retrieveCertificates(eq(requestId))
|
doReturn(response).whenever(it).retrieveCertificates(eq(requestId))
|
||||||
}
|
}
|
||||||
return NetworkRegistrationHelper(config, certService)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun saveTrustStoreWithRootCa(rootCa: X509Certificate) {
|
|
||||||
config.trustStoreFile.parent.createDirectories()
|
|
||||||
loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also {
|
|
||||||
it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCa)
|
|
||||||
it.save(config.trustStoreFile, config.trustStorePassword)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createCaCert(name: CordaX500Name): X509Certificate {
|
|
||||||
return X509Utilities.createSelfSignedCACertificate(name, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)).cert
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,13 @@ import net.corda.cordform.CordformContext
|
|||||||
import net.corda.cordform.CordformDefinition
|
import net.corda.cordform.CordformDefinition
|
||||||
import net.corda.cordform.CordformNode
|
import net.corda.cordform.CordformNode
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
|
import net.corda.core.node.services.NotaryService
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.node.services.config.BFTSMaRtConfiguration
|
import net.corda.node.services.config.BFTSMaRtConfiguration
|
||||||
import net.corda.node.services.config.NotaryConfig
|
import net.corda.node.services.config.NotaryConfig
|
||||||
|
import net.corda.node.services.transactions.BFTNonValidatingNotaryService
|
||||||
import net.corda.node.services.transactions.minCorrectReplicas
|
import net.corda.node.services.transactions.minCorrectReplicas
|
||||||
import net.corda.nodeapi.internal.IdentityGenerator
|
import net.corda.nodeapi.internal.ServiceIdentityGenerator
|
||||||
import net.corda.testing.node.internal.demorun.*
|
import net.corda.testing.node.internal.demorun.*
|
||||||
import net.corda.testing.ALICE_NAME
|
import net.corda.testing.ALICE_NAME
|
||||||
import net.corda.testing.BOB_NAME
|
import net.corda.testing.BOB_NAME
|
||||||
@ -22,7 +24,7 @@ private val notaryNames = createNotaryNames(clusterSize)
|
|||||||
// This is not the intended final design for how to use CordformDefinition, please treat this as experimental and DO
|
// This is not the intended final design for how to use CordformDefinition, please treat this as experimental and DO
|
||||||
// NOT use this as a design to copy.
|
// NOT use this as a design to copy.
|
||||||
class BFTNotaryCordform : CordformDefinition() {
|
class BFTNotaryCordform : CordformDefinition() {
|
||||||
private val clusterName = CordaX500Name("BFT", "Zurich", "CH")
|
private val clusterName = CordaX500Name(BFTNonValidatingNotaryService.id, "BFT", "Zurich", "CH")
|
||||||
|
|
||||||
init {
|
init {
|
||||||
nodesDirectory = Paths.get("build", "nodes", "nodesBFT")
|
nodesDirectory = Paths.get("build", "nodes", "nodesBFT")
|
||||||
@ -62,10 +64,10 @@ class BFTNotaryCordform : CordformDefinition() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun setup(context: CordformContext) {
|
override fun setup(context: CordformContext) {
|
||||||
IdentityGenerator.generateDistributedNotaryIdentity(
|
ServiceIdentityGenerator.generateToDisk(
|
||||||
notaryNames.map { context.baseDirectory(it.toString()) },
|
notaryNames.map { context.baseDirectory(it.toString()) },
|
||||||
clusterName,
|
clusterName,
|
||||||
minCorrectReplicas(clusterSize)
|
NotaryService.constructId(validating = false, bft = true),
|
||||||
)
|
minCorrectReplicas(clusterSize))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,8 @@ import net.corda.core.node.services.NotaryService
|
|||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.node.services.config.NotaryConfig
|
import net.corda.node.services.config.NotaryConfig
|
||||||
import net.corda.node.services.config.RaftConfig
|
import net.corda.node.services.config.RaftConfig
|
||||||
import net.corda.nodeapi.internal.IdentityGenerator
|
import net.corda.node.services.transactions.RaftValidatingNotaryService
|
||||||
|
import net.corda.nodeapi.internal.ServiceIdentityGenerator
|
||||||
import net.corda.testing.node.internal.demorun.*
|
import net.corda.testing.node.internal.demorun.*
|
||||||
import net.corda.testing.ALICE_NAME
|
import net.corda.testing.ALICE_NAME
|
||||||
import net.corda.testing.BOB_NAME
|
import net.corda.testing.BOB_NAME
|
||||||
@ -23,7 +24,7 @@ private val notaryNames = createNotaryNames(3)
|
|||||||
// This is not the intended final design for how to use CordformDefinition, please treat this as experimental and DO
|
// This is not the intended final design for how to use CordformDefinition, please treat this as experimental and DO
|
||||||
// NOT use this as a design to copy.
|
// NOT use this as a design to copy.
|
||||||
class RaftNotaryCordform : CordformDefinition() {
|
class RaftNotaryCordform : CordformDefinition() {
|
||||||
private val clusterName = CordaX500Name("Raft", "Zurich", "CH")
|
private val clusterName = CordaX500Name(RaftValidatingNotaryService.id, "Raft", "Zurich", "CH")
|
||||||
|
|
||||||
init {
|
init {
|
||||||
nodesDirectory = Paths.get("build", "nodes", "nodesRaft")
|
nodesDirectory = Paths.get("build", "nodes", "nodesRaft")
|
||||||
@ -59,9 +60,9 @@ class RaftNotaryCordform : CordformDefinition() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun setup(context: CordformContext) {
|
override fun setup(context: CordformContext) {
|
||||||
IdentityGenerator.generateDistributedNotaryIdentity(
|
ServiceIdentityGenerator.generateToDisk(
|
||||||
notaryNames.map { context.baseDirectory(it.toString()) },
|
notaryNames.map { context.baseDirectory(it.toString()) },
|
||||||
clusterName
|
clusterName,
|
||||||
)
|
NotaryService.constructId(validating = true, raft = true))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ dependencies {
|
|||||||
compile "net.corda.plugins:cordform-common:$gradle_plugins_version"
|
compile "net.corda.plugins:cordform-common:$gradle_plugins_version"
|
||||||
|
|
||||||
// Integration test helpers
|
// Integration test helpers
|
||||||
testCompile "org.assertj:assertj-core:$assertj_version"
|
integrationTestCompile "org.assertj:assertj-core:${assertj_version}"
|
||||||
integrationTestCompile "junit:junit:$junit_version"
|
integrationTestCompile "junit:junit:$junit_version"
|
||||||
|
|
||||||
// Jetty dependencies for NetworkMapClient test.
|
// Jetty dependencies for NetworkMapClient test.
|
||||||
|
@ -9,7 +9,6 @@ import net.corda.core.crypto.random63BitValue
|
|||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.identity.PartyAndCertificate
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
import net.corda.core.internal.VisibleForTesting
|
|
||||||
import net.corda.core.internal.createDirectories
|
import net.corda.core.internal.createDirectories
|
||||||
import net.corda.core.internal.createDirectory
|
import net.corda.core.internal.createDirectory
|
||||||
import net.corda.core.internal.uncheckedCast
|
import net.corda.core.internal.uncheckedCast
|
||||||
@ -38,7 +37,7 @@ import net.corda.node.services.transactions.BFTSMaRt
|
|||||||
import net.corda.node.services.transactions.InMemoryTransactionVerifierService
|
import net.corda.node.services.transactions.InMemoryTransactionVerifierService
|
||||||
import net.corda.node.utilities.AffinityExecutor
|
import net.corda.node.utilities.AffinityExecutor
|
||||||
import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor
|
import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor
|
||||||
import net.corda.nodeapi.internal.IdentityGenerator
|
import net.corda.nodeapi.internal.ServiceIdentityGenerator
|
||||||
import net.corda.nodeapi.internal.config.User
|
import net.corda.nodeapi.internal.config.User
|
||||||
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||||
import net.corda.nodeapi.internal.network.NotaryInfo
|
import net.corda.nodeapi.internal.network.NotaryInfo
|
||||||
@ -126,7 +125,7 @@ data class MockNodeArgs(
|
|||||||
* By default a single notary node is automatically started, which forms part of the network parameters for all the nodes.
|
* By default a single notary node is automatically started, which forms part of the network parameters for all the nodes.
|
||||||
* This node is available by calling [defaultNotaryNode].
|
* This node is available by calling [defaultNotaryNode].
|
||||||
*/
|
*/
|
||||||
open class MockNetwork(private val cordappPackages: List<String>,
|
class MockNetwork(private val cordappPackages: List<String>,
|
||||||
defaultParameters: MockNetworkParameters = MockNetworkParameters(),
|
defaultParameters: MockNetworkParameters = MockNetworkParameters(),
|
||||||
private val networkSendManuallyPumped: Boolean = defaultParameters.networkSendManuallyPumped,
|
private val networkSendManuallyPumped: Boolean = defaultParameters.networkSendManuallyPumped,
|
||||||
private val threadPerNode: Boolean = defaultParameters.threadPerNode,
|
private val threadPerNode: Boolean = defaultParameters.threadPerNode,
|
||||||
@ -142,7 +141,7 @@ open class MockNetwork(private val cordappPackages: List<String>,
|
|||||||
// Apache SSHD for whatever reason registers a SFTP FileSystemProvider - which gets loaded by JimFS.
|
// Apache SSHD for whatever reason registers a SFTP FileSystemProvider - which gets loaded by JimFS.
|
||||||
// This SFTP support loads BouncyCastle, which we want to avoid.
|
// This SFTP support loads BouncyCastle, which we want to avoid.
|
||||||
// Please see https://issues.apache.org/jira/browse/SSHD-736 - it's easier then to create our own fork of SSHD
|
// Please see https://issues.apache.org/jira/browse/SSHD-736 - it's easier then to create our own fork of SSHD
|
||||||
SecurityUtils.setAPrioriDisabledProvider("BC", true) // XXX: Why isn't this static?
|
SecurityUtils.setAPrioriDisabledProvider("BC", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
var nextNodeId = 0
|
var nextNodeId = 0
|
||||||
@ -160,7 +159,6 @@ open class MockNetwork(private val cordappPackages: List<String>,
|
|||||||
throw IllegalStateException("Using more than one MockNetwork simultaneously is not supported.", e)
|
throw IllegalStateException("Using more than one MockNetwork simultaneously is not supported.", e)
|
||||||
}
|
}
|
||||||
private val sharedUserCount = AtomicInteger(0)
|
private val sharedUserCount = AtomicInteger(0)
|
||||||
|
|
||||||
/** A read only view of the current set of nodes. */
|
/** A read only view of the current set of nodes. */
|
||||||
val nodes: List<MockNode> get() = _nodes
|
val nodes: List<MockNode> get() = _nodes
|
||||||
|
|
||||||
@ -174,7 +172,8 @@ open class MockNetwork(private val cordappPackages: List<String>,
|
|||||||
* Returns the single notary node on the network. Throws if there are none or more than one.
|
* Returns the single notary node on the network. Throws if there are none or more than one.
|
||||||
* @see notaryNodes
|
* @see notaryNodes
|
||||||
*/
|
*/
|
||||||
val defaultNotaryNode: StartedNode<MockNode> get() {
|
val defaultNotaryNode: StartedNode<MockNode>
|
||||||
|
get() {
|
||||||
return when (notaryNodes.size) {
|
return when (notaryNodes.size) {
|
||||||
0 -> throw IllegalStateException("There are no notaries defined on the network")
|
0 -> throw IllegalStateException("There are no notaries defined on the network")
|
||||||
1 -> notaryNodes[0]
|
1 -> notaryNodes[0]
|
||||||
@ -186,7 +185,8 @@ open class MockNetwork(private val cordappPackages: List<String>,
|
|||||||
* Return the identity of the default notary node.
|
* Return the identity of the default notary node.
|
||||||
* @see defaultNotaryNode
|
* @see defaultNotaryNode
|
||||||
*/
|
*/
|
||||||
val defaultNotaryIdentity: Party get() {
|
val defaultNotaryIdentity: Party
|
||||||
|
get() {
|
||||||
return defaultNotaryNode.info.legalIdentities.singleOrNull() ?: throw IllegalStateException("Default notary has multiple identities")
|
return defaultNotaryNode.info.legalIdentities.singleOrNull() ?: throw IllegalStateException("Default notary has multiple identities")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,7 +194,8 @@ open class MockNetwork(private val cordappPackages: List<String>,
|
|||||||
* Return the identity of the default notary node.
|
* Return the identity of the default notary node.
|
||||||
* @see defaultNotaryNode
|
* @see defaultNotaryNode
|
||||||
*/
|
*/
|
||||||
val defaultNotaryIdentityAndCert: PartyAndCertificate get() {
|
val defaultNotaryIdentityAndCert: PartyAndCertificate
|
||||||
|
get() {
|
||||||
return defaultNotaryNode.info.legalIdentitiesAndCerts.singleOrNull() ?: throw IllegalStateException("Default notary has multiple identities")
|
return defaultNotaryNode.info.legalIdentitiesAndCerts.singleOrNull() ?: throw IllegalStateException("Default notary has multiple identities")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,31 +222,27 @@ open class MockNetwork(private val cordappPackages: List<String>,
|
|||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
try {
|
|
||||||
filesystem.getPath("/nodes").createDirectory()
|
filesystem.getPath("/nodes").createDirectory()
|
||||||
val notaryInfos = generateNotaryIdentities()
|
val notaryInfos = generateNotaryIdentities()
|
||||||
// The network parameters must be serialised before starting any of the nodes
|
// The network parameters must be serialised before starting any of the nodes
|
||||||
networkParameters = NetworkParametersCopier(testNetworkParameters(notaryInfos))
|
networkParameters = NetworkParametersCopier(testNetworkParameters(notaryInfos))
|
||||||
@Suppress("LeakingThis")
|
|
||||||
notaryNodes = createNotaries()
|
notaryNodes = createNotaries()
|
||||||
} catch (t: Throwable) {
|
|
||||||
stopNodes()
|
|
||||||
throw t
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateNotaryIdentities(): List<NotaryInfo> {
|
private fun generateNotaryIdentities(): List<NotaryInfo> {
|
||||||
return notarySpecs.mapIndexed { index, (name, validating) ->
|
return notarySpecs.mapIndexed { index, (name, validating) ->
|
||||||
val identity = IdentityGenerator.generateNodeIdentity(baseDirectory(nextNodeId + index), name)
|
val identity = ServiceIdentityGenerator.generateToDisk(
|
||||||
|
dirs = listOf(baseDirectory(nextNodeId + index)),
|
||||||
|
serviceName = name,
|
||||||
|
serviceId = "identity")
|
||||||
NotaryInfo(identity, validating)
|
NotaryInfo(identity, validating)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
private fun createNotaries(): List<StartedNode<MockNode>> {
|
||||||
internal open fun createNotaries(): List<StartedNode<MockNode>> {
|
return notarySpecs.map { spec ->
|
||||||
return notarySpecs.map { (name, validating) ->
|
createNode(MockNodeParameters(legalName = spec.name, configOverrides = {
|
||||||
createNode(MockNodeParameters(legalName = name, configOverrides = {
|
doReturn(NotaryConfig(spec.validating)).whenever(it).notary
|
||||||
doReturn(NotaryConfig(validating)).whenever(it).notary
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -302,7 +299,7 @@ open class MockNetwork(private val cordappPackages: List<String>,
|
|||||||
id,
|
id,
|
||||||
serverThread,
|
serverThread,
|
||||||
myNotaryIdentity,
|
myNotaryIdentity,
|
||||||
configuration.myLegalName,
|
myLegalName,
|
||||||
database).also { runOnStop += it::stop }
|
database).also { runOnStop += it::stop }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -460,11 +457,8 @@ open class MockNetwork(private val cordappPackages: List<String>,
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun stopNodes() {
|
fun stopNodes() {
|
||||||
try {
|
|
||||||
nodes.forEach { it.started?.dispose() }
|
nodes.forEach { it.started?.dispose() }
|
||||||
} finally {
|
serializationEnv.unset()
|
||||||
serializationEnv.unset() // Must execute even if other parts of this method fail.
|
|
||||||
}
|
|
||||||
messagingNetwork.stop()
|
messagingNetwork.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ import net.corda.core.internal.createDirectories
|
|||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.messaging.CordaRPCOps
|
import net.corda.core.messaging.CordaRPCOps
|
||||||
import net.corda.core.node.services.NetworkMapCache
|
import net.corda.core.node.services.NetworkMapCache
|
||||||
|
import net.corda.core.node.services.NotaryService
|
||||||
import net.corda.core.toFuture
|
import net.corda.core.toFuture
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.core.utilities.contextLogger
|
import net.corda.core.utilities.contextLogger
|
||||||
@ -29,9 +30,12 @@ import net.corda.node.internal.NodeStartup
|
|||||||
import net.corda.node.internal.StartedNode
|
import net.corda.node.internal.StartedNode
|
||||||
import net.corda.node.services.Permissions
|
import net.corda.node.services.Permissions
|
||||||
import net.corda.node.services.config.*
|
import net.corda.node.services.config.*
|
||||||
|
import net.corda.node.services.transactions.BFTNonValidatingNotaryService
|
||||||
|
import net.corda.node.services.transactions.RaftNonValidatingNotaryService
|
||||||
|
import net.corda.node.services.transactions.RaftValidatingNotaryService
|
||||||
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
|
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
|
||||||
import net.corda.node.utilities.registration.NetworkRegistrationHelper
|
import net.corda.node.utilities.registration.NetworkRegistrationHelper
|
||||||
import net.corda.nodeapi.internal.IdentityGenerator
|
import net.corda.nodeapi.internal.ServiceIdentityGenerator
|
||||||
import net.corda.nodeapi.internal.addShutdownHook
|
import net.corda.nodeapi.internal.addShutdownHook
|
||||||
import net.corda.nodeapi.internal.config.User
|
import net.corda.nodeapi.internal.config.User
|
||||||
import net.corda.nodeapi.internal.config.parseAs
|
import net.corda.nodeapi.internal.config.parseAs
|
||||||
@ -241,9 +245,9 @@ class DriverDSLImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private enum class ClusterType(val validating: Boolean, val clusterName: CordaX500Name) {
|
private enum class ClusterType(val validating: Boolean, val clusterName: CordaX500Name) {
|
||||||
VALIDATING_RAFT(true, CordaX500Name("Raft", "Zurich", "CH")),
|
VALIDATING_RAFT(true, CordaX500Name(RaftValidatingNotaryService.id, "Raft", "Zurich", "CH")),
|
||||||
NON_VALIDATING_RAFT(false, CordaX500Name("Raft", "Zurich", "CH")),
|
NON_VALIDATING_RAFT(false, CordaX500Name(RaftNonValidatingNotaryService.id, "Raft", "Zurich", "CH")),
|
||||||
NON_VALIDATING_BFT(false, CordaX500Name("BFT", "Zurich", "CH"))
|
NON_VALIDATING_BFT(false, CordaX500Name(BFTNonValidatingNotaryService.id, "BFT", "Zurich", "CH"))
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun startCordformNodes(cordforms: List<CordformNode>): CordaFuture<*> {
|
internal fun startCordformNodes(cordforms: List<CordformNode>): CordaFuture<*> {
|
||||||
@ -266,15 +270,24 @@ class DriverDSLImpl(
|
|||||||
clusterNodes.put(ClusterType.NON_VALIDATING_BFT, name)
|
clusterNodes.put(ClusterType.NON_VALIDATING_BFT, name)
|
||||||
} else {
|
} else {
|
||||||
// We have all we need here to generate the identity for single node notaries
|
// We have all we need here to generate the identity for single node notaries
|
||||||
val identity = IdentityGenerator.generateNodeIdentity(baseDirectory(name), legalName = name)
|
val identity = ServiceIdentityGenerator.generateToDisk(
|
||||||
|
dirs = listOf(baseDirectory(name)),
|
||||||
|
serviceName = name,
|
||||||
|
serviceId = "identity"
|
||||||
|
)
|
||||||
notaryInfos += NotaryInfo(identity, notaryConfig.validating)
|
notaryInfos += NotaryInfo(identity, notaryConfig.validating)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clusterNodes.asMap().forEach { type, nodeNames ->
|
clusterNodes.asMap().forEach { type, nodeNames ->
|
||||||
val identity = IdentityGenerator.generateDistributedNotaryIdentity(
|
val identity = ServiceIdentityGenerator.generateToDisk(
|
||||||
dirs = nodeNames.map { baseDirectory(it) },
|
dirs = nodeNames.map { baseDirectory(it) },
|
||||||
notaryName = type.clusterName
|
serviceName = type.clusterName,
|
||||||
|
serviceId = NotaryService.constructId(
|
||||||
|
validating = type.validating,
|
||||||
|
raft = type in setOf(VALIDATING_RAFT, NON_VALIDATING_RAFT),
|
||||||
|
bft = type == ClusterType.NON_VALIDATING_BFT
|
||||||
|
)
|
||||||
)
|
)
|
||||||
notaryInfos += NotaryInfo(identity, type.validating)
|
notaryInfos += NotaryInfo(identity, type.validating)
|
||||||
}
|
}
|
||||||
@ -356,11 +369,20 @@ class DriverDSLImpl(
|
|||||||
private fun generateNotaryIdentities(): List<NotaryInfo> {
|
private fun generateNotaryIdentities(): List<NotaryInfo> {
|
||||||
return notarySpecs.map { spec ->
|
return notarySpecs.map { spec ->
|
||||||
val identity = if (spec.cluster == null) {
|
val identity = if (spec.cluster == null) {
|
||||||
IdentityGenerator.generateNodeIdentity(baseDirectory(spec.name), spec.name, compatibilityZone?.rootCert)
|
ServiceIdentityGenerator.generateToDisk(
|
||||||
|
dirs = listOf(baseDirectory(spec.name)),
|
||||||
|
serviceName = spec.name,
|
||||||
|
serviceId = "identity",
|
||||||
|
customRootCert = compatibilityZone?.rootCert
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
IdentityGenerator.generateDistributedNotaryIdentity(
|
ServiceIdentityGenerator.generateToDisk(
|
||||||
dirs = generateNodeNames(spec).map { baseDirectory(it) },
|
dirs = generateNodeNames(spec).map { baseDirectory(it) },
|
||||||
notaryName = spec.name,
|
serviceName = spec.name,
|
||||||
|
serviceId = NotaryService.constructId(
|
||||||
|
validating = spec.validating,
|
||||||
|
raft = spec.cluster is ClusterSpec.Raft
|
||||||
|
),
|
||||||
customRootCert = compatibilityZone?.rootCert
|
customRootCert = compatibilityZone?.rootCert
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
package net.corda.testing.node
|
|
||||||
|
|
||||||
import net.corda.core.serialization.internal.effectiveSerializationEnv
|
|
||||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
|
||||||
import org.junit.Test
|
|
||||||
|
|
||||||
class MockNetworkTests {
|
|
||||||
@Test
|
|
||||||
fun `does not leak serialization env if init fails`() {
|
|
||||||
val e = Exception("didn't work")
|
|
||||||
assertThatThrownBy {
|
|
||||||
object : MockNetwork(emptyList(), initialiseSerialization = true) {
|
|
||||||
override fun createNotaries() = throw e
|
|
||||||
}
|
|
||||||
}.isSameAs(e)
|
|
||||||
assertThatThrownBy { effectiveSerializationEnv }.isInstanceOf(IllegalStateException::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
@ -10,7 +10,6 @@ import net.corda.core.identity.CordaX500Name
|
|||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.identity.PartyAndCertificate
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
import net.corda.core.internal.cert
|
import net.corda.core.internal.cert
|
||||||
import net.corda.core.internal.unspecifiedCountry
|
|
||||||
import net.corda.core.internal.x500Name
|
import net.corda.core.internal.x500Name
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
@ -92,13 +91,13 @@ fun getTestPartyAndCertificate(party: Party): PartyAndCertificate {
|
|||||||
val trustRoot: X509CertificateHolder = DEV_TRUST_ROOT
|
val trustRoot: X509CertificateHolder = DEV_TRUST_ROOT
|
||||||
val intermediate: CertificateAndKeyPair = DEV_CA
|
val intermediate: CertificateAndKeyPair = DEV_CA
|
||||||
|
|
||||||
|
val nodeCaName = party.name.copy(commonName = X509Utilities.CORDA_CLIENT_CA_CN)
|
||||||
val nodeCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val nodeCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val nodeCaCert = X509Utilities.createCertificate(
|
val nodeCaCert = X509Utilities.createCertificate(
|
||||||
CertificateType.NODE_CA,
|
CertificateType.NODE_CA,
|
||||||
intermediate.certificate,
|
intermediate.certificate,
|
||||||
intermediate.keyPair,
|
intermediate.keyPair,
|
||||||
party.name,
|
nodeCaName,
|
||||||
nodeCaKeyPair.public,
|
nodeCaKeyPair.public,
|
||||||
nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, party.name.x500Name))), arrayOf()))
|
nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, party.name.x500Name))), arrayOf()))
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ import net.corda.demobench.pty.R3Pty
|
|||||||
import net.corda.nodeapi.internal.network.NetworkParameters
|
import net.corda.nodeapi.internal.network.NetworkParameters
|
||||||
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||||
import net.corda.nodeapi.internal.network.NotaryInfo
|
import net.corda.nodeapi.internal.network.NotaryInfo
|
||||||
import net.corda.nodeapi.internal.IdentityGenerator
|
import net.corda.nodeapi.internal.ServiceIdentityGenerator
|
||||||
import tornadofx.*
|
import tornadofx.*
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.lang.management.ManagementFactory
|
import java.lang.management.ManagementFactory
|
||||||
@ -153,7 +153,10 @@ class NodeController(check: atRuntime = ::checkExists) : Controller() {
|
|||||||
|
|
||||||
// Generate notary identity and save it into node's directory. This identity will be used in network parameters.
|
// Generate notary identity and save it into node's directory. This identity will be used in network parameters.
|
||||||
private fun getNotaryIdentity(config: NodeConfigWrapper): Party {
|
private fun getNotaryIdentity(config: NodeConfigWrapper): Party {
|
||||||
return IdentityGenerator.generateNodeIdentity(config.nodeDir, config.nodeConfig.myLegalName)
|
return ServiceIdentityGenerator.generateToDisk(
|
||||||
|
dirs = listOf(config.nodeDir),
|
||||||
|
serviceName = config.nodeConfig.myLegalName,
|
||||||
|
serviceId = "identity")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun reset() {
|
fun reset() {
|
||||||
|
Loading…
Reference in New Issue
Block a user