Revert "Shams os merge 191217 (#223)"

This reverts commit 2461421
This commit is contained in:
Shams Asari 2017-12-20 13:01:19 +00:00
parent d31f27571b
commit d48bc41a7c
41 changed files with 482 additions and 410 deletions

View File

@ -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()

View File

@ -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 {

View File

@ -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"

View File

@ -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()
} }
} }

View File

@ -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"

View File

@ -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 }
} }

View File

@ -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

View File

@ -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"]

View File

@ -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

View File

@ -1,60 +1,65 @@
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 orgName = nodeInfo.legalIdentities.first().name.organisation
val nodeCaCert = nodeInfo.legalIdentitiesAndCerts[0].certPath.certificates.find { CertRole.extract(it) == CertRole.NODE_CA } // 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)
builder.and(certPublicKeyHashEq, certStatusValid) builder.and(certPublicKeyHashEq, certStatusValid)
}
} }
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
* Possibly it should be moved at the network signing process at the network signing process
* as for a while the network map will have invalid entries (i.e. hashes for node info which have been
* removed). Either way, there will be a period of time when the network map data will be invalid
* but it has been confirmed that this fact has been acknowledged at the design time and we are fine with it.
*/
deleteRequest(NodeInfoEntity::class.java) { builder, path ->
builder.equal(path.get<CertificateSigningRequestEntity>(NodeInfoEntity::certificateSigningRequest.name), request.certificateSigningRequest)
}
val hash = signedNodeInfo.raw.hash
val hashedNodeInfo = NodeInfoEntity(
nodeInfoHash = hash.toString(),
certificateSigningRequest = request.certificateSigningRequest,
signedNodeInfoBytes = signedNodeInfo.serialize().bytes)
session.save(hashedNodeInfo)
hash
} }
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
* Possibly it should be moved at the network signing process at the network signing process
* as for a while the network map will have invalid entries (i.e. hashes for node info which have been
* removed). Either way, there will be a period of time when the network map data will be invalid
* but it has been confirmed that this fact has been acknowledged at the design time and we are fine with it.
*/
deleteRequest(NodeInfoEntity::class.java) { builder, path ->
builder.equal(path.get<CertificateSigningRequestEntity>(NodeInfoEntity::certificateSigningRequest.name), request.certificateSigningRequest)
}
val hash = signedNodeInfo.raw.hash
val hashedNodeInfo = NodeInfoEntity(
nodeInfoHash = hash.toString(),
certificateSigningRequest = request.certificateSigningRequest,
signedNodeInfoBytes = signedNodeInfo.serialize().bytes)
session.save(hashedNodeInfo)
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()
}
} }
} }

View File

@ -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 {

View File

@ -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)

View File

@ -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
}
} }

View File

@ -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())
} }
} }

View File

@ -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 {

View File

@ -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)
} }
} }

View File

@ -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

View File

@ -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))))

View File

@ -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()

View File

@ -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)

View File

@ -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

View File

@ -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())
} }

View File

@ -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()

View File

@ -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 {

View File

@ -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 {

View File

@ -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) : NotaryService() {
cluster: BFTSMaRt.Cluster
) : NotaryService() {
companion object { companion object {
val id = constructId(validating = false, bft = true)
private val log = contextLogger() private val log = contextLogger()
} }

View File

@ -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)
} }

View File

@ -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) : TrustedAuthorityNotaryService() {
override val uniquenessProvider: RaftUniquenessProvider companion object {
) : TrustedAuthorityNotaryService() { val id = constructId(validating = false, raft = true)
override val timeWindowChecker: TimeWindowChecker = TimeWindowChecker(services.clock) }
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)
} }

View File

@ -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) : TrustedAuthorityNotaryService() {
override val uniquenessProvider: RaftUniquenessProvider companion object {
) : TrustedAuthorityNotaryService() { val id = constructId(validating = true, raft = true)
override val timeWindowChecker: TimeWindowChecker = TimeWindowChecker(services.clock) }
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)
} }

View File

@ -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)

View File

@ -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.

View File

@ -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()

View File

@ -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
} }
} }

View File

@ -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))
} }
} }

View File

@ -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))
} }
} }

View File

@ -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.

View File

@ -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,14 +125,14 @@ 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,
servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy = defaultParameters.servicePeerAllocationStrategy, servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy = defaultParameters.servicePeerAllocationStrategy,
private val defaultFactory: (MockNodeArgs) -> MockNode = defaultParameters.defaultFactory, private val defaultFactory: (MockNodeArgs) -> MockNode = defaultParameters.defaultFactory,
initialiseSerialization: Boolean = defaultParameters.initialiseSerialization, initialiseSerialization: Boolean = defaultParameters.initialiseSerialization,
private val notarySpecs: List<NotarySpec> = defaultParameters.notarySpecs) { private val notarySpecs: List<NotarySpec> = defaultParameters.notarySpecs) {
/** Helper constructor for creating a [MockNetwork] with custom parameters from Java. */ /** Helper constructor for creating a [MockNetwork] with custom parameters from Java. */
@JvmOverloads @JvmOverloads
constructor(cordappPackages: List<String>, parameters: MockNetworkParameters = MockNetworkParameters()) : this(cordappPackages, defaultParameters = parameters) constructor(cordappPackages: List<String>, parameters: MockNetworkParameters = MockNetworkParameters()) : this(cordappPackages, defaultParameters = parameters)
@ -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,29 +172,32 @@ 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>
return when (notaryNodes.size) { get() {
0 -> throw IllegalStateException("There are no notaries defined on the network") return when (notaryNodes.size) {
1 -> notaryNodes[0] 0 -> throw IllegalStateException("There are no notaries defined on the network")
else -> throw IllegalStateException("There is more than one notary defined on the network") 1 -> notaryNodes[0]
else -> throw IllegalStateException("There is more than one notary defined on the network")
}
} }
}
/** /**
* 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
return defaultNotaryNode.info.legalIdentities.singleOrNull() ?: throw IllegalStateException("Default notary has multiple identities") get() {
} return defaultNotaryNode.info.legalIdentities.singleOrNull() ?: throw IllegalStateException("Default notary has multiple identities")
}
/** /**
* 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
return defaultNotaryNode.info.legalIdentitiesAndCerts.singleOrNull() ?: throw IllegalStateException("Default notary has multiple identities") get() {
} return defaultNotaryNode.info.legalIdentitiesAndCerts.singleOrNull() ?: throw IllegalStateException("Default notary has multiple identities")
}
/** /**
* Because this executor is shared, we need to be careful about nodes shutting it down. * Because this executor is shared, we need to be careful about nodes shutting it down.
@ -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)) notaryNodes = createNotaries()
@Suppress("LeakingThis")
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() } serializationEnv.unset()
} finally {
serializationEnv.unset() // Must execute even if other parts of this method fail.
}
messagingNetwork.stop() messagingNetwork.stop()
} }

View File

@ -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
) )
} }

View File

@ -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)
}
}

View File

@ -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()))

View File

@ -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() {