From ef74a0a618a5f623265754dafdea04540553fa78 Mon Sep 17 00:00:00 2001 From: Andrzej Cichocki Date: Wed, 21 Jun 2017 10:25:11 +0100 Subject: [PATCH] Fix the command line notary demos (#875) * The fancy classloader setup in Cordform.loadCordformDefinition is defeated by certificates in 2 ways ** Class cast when creating a cert (via TestConstants) in a CordformDefinition ** Some issue when persisting a cert in ServiceIdentityGenerator * This PR is a quickfix to get the demos working again * Proper fix (not this PR) is to work out why it's not enough for eddsa to be in the project runtime classpath - if it can be removed from the gradle environment that ought to properly fix the above problems --- .../net/corda/core/utilities/TestConstants.kt | 20 ++++++++------- .../node/services/BFTNotaryServiceTests.kt | 4 +-- .../services/messaging/P2PMessagingTest.kt | 1 - .../net/corda/node/internal/AbstractNode.kt | 19 ++++++++------ .../utilities/ServiceIdentityGenerator.kt | 25 ++++++------------- .../net/corda/notarydemo/BFTNotaryCordform.kt | 3 +-- .../corda/notarydemo/RaftNotaryCordform.kt | 3 +-- .../kotlin/net/corda/testing/driver/Driver.kt | 2 +- .../net/corda/testing/node/NodeBasedTest.kt | 2 -- 9 files changed, 34 insertions(+), 45 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/utilities/TestConstants.kt b/core/src/main/kotlin/net/corda/core/utilities/TestConstants.kt index 42ff6bf326..703252614c 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/TestConstants.kt +++ b/core/src/main/kotlin/net/corda/core/utilities/TestConstants.kt @@ -23,8 +23,8 @@ val DUMMY_KEY_2: KeyPair by lazy { generateKeyPair() } val DUMMY_NOTARY_KEY: KeyPair by lazy { entropyToKeyPair(BigInteger.valueOf(20)) } /** Dummy notary identity for tests and simulations */ -val DUMMY_NOTARY_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(X500Name("CN=Notary Service,O=R3,OU=corda,L=Zurich,C=CH"), DUMMY_NOTARY_KEY.public) -val DUMMY_NOTARY: Party get() = DUMMY_NOTARY_IDENTITY.party +val DUMMY_NOTARY_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(DUMMY_NOTARY) +val DUMMY_NOTARY: Party get() = Party(X500Name("CN=Notary Service,O=R3,OU=corda,L=Zurich,C=CH"), DUMMY_NOTARY_KEY.public) val DUMMY_MAP_KEY: KeyPair by lazy { entropyToKeyPair(BigInteger.valueOf(30)) } /** Dummy network map service identity for tests and simulations */ @@ -44,13 +44,13 @@ val DUMMY_BANK_C: Party get() = Party(X500Name("CN=Bank C,O=Bank C,L=Tokyo,C=JP" val ALICE_KEY: KeyPair by lazy { entropyToKeyPair(BigInteger.valueOf(70)) } /** Dummy individual identity for tests and simulations */ -val ALICE_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(X500Name("CN=Alice Corp,O=Alice Corp,L=Madrid,C=ES"), ALICE_KEY.public) -val ALICE: Party get() = ALICE_IDENTITY.party +val ALICE_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(ALICE) +val ALICE: Party get() = Party(X500Name("CN=Alice Corp,O=Alice Corp,L=Madrid,C=ES"), ALICE_KEY.public) val BOB_KEY: KeyPair by lazy { entropyToKeyPair(BigInteger.valueOf(80)) } /** Dummy individual identity for tests and simulations */ -val BOB_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(X500Name("CN=Bob Plc,O=Bob Plc,L=Rome,C=IT"), BOB_KEY.public) -val BOB: Party get() = BOB_IDENTITY.party +val BOB_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(BOB) +val BOB: Party get() = Party(X500Name("CN=Bob Plc,O=Bob Plc,L=Rome,C=IT"), BOB_KEY.public) val CHARLIE_KEY: KeyPair by lazy { entropyToKeyPair(BigInteger.valueOf(90)) } /** Dummy individual identity for tests and simulations */ @@ -70,9 +70,11 @@ val DUMMY_CA: CertificateAndKeyPair by lazy { /** * Build a test party with a nonsense certificate authority for testing purposes. */ -fun getTestPartyAndCertificate(name: X500Name, publicKey: PublicKey, trustRoot: CertificateAndKeyPair = DUMMY_CA): PartyAndCertificate { +fun getTestPartyAndCertificate(name: X500Name, publicKey: PublicKey, trustRoot: CertificateAndKeyPair = DUMMY_CA) = getTestPartyAndCertificate(Party(name, publicKey), trustRoot) + +fun getTestPartyAndCertificate(party: Party, trustRoot: CertificateAndKeyPair = DUMMY_CA): PartyAndCertificate { val certFactory = CertificateFactory.getInstance("X509") - val certHolder = X509Utilities.createCertificate(CertificateType.IDENTITY, trustRoot.certificate, trustRoot.keyPair, name, publicKey) + val certHolder = X509Utilities.createCertificate(CertificateType.IDENTITY, trustRoot.certificate, trustRoot.keyPair, party.name, party.owningKey) val certPath = certFactory.generateCertPath(listOf(certHolder.cert, trustRoot.certificate.cert)) - return PartyAndCertificate(name, publicKey, certHolder, certPath) + return PartyAndCertificate(party, certHolder, certPath) } diff --git a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt index 07c0bb0a1f..0aefe4de76 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt @@ -10,7 +10,6 @@ import net.corda.core.crypto.appendToCommonName import net.corda.core.identity.Party import net.corda.core.node.services.ServiceInfo import net.corda.core.utilities.ALICE -import net.corda.core.utilities.DUMMY_CA import net.corda.core.utilities.DUMMY_NOTARY import net.corda.flows.NotaryError import net.corda.flows.NotaryException @@ -39,9 +38,8 @@ class BFTNotaryServiceTests : NodeBasedTest() { val replicaNames = replicaIds.map { DUMMY_NOTARY.name.appendToCommonName(" $it") } val party = ServiceIdentityGenerator.generateToDisk( replicaNames.map { baseDirectory(it) }, - DUMMY_CA, serviceType.id, - clusterName).party + clusterName) val advertisedServices = setOf(ServiceInfo(serviceType, clusterName)) val config = mapOf("notaryClusterAddresses" to replicaIds.map { "localhost:${11000 + it * 10}" }) return Futures.allAsList(replicaIds.map { diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt index a59e81dafe..9ba5bac762 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt @@ -64,7 +64,6 @@ class P2PMessagingTest : NodeBasedTest() { fun `communicating with a distributed service which the network map node is part of`() { ServiceIdentityGenerator.generateToDisk( listOf(DUMMY_MAP.name, SERVICE_2_NAME).map { baseDirectory(it) }, - DUMMY_CA, RaftValidatingNotaryService.type.id, DISTRIBUTED_SERVICE_NAME) diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index dac13f9159..f534ae4a38 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -23,7 +23,9 @@ import net.corda.core.serialization.SerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.deserialize import net.corda.core.transactions.SignedTransaction +import net.corda.core.utilities.DUMMY_CA import net.corda.core.utilities.debug +import net.corda.core.utilities.getTestPartyAndCertificate import net.corda.flows.* import net.corda.node.services.* import net.corda.node.services.api.* @@ -57,7 +59,6 @@ import net.corda.node.utilities.configureDatabase import net.corda.node.utilities.transaction import org.apache.activemq.artemis.utils.ReusableLatch import org.bouncycastle.asn1.x500.X500Name -import org.bouncycastle.cert.X509CertificateHolder import org.jetbrains.exposed.sql.Database import org.slf4j.Logger import rx.Observable @@ -215,8 +216,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, // Do all of this in a database transaction so anything that might need a connection has one. initialiseDatabasePersistence { - val keyStoreWrapper = KeyStoreWrapper(configuration.trustStoreFile, configuration.trustStorePassword) - val tokenizableServices = makeServices(keyStoreWrapper) + val tokenizableServices = makeServices() smm = StateMachineManager(services, checkpointStorage, @@ -439,8 +439,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, * Builds node internal, advertised, and plugin services. * Returns a list of tokenizable services to be added to the serialisation context. */ - private fun makeServices(keyStoreWrapper: KeyStoreWrapper): MutableList { - val keyStore = keyStoreWrapper.keyStore + private fun makeServices(): MutableList { val storageServices = initialiseStorageService(configuration.baseDirectory) storage = storageServices.first checkpointStorage = storageServices.second @@ -778,7 +777,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, // Check that the identity in the config file matches the identity file we have stored to disk. // This is just a sanity check. It shouldn't fail unless the admin has fiddled with the files and messed // things up for us. - val myIdentity = pubIdentityFile.readAll().deserialize() + val myIdentity = pubIdentityFile.readAll().deserialize() if (myIdentity.name != serviceName) throw ConfigurationException("The legal name in the config file doesn't match the stored identity file:" + "$serviceName vs ${myIdentity.name}") @@ -787,7 +786,13 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, if (myIdentity.owningKey !is CompositeKey) { // TODO: Support case where owningKey is a composite key. keyStore.save(serviceName, privateKeyAlias, keyPair) } - Pair(myIdentity, keyPair) + val partyAndCertificate = getTestPartyAndCertificate(myIdentity) + // Sanity check the certificate and path + val validatorParameters = PKIXParameters(setOf(TrustAnchor(DUMMY_CA.certificate.cert, null))) + val validator = CertPathValidator.getInstance("PKIX") + validatorParameters.isRevocationEnabled = false + validator.validate(partyAndCertificate.certPath, validatorParameters) as PKIXCertPathValidatorResult + Pair(partyAndCertificate, keyPair) } else { val clientCertPath = keyStore.keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA) val clientCA = keyStore.certificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)!! diff --git a/node/src/main/kotlin/net/corda/node/utilities/ServiceIdentityGenerator.kt b/node/src/main/kotlin/net/corda/node/utilities/ServiceIdentityGenerator.kt index e9bf4f8266..6629f7ca26 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/ServiceIdentityGenerator.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/ServiceIdentityGenerator.kt @@ -1,7 +1,9 @@ package net.corda.node.utilities -import net.corda.core.crypto.* -import net.corda.core.identity.PartyAndCertificate +import net.corda.core.crypto.CompositeKey +import net.corda.core.crypto.X509Utilities +import net.corda.core.crypto.generateKeyPair +import net.corda.core.identity.Party import net.corda.core.serialization.serialize import net.corda.core.serialization.storageKryo import net.corda.core.utilities.loggerFor @@ -9,7 +11,6 @@ import net.corda.core.utilities.trace import org.bouncycastle.asn1.x500.X500Name import java.nio.file.Files import java.nio.file.Path -import java.security.cert.* object ServiceIdentityGenerator { private val log = loggerFor() @@ -20,35 +21,23 @@ object ServiceIdentityGenerator { * 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 serviceCa Certificate authority to use when signing identity certificates. * @param serviceId The service id of the distributed service. * @param serviceName The legal name of the distributed service. * @param threshold The threshold for the generated group [CompositeKey]. */ // TODO: This needs to write out to the key store, not just files on disk fun generateToDisk(dirs: List, - serviceCa: CertificateAndKeyPair, serviceId: String, serviceName: X500Name, - threshold: Int = 1): PartyAndCertificate { + threshold: Int = 1): Party { log.trace { "Generating a group identity \"serviceName\" for nodes: ${dirs.joinToString()}" } val keyPairs = (1..dirs.size).map { generateKeyPair() } val notaryKey = CompositeKey.Builder().addKeys(keyPairs.map { it.public }).build(threshold) - val certFactory = CertificateFactory.getInstance("X509") - val notaryCert = X509Utilities.createCertificate(CertificateType.IDENTITY, serviceCa.certificate, - serviceCa.keyPair, serviceName, notaryKey) - val notaryCertPath = certFactory.generateCertPath(listOf(notaryCert.cert, serviceCa.certificate.cert)) - val notaryParty = PartyAndCertificate(serviceName, notaryKey, notaryCert, notaryCertPath) + // Avoid adding complexity! This class is a hack that needs to stay runnable in the gradle environment. + val notaryParty = Party(serviceName, notaryKey) val notaryPartyBytes = notaryParty.serialize() val privateKeyFile = "$serviceId-private-key" val publicKeyFile = "$serviceId-public" - - // Sanity check the certificate and path - val validatorParameters = PKIXParameters(setOf(TrustAnchor(serviceCa.certificate.cert, null))) - val validator = CertPathValidator.getInstance("PKIX") - validatorParameters.isRevocationEnabled = false - validator.validate(notaryCertPath, validatorParameters) as PKIXCertPathValidatorResult - keyPairs.zip(dirs) { keyPair, dir -> Files.createDirectories(dir) notaryPartyBytes.writeToFile(dir.resolve(publicKeyFile)) diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt index 5fe9dad98a..4a0a5632f9 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt @@ -5,7 +5,6 @@ import net.corda.core.div import net.corda.core.node.services.ServiceInfo import net.corda.core.utilities.ALICE import net.corda.core.utilities.BOB -import net.corda.core.utilities.DUMMY_CA import net.corda.demorun.util.* import net.corda.demorun.runNodes import net.corda.node.services.transactions.BFTNonValidatingNotaryService @@ -66,6 +65,6 @@ object BFTNotaryCordform : CordformDefinition("build" / "notary-demo-nodes", not } override fun setup(context: CordformContext) { - ServiceIdentityGenerator.generateToDisk(notaryNames.map { context.baseDirectory(it) }, DUMMY_CA, advertisedService.type.id, clusterName, minCorrectReplicas(clusterSize)) + ServiceIdentityGenerator.generateToDisk(notaryNames.map { context.baseDirectory(it) }, advertisedService.type.id, clusterName, minCorrectReplicas(clusterSize)) } } diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt index fd818bb9a3..7c2dd027bf 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt @@ -6,7 +6,6 @@ import net.corda.core.div import net.corda.core.node.services.ServiceInfo import net.corda.core.utilities.ALICE import net.corda.core.utilities.BOB -import net.corda.core.utilities.DUMMY_CA import net.corda.core.utilities.DUMMY_NOTARY import net.corda.demorun.util.* import net.corda.node.services.transactions.RaftValidatingNotaryService @@ -66,6 +65,6 @@ object RaftNotaryCordform : CordformDefinition("build" / "notary-demo-nodes", no } override fun setup(context: CordformContext) { - ServiceIdentityGenerator.generateToDisk(notaryNames.map { context.baseDirectory(it) }, DUMMY_CA, advertisedService.type.id, clusterName) + ServiceIdentityGenerator.generateToDisk(notaryNames.map { context.baseDirectory(it) }, advertisedService.type.id, clusterName) } } diff --git a/test-utils/src/main/kotlin/net/corda/testing/driver/Driver.kt b/test-utils/src/main/kotlin/net/corda/testing/driver/Driver.kt index 53798b4c37..8dca081231 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/driver/Driver.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/driver/Driver.kt @@ -613,7 +613,7 @@ class DriverDSL( ): ListenableFuture>> { val nodeNames = (0 until clusterSize).map { DUMMY_NOTARY.name.appendToCommonName(" $it") } val paths = nodeNames.map { baseDirectory(it) } - ServiceIdentityGenerator.generateToDisk(paths, DUMMY_CA, type.id, notaryName) + ServiceIdentityGenerator.generateToDisk(paths, type.id, notaryName) val advertisedServices = setOf(ServiceInfo(type, notaryName)) val notaryClusterAddress = portAllocation.nextHostAndPort() diff --git a/test-utils/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt b/test-utils/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt index 9d6c5fb3e4..cadcc93871 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt @@ -9,7 +9,6 @@ import net.corda.core.crypto.appendToCommonName import net.corda.core.crypto.commonName import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceType -import net.corda.core.utilities.DUMMY_CA import net.corda.core.utilities.DUMMY_MAP import net.corda.core.utilities.WHITESPACE import net.corda.node.internal.Node @@ -113,7 +112,6 @@ abstract class NodeBasedTest { serviceType: ServiceType = RaftValidatingNotaryService.type): ListenableFuture> { ServiceIdentityGenerator.generateToDisk( (0 until clusterSize).map { baseDirectory(notaryName.appendToCommonName("-$it")) }, - DUMMY_CA, serviceType.id, notaryName)