From 20683c3239bbec97c41af7bc215ff96183cf7e75 Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Tue, 2 Jan 2018 15:51:13 +0000 Subject: [PATCH 01/19] Added checks on the received node CA cert from the doorman service. (#2301) --- .../registration/NetworkRegistrationHelper.kt | 123 +++++++++++------- .../NetworkRegistrationHelperTest.kt | 83 ++++++++---- 2 files changed, 132 insertions(+), 74 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt index af9735b89a..09a46121e8 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt @@ -22,9 +22,9 @@ import java.security.cert.X509Certificate * needed. */ class NetworkRegistrationHelper(private val config: NodeConfiguration, private val certService: NetworkRegistrationService) { - companion object { + private companion object { val pollInterval = 10.seconds - val SELF_SIGNED_PRIVATE_KEY = "Self Signed Private Key" + const val SELF_SIGNED_PRIVATE_KEY = "Self Signed Private Key" } private val requestIdStore = config.certificatesDirectory / "certificate-request-id.txt" @@ -62,54 +62,81 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v */ fun buildKeystore() { config.certificatesDirectory.createDirectories() - val caKeyStore = loadOrCreateKeyStore(config.nodeKeystore, keystorePassword) - if (!caKeyStore.containsAlias(CORDA_CLIENT_CA)) { - // Create or load self signed keypair from the key store. - // We use the self sign certificate to store the key temporarily in the keystore while waiting for the request approval. - if (!caKeyStore.containsAlias(SELF_SIGNED_PRIVATE_KEY)) { - val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val selfSignCert = X509Utilities.createSelfSignedCACertificate(config.myLegalName, keyPair) - // Save to the key store. - caKeyStore.addOrReplaceKey(SELF_SIGNED_PRIVATE_KEY, keyPair.private, privateKeyPassword.toCharArray(), - arrayOf(selfSignCert)) - caKeyStore.save(config.nodeKeystore, keystorePassword) - } - val keyPair = caKeyStore.getKeyPair(SELF_SIGNED_PRIVATE_KEY, privateKeyPassword) - val requestId = submitOrResumeCertificateSigningRequest(keyPair) - - val certificates = try { - pollServerForCertificates(requestId) - } catch (certificateRequestException: CertificateRequestException) { - System.err.println(certificateRequestException.message) - System.err.println("Please make sure the details in configuration file are correct and try again.") - System.err.println("Corda node will now terminate.") - requestIdStore.deleteIfExists() - throw certificateRequestException - } - - println("Certificate signing request approved, storing private key with the certificate chain.") - // Save private key and certificate chain to the key store. - caKeyStore.addOrReplaceKey(CORDA_CLIENT_CA, keyPair.private, privateKeyPassword.toCharArray(), certificates) - caKeyStore.deleteEntry(SELF_SIGNED_PRIVATE_KEY) - caKeyStore.save(config.nodeKeystore, keystorePassword) - println("Node private key and certificate stored in ${config.nodeKeystore}.") - - println("Checking root of the certificate path is what we expect.") - X509Utilities.validateCertificateChain(rootCert, *certificates) - - println("Generating SSL certificate for node messaging service.") - val sslKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val caCert = caKeyStore.getX509Certificate(CORDA_CLIENT_CA).toX509CertHolder() - val sslCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, keyPair, CordaX500Name.build(caCert.cert.subjectX500Principal), sslKey.public) - val sslKeyStore = loadOrCreateKeyStore(config.sslKeystore, keystorePassword) - sslKeyStore.addOrReplaceKey(CORDA_CLIENT_TLS, sslKey.private, privateKeyPassword.toCharArray(), arrayOf(sslCert.cert, *certificates)) - sslKeyStore.save(config.sslKeystore, config.keyStorePassword) - println("SSL private key and certificate stored in ${config.sslKeystore}.") - // All done, clean up temp files. - requestIdStore.deleteIfExists() - } else { + val nodeKeyStore = loadOrCreateKeyStore(config.nodeKeystore, keystorePassword) + if (nodeKeyStore.containsAlias(CORDA_CLIENT_CA)) { println("Certificate already exists, Corda node will now terminate...") + return } + + // Create or load self signed keypair from the key store. + // We use the self sign certificate to store the key temporarily in the keystore while waiting for the request approval. + if (!nodeKeyStore.containsAlias(SELF_SIGNED_PRIVATE_KEY)) { + val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) + val selfSignCert = X509Utilities.createSelfSignedCACertificate(config.myLegalName, keyPair) + // Save to the key store. + nodeKeyStore.addOrReplaceKey(SELF_SIGNED_PRIVATE_KEY, keyPair.private, privateKeyPassword.toCharArray(), + arrayOf(selfSignCert)) + nodeKeyStore.save(config.nodeKeystore, keystorePassword) + } + + val keyPair = nodeKeyStore.getKeyPair(SELF_SIGNED_PRIVATE_KEY, privateKeyPassword) + val requestId = submitOrResumeCertificateSigningRequest(keyPair) + + val certificates = try { + pollServerForCertificates(requestId) + } catch (certificateRequestException: CertificateRequestException) { + System.err.println(certificateRequestException.message) + System.err.println("Please make sure the details in configuration file are correct and try again.") + System.err.println("Corda node will now terminate.") + requestIdStore.deleteIfExists() + throw certificateRequestException + } + + val nodeCaCert = certificates[0] as X509Certificate + + val nodeCaSubject = try { + CordaX500Name.build(nodeCaCert.subjectX500Principal) + } catch (e: IllegalArgumentException) { + throw CertificateRequestException("Received node CA cert has invalid subject name: ${e.message}") + } + if (nodeCaSubject != config.myLegalName) { + throw CertificateRequestException("Subject of received node CA cert doesn't match with node legal name: $nodeCaSubject") + } + + val nodeCaCertRole = try { + CertRole.extract(nodeCaCert) + } catch (e: IllegalArgumentException) { + throw CertificateRequestException("Unable to extract cert role from received node CA cert: ${e.message}") + } + if (nodeCaCertRole != CertRole.NODE_CA) { + throw CertificateRequestException("Received node CA cert has invalid role: $nodeCaCertRole") + } + + println("Checking root of the certificate path is what we expect.") + X509Utilities.validateCertificateChain (rootCert , * certificates) + + println("Certificate signing request approved, storing private key with the certificate chain.") + // Save private key and certificate chain to the key store. + nodeKeyStore.addOrReplaceKey(CORDA_CLIENT_CA, keyPair.private, privateKeyPassword.toCharArray(), certificates) + nodeKeyStore.deleteEntry(SELF_SIGNED_PRIVATE_KEY) + nodeKeyStore.save(config.nodeKeystore, keystorePassword) + println("Node private key and certificate stored in ${config.nodeKeystore}.") + + println("Generating SSL certificate for node messaging service.") + val sslKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) + val sslCert = X509Utilities.createCertificate( + CertificateType.TLS, + nodeCaCert.toX509CertHolder(), + keyPair, + config.myLegalName, + sslKeyPair.public) + val sslKeyStore = loadOrCreateKeyStore(config.sslKeystore, keystorePassword) + sslKeyStore.addOrReplaceKey(CORDA_CLIENT_TLS, sslKeyPair.private, privateKeyPassword.toCharArray(), arrayOf(sslCert.cert, *certificates)) + sslKeyStore.save(config.sslKeystore, config.keyStorePassword) + println("SSL private key and certificate stored in ${config.sslKeystore}.") + + // All done, clean up temp files. + requestIdStore.deleteIfExists() } /** diff --git a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt index 13113fccf9..5b8dab7282 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt @@ -11,18 +11,20 @@ import net.corda.core.crypto.SecureHash import net.corda.core.identity.CordaX500Name import net.corda.core.internal.cert import net.corda.core.internal.createDirectories +import net.corda.core.internal.x500Name import net.corda.node.services.config.NodeConfiguration import net.corda.nodeapi.internal.crypto.* import net.corda.testing.ALICE_NAME -import net.corda.testing.internal.createDevNodeCaCertPath +import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.testing.internal.rigorousMock -import org.assertj.core.api.Assertions.assertThat -import org.assertj.core.api.Assertions.assertThatThrownBy +import org.assertj.core.api.Assertions.* +import org.bouncycastle.asn1.x509.GeneralName +import org.bouncycastle.asn1.x509.GeneralSubtree +import org.bouncycastle.asn1.x509.NameConstraints import org.junit.After import org.junit.Before import org.junit.Test import java.security.cert.CertPathValidatorException -import java.security.cert.Certificate import java.security.cert.X509Certificate import kotlin.test.assertFalse import kotlin.test.assertTrue @@ -32,18 +34,10 @@ class NetworkRegistrationHelperTest { private val requestId = SecureHash.randomSHA256().toString() private val nodeLegalName = ALICE_NAME - private lateinit var rootCaCert: X509Certificate - private lateinit var intermediateCaCert: X509Certificate - private lateinit var nodeCaCert: X509Certificate private lateinit var config: NodeConfiguration @Before fun init() { - val (rootCa, intermediateCa, nodeCa) = createDevNodeCaCertPath(nodeLegalName) - this.rootCaCert = rootCa.certificate.cert - this.intermediateCaCert = intermediateCa.certificate.cert - this.nodeCaCert = nodeCa.certificate.cert - val baseDirectory = fs.getPath("/baseDir").createDirectories() abstract class AbstractNodeConfiguration : NodeConfiguration config = rigorousMock().also { @@ -66,9 +60,10 @@ class NetworkRegistrationHelperTest { assertThat(config.sslKeystore).doesNotExist() assertThat(config.trustStoreFile).doesNotExist() - saveTrustStoreWithRootCa(rootCaCert) + val nodeCaCertPath = createNodeCaCertPath() - createRegistrationHelper().buildKeystore() + saveTrustStoreWithRootCa(nodeCaCertPath.last()) + createRegistrationHelper(nodeCaCertPath).buildKeystore() val nodeKeystore = loadKeyStore(config.nodeKeystore, config.keyStorePassword) val sslKeystore = loadKeyStore(config.sslKeystore, config.keyStorePassword) @@ -79,8 +74,7 @@ class NetworkRegistrationHelperTest { assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA)) assertFalse(containsAlias(X509Utilities.CORDA_ROOT_CA)) assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_TLS)) - val nodeCaCertChain = getCertificateChain(X509Utilities.CORDA_CLIENT_CA) - assertThat(nodeCaCertChain).containsExactly(nodeCaCert, intermediateCaCert, rootCaCert) + assertThat(getCertificateChain(X509Utilities.CORDA_CLIENT_CA)).containsExactly(*nodeCaCertPath) } sslKeystore.run { @@ -92,40 +86,77 @@ class NetworkRegistrationHelperTest { assertThat(nodeTlsCertChain).hasSize(4) // The TLS cert has the same subject as the node CA cert assertThat(CordaX500Name.build((nodeTlsCertChain[0] as X509Certificate).subjectX500Principal)).isEqualTo(nodeLegalName) - assertThat(nodeTlsCertChain.drop(1)).containsExactly(nodeCaCert, intermediateCaCert, rootCaCert) + assertThat(nodeTlsCertChain.drop(1)).containsExactly(*nodeCaCertPath) } trustStore.run { assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_CA)) assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA)) assertTrue(containsAlias(X509Utilities.CORDA_ROOT_CA)) - val trustStoreRootCaCert = getCertificate(X509Utilities.CORDA_ROOT_CA) - assertThat(trustStoreRootCaCert).isEqualTo(rootCaCert) + assertThat(getCertificate(X509Utilities.CORDA_ROOT_CA)).isEqualTo(nodeCaCertPath.last()) } } @Test fun `missing truststore`() { + val nodeCaCertPath = createNodeCaCertPath() assertThatThrownBy { - createRegistrationHelper() + createRegistrationHelper(nodeCaCertPath) }.hasMessageContaining("This file must contain the root CA cert of your compatibility zone. Please contact your CZ operator.") } + @Test + fun `node CA with incorrect cert role`() { + val nodeCaCertPath = createNodeCaCertPath(type = CertificateType.TLS) + saveTrustStoreWithRootCa(nodeCaCertPath.last()) + val registrationHelper = createRegistrationHelper(nodeCaCertPath) + assertThatExceptionOfType(CertificateRequestException::class.java) + .isThrownBy { registrationHelper.buildKeystore() } + .withMessageContaining(CertificateType.TLS.toString()) + } + + @Test + fun `node CA with incorrect subject`() { + val invalidName = CordaX500Name("Foo", "MU", "GB") + val nodeCaCertPath = createNodeCaCertPath(legalName = invalidName) + saveTrustStoreWithRootCa(nodeCaCertPath.last()) + val registrationHelper = createRegistrationHelper(nodeCaCertPath) + assertThatExceptionOfType(CertificateRequestException::class.java) + .isThrownBy { registrationHelper.buildKeystore() } + .withMessageContaining(invalidName.toString()) + } + @Test fun `wrong root cert in truststore`() { - val rootKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val rootCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name("Foo", "MU", "GB"), rootKeyPair) - saveTrustStoreWithRootCa(rootCert.cert) - val registrationHelper = createRegistrationHelper() + val wrongRootCert = X509Utilities.createSelfSignedCACertificate( + CordaX500Name("Foo", "MU", "GB"), + Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) + saveTrustStoreWithRootCa(wrongRootCert.cert) + val registrationHelper = createRegistrationHelper(createNodeCaCertPath()) assertThatThrownBy { registrationHelper.buildKeystore() }.isInstanceOf(CertPathValidatorException::class.java) } - private fun createRegistrationHelper(): NetworkRegistrationHelper { + private fun createNodeCaCertPath(type: CertificateType = CertificateType.NODE_CA, + legalName: CordaX500Name = nodeLegalName): Array { + val (rootCa, intermediateCa) = createDevIntermediateCaCertPath() + val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) + val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.x500Name))), arrayOf()) + val nodeCaCert = X509Utilities.createCertificate( + type, + intermediateCa.certificate, + intermediateCa.keyPair, + legalName, + keyPair.public, + nameConstraints = nameConstraints) + return arrayOf(nodeCaCert.cert, intermediateCa.certificate.cert, rootCa.certificate.cert) + } + + private fun createRegistrationHelper(response: Array): NetworkRegistrationHelper { val certService = rigorousMock().also { doReturn(requestId).whenever(it).submitRequest(any()) - doReturn(arrayOf(nodeCaCert, intermediateCaCert, rootCaCert)).whenever(it).retrieveCertificates(eq(requestId)) + doReturn(response).whenever(it).retrieveCertificates(eq(requestId)) } return NetworkRegistrationHelper(config, certService) } From 5c85e80b177b99fc05c8a661847d73899482f9ba Mon Sep 17 00:00:00 2001 From: Maksymilian Pawlak <120831+m4ksio@users.noreply.github.com> Date: Tue, 2 Jan 2018 16:48:08 +0000 Subject: [PATCH 02/19] Restoring apparently missing SIMM demo documentation (#2293) (cherry picked from commit 2e84bad) --- samples/simm-valuation-demo/README.md | 176 +++++++++++++++++++++++++- 1 file changed, 174 insertions(+), 2 deletions(-) diff --git a/samples/simm-valuation-demo/README.md b/samples/simm-valuation-demo/README.md index 7931c876e0..8237872b33 100644 --- a/samples/simm-valuation-demo/README.md +++ b/samples/simm-valuation-demo/README.md @@ -1,8 +1,180 @@ -# SIMM Valuation Demo +# SIMM and Portfolio Demo - aka the Initial Margin Agreement Demo -See our [main documentation site](https://docs.corda.net/initial-margin-agreement.html) regarding the SIMM valuation and agreement on a distributed ledger. +## Background and SIMM Introduction + +This app is a demonstration of how Corda can be used for the real world requirement of initial margin calculation and +agreement; featuring the integration of complex and industry proven third party libraries into Corda nodes. + +SIMM is an acronym for "Standard Initial Margin Model". It is effectively the calculation of a "margin" that is paid +by one party to another when they agree a trade on certain types of transaction. + +The SIMM was introduced to standardise the calculation of how much margin counterparties charge each other on their +bilateral transactions. Before SIMM, each counterparty computed margins according to its own model and it was made it very + difficult to agree the exact margin with the counterparty that faces the same trade on the other side. + +To enact this, in September 2016, the ISDA committee - with full backing from various governing bodies - +[issued a ruling on what is known as the ISDA SIMM ™ model](http://www2.isda.org/news/isda-simm-deployed-today-new-industry-standard-for-calculating-initial-margin-widely-adopted-by-market-participants) +a way of fairly and consistently calculating this margin. Any parties wishing to trade a financial product that is +covered under this ruling would, independently, use this model and calculate their margin payment requirement, +agree it with their trading counterparty and then pay (or receive, depending on the results of this calculation) +this amount. In the case of disagreement that is not resolved in a timely fashion, this payment would increase +and so therefore it is in the parties' interest to reach agreement in as short as time frame as possible. + +To be more accurate, the SIMM calculation is not performed on just one trade - it is calculated on an aggregate of +intermediary values (which in this model are sensitivities to risk factors) from a portfolio of trades; therefore +the input to a SIMM is actually this data, not the individual trades themselves. + +Also note that implementations of the SIMM are actually protected and subject to license restrictions by ISDA +(this is due to the model itself being protected). We were fortunate enough to technically partner with +[OpenGamma](http://www.opengamma.com) who allowed us to demonstrate the SIMM process using their proprietary model. +In the source code released, we have replaced their analytics engine with very simple stub functions that allow +the process to run without actually calculating correct values, and can easily be swapped out in place for their real libraries. + +## What happens in the demo (notionally) +Preliminaries + - Ensure that there are a number of live trades with another party based on financial products that are covered under the + ISDA SIMM agreement (if none, then use the demo to enter some simple trades as described below). + +Initial Margin Agreement Process + - Agree that one will be performing the margining calculation against a portfolio of trades with another party, and agree the trades in that portfolio. In practice, one node will start the flow but it does not matter which node does. + - Individually (at the node level), identify the data (static, reference etc) one will need in order to be able to calculate the metrics on those trades + - Confirm with the other counterparty the dataset from the above set + - Calculate any intermediary steps and values needed for the margin calculation (ie sensitivities to risk factors) + - Agree on the results of these steps + - Calculate the initial margin + - Agree on the calculation of the above with the other party + - In practice, pay (or receive) this margin (omitted for the sake of complexity for this example) + +## Demo execution (step by step) + + +**Setting up the Corda infrastructure** + +To run from the command line in Unix: + +1. Deploy the nodes using ``./gradlew samples:simm-valuation-demo:deployNodes`` +2. Run the nodes using ``./samples/simm-valuation-demo/build/nodes/runnodes`` + +To run from the command line in Windows: + +1. Deploy the nodes using ``gradlew samples:simm-valuation-demo:deployNodes`` +2. Run the nodes using ``samples\simm-valuation-demo\build\nodes\runnodes`` + +**Getting Bank A's details** + +From the command line run + + curl http://localhost:10005/api/simmvaluationdemo/whoami + +The response should be something like + + { + "self" : { + "id" : "8Kqd4oWdx4KQGHGQW3FwXHQpjiv7cHaSsaAWMwRrK25bBJj792Z4rag7EtA", + "text" : "C=GB,L=London,O=Bank A" + }, + "counterparties" : [ + { + "id" : "8Kqd4oWdx4KQGHGL1DzULumUmZyyokeSGJDY1n5M6neUfAj2sjbf65wYwQM", + "text" : "C=JP,L=Tokyo,O=Bank C" + }, + { + "id" : "8Kqd4oWdx4KQGHGTBm34eCM2nrpcWKeM1ZG3DUYat3JTFUQTwB3Lv2WbPM8", + "text" : "C=US,L=New York,O=Bank B" + } + ] + } + +Now, if we ask the same question of Bank C we will see that it's id matches the id for Bank C as a counter +party to Bank A and Bank A will appear as a counter party + + curl -i -H "Content-Type: application/json" -X GET http://localhost:10011/api/simmvaluationdemo/whoami + +**Creating a trade with Bank C** + +In what follows, we assume we are Bank A (which is listening on port 10005) + +Notice the id field in the output of the ``whoami`` command. We are going to use the id assocatied +with Bank C, one of our counter parties, to create a trade. The general command for this is: + + curl -i -H "Content-Type: application/json" -X PUT -d <<>> http://localhost:10005/api/simmvaluationdemo/<<>>/trades + +where the representation of the trade is + + + { + "id" : "trade1", + "description" : "desc", + "tradeDate" : [ 2016, 6, 6 ], + "convention" : "EUR_FIXED_1Y_EURIBOR_3M", + "startDate" : [ 2016, 6, 6 ], + "endDate" : [ 2020, 1, 2 ], + "buySell" : "BUY", + "notional" : "1000", + "fixedRate" : "0.1" + } + +Continuing our example, the specific command we would run is + + curl -i -H "Content-Type: application/json" \ + -X PUT \ + -d '{"id":"trade1","description" : "desc","tradeDate" : [ 2016, 6, 6 ], "convention" : "EUR_FIXED_1Y_EURIBOR_3M", "startDate" : [ 2016, 6, 6 ], "endDate" : [ 2020, 1, 2 ], "buySell" : "BUY", "notional" : "1000", "fixedRate" : "0.1"}' \ + http://localhost:10005/api/simmvaluationdemo/8Kqd4oWdx4KQGHGL1DzULumUmZyyokeSGJDY1n5M6neUfAj2sjbf65wYwQM/trades + +With an expected response of + + HTTP/1.1 202 Accepted + Date: Thu, 28 Sep 2017 17:19:39 GMT + Content-Type: text/plain + Access-Control-Allow-Origin: * + Content-Length: 2 + Server: Jetty(9.3.9.v20160517) + +**Verifying trade completion** + +With the trade completed and stored by both parties, the complete list of trades with our couterparty can be seen with the following command + + curl -X GET http://localhost:10005/api/simmvaluationdemo/<<>>/trades + +The command for our example, using Bank A, would thus be + + curl -X GET http://localhost:10005/api/simmvaluationdemo/8Kqd4oWdx4KQGHGL1DzULumUmZyyokeSGJDY1n5M6neUfAj2sjbf65wYwQM/trades + +whilst a specific trade can be seen with + + + curl -X GET http://localhost:10005/api/simmvaluationdemo/<<>>/trades/<<>> + +If we look at the trade we created above, we assigned it the id "trade1", the complete command in this case would be + + curl -X GET http://localhost:10005/api/simmvaluationdemo/8Kqd4oWdx4KQGHGL1DzULumUmZyyokeSGJDY1n5M6neUfAj2sjbf65wYwQM/trades/trade1 + +**Generating a valuation** + + curl -i -H "Content-Type: application/json" \ + -X POST \ + -d <<>> + http://localhost:10005/api/simmvaluationdemo/<<>>/portfolio/valuations/calculate + +Again, the specific command to continue our example would be + + curl -i -H "Content-Type: application/json" \ + -X POST \ + -d '{"valuationDate":[2016,6,6]}' \ + http://localhost:10005/api/simmvaluationdemo/8Kqd4oWdx4KQGHGL1DzLumUmZyyokeSGJDY1n5M6neUfAj2sjbf65wYwQM/portfolio/valuations/calculate + +**Viewing a valuation** + +In the same way we can ask for specific instances of trades with a counter party, we can request details of valuations + + curl -i -H "Content-Type: application/json" -X GET http://localhost:10005/api/simmvaluationdemo/<<>>/portfolio/valuations + +The specific command for out Bank A example is + + curl -i -H "Content-Type: application/json" \ + -X GET http://localhost:10005/api/simmvaluationdemo/8Kqd4oWdx4KQGHGL1DzULumUmZyyokeSGJDY1n5M6neUfAj2sjbf65YwQM/portfolio/valuations ## SIMM Library Licensing From c1874cbd62613190209fa6a2c1242b1d380f4623 Mon Sep 17 00:00:00 2001 From: Joel Dudley Date: Wed, 3 Jan 2018 10:00:33 +0000 Subject: [PATCH 03/19] Fixes a broken link to the shell page. --- docs/source/hello-world-running.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/hello-world-running.rst b/docs/source/hello-world-running.rst index 4ac394d26c..58e3271c71 100644 --- a/docs/source/hello-world-running.rst +++ b/docs/source/hello-world-running.rst @@ -103,7 +103,7 @@ Go to the terminal window displaying the CRaSH shell of PartyA. Typing ``help`` commands. .. note:: Local terminal shell is available only in a development mode. In production environment SSH server can be enabled. - More about SSH and how to connect can be found on :doc:`Shell` page. + More about SSH and how to connect can be found on the :doc:`shell` page. We want to create an IOU of 100 with PartyB. We start the ``IOUFlow`` by typing: From dd329c860cfee36d751c2778295cfb7728a55d32 Mon Sep 17 00:00:00 2001 From: Tommy Lillehagen Date: Wed, 3 Jan 2018 14:19:03 +0000 Subject: [PATCH 04/19] CORDA-878 - Update IRS demo instructions --- samples/irs-demo/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/samples/irs-demo/README.md b/samples/irs-demo/README.md index 12af545191..a22d7acaaf 100644 --- a/samples/irs-demo/README.md +++ b/samples/irs-demo/README.md @@ -9,11 +9,12 @@ webapp which provides REST API and web frontend. Application communicate using C To run from the command line in Unix: 1. Run ``./gradlew samples:irs-demo:cordapp:deployNodes`` to install configs and a command line tool under - ``samples/irs-demo/build`` + ``samples/irs-demo/cordapp/build`` 2. Run ``./gradlew samples:irs-demo:web:deployWebapps`` to install configs and tools for running webservers 3. Move to the ``samples/irs-demo/`` directory 4. Run ``./cordapp/build/nodes/runnodes`` to open up three new terminals with the three nodes (you may have to install xterm) -5. Run ``./web/build/webapps/runwebapps`` to open three more terminals for associated webserver +5. On Linux, run ``./web/build/webapps/runwebapps`` to open three more terminals for associated webservers. On macOS, + use the following command instead: ``osascript ./web/build/webapps/runwebapps.scpt`` To run from the command line in Windows: From fb71a45be59899b43e410a57819196b113298157 Mon Sep 17 00:00:00 2001 From: Katelyn Baker Date: Wed, 3 Jan 2018 14:55:48 +0000 Subject: [PATCH 05/19] CORDA-882 - Better err messages when serializer encounters private property --- .../serialization/amqp/SerializationHelper.kt | 17 +++-- .../serialization/amqp/ErrorMessageTests.java | 40 +++++++++++ .../serialization/amqp/ErrorMessagesTests.kt | 69 +++++++++++++++++++ 3 files changed, 121 insertions(+), 5 deletions(-) create mode 100644 node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/ErrorMessageTests.java create mode 100644 node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/ErrorMessagesTests.kt diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationHelper.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationHelper.kt index 8b677630d2..57903f546e 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationHelper.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationHelper.kt @@ -1,9 +1,9 @@ package net.corda.nodeapi.internal.serialization.amqp -import net.corda.core.serialization.ClassWhitelist -import net.corda.core.serialization.CordaSerializable import com.google.common.primitives.Primitives import com.google.common.reflect.TypeToken +import net.corda.core.serialization.ClassWhitelist +import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.SerializationContext import org.apache.qpid.proton.codec.Data import java.beans.IndexedPropertyDescriptor @@ -81,10 +81,17 @@ private fun propertiesForSerializationFromConstructor(kotlinConstructo val rc: MutableList = ArrayList(kotlinConstructor.parameters.size) for (param in kotlinConstructor.parameters) { val name = param.name ?: throw NotSerializableException("Constructor parameter of $clazz has no name.") + val matchingProperty = properties[name] ?: - throw NotSerializableException("No property matching constructor parameter named '$name' of '$clazz'. " + - "If using Java, check that you have the -parameters option specified in the Java compiler. " + - "Alternately, provide a proxy serializer (SerializationCustomSerializer) if recompiling isn't an option") + try { + clazz.getDeclaredField(param.name) + throw NotSerializableException("Property '$name' or it's getter is non public, this renders class '$clazz' unserializable") + } catch (e: NoSuchFieldException) { + throw NotSerializableException("No property matching constructor parameter named '$name' of '$clazz'. " + + "If using Java, check that you have the -parameters option specified in the Java compiler. " + + "Alternately, provide a proxy serializer (SerializationCustomSerializer) if recompiling isn't an option") + } + // Check that the method has a getter in java. val getter = matchingProperty.readMethod ?: throw NotSerializableException("Property has no getter method for $name of $clazz. " + "If using Java and the parameter name looks anonymous, check that you have the -parameters option specified in the Java compiler." + diff --git a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/ErrorMessageTests.java b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/ErrorMessageTests.java new file mode 100644 index 0000000000..2df95a808d --- /dev/null +++ b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/ErrorMessageTests.java @@ -0,0 +1,40 @@ +package net.corda.nodeapi.internal.serialization.amqp; + +import net.corda.nodeapi.internal.serialization.AllWhitelist; +import org.assertj.core.api.Assertions; +import org.junit.Test; + +import java.io.NotSerializableException; + +public class ErrorMessageTests { + private String errMsg(String property, String testname) { + return "Property '" + + property + + "' or it's getter is non public, this renders class 'class " + + testname + + "$C' unserializable -> class " + + testname + + "$C"; + } + + static class C { + public Integer a; + + public C(Integer a) { + this.a = a; + } + + private Integer getA() { return this.a; } + } + + @Test + public void testJavaConstructorAnnotations() { + SerializerFactory factory1 = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader()); + SerializationOutput ser = new SerializationOutput(factory1); + + Assertions.assertThatThrownBy(() -> ser.serialize(new C(1))) + .isInstanceOf(NotSerializableException.class) + .hasMessage(errMsg("a", getClass().getName())); + } + +} diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/ErrorMessagesTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/ErrorMessagesTests.kt new file mode 100644 index 0000000000..775e5c3405 --- /dev/null +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/ErrorMessagesTests.kt @@ -0,0 +1,69 @@ +package net.corda.nodeapi.internal.serialization.amqp + +import org.assertj.core.api.Assertions +import org.junit.Test +import java.io.NotSerializableException + +class ErrorMessagesTests { + companion object { + val VERBOSE get() = false + } + + private fun errMsg(property:String, testname: String) = + "Property '$property' or it's getter is non public, this renders class 'class $testname\$C' unserializable -> class $testname\$C" + + @Test + fun privateProperty() { + data class C(private val a: Int) + + val sf = testDefaultFactory() + + val testname = "${javaClass.name}\$${testName()}" + + Assertions.assertThatThrownBy { + TestSerializationOutput(VERBOSE, sf).serialize(C(1)) + }.isInstanceOf(NotSerializableException::class.java).hasMessage(errMsg("a", testname)) + } + + @Test + fun privateProperty2() { + data class C(val a: Int, private val b: Int) + + val sf = testDefaultFactory() + + val testname = "${javaClass.name}\$${testName()}" + + Assertions.assertThatThrownBy { + TestSerializationOutput(VERBOSE, sf).serialize(C(1, 2)) + }.isInstanceOf(NotSerializableException::class.java).hasMessage(errMsg("b", testname)) + } + + @Test + fun privateProperty3() { + // despite b being private, the getter we've added is public and thus allows for the serialisation + // of the object + data class C(val a: Int, private val b: Int) { + public fun getB() = b + } + + val sf = testDefaultFactory() + + val testname = "${javaClass.name}\$${testName()}" + + val bytes = TestSerializationOutput(VERBOSE, sf).serialize(C(1, 2)) + val c = DeserializationInput(sf).deserialize(bytes) + } + + @Test + fun protectedProperty() { + data class C(protected val a: Int) + + val sf = testDefaultFactory() + + val testname = "${javaClass.name}\$${testName()}" + + Assertions.assertThatThrownBy { + TestSerializationOutput(VERBOSE, sf).serialize(C(1)) + }.isInstanceOf(NotSerializableException::class.java).hasMessage(errMsg("a", testname)) + } +} \ No newline at end of file From d84105b60ea8f6a019b0a8e8f09096d4800581ad Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Wed, 3 Jan 2018 22:00:39 +0000 Subject: [PATCH 06/19] Using X509Certificate consistently throughout, rather than BC's X509CertificateHolder. (#2305) The later is now only used where needed. This has reduced the amount of converting we have to do back and forth. --- .../corda/confidential/SwapIdentitiesFlow.kt | 7 - .../net/corda/core/identity/CordaX500Name.kt | 22 +-- .../net/corda/core/internal/InternalUtils.kt | 26 ++- .../net/corda/core/internal/X500NameUtils.kt | 34 ---- .../corda/core/crypto/CompositeKeyTests.kt | 7 +- .../core/crypto/X509NameConstraintsTest.kt | 13 +- .../corda/core/identity/CordaX500NameTest.kt | 12 +- .../core/identity/PartyAndCertificateTest.kt | 7 +- docs/source/serialization.rst | 3 - .../nodeapi/internal/DevIdentityGenerator.kt | 17 +- .../nodeapi/internal/KeyStoreConfigHelpers.kt | 8 +- .../internal/crypto/KeyStoreUtilities.kt | 23 +-- .../internal/crypto/KeyStoreWrapper.kt | 10 +- .../nodeapi/internal/crypto/X509Utilities.kt | 179 ++++++++---------- .../internal/crypto/X509UtilitiesTest.kt | 106 +++++------ .../net/corda/node/NodeKeystoreCheckTest.kt | 12 +- .../net/corda/node/amqp/ProtonWrapperTests.kt | 39 ++-- .../registration/NodeRegistrationTest.kt | 25 ++- .../messaging/MQSecurityAsNodeTest.kt | 28 ++- .../net/corda/node/internal/AbstractNode.kt | 4 +- .../protonwrapper/netty/AMQPChannelHandler.kt | 23 ++- .../protonwrapper/netty/ConnectionChange.kt | 4 +- .../node/services/config/ConfigUtilities.kt | 3 +- .../identity/InMemoryIdentityService.kt | 13 +- .../identity/PersistentIdentityService.kt | 6 +- .../net/corda/node/services/keys/KMSUtils.kt | 15 +- .../registration/NetworkRegistrationHelper.kt | 14 +- .../identity/InMemoryIdentityServiceTests.kt | 23 ++- .../PersistentIdentityServiceTests.kt | 18 +- .../services/network/NetworkMapClientTest.kt | 5 +- .../node/utilities/TLSAuthenticationTests.kt | 17 +- .../NetworkRegistrationHelperTest.kt | 10 +- .../net/corda/testing/node/MockServices.kt | 16 +- .../node/internal/network/NetworkMapServer.kt | 17 +- .../kotlin/net/corda/testing/CoreTestUtils.kt | 15 +- .../kotlin/net/corda/testing/TestConstants.kt | 11 +- .../testing/internal/InternalTestUtils.kt | 18 +- 37 files changed, 374 insertions(+), 436 deletions(-) delete mode 100644 core/src/main/kotlin/net/corda/core/internal/X500NameUtils.kt diff --git a/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesFlow.kt b/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesFlow.kt index 575c1f8d52..f98970c322 100644 --- a/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesFlow.kt +++ b/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesFlow.kt @@ -10,7 +10,6 @@ import net.corda.core.identity.AnonymousParty import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate -import net.corda.core.internal.toX509CertHolder import net.corda.core.node.services.IdentityService import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.SerializedBytes @@ -18,14 +17,8 @@ import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.unwrap -import org.bouncycastle.asn1.DERSet -import org.bouncycastle.asn1.pkcs.CertificationRequestInfo -import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo -import java.io.ByteArrayOutputStream -import java.nio.charset.Charset import java.security.PublicKey import java.security.SignatureException -import java.security.cert.CertPath import java.util.* /** diff --git a/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt b/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt index ad315c065e..abe3d21fd4 100644 --- a/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt +++ b/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt @@ -80,8 +80,10 @@ data class CordaX500Name(val commonName: String?, const val MAX_LENGTH_STATE = 64 const val MAX_LENGTH_ORGANISATION_UNIT = 64 const val MAX_LENGTH_COMMON_NAME = 64 + private val supportedAttributes = setOf(BCStyle.O, BCStyle.C, BCStyle.L, BCStyle.CN, BCStyle.ST, BCStyle.OU) private val countryCodes: Set = ImmutableSet.copyOf(Locale.getISOCountries() + unspecifiedCountry) + @JvmStatic fun build(principal: X500Principal): CordaX500Name { val x500Name = X500Name.getInstance(principal.encoded) @@ -115,20 +117,12 @@ data class CordaX500Name(val commonName: String?, } @Transient - private var x500Cache: X500Name? = null + private var _x500Principal: X500Principal? = null - val x500Principal: X500Principal - get() { - if (x500Cache == null) { - x500Cache = this.x500Name - } - return X500Principal(x500Cache!!.encoded) - } - - override fun toString(): String { - if (x500Cache == null) { - x500Cache = this.x500Name - } - return x500Cache.toString() + /** Return the [X500Principal] equivalent of this name. */ + val x500Principal: X500Principal get() { + return _x500Principal ?: X500Principal(this.x500Name.encoded).also { _x500Principal = it } } + + override fun toString(): String = x500Principal.toString() } \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt index 42145a2f26..1ad9e16630 100644 --- a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt @@ -10,8 +10,9 @@ import net.corda.core.node.ServicesForResolution import net.corda.core.serialization.SerializationContext import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.WireTransaction -import org.bouncycastle.cert.X509CertificateHolder -import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter +import org.bouncycastle.asn1.x500.X500Name +import org.bouncycastle.asn1.x500.X500NameBuilder +import org.bouncycastle.asn1.x500.style.BCStyle import org.slf4j.Logger import rx.Observable import rx.Observer @@ -26,8 +27,6 @@ import java.nio.charset.Charset import java.nio.charset.StandardCharsets.UTF_8 import java.nio.file.* import java.nio.file.attribute.FileAttribute -import java.security.cert.Certificate -import java.security.cert.X509Certificate import java.time.Duration import java.time.temporal.Temporal import java.util.* @@ -186,9 +185,6 @@ fun logElapsedTime(label: String, logger: Logger? = null, body: () -> T): T } } -fun Certificate.toX509CertHolder() = X509CertificateHolder(encoded) -val X509CertificateHolder.cert: X509Certificate get() = JcaX509CertificateConverter().getCertificate(this) - /** Convert a [ByteArrayOutputStream] to [InputStreamAndHash]. */ fun ByteArrayOutputStream.toInputStreamAndHash(): InputStreamAndHash { val bytes = toByteArray() @@ -320,6 +316,22 @@ fun ExecutorService.join() { } } +/** + * Return the underlying X.500 name from this Corda-safe X.500 name. These are guaranteed to have a consistent + * ordering, such that their `toString()` function returns the same value every time for the same [CordaX500Name]. + */ +val CordaX500Name.x500Name: X500Name + get() { + return X500NameBuilder(BCStyle.INSTANCE).apply { + addRDN(BCStyle.C, country) + state?.let { addRDN(BCStyle.ST, it) } + addRDN(BCStyle.L, locality) + addRDN(BCStyle.O, organisation) + organisationUnit?.let { addRDN(BCStyle.OU, it) } + commonName?.let { addRDN(BCStyle.CN, it) } + }.build() + } + @Suppress("unused") @VisibleForTesting val CordaX500Name.Companion.unspecifiedCountry diff --git a/core/src/main/kotlin/net/corda/core/internal/X500NameUtils.kt b/core/src/main/kotlin/net/corda/core/internal/X500NameUtils.kt deleted file mode 100644 index 7d57d7d522..0000000000 --- a/core/src/main/kotlin/net/corda/core/internal/X500NameUtils.kt +++ /dev/null @@ -1,34 +0,0 @@ -@file:JvmName("X500NameUtils") - -package net.corda.core.internal - -import net.corda.core.identity.CordaX500Name -import org.bouncycastle.asn1.ASN1ObjectIdentifier -import org.bouncycastle.asn1.x500.X500Name -import org.bouncycastle.asn1.x500.X500NameBuilder -import org.bouncycastle.asn1.x500.style.BCStyle - -val X500Name.commonName: String? get() = getRDNValueString(BCStyle.CN) -val X500Name.state: String? get() = getRDNValueString(BCStyle.ST) -val X500Name.organisation: String get() = getRDNValueString(BCStyle.O) ?: throw IllegalArgumentException("Malformed X500 name, organisation attribute (O) cannot be empty.") -val X500Name.locality: String get() = getRDNValueString(BCStyle.L) ?: throw IllegalArgumentException("Malformed X500 name, locality attribute (L) cannot be empty.") -val X500Name.country: String get() = getRDNValueString(BCStyle.C) ?: throw IllegalArgumentException("Malformed X500 name, country attribute (C) cannot be empty.") - -private fun X500Name.getRDNValueString(identifier: ASN1ObjectIdentifier): String? = getRDNs(identifier).firstOrNull()?.first?.value?.toString() - - -/** - * Return the underlying X.500 name from this Corda-safe X.500 name. These are guaranteed to have a consistent - * ordering, such that their `toString()` function returns the same value every time for the same [CordaX500Name]. - */ -val CordaX500Name.x500Name: X500Name - get() { - return X500NameBuilder(BCStyle.INSTANCE).apply { - addRDN(BCStyle.C, country) - state?.let { addRDN(BCStyle.ST, it) } - addRDN(BCStyle.L, locality) - addRDN(BCStyle.O, organisation) - organisationUnit?.let { addRDN(BCStyle.OU, it) } - commonName?.let { addRDN(BCStyle.CN, it) } - }.build() - } \ No newline at end of file diff --git a/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt b/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt index 751037b1ee..61d0c3d2f6 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt @@ -1,8 +1,6 @@ package net.corda.core.crypto import net.corda.core.crypto.CompositeKey.NodeAndWeight -import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.cert import net.corda.core.internal.declaredField import net.corda.core.internal.div import net.corda.core.serialization.serialize @@ -15,6 +13,7 @@ import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder import java.security.PublicKey +import javax.security.auth.x500.X500Principal import kotlin.test.assertEquals import kotlin.test.assertFailsWith import kotlin.test.assertFalse @@ -334,7 +333,7 @@ class CompositeKeyTests { // Create self sign CA. val caKeyPair = Crypto.generateKeyPair() - val caName = CordaX500Name(commonName = "Test CA", organisation = "R3 Ltd", locality = "London", country = "GB") + val caName = X500Principal("CN=Test CA,O=R3 Ltd,L=London,C=GB") val ca = X509Utilities.createSelfSignedCACertificate(caName, caKeyPair) // Sign the composite key with the self sign CA. @@ -343,7 +342,7 @@ class CompositeKeyTests { // Store certificate to keystore. val keystorePath = tempFolder.root.toPath() / "keystore.jks" val keystore = loadOrCreateKeyStore(keystorePath, "password") - keystore.setCertificateEntry("CompositeKey", compositeKeyCert.cert) + keystore.setCertificateEntry("CompositeKey", compositeKeyCert) keystore.save(keystorePath, "password") // Load keystore from disk. diff --git a/core/src/test/kotlin/net/corda/core/crypto/X509NameConstraintsTest.kt b/core/src/test/kotlin/net/corda/core/crypto/X509NameConstraintsTest.kt index 707a32bba4..3e51ad7b9f 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/X509NameConstraintsTest.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/X509NameConstraintsTest.kt @@ -1,7 +1,6 @@ package net.corda.core.crypto import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.cert import net.corda.nodeapi.internal.crypto.* import net.corda.testing.internal.createDevIntermediateCaCertPath import org.bouncycastle.asn1.x500.X500Name @@ -14,6 +13,7 @@ import java.security.KeyStore import java.security.cert.CertPathValidator import java.security.cert.CertPathValidatorException import java.security.cert.PKIXParameters +import javax.security.auth.x500.X500Principal import kotlin.test.assertFailsWith import kotlin.test.assertTrue @@ -26,17 +26,22 @@ class X509NameConstraintsTest { CertificateType.NODE_CA, intermediateCa.certificate, intermediateCa.keyPair, - CordaX500Name("Corda Client CA", "R3 Ltd", "London", "GB"), + CordaX500Name("Corda Client CA", "R3 Ltd", "London", "GB").x500Principal, nodeCaKeyPair.public, nameConstraints = nameConstraints) val keyPass = "password" val trustStore = KeyStore.getInstance(KEYSTORE_TYPE) trustStore.load(null, keyPass.toCharArray()) - trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCa.certificate.cert) + trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCa.certificate) val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, subjectName, tlsKeyPair.public) + val tlsCert = X509Utilities.createCertificate( + CertificateType.TLS, + nodeCaCert, + nodeCaKeyPair, + X500Principal(subjectName.encoded), + tlsKeyPair.public) val keyStore = KeyStore.getInstance(KEYSTORE_TYPE) keyStore.load(null, keyPass.toCharArray()) diff --git a/core/src/test/kotlin/net/corda/core/identity/CordaX500NameTest.kt b/core/src/test/kotlin/net/corda/core/identity/CordaX500NameTest.kt index 766fa202a4..3cfc7c089b 100644 --- a/core/src/test/kotlin/net/corda/core/identity/CordaX500NameTest.kt +++ b/core/src/test/kotlin/net/corda/core/identity/CordaX500NameTest.kt @@ -7,30 +7,36 @@ import kotlin.test.assertNull class CordaX500NameTest { @Test - fun `parse service name with organisational unit`() { + fun `service name with organisational unit`() { val name = CordaX500Name.parse("O=Bank A, L=New York, C=US, OU=Org Unit, CN=Service Name") assertEquals("Service Name", name.commonName) assertEquals("Org Unit", name.organisationUnit) assertEquals("Bank A", name.organisation) assertEquals("New York", name.locality) + assertEquals(CordaX500Name.parse(name.toString()), name) + assertEquals(CordaX500Name.build(name.x500Principal), name) } @Test - fun `parse service name`() { + fun `service name`() { val name = CordaX500Name.parse("O=Bank A, L=New York, C=US, CN=Service Name") assertEquals("Service Name", name.commonName) assertNull(name.organisationUnit) assertEquals("Bank A", name.organisation) assertEquals("New York", name.locality) + assertEquals(CordaX500Name.parse(name.toString()), name) + assertEquals(CordaX500Name.build(name.x500Principal), name) } @Test - fun `parse legal entity name`() { + fun `legal entity name`() { val name = CordaX500Name.parse("O=Bank A, L=New York, C=US") assertNull(name.commonName) assertNull(name.organisationUnit) assertEquals("Bank A", name.organisation) assertEquals("New York", name.locality) + assertEquals(CordaX500Name.parse(name.toString()), name) + assertEquals(CordaX500Name.build(name.x500Principal), name) } @Test diff --git a/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt b/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt index f64ac5440a..ff8207db8d 100644 --- a/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt +++ b/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt @@ -1,14 +1,13 @@ package net.corda.core.identity import net.corda.core.crypto.entropyToKeyPair -import net.corda.core.internal.cert import net.corda.core.internal.read import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.nodeapi.internal.crypto.KEYSTORE_TYPE import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.save -import net.corda.testing.DEV_CA +import net.corda.testing.DEV_ROOT_CA import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.getTestPartyAndCertificate import org.assertj.core.api.Assertions.assertThat @@ -25,8 +24,8 @@ class PartyAndCertificateTest { val testSerialization = SerializationEnvironmentRule() @Test - fun `should reject a path with no roles`() { - val path = X509CertificateFactory().generateCertPath(DEV_CA.certificate.cert) + fun `reject a path with no roles`() { + val path = X509CertificateFactory().generateCertPath(DEV_ROOT_CA.certificate) assertFailsWith { PartyAndCertificate(path) } } diff --git a/docs/source/serialization.rst b/docs/source/serialization.rst index 70dcb44dee..b4f87b912a 100644 --- a/docs/source/serialization.rst +++ b/docs/source/serialization.rst @@ -192,9 +192,6 @@ The following 3rd party types are supported. org.apache.activemq.artemis.api.core.SimpleString - org.bouncycastle.asn1.x500.X500Name - org.bouncycastle.cert.X509CertificateHolder - Corda Types ``````````` diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/DevIdentityGenerator.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/DevIdentityGenerator.kt index ef58a930b2..0cb8472f89 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/DevIdentityGenerator.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/DevIdentityGenerator.kt @@ -5,10 +5,8 @@ import net.corda.core.crypto.Crypto import net.corda.core.crypto.generateKeyPair import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party -import net.corda.core.internal.cert import net.corda.core.internal.createDirectories import net.corda.core.internal.div -import net.corda.core.internal.toX509CertHolder import net.corda.core.utilities.trace import net.corda.nodeapi.internal.config.NodeSSLConfiguration import net.corda.nodeapi.internal.crypto.* @@ -39,10 +37,10 @@ object DevIdentityGenerator { // TODO The passwords for the dev key stores are spread everywhere and should be constants in a single location val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass") val intermediateCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass") - val rootCert = caKeyStore.getCertificate(X509Utilities.CORDA_ROOT_CA) + val rootCert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA) nodeSslConfig.certificatesDirectory.createDirectories() - nodeSslConfig.createDevKeyStores(rootCert.toX509CertHolder(), intermediateCa, legalName) + nodeSslConfig.createDevKeyStores(rootCert, intermediateCa, legalName) val keyStoreWrapper = KeyStoreWrapper(nodeSslConfig.nodeKeystore, nodeSslConfig.keyStorePassword) val identity = keyStoreWrapper.storeLegalIdentity(legalName, "$NODE_IDENTITY_ALIAS_PREFIX-private-key", Crypto.generateKeyPair()) @@ -62,16 +60,21 @@ object DevIdentityGenerator { keyPairs.zip(dirs) { keyPair, nodeDir -> val (serviceKeyCert, compositeKeyCert) = listOf(keyPair.public, compositeKey).map { publicKey -> - X509Utilities.createCertificate(CertificateType.SERVICE_IDENTITY, intermediateCa.certificate, intermediateCa.keyPair, notaryName, publicKey) + X509Utilities.createCertificate( + CertificateType.SERVICE_IDENTITY, + intermediateCa.certificate, + intermediateCa.keyPair, + notaryName.x500Principal, + publicKey) } val distServKeyStoreFile = (nodeDir / "certificates").createDirectories() / "distributedService.jks" val keystore = loadOrCreateKeyStore(distServKeyStoreFile, "cordacadevpass") - keystore.setCertificateEntry("$DISTRIBUTED_NOTARY_ALIAS_PREFIX-composite-key", compositeKeyCert.cert) + keystore.setCertificateEntry("$DISTRIBUTED_NOTARY_ALIAS_PREFIX-composite-key", compositeKeyCert) keystore.setKeyEntry( "$DISTRIBUTED_NOTARY_ALIAS_PREFIX-private-key", keyPair.private, "cordacadevkeypass".toCharArray(), - arrayOf(serviceKeyCert.cert, intermediateCa.certificate.cert, rootCert)) + arrayOf(serviceKeyCert, intermediateCa.certificate, rootCert)) keystore.save(distServKeyStoreFile, "cordacadevpass") } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt index 03bd39e635..374a4cdf24 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt @@ -8,13 +8,13 @@ import net.corda.nodeapi.internal.crypto.* import org.bouncycastle.asn1.x509.GeneralName import org.bouncycastle.asn1.x509.GeneralSubtree import org.bouncycastle.asn1.x509.NameConstraints -import org.bouncycastle.cert.X509CertificateHolder +import java.security.cert.X509Certificate /** * Create the node and SSL key stores needed by a node. The node key store will be populated with a node CA cert (using * the given legal name), and the SSL key store will store the TLS cert which is a sub-cert of the node CA. */ -fun SSLConfiguration.createDevKeyStores(rootCert: X509CertificateHolder, intermediateCa: CertificateAndKeyPair, legalName: CordaX500Name) { +fun SSLConfiguration.createDevKeyStores(rootCert: X509Certificate, intermediateCa: CertificateAndKeyPair, legalName: CordaX500Name) { val (nodeCaCert, nodeCaKeyPair) = createDevNodeCa(intermediateCa, legalName) loadOrCreateKeyStore(nodeKeystore, keyStorePassword).apply { @@ -27,7 +27,7 @@ fun SSLConfiguration.createDevKeyStores(rootCert: X509CertificateHolder, interme } val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, legalName, tlsKeyPair.public) + val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, legalName.x500Principal, tlsKeyPair.public) loadOrCreateKeyStore(sslKeystore, keyStorePassword).apply { addOrReplaceKey( @@ -50,7 +50,7 @@ fun createDevNodeCa(intermediateCa: CertificateAndKeyPair, legalName: CordaX500N CertificateType.NODE_CA, intermediateCa.certificate, intermediateCa.keyPair, - legalName, + legalName.x500Principal, keyPair.public, nameConstraints = nameConstraints) return CertificateAndKeyPair(cert, keyPair) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreUtilities.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreUtilities.kt index b49493ded2..d060c756b0 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreUtilities.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreUtilities.kt @@ -3,8 +3,9 @@ package net.corda.nodeapi.internal.crypto import net.corda.core.crypto.Crypto -import net.corda.core.internal.* -import org.bouncycastle.cert.X509CertificateHolder +import net.corda.core.internal.exists +import net.corda.core.internal.read +import net.corda.core.internal.write import java.io.IOException import java.io.InputStream import java.io.OutputStream @@ -67,18 +68,6 @@ fun loadKeyStore(input: InputStream, storePassword: String): KeyStore { return keyStore } -/** - * Helper extension method to add, or overwrite any key data in store. - * @param alias name to record the private key and certificate chain under. - * @param key cryptographic key to store. - * @param password password for unlocking the key entry in the future. This does not have to be the same password as any keys stored, - * but for SSL purposes this is recommended. - * @param chain the sequence of certificates starting with the public key certificate for this key and extending to the root CA cert. - */ -fun KeyStore.addOrReplaceKey(alias: String, key: Key, password: CharArray, chain: Array) { - addOrReplaceKey(alias, key, password, chain.map { it.cert }.toTypedArray()) -} - /** * Helper extension method to add, or overwrite any key data in store. * @param alias name to record the private key and certificate chain under. @@ -132,9 +121,9 @@ fun KeyStore.getKeyPair(alias: String, keyPassword: String): KeyPair = getCertif * @param keyPassword The password for the PrivateKey (not the store access password). */ fun KeyStore.getCertificateAndKeyPair(alias: String, keyPassword: String): CertificateAndKeyPair { - val cert = getX509Certificate(alias).toX509CertHolder() - val publicKey = Crypto.toSupportedPublicKey(cert.subjectPublicKeyInfo) - return CertificateAndKeyPair(cert, KeyPair(publicKey, getSupportedKey(alias, keyPassword))) + val certificate = getX509Certificate(alias) + val publicKey = Crypto.toSupportedPublicKey(certificate.publicKey) + return CertificateAndKeyPair(certificate, KeyPair(publicKey, getSupportedKey(alias, keyPassword))) } /** diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreWrapper.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreWrapper.kt index 2504ce221b..f7cae5b441 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreWrapper.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreWrapper.kt @@ -2,7 +2,6 @@ package net.corda.nodeapi.internal.crypto import net.corda.core.identity.CordaX500Name import net.corda.core.identity.PartyAndCertificate -import net.corda.core.internal.cert import net.corda.core.internal.read import java.nio.file.Path import java.security.KeyPair @@ -15,8 +14,13 @@ class KeyStoreWrapper(private val storePath: Path, private val storePassword: St fun storeLegalIdentity(legalName: CordaX500Name, alias: String, keyPair: KeyPair): PartyAndCertificate { val nodeCaCertChain = keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA) val nodeCa = getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA) - val identityCert = X509Utilities.createCertificate(CertificateType.LEGAL_IDENTITY, nodeCa.certificate, nodeCa.keyPair, legalName, keyPair.public) - val identityCertPath = X509CertificateFactory().generateCertPath(identityCert.cert, *nodeCaCertChain) + val identityCert = X509Utilities.createCertificate( + CertificateType.LEGAL_IDENTITY, + nodeCa.certificate, + nodeCa.keyPair, + legalName.x500Principal, + keyPair.public) + val identityCertPath = X509CertificateFactory().generateCertPath(identityCert, *nodeCaCertChain) // Assume key password = store password. keyStore.addOrReplaceKey(alias, keyPair.private, storePassword.toCharArray(), identityCertPath.certificates.toTypedArray()) keyStore.save(storePath, storePassword) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt index 78b44a8786..fb2e512d38 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt @@ -4,12 +4,14 @@ import net.corda.core.CordaOID import net.corda.core.crypto.Crypto import net.corda.core.crypto.SignatureScheme import net.corda.core.crypto.random63BitValue +import net.corda.core.internal.CertRole +import net.corda.core.internal.reader +import net.corda.core.internal.writer import net.corda.core.identity.CordaX500Name import net.corda.core.internal.* import net.corda.core.utilities.days import net.corda.core.utilities.millis import org.bouncycastle.asn1.* -import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.style.BCStyle import org.bouncycastle.asn1.x509.* import org.bouncycastle.asn1.x509.Extension @@ -34,6 +36,7 @@ import java.time.Duration import java.time.Instant import java.time.temporal.ChronoUnit import java.util.* +import javax.security.auth.x500.X500Principal object X509Utilities { val DEFAULT_IDENTITY_SIGNATURE_SCHEME = Crypto.EDDSA_ED25519_SHA512 @@ -74,7 +77,7 @@ object X509Utilities { * @param after duration to roll forward returned end date relative to current date. * @param parent if provided certificate whose validity should bound the date interval returned. */ - fun getCertificateValidityWindow(before: Duration, after: Duration, parent: X509CertificateHolder? = null): Pair { + fun getCertificateValidityWindow(before: Duration, after: Duration, parent: X509Certificate? = null): Pair { val startOfDayUTC = Instant.now().truncatedTo(ChronoUnit.DAYS) val notBefore = max(startOfDayUTC - before, parent?.notBefore) val notAfter = min(startOfDayUTC + after, parent?.notAfter) @@ -85,60 +88,11 @@ object X509Utilities { * Create a de novo root self-signed X509 v3 CA cert. */ @JvmStatic - fun createSelfSignedCACertificate(subject: CordaX500Name, + fun createSelfSignedCACertificate(subject: X500Principal, keyPair: KeyPair, - validityWindow: Pair = DEFAULT_VALIDITY_WINDOW): X509CertificateHolder { + validityWindow: Pair = DEFAULT_VALIDITY_WINDOW): X509Certificate { val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second) - return createCertificate(CertificateType.ROOT_CA, subject.x500Name, keyPair, subject.x500Name, keyPair.public, window) - } - - /** - * Create a X509 v3 certificate for use as a CA or for TLS. This does not require a [CordaX500Name] because the - * constraints are inappropriate for TLS/CA usage, however as a result this is unsuitable for Corda identity - * certificate generation. - * - * @param issuerCertificate The Public certificate of the root CA above this used to sign it. - * @param issuerKeyPair The KeyPair of the root CA above this used to sign it. - * @param subject subject of the generated certificate. - * @param subjectPublicKey subject's public key. - * @param validityWindow The certificate's validity window. Default to [DEFAULT_VALIDITY_WINDOW] if not provided. - * @return A data class is returned containing the new intermediate CA Cert and its KeyPair for signing downstream certificates. - * Note the generated certificate tree is capped at max depth of 1 below this to be in line with commercially available certificates. - */ - @JvmStatic - fun createCertificate(certificateType: CertificateType, - issuerCertificate: X509CertificateHolder, - issuerKeyPair: KeyPair, - subject: CordaX500Name, - subjectPublicKey: PublicKey, - validityWindow: Pair = DEFAULT_VALIDITY_WINDOW, - nameConstraints: NameConstraints? = null): X509CertificateHolder { - return createCertificate(certificateType, issuerCertificate, issuerKeyPair, subject.x500Name, subjectPublicKey, validityWindow, nameConstraints) - } - - /** - * Create a X509 v3 certificate for use as a CA or for TLS. This does not require a [CordaX500Name] because the - * constraints are inappropriate for TLS/CA usage, however as a result this is unsuitable for Corda identity - * certificate generation. - * - * @param issuerCertificate The Public certificate of the root CA above this used to sign it. - * @param issuerKeyPair The KeyPair of the root CA above this used to sign it. - * @param subject subject of the generated certificate. - * @param subjectPublicKey subject's public key. - * @param validityWindow The certificate's validity window. Default to [DEFAULT_VALIDITY_WINDOW] if not provided. - * @return A data class is returned containing the new intermediate CA Cert and its KeyPair for signing downstream certificates. - * Note the generated certificate tree is capped at max depth of 1 below this to be in line with commercially available certificates. - */ - @JvmStatic - fun createCertificate(certificateType: CertificateType, - issuerCertificate: X509CertificateHolder, - issuerKeyPair: KeyPair, - subject: X500Name, - subjectPublicKey: PublicKey, - validityWindow: Pair = DEFAULT_VALIDITY_WINDOW, - nameConstraints: NameConstraints? = null): X509CertificateHolder { - val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second, issuerCertificate) - return createCertificate(certificateType, issuerCertificate.subject, issuerKeyPair, subject, subjectPublicKey, window, nameConstraints) + return createCertificate(CertificateType.ROOT_CA, subject, keyPair, subject, keyPair.public, window) } @Throws(CertPathValidatorException::class) @@ -153,13 +107,13 @@ object X509Utilities { /** * Helper method to store a .pem/.cer format file copy of a certificate if required for import into a PC/Mac, or for inspection. - * @param x509Certificate certificate to save. + * @param certificate certificate to save. * @param file Target file. */ @JvmStatic - fun saveCertificateAsPEMFile(x509Certificate: X509Certificate, file: Path) { + fun saveCertificateAsPEMFile(certificate: X509Certificate, file: Path) { JcaPEMWriter(file.writer()).use { - it.writeObject(x509Certificate) + it.writeObject(certificate) } } @@ -172,9 +126,10 @@ object X509Utilities { fun loadCertificateFromPEMFile(file: Path): X509Certificate { return file.reader().use { val pemObject = PemReader(it).readPemObject() - val certHolder = X509CertificateHolder(pemObject.content) - certHolder.isValidOn(Date()) - certHolder.cert + X509CertificateHolder(pemObject.content).run { + isValidOn(Date()) + toJca() + } } } @@ -187,38 +142,18 @@ object X509Utilities { * @param validityWindow the time period the certificate is valid for. * @param nameConstraints any name constraints to impose on certificates signed by the generated certificate. */ - fun createCertificate(certificateType: CertificateType, - issuer: CordaX500Name, - subject: CordaX500Name, - subjectPublicKey: PublicKey, - validityWindow: Pair, - nameConstraints: NameConstraints? = null): X509v3CertificateBuilder { - return createCertificate(certificateType, issuer.x500Name, subject.x500Name, subjectPublicKey, validityWindow, nameConstraints) - } - - /** - * Build a partial X.509 certificate ready for signing. - * - * @param issuer name of the issuing entity. - * @param subject name of the certificate subject. - * @param subjectPublicKey public key of the certificate subject. - * @param validityWindow the time period the certificate is valid for. - * @param nameConstraints any name constraints to impose on certificates signed by the generated certificate. - */ - internal fun createCertificate(certificateType: CertificateType, - issuer: X500Name, - subject: X500Name, - subjectPublicKey: PublicKey, - validityWindow: Pair, - nameConstraints: NameConstraints? = null): X509v3CertificateBuilder { - + fun createPartialCertificate(certificateType: CertificateType, + issuer: X500Principal, + subject: X500Principal, + subjectPublicKey: PublicKey, + validityWindow: Pair, + nameConstraints: NameConstraints? = null): X509v3CertificateBuilder { val serial = BigInteger.valueOf(random63BitValue()) val keyPurposes = DERSequence(ASN1EncodableVector().apply { certificateType.purposes.forEach { add(it) } }) val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(subjectPublicKey.encoded)) val role = certificateType.role - val builder = JcaX509v3CertificateBuilder(issuer, serial, validityWindow.first, validityWindow.second, - subject, subjectPublicKey) + val builder = JcaX509v3CertificateBuilder(issuer, serial, validityWindow.first, validityWindow.second, subject, subjectPublicKey) .addExtension(Extension.subjectKeyIdentifier, false, BcX509ExtensionUtils().createSubjectKeyIdentifier(subjectPublicKeyInfo)) .addExtension(Extension.basicConstraints, certificateType.isCA, BasicConstraints(certificateType.isCA)) .addExtension(Extension.keyUsage, false, certificateType.keyUsage) @@ -234,6 +169,37 @@ object X509Utilities { return builder } + /** + * Create a X509 v3 certificate using the given issuer certificate and key pair. + * + * @param issuerCertificate The Public certificate of the root CA above this used to sign it. + * @param issuerKeyPair The KeyPair of the root CA above this used to sign it. + * @param subject subject of the generated certificate. + * @param subjectPublicKey subject's public key. + * @param validityWindow The certificate's validity window. Default to [DEFAULT_VALIDITY_WINDOW] if not provided. + * @return A data class is returned containing the new intermediate CA Cert and its KeyPair for signing downstream certificates. + * Note the generated certificate tree is capped at max depth of 1 below this to be in line with commercially available certificates. + */ + @JvmStatic + fun createCertificate(certificateType: CertificateType, + issuerCertificate: X509Certificate, + issuerKeyPair: KeyPair, + subject: X500Principal, + subjectPublicKey: PublicKey, + validityWindow: Pair = DEFAULT_VALIDITY_WINDOW, + nameConstraints: NameConstraints? = null): X509Certificate { + val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second, issuerCertificate) + return createCertificate( + certificateType, + issuerCertificate.subjectX500Principal, + issuerKeyPair, + subject, + subjectPublicKey, + window, + nameConstraints + ) + } + /** * Build and sign an X.509 certificate with the given signer. * @@ -245,15 +211,16 @@ object X509Utilities { * @param nameConstraints any name constraints to impose on certificates signed by the generated certificate. */ fun createCertificate(certificateType: CertificateType, - issuer: X500Name, + issuer: X500Principal, issuerSigner: ContentSigner, - subject: CordaX500Name, + subject: X500Principal, subjectPublicKey: PublicKey, validityWindow: Pair, - nameConstraints: NameConstraints? = null): X509CertificateHolder { - val builder = createCertificate(certificateType, issuer, subject.x500Name, subjectPublicKey, validityWindow, nameConstraints) - return builder.build(issuerSigner).apply { + nameConstraints: NameConstraints? = null): X509Certificate { + val builder = createPartialCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints) + return builder.build(issuerSigner).run { require(isValidOn(Date())) + toJca() } } @@ -268,39 +235,47 @@ object X509Utilities { * @param nameConstraints any name constraints to impose on certificates signed by the generated certificate. */ fun createCertificate(certificateType: CertificateType, - issuer: X500Name, + issuer: X500Principal, issuerKeyPair: KeyPair, - subject: X500Name, + subject: X500Principal, subjectPublicKey: PublicKey, validityWindow: Pair, - nameConstraints: NameConstraints? = null): X509CertificateHolder { + nameConstraints: NameConstraints? = null): X509Certificate { val signatureScheme = Crypto.findSignatureScheme(issuerKeyPair.private) val provider = Crypto.findProvider(signatureScheme.providerName) - val builder = createCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints) - val signer = ContentSignerBuilder.build(signatureScheme, issuerKeyPair.private, provider) - return builder.build(signer).apply { + val builder = createPartialCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints) + return builder.build(signer).run { require(isValidOn(Date())) require(isSignatureValid(JcaContentVerifierProviderBuilder().build(issuerKeyPair.public))) + toJca() } } /** * Create certificate signing request using provided information. */ - private fun createCertificateSigningRequest(subject: CordaX500Name, + private fun createCertificateSigningRequest(subject: X500Principal, email: String, keyPair: KeyPair, signatureScheme: SignatureScheme): PKCS10CertificationRequest { val signer = ContentSignerBuilder.build(signatureScheme, keyPair.private, Crypto.findProvider(signatureScheme.providerName)) - return JcaPKCS10CertificationRequestBuilder(subject.x500Name, keyPair.public).addAttribute(BCStyle.E, DERUTF8String(email)).build(signer) + return JcaPKCS10CertificationRequestBuilder(subject, keyPair.public).addAttribute(BCStyle.E, DERUTF8String(email)).build(signer) } - fun createCertificateSigningRequest(subject: CordaX500Name, email: String, keyPair: KeyPair): PKCS10CertificationRequest { + fun createCertificateSigningRequest(subject: X500Principal, email: String, keyPair: KeyPair): PKCS10CertificationRequest { return createCertificateSigningRequest(subject, email, keyPair, DEFAULT_TLS_SIGNATURE_SCHEME) } } +/** + * Convert a [X509Certificate] into Bouncycastle's [X509CertificateHolder]. + * + * NOTE: To avoid unnecessary copying use [X509Certificate] where possible. + */ +fun X509Certificate.toBc() = X509CertificateHolder(encoded) +fun X509CertificateHolder.toJca(): X509Certificate = X509CertificateFactory().generateCertificate(encoded.inputStream()) + /** * Wraps a [CertificateFactory] to remove boilerplate. It's unclear whether [CertificateFactory] is threadsafe so best * so assume this class is not. @@ -396,4 +371,4 @@ enum class CertificateType(val keyUsage: KeyUsage, vararg val purposes: KeyPurpo ) } -data class CertificateAndKeyPair(val certificate: X509CertificateHolder, val keyPair: KeyPair) +data class CertificateAndKeyPair(val certificate: X509Certificate, val keyPair: KeyPair) diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt index 47c2c8b9ef..af38597bfe 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt @@ -4,10 +4,7 @@ import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto.EDDSA_ED25519_SHA512 import net.corda.core.crypto.Crypto.generateKeyPair import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.cert import net.corda.core.internal.div -import net.corda.core.internal.toTypedArray -import net.corda.core.internal.x500Name import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize @@ -23,12 +20,9 @@ import net.corda.testing.BOB_NAME import net.corda.testing.TestIdentity import net.corda.testing.internal.createDevIntermediateCaCertPath import org.assertj.core.api.Assertions.assertThat -import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x509.BasicConstraints import org.bouncycastle.asn1.x509.Extension import org.bouncycastle.asn1.x509.KeyUsage -import org.bouncycastle.cert.X509CertificateHolder -import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder @@ -42,18 +36,16 @@ import java.security.SecureRandom import java.security.cert.CertPath import java.security.cert.X509Certificate import java.util.* -import java.util.stream.Stream import javax.net.ssl.* +import javax.security.auth.x500.X500Principal import kotlin.concurrent.thread import kotlin.test.* class X509UtilitiesTest { private companion object { val ALICE = TestIdentity(ALICE_NAME, 70).party - val bob = TestIdentity(BOB_NAME, 80) + val BOB = TestIdentity(BOB_NAME, 80) val MEGA_CORP = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).party - val BOB get() = bob.party - val BOB_PUBKEY get() = bob.publicKey val CIPHER_SUITES = arrayOf( "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", @@ -63,27 +55,30 @@ class X509UtilitiesTest { @Rule @JvmField - val tempFolder: TemporaryFolder = TemporaryFolder() + val tempFolder = TemporaryFolder() @Test fun `create valid self-signed CA certificate`() { val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val caCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Test Cert", organisation = "R3 Ltd", locality = "London", country = "GB"), caKey) - assertEquals(X500Name("CN=Test Cert,O=R3 Ltd,L=London,C=GB"), caCert.subject) // using our subject common name - assertEquals(caCert.issuer, caCert.subject) //self-signed - caCert.isValidOn(Date()) // throws on verification problems - caCert.isSignatureValid(JcaContentVerifierProviderBuilder().build(caKey.public)) // throws on verification problems - val basicConstraints = BasicConstraints.getInstance(caCert.getExtension(Extension.basicConstraints).parsedValue) - val keyUsage = KeyUsage.getInstance(caCert.getExtension(Extension.keyUsage).parsedValue) - assertFalse { keyUsage.hasUsages(5) } // Bit 5 == keyCertSign according to ASN.1 spec (see full comment on KeyUsage property) - assertNull(basicConstraints.pathLenConstraint) // No length constraint specified on this CA certificate + val subject = X500Principal("CN=Test Cert,O=R3 Ltd,L=London,C=GB") + val caCert = X509Utilities.createSelfSignedCACertificate(subject, caKey) + assertEquals(subject, caCert.subjectX500Principal) // using our subject common name + assertEquals(caCert.issuerX500Principal, caCert.subjectX500Principal) //self-signed + caCert.checkValidity(Date()) // throws on verification problems + caCert.verify(caKey.public) // throws on verification problems + caCert.toBc().run { + val basicConstraints = BasicConstraints.getInstance(getExtension(Extension.basicConstraints).parsedValue) + val keyUsage = KeyUsage.getInstance(getExtension(Extension.keyUsage).parsedValue) + assertFalse { keyUsage.hasUsages(5) } // Bit 5 == keyCertSign according to ASN.1 spec (see full comment on KeyUsage property) + assertNull(basicConstraints.pathLenConstraint) // No length constraint specified on this CA certificate + } } @Test fun `load and save a PEM file certificate`() { val tmpCertificateFile = tempFile("cacert.pem") val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val caCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Test Cert", organisation = "R3 Ltd", locality = "London", country = "GB"), caKey).cert + val caCert = X509Utilities.createSelfSignedCACertificate(X500Principal("CN=Test Cert,O=R3 Ltd,L=London,C=GB"), caKey) X509Utilities.saveCertificateAsPEMFile(caCert, tmpCertificateFile) val readCertificate = X509Utilities.loadCertificateFromPEMFile(tmpCertificateFile) assertEquals(caCert, readCertificate) @@ -92,18 +87,20 @@ class X509UtilitiesTest { @Test fun `create valid server certificate chain`() { val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val caCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Test CA Cert", organisation = "R3 Ltd", locality = "London", country = "GB"), caKey) - val subject = CordaX500Name(commonName = "Server Cert", organisation = "R3 Ltd", locality = "London", country = "GB") + val caCert = X509Utilities.createSelfSignedCACertificate(X500Principal("CN=Test CA Cert,O=R3 Ltd,L=London,C=GB"), caKey) + val subject = X500Principal("CN=Server Cert,O=R3 Ltd,L=London,C=GB") val keyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val serverCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, caKey, subject, keyPair.public) - assertEquals(X500Name("C=GB,L=London,O=R3 Ltd,CN=Server Cert"), serverCert.subject) // using our subject common name - assertEquals(caCert.issuer, serverCert.issuer) // Issued by our CA cert - serverCert.isValidOn(Date()) // throws on verification problems - serverCert.isSignatureValid(JcaContentVerifierProviderBuilder().build(caKey.public)) // throws on verification problems - val basicConstraints = BasicConstraints.getInstance(serverCert.getExtension(Extension.basicConstraints).parsedValue) - val keyUsage = KeyUsage.getInstance(serverCert.getExtension(Extension.keyUsage).parsedValue) - assertFalse { keyUsage.hasUsages(5) } // Bit 5 == keyCertSign according to ASN.1 spec (see full comment on KeyUsage property) - assertNull(basicConstraints.pathLenConstraint) // Non-CA certificate + assertEquals(subject, serverCert.subjectX500Principal) // using our subject common name + assertEquals(caCert.issuerX500Principal, serverCert.issuerX500Principal) // Issued by our CA cert + serverCert.checkValidity(Date()) // throws on verification problems + serverCert.verify(caKey.public) // throws on verification problems + serverCert.toBc().run { + val basicConstraints = BasicConstraints.getInstance(getExtension(Extension.basicConstraints).parsedValue) + val keyUsage = KeyUsage.getInstance(getExtension(Extension.keyUsage).parsedValue) + assertFalse { keyUsage.hasUsages(5) } // Bit 5 == keyCertSign according to ASN.1 spec (see full comment on KeyUsage property) + assertNull(basicConstraints.pathLenConstraint) // Non-CA certificate + } } @Test @@ -111,15 +108,14 @@ class X509UtilitiesTest { val tmpKeyStore = tempFile("keystore.jks") val keyPair = generateKeyPair(EDDSA_ED25519_SHA512) - val testName = CordaX500Name(commonName = "Test", organisation = "R3 Ltd", locality = "London", country = "GB") + val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB") val selfSignCert = X509Utilities.createSelfSignedCACertificate(testName, keyPair) - assertTrue(Arrays.equals(selfSignCert.subjectPublicKeyInfo.encoded, keyPair.public.encoded)) + assertTrue(Arrays.equals(selfSignCert.publicKey.encoded, keyPair.public.encoded)) // Save the EdDSA private key with self sign cert in the keystore. val keyStore = loadOrCreateKeyStore(tmpKeyStore, "keystorepass") - keyStore.setKeyEntry("Key", keyPair.private, "password".toCharArray(), - Stream.of(selfSignCert).map { it.cert }.toTypedArray()) + keyStore.setKeyEntry("Key", keyPair.private, "password".toCharArray(), arrayOf(selfSignCert)) keyStore.save(tmpKeyStore, "keystorepass") // Load the keystore from file and make sure keys are intact. @@ -137,15 +133,14 @@ class X509UtilitiesTest { fun `signing EdDSA key with EcDSA certificate`() { val tmpKeyStore = tempFile("keystore.jks") val ecDSAKey = generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256) - val testName = CordaX500Name(commonName = "Test", organisation = "R3 Ltd", locality = "London", country = "GB") + val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB") val ecDSACert = X509Utilities.createSelfSignedCACertificate(testName, ecDSAKey) val edDSAKeypair = generateKeyPair(EDDSA_ED25519_SHA512) - val edDSACert = X509Utilities.createCertificate(CertificateType.TLS, ecDSACert, ecDSAKey, X500Name("CN=TestEdDSA"), edDSAKeypair.public) + val edDSACert = X509Utilities.createCertificate(CertificateType.TLS, ecDSACert, ecDSAKey, BOB.name.x500Principal, edDSAKeypair.public) // Save the EdDSA private key with cert chains. val keyStore = loadOrCreateKeyStore(tmpKeyStore, "keystorepass") - keyStore.setKeyEntry("Key", edDSAKeypair.private, "password".toCharArray(), - Stream.of(ecDSACert, edDSACert).map { it.cert }.toTypedArray()) + keyStore.setKeyEntry("Key", edDSAKeypair.private, "password".toCharArray(), arrayOf(ecDSACert, edDSACert)) keyStore.save(tmpKeyStore, "keystorepass") // Load the keystore from file and make sure keys are intact. @@ -179,23 +174,22 @@ class X509UtilitiesTest { val serverKeyStore = loadKeyStore(sslConfig.nodeKeystore, sslConfig.keyStorePassword) val (serverCert, serverKeyPair) = serverKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, sslConfig.keyStorePassword) - serverCert.cert.checkValidity() - serverCert.cert.verify(intermediateCa.certificate.cert.publicKey) - assertThat(CordaX500Name.parse(serverCert.subject.toString())).isEqualTo(MEGA_CORP.name) + serverCert.checkValidity() + serverCert.verify(intermediateCa.certificate.publicKey) + assertThat(CordaX500Name.build(serverCert.subjectX500Principal)).isEqualTo(MEGA_CORP.name) // Load back SSL certificate val sslKeyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword) val (sslCert) = sslKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_TLS, sslConfig.keyStorePassword) - sslCert.cert.checkValidity() - sslCert.cert.verify(serverCert.cert.publicKey) - assertThat(CordaX500Name.parse(sslCert.subject.toString())).isEqualTo(MEGA_CORP.name) + sslCert.checkValidity() + sslCert.verify(serverCert.publicKey) + assertThat(CordaX500Name.build(sslCert.subjectX500Principal)).isEqualTo(MEGA_CORP.name) // Now sign something with private key and verify against certificate public key val testData = "123456".toByteArray() val signature = Crypto.doSign(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME, serverKeyPair.private, testData) - val publicKey = Crypto.toSupportedPublicKey(serverCert.subjectPublicKeyInfo) - assertTrue { Crypto.isValid(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME, publicKey, signature, testData) } + assertTrue { Crypto.isValid(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME, serverCert.publicKey, signature, testData) } } @Test @@ -210,7 +204,7 @@ class X509UtilitiesTest { // Generate server cert and private key and populate another keystore suitable for SSL sslConfig.createDevKeyStores(rootCa.certificate, intermediateCa, MEGA_CORP.name) - sslConfig.createTrustStore(rootCa.certificate.cert) + sslConfig.createTrustStore(rootCa.certificate) val keyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword) val trustStore = loadKeyStore(sslConfig.trustStoreFile, sslConfig.trustStorePassword) @@ -303,10 +297,10 @@ class X509UtilitiesTest { @Test fun `get correct private key type from Keystore`() { val keyPair = generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256) - val testName = CordaX500Name(commonName = "Test", organisation = "R3 Ltd", locality = "London", country = "GB") + val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB") val selfSignCert = X509Utilities.createSelfSignedCACertificate(testName, keyPair) val keyStore = loadOrCreateKeyStore(tempFile("testKeystore.jks"), "keystorepassword") - keyStore.setKeyEntry("Key", keyPair.private, "keypassword".toCharArray(), arrayOf(selfSignCert.cert)) + keyStore.setKeyEntry("Key", keyPair.private, "keypassword".toCharArray(), arrayOf(selfSignCert)) val keyFromKeystore = keyStore.getKey("Key", "keypassword".toCharArray()) val keyFromKeystoreCasted = keyStore.getSupportedKey("Key", "keypassword") @@ -316,7 +310,7 @@ class X509UtilitiesTest { } @Test - fun `serialize - deserialize X509CertififcateHolder`() { + fun `serialize - deserialize X509Certififcate`() { val factory = SerializationFactoryImpl().apply { registerScheme(KryoServerSerializationScheme()) } val context = SerializationContextImpl(KryoHeaderV0_1, javaClass.classLoader, @@ -324,9 +318,9 @@ class X509UtilitiesTest { emptyMap(), true, SerializationContext.UseCase.P2P) - val expected: X509CertificateHolder = X509Utilities.createSelfSignedCACertificate(ALICE.name, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) + val expected = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Principal, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) val serialized = expected.serialize(factory, context).bytes - val actual: X509CertificateHolder = serialized.deserialize(factory, context) + val actual = serialized.deserialize(factory, context) assertEquals(expected, actual) } @@ -340,9 +334,9 @@ class X509UtilitiesTest { true, SerializationContext.UseCase.P2P) val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val rootCACert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootCAKey) - val certificate = X509Utilities.createCertificate(CertificateType.TLS, rootCACert, rootCAKey, BOB.name.x500Name, BOB_PUBKEY) - val expected = X509CertificateFactory().generateCertPath(certificate.cert, rootCACert.cert) + val rootCACert = X509Utilities.createSelfSignedCACertificate(ALICE_NAME.x500Principal, rootCAKey) + val certificate = X509Utilities.createCertificate(CertificateType.TLS, rootCACert, rootCAKey, BOB_NAME.x500Principal, BOB.publicKey) + val expected = X509CertificateFactory().generateCertPath(certificate, rootCACert) val serialized = expected.serialize(factory, context).bytes val actual: CertPath = serialized.deserialize(factory, context) assertEquals(expected, actual) diff --git a/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt b/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt index 17cad81d22..4a3992213e 100644 --- a/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt @@ -1,8 +1,6 @@ package net.corda.node import net.corda.core.crypto.Crypto -import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.cert import net.corda.core.internal.div import net.corda.core.utilities.getOrThrow import net.corda.node.services.config.configureDevKeyAndTrustStores @@ -13,6 +11,10 @@ import net.corda.testing.driver.driver import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Test import java.nio.file.Path +import javax.security.auth.x500.X500Principal +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertTrue class NodeKeystoreCheckTest { @Test @@ -50,10 +52,10 @@ class NodeKeystoreCheckTest { // Self signed root val badRootKeyPair = Crypto.generateKeyPair() - val badRoot = X509Utilities.createSelfSignedCACertificate(CordaX500Name("Bad Root", "Lodnon", "GB"), badRootKeyPair) + val badRoot = X509Utilities.createSelfSignedCACertificate(X500Principal("O=Bad Root,L=Lodnon,C=GB"), badRootKeyPair) val nodeCA = keystore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, config.keyStorePassword) - val badNodeCACert = X509Utilities.createCertificate(CertificateType.NODE_CA, badRoot, badRootKeyPair, ALICE_NAME, nodeCA.keyPair.public) - keystore.setKeyEntry(X509Utilities.CORDA_CLIENT_CA, nodeCA.keyPair.private, config.keyStorePassword.toCharArray(), arrayOf(badNodeCACert.cert, badRoot.cert)) + val badNodeCACert = X509Utilities.createCertificate(CertificateType.NODE_CA, badRoot, badRootKeyPair, ALICE_NAME.x500Principal, nodeCA.keyPair.public) + keystore.setKeyEntry(X509Utilities.CORDA_CLIENT_CA, nodeCA.keyPair.private, config.keyStorePassword.toCharArray(), arrayOf(badNodeCACert, badRoot)) keystore.save(config.nodeKeystore, config.keyStorePassword) assertThatThrownBy { diff --git a/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt b/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt index 871c7de400..88c299dd41 100644 --- a/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt @@ -59,10 +59,10 @@ class ProtonWrapperTests { amqpClient.start() val serverConnect = serverConnected.get() assertEquals(true, serverConnect.connected) - assertEquals(BOB_NAME, CordaX500Name.parse(serverConnect.remoteCert!!.subject.toString())) + assertEquals(BOB_NAME, CordaX500Name.build(serverConnect.remoteCert!!.subjectX500Principal)) val clientConnect = clientConnected.get() assertEquals(true, clientConnect.connected) - assertEquals(ALICE_NAME, CordaX500Name.parse(clientConnect.remoteCert!!.subject.toString())) + assertEquals(ALICE_NAME, CordaX500Name.build(clientConnect.remoteCert!!.subjectX500Principal)) val msg = amqpClient.createMessage("Test".toByteArray(), "p2p.inbound", ALICE_NAME.toString(), @@ -102,10 +102,10 @@ class ProtonWrapperTests { amqpClient.start() val serverConn1 = serverConnected.get() assertEquals(true, serverConn1.connected) - assertEquals(BOB_NAME, CordaX500Name.parse(serverConn1.remoteCert!!.subject.toString())) + assertEquals(BOB_NAME, CordaX500Name.build(serverConn1.remoteCert!!.subjectX500Principal)) val connState1 = clientConnected.next() assertEquals(true, connState1.connected) - assertEquals(ALICE_NAME, CordaX500Name.parse(connState1.remoteCert!!.subject.toString())) + assertEquals(ALICE_NAME, CordaX500Name.build(connState1.remoteCert!!.subjectX500Principal)) assertEquals(serverPort, connState1.remoteAddress.port) // Fail over @@ -116,10 +116,10 @@ class ProtonWrapperTests { assertEquals(serverPort, connState2.remoteAddress.port) val serverConn2 = serverConnected2.get() assertEquals(true, serverConn2.connected) - assertEquals(BOB_NAME, CordaX500Name.parse(serverConn2.remoteCert!!.subject.toString())) + assertEquals(BOB_NAME, CordaX500Name.build(serverConn2.remoteCert!!.subjectX500Principal)) val connState3 = clientConnected.next() assertEquals(true, connState3.connected) - assertEquals(ALICE_NAME, CordaX500Name.parse(connState3.remoteCert!!.subject.toString())) + assertEquals(ALICE_NAME, CordaX500Name.build(connState3.remoteCert!!.subjectX500Principal)) assertEquals(serverPort2, connState3.remoteAddress.port) // Fail back @@ -130,10 +130,10 @@ class ProtonWrapperTests { assertEquals(serverPort2, connState4.remoteAddress.port) val serverConn3 = serverConnected.get() assertEquals(true, serverConn3.connected) - assertEquals(BOB_NAME, CordaX500Name.parse(serverConn3.remoteCert!!.subject.toString())) + assertEquals(BOB_NAME, CordaX500Name.build(serverConn3.remoteCert!!.subjectX500Principal)) val connState5 = clientConnected.next() assertEquals(true, connState5.connected) - assertEquals(ALICE_NAME, CordaX500Name.parse(connState5.remoteCert!!.subject.toString())) + assertEquals(ALICE_NAME, CordaX500Name.build(connState5.remoteCert!!.subjectX500Principal)) assertEquals(serverPort, connState5.remoteAddress.port) } finally { amqpClient.close() @@ -149,7 +149,7 @@ class ProtonWrapperTests { val clientConnected = amqpClient.onConnection.toFuture() amqpClient.start() assertEquals(true, clientConnected.get().connected) - assertEquals(CHARLIE_NAME, CordaX500Name.parse(clientConnected.get().remoteCert!!.subject.toString())) + assertEquals(CHARLIE_NAME, CordaX500Name.build(clientConnected.get().remoteCert!!.subjectX500Principal)) val artemis = artemisClient.started!! val sendAddress = "p2p.inbound" artemis.session.createQueue(sendAddress, RoutingType.MULTICAST, "queue", true) @@ -180,13 +180,13 @@ class ProtonWrapperTests { amqpClient1.start() val connection1 = connectionEvents.next() assertEquals(true, connection1.connected) - val connection1ID = CordaX500Name.parse(connection1.remoteCert!!.subject.toString()) + val connection1ID = CordaX500Name.build(connection1.remoteCert!!.subjectX500Principal) assertEquals("client 0", connection1ID.organisationUnit) val source1 = connection1.remoteAddress amqpClient2.start() val connection2 = connectionEvents.next() assertEquals(true, connection2.connected) - val connection2ID = CordaX500Name.parse(connection2.remoteCert!!.subject.toString()) + val connection2ID = CordaX500Name.build(connection2.remoteCert!!.subjectX500Principal) assertEquals("client 1", connection2ID.organisationUnit) val source2 = connection2.remoteAddress // Stopping one shouldn't disconnect the other @@ -207,7 +207,7 @@ class ProtonWrapperTests { amqpClient1.start() val connection5 = connectionEvents.next() assertEquals(true, connection5.connected) - val connection5ID = CordaX500Name.parse(connection5.remoteCert!!.subject.toString()) + val connection5ID = CordaX500Name.build(connection5.remoteCert!!.subjectX500Principal) assertEquals("client 0", connection5ID.organisationUnit) assertEquals(true, amqpClient1.connected) assertEquals(false, amqpClient2.connected) @@ -252,7 +252,8 @@ class ProtonWrapperTests { val clientTruststore = loadKeyStore(clientConfig.trustStoreFile, clientConfig.trustStorePassword) val clientKeystore = loadKeyStore(clientConfig.sslKeystore, clientConfig.keyStorePassword) - val amqpClient = AMQPClient(listOf(NetworkHostAndPort("localhost", serverPort), + return AMQPClient( + listOf(NetworkHostAndPort("localhost", serverPort), NetworkHostAndPort("localhost", serverPort2), NetworkHostAndPort("localhost", artemisPort)), setOf(ALICE_NAME, CHARLIE_NAME), @@ -261,7 +262,6 @@ class ProtonWrapperTests { clientKeystore, clientConfig.keyStorePassword, clientTruststore, true) - return amqpClient } private fun createSharedThreadsClient(sharedEventGroup: EventLoopGroup, id: Int): AMQPClient { @@ -275,14 +275,14 @@ class ProtonWrapperTests { val clientTruststore = loadKeyStore(clientConfig.trustStoreFile, clientConfig.trustStorePassword) val clientKeystore = loadKeyStore(clientConfig.sslKeystore, clientConfig.keyStorePassword) - val amqpClient = AMQPClient(listOf(NetworkHostAndPort("localhost", serverPort)), + return AMQPClient( + listOf(NetworkHostAndPort("localhost", serverPort)), setOf(ALICE_NAME), PEER_USER, PEER_USER, clientKeystore, clientConfig.keyStorePassword, clientTruststore, true, sharedEventGroup) - return amqpClient } private fun createServer(port: Int, name: CordaX500Name = ALICE_NAME): AMQPServer { @@ -296,14 +296,13 @@ class ProtonWrapperTests { val serverTruststore = loadKeyStore(serverConfig.trustStoreFile, serverConfig.trustStorePassword) val serverKeystore = loadKeyStore(serverConfig.sslKeystore, serverConfig.keyStorePassword) - val amqpServer = AMQPServer("0.0.0.0", + return AMQPServer( + "0.0.0.0", port, PEER_USER, PEER_USER, serverKeystore, serverConfig.keyStorePassword, serverTruststore) - return amqpServer } - -} \ No newline at end of file +} diff --git a/node/src/integration-test/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt b/node/src/integration-test/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt index 2fe371202a..17544c8bd0 100644 --- a/node/src/integration-test/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt @@ -2,9 +2,7 @@ package net.corda.node.utilities.registration import net.corda.core.crypto.Crypto import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.cert import net.corda.core.internal.concurrent.transpose -import net.corda.core.internal.toX509CertHolder import net.corda.core.messaging.startFlow import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.OpaqueBytes @@ -19,7 +17,7 @@ import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_CA import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_INTERMEDIATE_CA import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA -import net.corda.testing.ROOT_CA +import net.corda.testing.DEV_ROOT_CA import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.driver.PortAllocation @@ -40,8 +38,10 @@ import java.security.KeyPair import java.security.cert.CertPath import java.security.cert.CertPathValidatorException import java.security.cert.Certificate +import java.security.cert.X509Certificate import java.util.zip.ZipEntry import java.util.zip.ZipOutputStream +import javax.security.auth.x500.X500Principal import javax.ws.rs.* import javax.ws.rs.core.MediaType import javax.ws.rs.core.Response @@ -58,14 +58,13 @@ class NodeRegistrationTest { val testSerialization = SerializationEnvironmentRule(true) private val portAllocation = PortAllocation.Incremental(13000) - private val registrationHandler = RegistrationHandler(ROOT_CA) - + private val registrationHandler = RegistrationHandler(DEV_ROOT_CA) private lateinit var server: NetworkMapServer private lateinit var serverHostAndPort: NetworkHostAndPort @Before fun startServer() { - server = NetworkMapServer(1.minutes, portAllocation.nextHostAndPort(), ROOT_CA, "localhost", registrationHandler) + server = NetworkMapServer(1.minutes, portAllocation.nextHostAndPort(), DEV_ROOT_CA, "localhost", registrationHandler) serverHostAndPort = server.start() } @@ -79,7 +78,7 @@ class NodeRegistrationTest { val compatibilityZone = CompatibilityZoneParams( URL("http://$serverHostAndPort"), publishNotaries = { server.networkParameters = testNetworkParameters(it) }, - rootCert = ROOT_CA.certificate.cert) + rootCert = DEV_ROOT_CA.certificate) internalDriver( portAllocation = portAllocation, compatibilityZone = compatibilityZone, @@ -115,12 +114,12 @@ class NodeRegistrationTest { @Test fun `node registration wrong root cert`() { val someRootCert = X509Utilities.createSelfSignedCACertificate( - CordaX500Name("Integration Test Corda Node Root CA", "R3 Ltd", "London", "GB"), + X500Principal("CN=Integration Test Corda Node Root CA,O=R3 Ltd,L=London,C=GB"), Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) val compatibilityZone = CompatibilityZoneParams( URL("http://$serverHostAndPort"), publishNotaries = { server.networkParameters = testNetworkParameters(it) }, - rootCert = someRootCert.cert) + rootCert = someRootCert) internalDriver( portAllocation = portAllocation, compatibilityZone = compatibilityZone, @@ -148,7 +147,7 @@ class RegistrationHandler(private val rootCertAndKeyPair: CertificateAndKeyPair) val (certPath, name) = createSignedClientCertificate( certificationRequest, rootCertAndKeyPair.keyPair, - arrayOf(rootCertAndKeyPair.certificate.cert)) + arrayOf(rootCertAndKeyPair.certificate)) require(!name.organisation.contains("\\s".toRegex())) { "Whitespace in the organisation name not supported" } certPaths[name.organisation] = certPath return Response.ok(name.organisation).build() @@ -182,12 +181,12 @@ class RegistrationHandler(private val rootCertAndKeyPair: CertificateAndKeyPair) val name = CordaX500Name.parse(request.subject.toString()) val nodeCaCert = X509Utilities.createCertificate( CertificateType.NODE_CA, - caCertPath.first().toX509CertHolder(), + caCertPath[0] as X509Certificate , caKeyPair, - name, + name.x500Principal, request.publicKey, nameConstraints = null) - val certPath = X509CertificateFactory().generateCertPath(nodeCaCert.cert, *caCertPath) + val certPath = X509CertificateFactory().generateCertPath(nodeCaCert, *caCertPath) return Pair(certPath, name) } } diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt index f8da5302a4..8252173ca0 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt @@ -94,22 +94,34 @@ class MQSecurityAsNodeTest : MQSecurityTest() { javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass") - val rootCACert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA).toX509CertHolder() + val rootCACert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA) val intermediateCA = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass") - val clientKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) + val clientKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) // Set name constrain to the legal name. val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.x500Name))), arrayOf()) - val clientCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, intermediateCA.certificate, - intermediateCA.keyPair, legalName, clientKey.public, nameConstraints = nameConstraints) - val tlsKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) + val clientCACert = X509Utilities.createCertificate( + CertificateType.INTERMEDIATE_CA, + intermediateCA.certificate, + intermediateCA.keyPair, + legalName.x500Principal, + clientKeyPair.public, + nameConstraints = nameConstraints) + + val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) // Using different x500 name in the TLS cert which is not allowed in the name constraints. - val clientTLSCert = X509Utilities.createCertificate(CertificateType.TLS, clientCACert, clientKey, CordaX500Name("MiniCorp", "London", "GB"), tlsKey.public) + val clientTLSCert = X509Utilities.createCertificate( + CertificateType.TLS, + clientCACert, + clientKeyPair, + CordaX500Name("MiniCorp", "London", "GB").x500Principal, + tlsKeyPair.public) + val keyPass = keyStorePassword.toCharArray() val clientCAKeystore = loadOrCreateKeyStore(nodeKeystore, keyStorePassword) clientCAKeystore.addOrReplaceKey( X509Utilities.CORDA_CLIENT_CA, - clientKey.private, + clientKeyPair.private, keyPass, arrayOf(clientCACert, intermediateCA.certificate, rootCACert)) clientCAKeystore.save(nodeKeystore, keyStorePassword) @@ -117,7 +129,7 @@ class MQSecurityAsNodeTest : MQSecurityTest() { val tlsKeystore = loadOrCreateKeyStore(sslKeystore, keyStorePassword) tlsKeystore.addOrReplaceKey( X509Utilities.CORDA_CLIENT_TLS, - tlsKey.private, + tlsKeyPair.private, keyPass, arrayOf(clientTLSCert, clientCACert, intermediateCA.certificate, rootCACert)) tlsKeystore.save(sslKeystore, keyStorePassword) 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 c924c42a91..0edc5a1624 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -698,7 +698,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, val caKeyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword) val trustRoot = trustStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA) val clientCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA) - val caCertificates = arrayOf(identityCert, clientCa.certificate.cert) + val caCertificates = arrayOf(identityCert, clientCa.certificate) return PersistentIdentityService(trustRoot, *caCertificates) } @@ -756,7 +756,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, listOf(certificate) + keyStore.getCertificateChain(privateKeyAlias).drop(1) } else { keyStore.getCertificateChain(privateKeyAlias).let { - check(it[0].toX509CertHolder() == x509Cert) { "Certificates from key store do not line up!" } + check(it[0] == x509Cert) { "Certificates from key store do not line up!" } it.asList() } } diff --git a/node/src/main/kotlin/net/corda/node/internal/protonwrapper/netty/AMQPChannelHandler.kt b/node/src/main/kotlin/net/corda/node/internal/protonwrapper/netty/AMQPChannelHandler.kt index 0f9e6cd607..f31f9fbe88 100644 --- a/node/src/main/kotlin/net/corda/node/internal/protonwrapper/netty/AMQPChannelHandler.kt +++ b/node/src/main/kotlin/net/corda/node/internal/protonwrapper/netty/AMQPChannelHandler.kt @@ -9,7 +9,6 @@ import io.netty.handler.ssl.SslHandler import io.netty.handler.ssl.SslHandshakeCompletionEvent import io.netty.util.ReferenceCountUtil import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.toX509CertHolder import net.corda.core.utilities.debug import net.corda.node.internal.protonwrapper.engine.EventProcessor import net.corda.node.internal.protonwrapper.messages.ReceivedMessage @@ -19,9 +18,9 @@ import org.apache.qpid.proton.engine.ProtonJTransport import org.apache.qpid.proton.engine.Transport import org.apache.qpid.proton.engine.impl.ProtocolTracer import org.apache.qpid.proton.framing.TransportFrame -import org.bouncycastle.cert.X509CertificateHolder import org.slf4j.LoggerFactory import java.net.InetSocketAddress +import java.security.cert.X509Certificate /** * An instance of AMQPChannelHandler sits inside the netty pipeline and controls the socket level lifecycle. @@ -38,20 +37,20 @@ internal class AMQPChannelHandler(private val serverMode: Boolean, private val onReceive: (ReceivedMessage) -> Unit) : ChannelDuplexHandler() { private val log = LoggerFactory.getLogger(allowedRemoteLegalNames?.firstOrNull()?.toString() ?: "AMQPChannelHandler") private lateinit var remoteAddress: InetSocketAddress - private lateinit var localCert: X509CertificateHolder - private lateinit var remoteCert: X509CertificateHolder + private lateinit var localCert: X509Certificate + private lateinit var remoteCert: X509Certificate private var eventProcessor: EventProcessor? = null override fun channelActive(ctx: ChannelHandlerContext) { val ch = ctx.channel() remoteAddress = ch.remoteAddress() as InetSocketAddress val localAddress = ch.localAddress() as InetSocketAddress - log.info("New client connection ${ch.id()} from ${remoteAddress} to ${localAddress}") + log.info("New client connection ${ch.id()} from $remoteAddress to $localAddress") } private fun createAMQPEngine(ctx: ChannelHandlerContext) { val ch = ctx.channel() - eventProcessor = EventProcessor(ch, serverMode, localCert.subject.toString(), remoteCert.subject.toString(), userName, password) + eventProcessor = EventProcessor(ch, serverMode, localCert.subjectX500Principal.toString(), remoteCert.subjectX500Principal.toString(), userName, password) val connection = eventProcessor!!.connection val transport = connection.transport as ProtonJTransport if (trace) { @@ -71,7 +70,7 @@ internal class AMQPChannelHandler(private val serverMode: Boolean, override fun channelInactive(ctx: ChannelHandlerContext) { val ch = ctx.channel() - log.info("Closed client connection ${ch.id()} from ${remoteAddress} to ${ch.localAddress()}") + log.info("Closed client connection ${ch.id()} from $remoteAddress to ${ch.localAddress()}") onClose(Pair(ch as SocketChannel, ConnectionChange(remoteAddress, null, false))) eventProcessor?.close() ctx.fireChannelInactive() @@ -81,12 +80,12 @@ internal class AMQPChannelHandler(private val serverMode: Boolean, if (evt is SslHandshakeCompletionEvent) { if (evt.isSuccess) { val sslHandler = ctx.pipeline().get(SslHandler::class.java) - localCert = sslHandler.engine().session.localCertificates.first().toX509CertHolder() - remoteCert = sslHandler.engine().session.peerCertificates.first().toX509CertHolder() + localCert = sslHandler.engine().session.localCertificates[0] as X509Certificate + remoteCert = sslHandler.engine().session.peerCertificates[0] as X509Certificate try { - val remoteX500Name = CordaX500Name.parse(remoteCert.subject.toString()) + val remoteX500Name = CordaX500Name.build(remoteCert.subjectX500Principal) require(allowedRemoteLegalNames == null || remoteX500Name in allowedRemoteLegalNames) - log.info("handshake completed subject: ${remoteX500Name}") + log.info("handshake completed subject: $remoteX500Name") } catch (ex: IllegalArgumentException) { log.error("Invalid certificate subject", ex) ctx.close() @@ -124,7 +123,7 @@ internal class AMQPChannelHandler(private val serverMode: Boolean, require(inetAddress == remoteAddress) { "Message for incorrect endpoint" } - require(CordaX500Name.parse(msg.destinationLegalName) == CordaX500Name.parse(remoteCert.subject.toString())) { + require(CordaX500Name.parse(msg.destinationLegalName) == CordaX500Name.build(remoteCert.subjectX500Principal)) { "Message for incorrect legal identity" } log.debug { "channel write ${msg.applicationProperties["_AMQ_DUPL_ID"]}" } diff --git a/node/src/main/kotlin/net/corda/node/internal/protonwrapper/netty/ConnectionChange.kt b/node/src/main/kotlin/net/corda/node/internal/protonwrapper/netty/ConnectionChange.kt index a576a25d2b..f0d83a8cf5 100644 --- a/node/src/main/kotlin/net/corda/node/internal/protonwrapper/netty/ConnectionChange.kt +++ b/node/src/main/kotlin/net/corda/node/internal/protonwrapper/netty/ConnectionChange.kt @@ -1,6 +1,6 @@ package net.corda.node.internal.protonwrapper.netty -import org.bouncycastle.cert.X509CertificateHolder import java.net.InetSocketAddress +import java.security.cert.X509Certificate -data class ConnectionChange(val remoteAddress: InetSocketAddress, val remoteCert: X509CertificateHolder?, val connected: Boolean) \ No newline at end of file +data class ConnectionChange(val remoteAddress: InetSocketAddress, val remoteCert: X509Certificate?, val connected: Boolean) \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt index bb1e3da993..f48e8d4022 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt @@ -8,7 +8,6 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.internal.createDirectories import net.corda.core.internal.div import net.corda.core.internal.exists -import net.corda.core.internal.toX509CertHolder import net.corda.nodeapi.internal.config.SSLConfiguration import net.corda.nodeapi.internal.createDevKeyStores import net.corda.nodeapi.internal.crypto.* @@ -54,7 +53,7 @@ fun SSLConfiguration.configureDevKeyAndTrustStores(myLegalName: CordaX500Name) { } if (!sslKeystore.exists() || !nodeKeystore.exists()) { val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass") - val rootCert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA).toX509CertHolder() + val rootCert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA) val intermediateCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass") createDevKeyStores(rootCert, intermediateCa, myLegalName) diff --git a/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt b/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt index 4654473ef1..4876533003 100644 --- a/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt +++ b/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt @@ -4,15 +4,12 @@ import net.corda.core.contracts.PartyAndReference import net.corda.core.crypto.toStringShort import net.corda.core.identity.* import net.corda.core.internal.CertRole -import net.corda.core.internal.cert -import net.corda.core.internal.toX509CertHolder import net.corda.core.node.services.UnknownAnonymousPartyException import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.utilities.contextLogger import net.corda.core.utilities.trace import net.corda.node.services.api.IdentityServiceInternal import net.corda.nodeapi.internal.crypto.X509CertificateFactory -import org.bouncycastle.cert.X509CertificateHolder import java.security.InvalidAlgorithmParameterException import java.security.PublicKey import java.security.cert.* @@ -27,7 +24,7 @@ import javax.annotation.concurrent.ThreadSafe // TODO There is duplicated logic between this and PersistentIdentityService @ThreadSafe class InMemoryIdentityService(identities: Array, - trustRoot: X509CertificateHolder) : SingletonSerializeAsToken(), IdentityServiceInternal { + override val trustRoot: X509Certificate) : SingletonSerializeAsToken(), IdentityServiceInternal { companion object { private val log = contextLogger() } @@ -35,14 +32,12 @@ class InMemoryIdentityService(identities: Array, /** * Certificate store for certificate authority and intermediary certificates. */ - override val caCertStore: CertStore - override val trustRoot = trustRoot.cert - override val trustAnchor: TrustAnchor = TrustAnchor(this.trustRoot, null) + override val caCertStore: CertStore = CertStore.getInstance("Collection", CollectionCertStoreParameters(setOf(trustRoot))) + override val trustAnchor: TrustAnchor = TrustAnchor(trustRoot, null) private val keyToParties = ConcurrentHashMap() private val principalToParties = ConcurrentHashMap() init { - caCertStore = CertStore.getInstance("Collection", CollectionCertStoreParameters(setOf(this.trustRoot))) keyToParties.putAll(identities.associateBy { it.owningKey }) principalToParties.putAll(identities.associateBy { it.name }) } @@ -57,7 +52,7 @@ class InMemoryIdentityService(identities: Array, log.warn("Certificate path :") identity.certPath.certificates.reversed().forEachIndexed { index, certificate -> val space = (0 until index).joinToString("") { " " } - log.warn("$space${certificate.toX509CertHolder().subject}") + log.warn("$space${(certificate as X509Certificate).subjectX500Principal}") } throw e } diff --git a/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt b/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt index 0a07bc40c6..c21f7e9c91 100644 --- a/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt +++ b/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt @@ -5,8 +5,6 @@ import net.corda.core.crypto.SecureHash import net.corda.core.crypto.toStringShort import net.corda.core.identity.* import net.corda.core.internal.CertRole -import net.corda.core.internal.cert -import net.corda.core.internal.toX509CertHolder import net.corda.core.node.services.UnknownAnonymousPartyException import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.utilities.MAX_HASH_HEX_SIZE @@ -16,7 +14,6 @@ import net.corda.node.services.api.IdentityServiceInternal import net.corda.node.utilities.AppendOnlyPersistentMap import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX -import org.bouncycastle.cert.X509CertificateHolder import java.security.InvalidAlgorithmParameterException import java.security.PublicKey import java.security.cert.* @@ -30,7 +27,6 @@ import javax.persistence.Lob @ThreadSafe class PersistentIdentityService(override val trustRoot: X509Certificate, vararg caCertificates: X509Certificate) : SingletonSerializeAsToken(), IdentityServiceInternal { - constructor(trustRoot: X509CertificateHolder) : this(trustRoot.cert) companion object { private val log = contextLogger() @@ -121,7 +117,7 @@ class PersistentIdentityService(override val trustRoot: X509Certificate, log.warn(e.localizedMessage) log.warn("Path = ") identity.certPath.certificates.reversed().forEach { - log.warn(it.toX509CertHolder().subject.toString()) + log.warn((it as X509Certificate).subjectX500Principal.toString()) } throw e } diff --git a/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt b/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt index df99435dd3..dad6a7b2a7 100644 --- a/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt +++ b/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt @@ -3,8 +3,6 @@ package net.corda.node.services.keys import net.corda.core.crypto.Crypto import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.CertRole -import net.corda.core.internal.cert -import net.corda.core.internal.toX509CertHolder import net.corda.core.utilities.days import net.corda.node.services.api.IdentityServiceInternal import net.corda.nodeapi.internal.crypto.CertificateType @@ -36,11 +34,16 @@ fun freshCertificate(identityService: IdentityServiceInternal, revocationEnabled: Boolean = false): PartyAndCertificate { val issuerRole = CertRole.extract(issuer.certificate) require(issuerRole == CertRole.LEGAL_IDENTITY) { "Confidential identities can only be issued from well known identities, provided issuer ${issuer.name} has role $issuerRole" } - val issuerCert = issuer.certificate.toX509CertHolder() + val issuerCert = issuer.certificate val window = X509Utilities.getCertificateValidityWindow(Duration.ZERO, 3650.days, issuerCert) - val ourCertificate = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuerCert.subject, - issuerSigner, issuer.name, subjectPublicKey, window) - val ourCertPath = X509CertificateFactory().generateCertPath(listOf(ourCertificate.cert) + issuer.certPath.certificates) + val ourCertificate = X509Utilities.createCertificate( + CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, + issuerCert.subjectX500Principal, + issuerSigner, + issuer.name.x500Principal, + subjectPublicKey, + window) + val ourCertPath = X509CertificateFactory().generateCertPath(listOf(ourCertificate) + issuer.certPath.certificates) val anonymisedIdentity = PartyAndCertificate(ourCertPath) identityService.justVerifyAndRegisterIdentity(anonymisedIdentity) return anonymisedIdentity diff --git a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt index 09a46121e8..090eef335c 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt @@ -72,7 +72,7 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v // We use the self sign certificate to store the key temporarily in the keystore while waiting for the request approval. if (!nodeKeyStore.containsAlias(SELF_SIGNED_PRIVATE_KEY)) { val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val selfSignCert = X509Utilities.createSelfSignedCACertificate(config.myLegalName, keyPair) + val selfSignCert = X509Utilities.createSelfSignedCACertificate(config.myLegalName.x500Principal, keyPair) // Save to the key store. nodeKeyStore.addOrReplaceKey(SELF_SIGNED_PRIVATE_KEY, keyPair.private, privateKeyPassword.toCharArray(), arrayOf(selfSignCert)) @@ -112,8 +112,8 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v throw CertificateRequestException("Received node CA cert has invalid role: $nodeCaCertRole") } - println("Checking root of the certificate path is what we expect.") - X509Utilities.validateCertificateChain (rootCert , * certificates) + println("Checking root of the certificate path is what we expect.") + X509Utilities.validateCertificateChain(rootCert, *certificates) println("Certificate signing request approved, storing private key with the certificate chain.") // Save private key and certificate chain to the key store. @@ -126,12 +126,12 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v val sslKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val sslCert = X509Utilities.createCertificate( CertificateType.TLS, - nodeCaCert.toX509CertHolder(), + nodeCaCert, keyPair, - config.myLegalName, + config.myLegalName.x500Principal, sslKeyPair.public) val sslKeyStore = loadOrCreateKeyStore(config.sslKeystore, keystorePassword) - sslKeyStore.addOrReplaceKey(CORDA_CLIENT_TLS, sslKeyPair.private, privateKeyPassword.toCharArray(), arrayOf(sslCert.cert, *certificates)) + sslKeyStore.addOrReplaceKey(CORDA_CLIENT_TLS, sslKeyPair.private, privateKeyPassword.toCharArray(), arrayOf(sslCert, *certificates)) sslKeyStore.save(config.sslKeystore, config.keyStorePassword) println("SSL private key and certificate stored in ${config.sslKeystore}.") @@ -165,7 +165,7 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v private fun submitOrResumeCertificateSigningRequest(keyPair: KeyPair): String { // Retrieve request id from file if exists, else post a request to server. return if (!requestIdStore.exists()) { - val request = X509Utilities.createCertificateSigningRequest(config.myLegalName, config.emailAddress, keyPair) + val request = X509Utilities.createCertificateSigningRequest(config.myLegalName.x500Principal, config.emailAddress, keyPair) val writer = StringWriter() JcaPEMWriter(writer).use { it.writeObject(PemObject("CERTIFICATE REQUEST", request.encoded)) diff --git a/node/src/test/kotlin/net/corda/node/services/identity/InMemoryIdentityServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/identity/InMemoryIdentityServiceTests.kt index 30717cdad4..9131143e9f 100644 --- a/node/src/test/kotlin/net/corda/node/services/identity/InMemoryIdentityServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/identity/InMemoryIdentityServiceTests.kt @@ -6,8 +6,6 @@ import net.corda.core.identity.AnonymousParty import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate -import net.corda.core.internal.cert -import net.corda.core.internal.toX509CertHolder import net.corda.core.node.services.UnknownAnonymousPartyException import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509CertificateFactory @@ -32,7 +30,7 @@ class InMemoryIdentityServiceTests { val BOB get() = bob.party val BOB_IDENTITY get() = bob.identity val BOB_PUBKEY get() = bob.publicKey - fun createService(vararg identities: PartyAndCertificate) = InMemoryIdentityService(identities, DEV_TRUST_ROOT) + fun createService(vararg identities: PartyAndCertificate) = InMemoryIdentityService(identities, DEV_ROOT_CA.certificate) } @Rule @@ -100,11 +98,11 @@ class InMemoryIdentityServiceTests { @Test fun `assert unknown anonymous key is unrecognised`() { val rootKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootKey) + val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Principal, rootKey) val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val service = createService() // TODO: Generate certificate with an EdDSA key rather than ECDSA - val identity = Party(rootCert.cert) + val identity = Party(rootCert) val txIdentity = AnonymousParty(txKey.public) assertFailsWith { @@ -159,8 +157,8 @@ class InMemoryIdentityServiceTests { } assertFailsWith { - val owningKey = Crypto.decodePublicKey(DEV_CA.certificate.subjectPublicKeyInfo.encoded) - val subject = CordaX500Name.build(DEV_CA.certificate.cert.subjectX500Principal) + val owningKey = DEV_INTERMEDIATE_CA.certificate.publicKey + val subject = CordaX500Name.build(DEV_INTERMEDIATE_CA.certificate.subjectX500Principal) service.assertOwnership(Party(subject, owningKey), anonymousAlice.party.anonymise()) } } @@ -168,9 +166,14 @@ class InMemoryIdentityServiceTests { private fun createConfidentialIdentity(x500Name: CordaX500Name): Pair { val issuerKeyPair = generateKeyPair() val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public) - val txKey = Crypto.generateKeyPair() - val txCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuer.certificate.toX509CertHolder(), issuerKeyPair, x500Name, txKey.public) - val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates) + val txKeyPair = Crypto.generateKeyPair() + val txCert = X509Utilities.createCertificate( + CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, + issuer.certificate, + issuerKeyPair, + x500Name.x500Principal, + txKeyPair.public) + val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert) + issuer.certPath.certificates) return Pair(issuer, PartyAndCertificate(txCertPath)) } diff --git a/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt index 2553d30ef5..5d6cc41ce1 100644 --- a/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt @@ -6,8 +6,6 @@ import net.corda.core.identity.AnonymousParty import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate -import net.corda.core.internal.cert -import net.corda.core.internal.toX509CertHolder import net.corda.core.node.services.IdentityService import net.corda.core.node.services.UnknownAnonymousPartyException import net.corda.node.internal.configureDatabase @@ -50,7 +48,7 @@ class PersistentIdentityServiceTests { @Before fun setup() { - identityService = PersistentIdentityService(DEV_TRUST_ROOT) + identityService = PersistentIdentityService(DEV_ROOT_CA.certificate) database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), identityService) } @@ -143,9 +141,9 @@ class PersistentIdentityServiceTests { @Test fun `assert unknown anonymous key is unrecognised`() { val rootKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootKey) + val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Principal, rootKey) val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_IDENTITY_SIGNATURE_SCHEME) - val identity = Party(rootCert.cert) + val identity = Party(rootCert) val txIdentity = AnonymousParty(txKey.public) assertFailsWith { @@ -219,9 +217,9 @@ class PersistentIdentityServiceTests { } assertFailsWith { - val owningKey = Crypto.decodePublicKey(DEV_CA.certificate.subjectPublicKeyInfo.encoded) + val owningKey = DEV_INTERMEDIATE_CA.certificate.publicKey database.transaction { - val subject = CordaX500Name.build(DEV_CA.certificate.cert.subjectX500Principal) + val subject = CordaX500Name.build(DEV_INTERMEDIATE_CA.certificate.subjectX500Principal) identityService.assertOwnership(Party(subject, owningKey), anonymousAlice.party.anonymise()) } } @@ -243,7 +241,7 @@ class PersistentIdentityServiceTests { // Create new identity service mounted onto same DB val newPersistentIdentityService = database.transaction { - PersistentIdentityService(DEV_TRUST_ROOT) + PersistentIdentityService(DEV_ROOT_CA.certificate) } database.transaction { @@ -266,8 +264,8 @@ class PersistentIdentityServiceTests { val issuerKeyPair = generateKeyPair() val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public) val txKey = Crypto.generateKeyPair() - val txCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuer.certificate.toX509CertHolder(), issuerKeyPair, x500Name, txKey.public) - val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates) + val txCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuer.certificate, issuerKeyPair, x500Name.x500Principal, txKey.public) + val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert) + issuer.certPath.certificates) return Pair(issuer, PartyAndCertificate(txCertPath)) } diff --git a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapClientTest.kt b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapClientTest.kt index c609d59f2c..9f342b2e0b 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapClientTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapClientTest.kt @@ -2,12 +2,11 @@ package net.corda.node.services.network import net.corda.core.crypto.SecureHash import net.corda.core.crypto.sha256 -import net.corda.core.internal.cert import net.corda.core.serialization.serialize import net.corda.core.utilities.seconds import net.corda.testing.ALICE_NAME import net.corda.testing.BOB_NAME -import net.corda.testing.DEV_TRUST_ROOT +import net.corda.testing.DEV_ROOT_CA import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.driver.PortAllocation import net.corda.testing.internal.createNodeInfoAndSigned @@ -35,7 +34,7 @@ class NetworkMapClientTest { fun setUp() { server = NetworkMapServer(cacheTimeout, PortAllocation.Incremental(10000).nextHostAndPort()) val hostAndPort = server.start() - networkMapClient = NetworkMapClient(URL("http://${hostAndPort.host}:${hostAndPort.port}"), DEV_TRUST_ROOT.cert) + networkMapClient = NetworkMapClient(URL("http://${hostAndPort.host}:${hostAndPort.port}"), DEV_ROOT_CA.certificate) } @After diff --git a/node/src/test/kotlin/net/corda/node/utilities/TLSAuthenticationTests.kt b/node/src/test/kotlin/net/corda/node/utilities/TLSAuthenticationTests.kt index 98ff559fe8..ef933d5ccc 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/TLSAuthenticationTests.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/TLSAuthenticationTests.kt @@ -18,6 +18,7 @@ import java.net.ServerSocket import java.nio.file.Path import java.security.KeyStore import javax.net.ssl.* +import javax.security.auth.x500.X500Principal import kotlin.concurrent.thread import kotlin.test.* @@ -51,9 +52,9 @@ class TLSAuthenticationTests { val tempFolder: TemporaryFolder = TemporaryFolder() // Root CA. - private val ROOT_X500 = CordaX500Name(commonName = "Root_CA_1", organisation = "R3CEV", locality = "London", country = "GB") + private val ROOT_X500 = X500Principal("CN=Root_CA_1,O=R3CEV,L=London,C=GB") // Intermediate CA. - private val INTERMEDIATE_X500 = CordaX500Name(commonName = "Intermediate_CA_1", organisation = "R3CEV", locality = "London", country = "GB") + private val INTERMEDIATE_X500 = X500Principal("CN=Intermediate_CA_1,O=R3CEV,L=London,C=GB") // TLS server (client1). private val CLIENT_1_X500 = CordaX500Name(commonName = "Client_1", organisation = "R3CEV", locality = "London", country = "GB") // TLS client (client2). @@ -274,7 +275,7 @@ class TLSAuthenticationTests { CertificateType.NODE_CA, intermediateCACert, intermediateCAKeyPair, - CLIENT_1_X500, + CLIENT_1_X500.x500Principal, client1CAKeyPair.public ) @@ -283,7 +284,7 @@ class TLSAuthenticationTests { CertificateType.TLS, client1CACert, client1CAKeyPair, - CLIENT_1_X500, + CLIENT_1_X500.x500Principal, client1TLSKeyPair.public ) @@ -301,7 +302,7 @@ class TLSAuthenticationTests { CertificateType.NODE_CA, intermediateCACert, intermediateCAKeyPair, - CLIENT_2_X500, + CLIENT_2_X500.x500Principal, client2CAKeyPair.public ) @@ -310,7 +311,7 @@ class TLSAuthenticationTests { CertificateType.TLS, client2CACert, client2CAKeyPair, - CLIENT_2_X500, + CLIENT_2_X500.x500Principal, client2TLSKeyPair.public ) @@ -323,8 +324,8 @@ class TLSAuthenticationTests { // client2TLSKeyStore.save(client2TLSKeyStorePath, PASSWORD) val trustStore = loadOrCreateKeyStore(trustStorePath, PASSWORD) - trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCACert.cert) - trustStore.addOrReplaceCertificate(X509Utilities.CORDA_INTERMEDIATE_CA, intermediateCACert.cert) + trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCACert) + trustStore.addOrReplaceCertificate(X509Utilities.CORDA_INTERMEDIATE_CA, intermediateCACert) // trustStore.save(trustStorePath, PASSWORD) val client1SSLContext = sslContext(client1TLSKeyStore, PASSWORD, trustStore) diff --git a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt index 5b8dab7282..f56c82a6cf 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt @@ -9,7 +9,6 @@ import com.nhaarman.mockito_kotlin.whenever import net.corda.core.crypto.Crypto import net.corda.core.crypto.SecureHash import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.cert import net.corda.core.internal.createDirectories import net.corda.core.internal.x500Name import net.corda.node.services.config.NodeConfiguration @@ -26,6 +25,7 @@ import org.junit.Before import org.junit.Test import java.security.cert.CertPathValidatorException import java.security.cert.X509Certificate +import javax.security.auth.x500.X500Principal import kotlin.test.assertFalse import kotlin.test.assertTrue @@ -129,9 +129,9 @@ class NetworkRegistrationHelperTest { @Test fun `wrong root cert in truststore`() { val wrongRootCert = X509Utilities.createSelfSignedCACertificate( - CordaX500Name("Foo", "MU", "GB"), + X500Principal("O=Foo,L=MU,C=GB"), Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) - saveTrustStoreWithRootCa(wrongRootCert.cert) + saveTrustStoreWithRootCa(wrongRootCert) val registrationHelper = createRegistrationHelper(createNodeCaCertPath()) assertThatThrownBy { registrationHelper.buildKeystore() @@ -147,10 +147,10 @@ class NetworkRegistrationHelperTest { type, intermediateCa.certificate, intermediateCa.keyPair, - legalName, + legalName.x500Principal, keyPair.public, nameConstraints = nameConstraints) - return arrayOf(nodeCaCert.cert, intermediateCa.certificate.cert, rootCa.certificate.cert) + return arrayOf(nodeCaCert, intermediateCa.certificate, rootCa.certificate) } private fun createRegistrationHelper(response: Array): NetworkRegistrationHelper { diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt index bc3add840c..25403c008f 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt @@ -15,7 +15,9 @@ import net.corda.core.serialization.SerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.transactions.SignedTransaction import net.corda.node.VersionInfo +import net.corda.node.internal.configureDatabase import net.corda.node.internal.cordapp.CordappLoader +import net.corda.node.services.api.IdentityServiceInternal import net.corda.node.services.api.SchemaService import net.corda.node.services.api.VaultServiceInternal import net.corda.node.services.api.WritableTransactionStorage @@ -26,12 +28,11 @@ import net.corda.node.services.schema.HibernateObserver import net.corda.node.services.schema.NodeSchemaService import net.corda.node.services.transactions.InMemoryTransactionVerifierService import net.corda.node.services.vault.NodeVaultService -import net.corda.node.internal.configureDatabase -import net.corda.node.services.api.IdentityServiceInternal import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.nodeapi.internal.persistence.HibernateConfiguration -import net.corda.testing.* +import net.corda.testing.DEV_ROOT_CA +import net.corda.testing.TestIdentity import net.corda.testing.services.MockAttachmentStorage import net.corda.testing.services.MockCordappProvider import org.bouncycastle.operator.ContentSigner @@ -44,7 +45,7 @@ import java.sql.Connection import java.time.Clock import java.util.* -fun makeTestIdentityService(vararg identities: PartyAndCertificate) = InMemoryIdentityService(identities, DEV_TRUST_ROOT) +fun makeTestIdentityService(vararg identities: PartyAndCertificate) = InMemoryIdentityService(identities, DEV_ROOT_CA.certificate) /** * A singleton utility that only provides a mock identity, key and storage service. However, this is sufficient for * building chains of transactions and verifying them. It isn't sufficient for testing flows however. @@ -164,7 +165,7 @@ class MockKeyManagementService(val identityService: IdentityServiceInternal, override val keys: Set get() = keyStore.keys - val nextKeys = LinkedList() + private val nextKeys = LinkedList() override fun freshKey(): PublicKey { val k = nextKeys.poll() ?: generateKeyPair() @@ -222,11 +223,10 @@ open class MockTransactionStorage : WritableTransactionStorage, SingletonSeriali } fun createMockCordaService(serviceHub: MockServices, serviceConstructor: (AppServiceHub) -> T): T { - class MockAppServiceHubImpl(val serviceHub: MockServices, serviceConstructor: (AppServiceHub) -> T) : AppServiceHub, ServiceHub by serviceHub { - val serviceInstance: T + class MockAppServiceHubImpl(val serviceHub: MockServices, serviceConstructor: (AppServiceHub) -> T) : AppServiceHub, ServiceHub by serviceHub { + val serviceInstance: T = serviceConstructor(this) init { - serviceInstance = serviceConstructor(this) serviceHub.cordappServices.putInstance(serviceInstance.javaClass, serviceInstance) } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/network/NetworkMapServer.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/network/NetworkMapServer.kt index 345eaf84b8..be1a11c293 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/network/NetworkMapServer.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/network/NetworkMapServer.kt @@ -2,8 +2,6 @@ package net.corda.testing.node.internal.network import net.corda.core.crypto.* import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.cert -import net.corda.core.internal.toX509CertHolder import net.corda.core.node.NodeInfo import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize @@ -16,7 +14,7 @@ import net.corda.nodeapi.internal.network.DigitalSignatureWithCert import net.corda.nodeapi.internal.network.NetworkMap import net.corda.nodeapi.internal.network.NetworkParameters import net.corda.nodeapi.internal.network.SignedNetworkMap -import net.corda.testing.ROOT_CA +import net.corda.testing.DEV_ROOT_CA import org.eclipse.jetty.server.Server import org.eclipse.jetty.server.ServerConnector import org.eclipse.jetty.server.handler.HandlerCollection @@ -29,6 +27,7 @@ import java.io.InputStream import java.net.InetSocketAddress import java.time.Duration import java.time.Instant +import javax.security.auth.x500.X500Principal import javax.ws.rs.* import javax.ws.rs.core.MediaType import javax.ws.rs.core.Response @@ -36,7 +35,7 @@ import javax.ws.rs.core.Response.ok class NetworkMapServer(cacheTimeout: Duration, hostAndPort: NetworkHostAndPort, - rootCa: CertificateAndKeyPair = ROOT_CA, // Default to ROOT_CA for testing. + rootCa: CertificateAndKeyPair = DEV_ROOT_CA, private val myHostNameValue: String = "test.host.name", vararg additionalServices: Any) : Closeable { companion object { @@ -48,12 +47,12 @@ class NetworkMapServer(cacheTimeout: Duration, CertificateType.NETWORK_MAP, rootCAKeyAndCert.certificate, rootCAKeyAndCert.keyPair, - CordaX500Name("Corda Network Map", "R3 Ltd", "London","GB"), - networkMapKey.public).cert + X500Principal("CN=Corda Network Map,O=R3 Ltd,L=London,C=GB"), + networkMapKey.public) // Check that the certificate validates. Nodes will perform this check upon receiving a network map, // it's better to fail here than there. - X509Utilities.validateCertificateChain(rootCAKeyAndCert.certificate.cert, networkMapCert) - return CertificateAndKeyPair(networkMapCert.toX509CertHolder(), networkMapKey) + X509Utilities.validateCertificateChain(rootCAKeyAndCert.certificate, networkMapCert) + return CertificateAndKeyPair(networkMapCert, networkMapKey) } } @@ -130,7 +129,7 @@ class NetworkMapServer(cacheTimeout: Duration, val networkMap = NetworkMap(nodeInfoMap.keys.toList(), parametersHash) val serializedNetworkMap = networkMap.serialize() val signature = Crypto.doSign(networkMapKeyAndCert.keyPair.private, serializedNetworkMap.bytes) - val signedNetworkMap = SignedNetworkMap(networkMap.serialize(), DigitalSignatureWithCert(networkMapKeyAndCert.certificate.cert, signature)) + val signedNetworkMap = SignedNetworkMap(networkMap.serialize(), DigitalSignatureWithCert(networkMapKeyAndCert.certificate, signature)) return Response.ok(signedNetworkMap.serialize().bytes).header("Cache-Control", "max-age=${cacheTimeout.seconds}").build() } diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt index 362672ce89..e24496c50c 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt @@ -12,7 +12,6 @@ import net.corda.core.crypto.toStringShort import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate -import net.corda.core.internal.cert import net.corda.core.internal.unspecifiedCountry import net.corda.core.node.NodeInfo import net.corda.core.utilities.NetworkHostAndPort @@ -21,10 +20,13 @@ import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509Utilities -import org.bouncycastle.cert.X509CertificateHolder +import org.bouncycastle.asn1.x509.GeneralName +import org.bouncycastle.asn1.x509.GeneralSubtree +import org.bouncycastle.asn1.x509.NameConstraints import java.math.BigInteger import java.security.KeyPair import java.security.PublicKey +import java.security.cert.X509Certificate import java.util.concurrent.atomic.AtomicInteger /** @@ -76,8 +78,8 @@ fun getFreeLocalPorts(hostName: String, numberToAlloc: Int): List(DummyCommandData, signers.toList()) diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt index 87a73491b5..ccf34284e7 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt @@ -15,6 +15,7 @@ import org.mockito.internal.stubbing.answers.ThrowsException import java.lang.reflect.Modifier import java.nio.file.Files import java.util.* +import javax.security.auth.x500.X500Principal @Suppress("unused") inline fun T.kryoSpecific(reason: String, function: () -> Unit) = if (!AMQP_ENABLED) { @@ -64,8 +65,8 @@ fun configureTestSSL(legalName: CordaX500Name): SSLConfiguration { } } -private val defaultRootCaName = CordaX500Name("Corda Root CA", "R3 Ltd", "London", "GB") -private val defaultIntermediateCaName = CordaX500Name("Corda Intermediate CA", "R3 Ltd", "London", "GB") +private val defaultRootCaName = X500Principal("CN=Corda Root CA,O=R3 Ltd,L=London,C=GB") +private val defaultIntermediateCaName = X500Principal("CN=Corda Intermediate CA,O=R3 Ltd,L=London,C=GB") /** * Returns a pair of [CertificateAndKeyPair]s, the first being the root CA and the second the intermediate CA. @@ -73,8 +74,8 @@ private val defaultIntermediateCaName = CordaX500Name("Corda Intermediate CA", " * @param intermediateCaName The subject name for the intermediate CA cert. */ fun createDevIntermediateCaCertPath( - rootCaName: CordaX500Name = defaultRootCaName, - intermediateCaName: CordaX500Name = defaultIntermediateCaName + rootCaName: X500Principal = defaultRootCaName, + intermediateCaName: X500Principal = defaultIntermediateCaName ): Pair { val rootKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val rootCert = X509Utilities.createSelfSignedCACertificate(rootCaName, rootKeyPair) @@ -87,7 +88,10 @@ fun createDevIntermediateCaCertPath( intermediateCaName, intermediateCaKeyPair.public) - return Pair(CertificateAndKeyPair(rootCert, rootKeyPair), CertificateAndKeyPair(intermediateCaCert, intermediateCaKeyPair)) + return Pair( + CertificateAndKeyPair(rootCert, rootKeyPair), + CertificateAndKeyPair(intermediateCaCert, intermediateCaKeyPair) + ) } /** @@ -97,8 +101,8 @@ fun createDevIntermediateCaCertPath( */ fun createDevNodeCaCertPath( legalName: CordaX500Name, - rootCaName: CordaX500Name = defaultRootCaName, - intermediateCaName: CordaX500Name = defaultIntermediateCaName + rootCaName: X500Principal = defaultRootCaName, + intermediateCaName: X500Principal = defaultIntermediateCaName ): Triple { val (rootCa, intermediateCa) = createDevIntermediateCaCertPath(rootCaName, intermediateCaName) val nodeCa = createDevNodeCa(intermediateCa, legalName) From 6d485a33296b3f27568a819162c83db012c3b430 Mon Sep 17 00:00:00 2001 From: Katelyn Baker Date: Thu, 4 Jan 2018 10:03:04 +0000 Subject: [PATCH 07/19] SPELLING ERROR FIX --- .../nodeapi/internal/serialization/amqp/SerializationHelper.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationHelper.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationHelper.kt index 57903f546e..b3f6d5ce53 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationHelper.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationHelper.kt @@ -85,7 +85,7 @@ private fun propertiesForSerializationFromConstructor(kotlinConstructo val matchingProperty = properties[name] ?: try { clazz.getDeclaredField(param.name) - throw NotSerializableException("Property '$name' or it's getter is non public, this renders class '$clazz' unserializable") + throw NotSerializableException("Property '$name' or its getter is non public, this renders class '$clazz' unserializable") } catch (e: NoSuchFieldException) { throw NotSerializableException("No property matching constructor parameter named '$name' of '$clazz'. " + "If using Java, check that you have the -parameters option specified in the Java compiler. " + From 8a3299fa2a7d4183b0c35673a416b932b0ec5fe4 Mon Sep 17 00:00:00 2001 From: Andrzej Cichocki Date: Thu, 4 Jan 2018 10:09:04 +0000 Subject: [PATCH 08/19] CORDA-716 Configure api scanner for test modules (#2259) --- .ci/api-current.txt | 3 +++ testing/node-driver/build.gradle | 1 + .../src/main/kotlin/net/corda/testing/driver/Driver.kt | 3 +++ .../src/main/kotlin/net/corda/testing/driver/DriverDSL.kt | 2 ++ .../kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt | 4 +++- .../src/main/kotlin/net/corda/testing/node/MockNode.kt | 2 ++ .../src/main/kotlin/net/corda/testing/node/NotarySpec.kt | 2 ++ testing/test-common/build.gradle | 1 + testing/test-utils/build.gradle | 1 + .../test-utils/src/main/kotlin/net/corda/testing/Expect.kt | 2 ++ .../main/kotlin/net/corda/testing/SerializationTestHelpers.kt | 2 ++ .../main/kotlin/net/corda/testing/contracts/DummyContract.kt | 2 ++ .../main/kotlin/net/corda/testing/dsl/LedgerDSLInterpreter.kt | 4 ++++ .../src/main/kotlin/net/corda/testing/dsl/TestDSL.kt | 2 ++ .../kotlin/net/corda/testing/dsl/TransactionDSLInterpreter.kt | 2 ++ 15 files changed, 32 insertions(+), 1 deletion(-) diff --git a/.ci/api-current.txt b/.ci/api-current.txt index 896cdd2592..f3eee165fa 100644 --- a/.ci/api-current.txt +++ b/.ci/api-current.txt @@ -3522,6 +3522,9 @@ public static final class net.corda.client.jackson.StringToMethodCallParser$Unpa public (String) @org.jetbrains.annotations.NotNull public final String getMethodName() ## +public static interface net.corda.testing.node.InMemoryMessagingNetwork$LatencyCalculator + @org.jetbrains.annotations.NotNull public abstract java.time.Duration between(net.corda.core.messaging.SingleMessageRecipient, net.corda.core.messaging.SingleMessageRecipient) +## public final class net.corda.client.rpc.CordaRPCClient extends java.lang.Object public (net.corda.core.utilities.NetworkHostAndPort) public (net.corda.core.utilities.NetworkHostAndPort, net.corda.client.rpc.CordaRPCClientConfiguration) diff --git a/testing/node-driver/build.gradle b/testing/node-driver/build.gradle index 5663251bc2..90f551c4aa 100644 --- a/testing/node-driver/build.gradle +++ b/testing/node-driver/build.gradle @@ -2,6 +2,7 @@ apply plugin: 'kotlin' apply plugin: 'kotlin-jpa' apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.publish-utils' +apply plugin: 'net.corda.plugins.api-scanner' apply plugin: 'com.jfrog.artifactory' //noinspection GroovyAssignabilityCheck diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt index 577a00b479..11e95e450b 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt @@ -3,6 +3,7 @@ package net.corda.testing.driver import net.corda.client.rpc.CordaRPCClient +import net.corda.core.DoNotImplement import net.corda.core.concurrent.CordaFuture import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party @@ -30,6 +31,7 @@ import java.util.concurrent.atomic.AtomicInteger */ data class NotaryHandle(val identity: Party, val validating: Boolean, val nodeHandles: CordaFuture>) +@DoNotImplement sealed class NodeHandle { abstract val nodeInfo: NodeInfo /** @@ -90,6 +92,7 @@ data class WebserverHandle( val process: Process ) +@DoNotImplement sealed class PortAllocation { abstract fun nextPort(): Int fun nextHostAndPort() = NetworkHostAndPort("localhost", nextPort()) diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/DriverDSL.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/DriverDSL.kt index d8d3d1b49a..a4908d5f53 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/DriverDSL.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/DriverDSL.kt @@ -1,5 +1,6 @@ package net.corda.testing.driver +import net.corda.core.DoNotImplement import net.corda.core.concurrent.CordaFuture import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party @@ -10,6 +11,7 @@ import net.corda.nodeapi.internal.config.User import net.corda.testing.node.NotarySpec import java.nio.file.Path +@DoNotImplement interface DriverDSL { /** Returns a list of [NotaryHandle]s matching the list of [NotarySpec]s passed into [driver]. */ val notaryHandles: List diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt index 5d805a22b4..96b9a9d4c1 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt @@ -1,5 +1,6 @@ package net.corda.testing.node +import net.corda.core.DoNotImplement import net.corda.core.crypto.CompositeKey import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party @@ -120,7 +121,7 @@ class InMemoryMessagingNetwork internal constructor( } } - interface LatencyCalculator { + interface LatencyCalculator { // XXX: Used? fun between(sender: SingleMessageRecipient, receiver: SingleMessageRecipient): Duration } @@ -178,6 +179,7 @@ class InMemoryMessagingNetwork internal constructor( /** * Mock service loadbalancing */ + @DoNotImplement sealed class ServicePeerAllocationStrategy { abstract fun pickNext(service: ServiceHandle, pickFrom: List): A class Random(val random: SplittableRandom = SplittableRandom()) : ServicePeerAllocationStrategy() { diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt index fb4a7ec2e3..fabb3d999a 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt @@ -4,6 +4,7 @@ import com.google.common.jimfs.Configuration.unix import com.google.common.jimfs.Jimfs import com.nhaarman.mockito_kotlin.doReturn import com.nhaarman.mockito_kotlin.whenever +import net.corda.core.DoNotImplement import net.corda.core.crypto.entropyToKeyPair import net.corda.core.crypto.random63BitValue import net.corda.core.identity.CordaX500Name @@ -490,6 +491,7 @@ fun StartedNode.setMessagingServiceSpy(messagingServiceSpy } private fun mockNodeConfiguration(): NodeConfiguration { + @DoNotImplement abstract class AbstractNodeConfiguration : NodeConfiguration return rigorousMock().also { doReturn("cordacadevpass").whenever(it).keyStorePassword diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/NotarySpec.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/NotarySpec.kt index b8988e50c7..b6533ebd98 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/NotarySpec.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/NotarySpec.kt @@ -1,5 +1,6 @@ package net.corda.testing.node +import net.corda.core.DoNotImplement import net.corda.core.identity.CordaX500Name import net.corda.node.services.config.VerifierType import net.corda.nodeapi.internal.config.User @@ -12,6 +13,7 @@ data class NotarySpec( val cluster: ClusterSpec? = null ) +@DoNotImplement sealed class ClusterSpec { abstract val clusterSize: Int diff --git a/testing/test-common/build.gradle b/testing/test-common/build.gradle index df37173fc1..e68a9dba67 100644 --- a/testing/test-common/build.gradle +++ b/testing/test-common/build.gradle @@ -1,4 +1,5 @@ apply plugin: 'net.corda.plugins.publish-utils' +apply plugin: 'net.corda.plugins.api-scanner' apply plugin: 'com.jfrog.artifactory' dependencies { diff --git a/testing/test-utils/build.gradle b/testing/test-utils/build.gradle index c90b00304e..b67dcbcb2f 100644 --- a/testing/test-utils/build.gradle +++ b/testing/test-utils/build.gradle @@ -2,6 +2,7 @@ apply plugin: 'kotlin' apply plugin: 'kotlin-jpa' apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.publish-utils' +apply plugin: 'net.corda.plugins.api-scanner' apply plugin: 'com.jfrog.artifactory' description 'Testing utilities for Corda' diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/Expect.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/Expect.kt index 0db4754f4c..328a6fdbb3 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/Expect.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/Expect.kt @@ -1,6 +1,7 @@ package net.corda.testing import com.google.common.util.concurrent.SettableFuture +import net.corda.core.DoNotImplement import net.corda.core.internal.uncheckedCast import net.corda.core.utilities.getOrThrow import org.slf4j.Logger @@ -189,6 +190,7 @@ fun S.genericExpectEvents( finishFuture.getOrThrow() } +@DoNotImplement sealed class ExpectCompose { internal class Single(val expect: Expect) : ExpectCompose() internal class Sequential(val sequence: List>) : ExpectCompose() diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/SerializationTestHelpers.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/SerializationTestHelpers.kt index 8aa5a0cc61..b76f571bc3 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/SerializationTestHelpers.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/SerializationTestHelpers.kt @@ -2,6 +2,7 @@ package net.corda.testing import com.nhaarman.mockito_kotlin.* import net.corda.client.rpc.internal.KryoClientSerializationScheme +import net.corda.core.DoNotImplement import net.corda.core.internal.staticField import net.corda.core.serialization.internal.* import net.corda.node.serialization.KryoServerSerializationScheme @@ -62,6 +63,7 @@ class SerializationEnvironmentRule(private val inheritable: Boolean = false) : T } } +@DoNotImplement interface GlobalSerializationEnvironment : SerializationEnvironment { /** Unset this environment. */ fun unset() diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContract.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContract.kt index 28341e2e92..3355b1fdf0 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContract.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContract.kt @@ -1,5 +1,6 @@ package net.corda.testing.contracts +import net.corda.core.DoNotImplement import net.corda.core.contracts.* import net.corda.core.identity.AbstractParty import net.corda.core.identity.Party @@ -12,6 +13,7 @@ data class DummyContract(val blank: Any? = null) : Contract { val PROGRAM_ID = "net.corda.testing.contracts.DummyContract" + @DoNotImplement // State is effectively a sealed class. interface State : ContractState { val magicNumber: Int } diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/LedgerDSLInterpreter.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/LedgerDSLInterpreter.kt index 869643d2aa..89e9ecb202 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/LedgerDSLInterpreter.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/LedgerDSLInterpreter.kt @@ -1,5 +1,6 @@ package net.corda.testing.dsl +import net.corda.core.DoNotImplement import net.corda.core.contracts.ContractState import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.TransactionState @@ -14,6 +15,7 @@ import java.io.InputStream * This interface defines output state lookup by label. It is split from the interpreter interfaces so that outputs may * be looked up both in ledger{..} and transaction{..} blocks. */ +@DoNotImplement interface OutputStateLookup { /** * Retrieves an output previously defined by [TransactionDSLInterpreter.output] with a label passed in. @@ -27,6 +29,7 @@ interface OutputStateLookup { /** * This interface asserts that the DSL at hand is capable of verifying its underlying construct(ledger/transaction). */ +@DoNotImplement interface Verifies { /** * Verifies the ledger/transaction, throws if the verification fails. @@ -83,6 +86,7 @@ interface Verifies { * * TODO (Kotlin 1.1): Use type synonyms to make the type params less unwieldy */ +@DoNotImplement interface LedgerDSLInterpreter : Verifies, OutputStateLookup { /** * Creates and adds a transaction to the ledger. diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/TestDSL.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/TestDSL.kt index bf48a07240..3a0e36e5f3 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/TestDSL.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/TestDSL.kt @@ -1,5 +1,6 @@ package net.corda.testing.dsl +import net.corda.core.DoNotImplement import net.corda.core.contracts.* import net.corda.core.cordapp.CordappProvider import net.corda.core.crypto.* @@ -50,6 +51,7 @@ import kotlin.collections.set * will have as the last line either an accept or a failure test. The name is deliberately long to help make sense of * the triggered diagnostic. */ +@DoNotImplement sealed class EnforceVerifyOrFail { internal object Token : EnforceVerifyOrFail() } diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/TransactionDSLInterpreter.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/TransactionDSLInterpreter.kt index b905e62462..61e7ea1c9a 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/TransactionDSLInterpreter.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/dsl/TransactionDSLInterpreter.kt @@ -1,5 +1,6 @@ package net.corda.testing.dsl +import net.corda.core.DoNotImplement import net.corda.core.contracts.* import net.corda.core.crypto.SecureHash import net.corda.core.identity.Party @@ -14,6 +15,7 @@ import java.time.Instant * @param The return type of [verifies]/[failsWith] and the like. It is generic so that we have control over whether * we want to enforce users to call these methods (see [EnforceVerifyOrFail]) or not. */ +@DoNotImplement interface TransactionDSLInterpreter : Verifies, OutputStateLookup { /** * A reference to the enclosing ledger{..}'s interpreter. From 412fead02e76596f6162d4b35d48da25fd8d8d7e Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Thu, 4 Jan 2018 13:32:10 +0000 Subject: [PATCH 09/19] CORDA-785: Add functions for constructing FlowLogicRef without the class (#2134) Add functions for constructing `FlowLogicRef` from class name, rather than requiring the class itself. This avoids requiring that schedulable states have access to the scheduled flow to instantiate, but instead can require it only actually scheduling the flow. This reduces the size of the JAR required to validate transactions containing these states. --- .../net/corda/core/flows/FlowLogicRef.kt | 10 ++++++++++ .../statemachine/FlowLogicRefFactoryImpl.kt | 12 ++++++++++++ .../node/services/events/ScheduledFlowTests.kt | 3 ++- .../FlowLogicRefFactoryImplTest.kt} | 18 +++++++++++------- .../main/kotlin/net/corda/irs/contract/IRS.kt | 2 +- .../net/corda/vega/contracts/PortfolioState.kt | 3 +-- 6 files changed, 37 insertions(+), 11 deletions(-) rename node/src/test/kotlin/net/corda/node/services/{events/FlowLogicRefTest.kt => statemachine/FlowLogicRefFactoryImplTest.kt} (81%) diff --git a/core/src/main/kotlin/net/corda/core/flows/FlowLogicRef.kt b/core/src/main/kotlin/net/corda/core/flows/FlowLogicRef.kt index 4768ff9259..36b0971cb0 100644 --- a/core/src/main/kotlin/net/corda/core/flows/FlowLogicRef.kt +++ b/core/src/main/kotlin/net/corda/core/flows/FlowLogicRef.kt @@ -10,7 +10,17 @@ import net.corda.core.serialization.CordaSerializable */ @DoNotImplement interface FlowLogicRefFactory { + /** + * Construct a FlowLogicRef. This is intended for cases where the calling code has the relevant class already + * and can provide it directly. + */ + @Deprecated("This should be avoided, and the version which takes a class name used instead to avoid requiring the class on the classpath to deserialize calling code") fun create(flowClass: Class>, vararg args: Any?): FlowLogicRef + /** + * Construct a FlowLogicRef. This is intended for cases where the calling code does not want to require the flow + * class on the classpath for all cases where the calling code is loaded. + */ + fun create(flowClassName: String, vararg args: Any?): FlowLogicRef fun createForRPC(flowClass: Class>, vararg args: Any?): FlowLogicRef fun toFlowLogic(ref: FlowLogicRef): FlowLogic<*> } diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowLogicRefFactoryImpl.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowLogicRefFactoryImpl.kt index 1b20e75c5b..8d1aba86c6 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowLogicRefFactoryImpl.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowLogicRefFactoryImpl.kt @@ -40,6 +40,18 @@ class FlowLogicRefFactoryImpl(private val classloader: ClassLoader) : SingletonS return createForRPC(flowClass, *args) } + override fun create(flowClassName: String, vararg args: Any?): FlowLogicRef { + val flowClass = Class.forName(flowClassName, true, classloader).asSubclass(FlowLogic::class.java) + if (flowClass == null) { + throw IllegalArgumentException("The class $flowClassName is not a subclass of FlowLogic.") + } else { + if (!flowClass.isAnnotationPresent(SchedulableFlow::class.java)) { + throw IllegalFlowLogicException(flowClass, "because it's not a schedulable flow") + } + return createForRPC(flowClass, *args) + } + } + override fun createForRPC(flowClass: Class>, vararg args: Any?): FlowLogicRef { // TODO: This is used via RPC but it's probably better if we pass in argument names and values explicitly // to avoid requiring only a single constructor. diff --git a/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt b/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt index d963030134..600d99e50c 100644 --- a/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt @@ -30,6 +30,7 @@ import org.junit.Assert.* import org.junit.Before import org.junit.Test import java.time.Instant +import kotlin.reflect.jvm.jvmName import kotlin.test.assertEquals class ScheduledFlowTests { @@ -52,7 +53,7 @@ class ScheduledFlowTests { override val linearId: UniqueIdentifier = UniqueIdentifier()) : SchedulableState, LinearState { override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? { return if (!processed) { - val logicRef = flowLogicRefFactory.create(ScheduledFlow::class.java, thisStateRef) + val logicRef = flowLogicRefFactory.create(ScheduledFlow::class.jvmName, thisStateRef) ScheduledActivity(logicRef, creationTime) } else { null diff --git a/node/src/test/kotlin/net/corda/node/services/events/FlowLogicRefTest.kt b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowLogicRefFactoryImplTest.kt similarity index 81% rename from node/src/test/kotlin/net/corda/node/services/events/FlowLogicRefTest.kt rename to node/src/test/kotlin/net/corda/node/services/statemachine/FlowLogicRefFactoryImplTest.kt index 3794a39ebf..b0953afdc3 100644 --- a/node/src/test/kotlin/net/corda/node/services/events/FlowLogicRefTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowLogicRefFactoryImplTest.kt @@ -1,17 +1,20 @@ -package net.corda.node.services.events +package net.corda.node.services.statemachine import net.corda.core.flows.FlowLogic import net.corda.core.flows.IllegalFlowLogicException -import net.corda.node.services.statemachine.FlowLogicRefFactoryImpl +import net.corda.core.flows.SchedulableFlow import org.junit.Test import java.time.Duration +import kotlin.reflect.jvm.jvmName +import kotlin.test.assertEquals -class FlowLogicRefTest { +class FlowLogicRefFactoryImplTest { data class ParamType1(val value: Int) data class ParamType2(val value: String) @Suppress("UNUSED_PARAMETER", "unused") // Things are used via reflection. + @SchedulableFlow class KotlinFlowLogic(A: ParamType1, b: ParamType2) : FlowLogic() { constructor() : this(ParamType1(1), ParamType2("2")) @@ -26,6 +29,7 @@ class FlowLogicRefTest { override fun call() = Unit } + @SchedulableFlow class KotlinNoArgFlowLogic : FlowLogic() { override fun call() = Unit } @@ -37,18 +41,18 @@ class FlowLogicRefTest { private val flowLogicRefFactory = FlowLogicRefFactoryImpl(FlowLogicRefFactoryImpl::class.java.classLoader) @Test fun `create kotlin no arg`() { - flowLogicRefFactory.createForRPC(KotlinNoArgFlowLogic::class.java) + flowLogicRefFactory.create(KotlinNoArgFlowLogic::class.jvmName) } @Test - fun `create kotlin`() { + fun `should create kotlin types`() { val args = mapOf(Pair("A", ParamType1(1)), Pair("b", ParamType2("Hello Jack"))) flowLogicRefFactory.createKotlin(KotlinFlowLogic::class.java, args) } @Test fun `create primary`() { - flowLogicRefFactory.createForRPC(KotlinFlowLogic::class.java, ParamType1(1), ParamType2("Hello Jack")) + flowLogicRefFactory.create(KotlinFlowLogic::class.jvmName, ParamType1(1), ParamType2("Hello Jack")) } @Test @@ -76,6 +80,6 @@ class FlowLogicRefTest { @Test(expected = IllegalFlowLogicException::class) fun `create for non-schedulable flow logic`() { - flowLogicRefFactory.create(NonSchedulableFlow::class.java) + flowLogicRefFactory.create(NonSchedulableFlow::class.jvmName) } } diff --git a/samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/contract/IRS.kt b/samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/contract/IRS.kt index c81d80592c..735c265ed6 100644 --- a/samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/contract/IRS.kt +++ b/samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/contract/IRS.kt @@ -616,7 +616,7 @@ class InterestRateSwap : Contract { // This is perhaps not how we should determine the time point in the business day, but instead expect the schedule to detail some of these aspects val instant = suggestInterestRateAnnouncementTimeWindow(index = nextFixingOf.name, source = floatingLeg.indexSource, date = nextFixingOf.forDay).fromTime!! - return ScheduledActivity(flowLogicRefFactory.create(FixingFlow.FixingRoleDecider::class.java, thisStateRef), instant) + return ScheduledActivity(flowLogicRefFactory.create("net.corda.irs.flows.FixingFlow\$FixingRoleDecider", thisStateRef), instant) } // DOCEND 1 diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/PortfolioState.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/PortfolioState.kt index 8b9b1d7414..98e490e68d 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/PortfolioState.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/PortfolioState.kt @@ -7,7 +7,6 @@ import net.corda.core.identity.Party import net.corda.core.serialization.CordaSerializable import net.corda.core.transactions.TransactionBuilder import net.corda.finance.contracts.DealState -import net.corda.vega.flows.SimmRevaluation import java.time.LocalDate import java.time.ZoneOffset import java.time.temporal.ChronoUnit @@ -32,7 +31,7 @@ data class PortfolioState(val portfolio: List, val valuer: AbstractParty get() = participants[0] override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity { - val flow = flowLogicRefFactory.create(SimmRevaluation.Initiator::class.java, thisStateRef, LocalDate.now()) + val flow = flowLogicRefFactory.create("net.corda.vega.flows.SimmRevaluation\$Initiator", thisStateRef, LocalDate.now()) return ScheduledActivity(flow, LocalDate.now().plus(1, ChronoUnit.DAYS).atStartOfDay().toInstant(ZoneOffset.UTC)) } From 63734aa3edc91c1f7c6753ef50132d1663cb4c0f Mon Sep 17 00:00:00 2001 From: Joel Dudley Date: Thu, 4 Jan 2018 13:35:56 +0000 Subject: [PATCH 10/19] Harmonises the names of the parameters across CashExitFlow constructors. --- .../src/main/kotlin/net/corda/finance/flows/CashExitFlow.kt | 6 +++--- .../main/kotlin/net/corda/explorer/ExplorerSimulation.kt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/finance/src/main/kotlin/net/corda/finance/flows/CashExitFlow.kt b/finance/src/main/kotlin/net/corda/finance/flows/CashExitFlow.kt index 23b05a1e8f..adc6f1b55a 100644 --- a/finance/src/main/kotlin/net/corda/finance/flows/CashExitFlow.kt +++ b/finance/src/main/kotlin/net/corda/finance/flows/CashExitFlow.kt @@ -29,8 +29,8 @@ import java.util.* class CashExitFlow(private val amount: Amount, private val issuerRef: OpaqueBytes, progressTracker: ProgressTracker) : AbstractCashFlow(progressTracker) { - constructor(amount: Amount, issueRef: OpaqueBytes) : this(amount, issueRef, tracker()) - constructor(request: ExitRequest) : this(request.amount, request.issueRef, tracker()) + constructor(amount: Amount, issuerRef: OpaqueBytes) : this(amount, issuerRef, tracker()) + constructor(request: ExitRequest) : this(request.amount, request.issuerRef, tracker()) companion object { fun tracker() = ProgressTracker(GENERATING_TX, SIGNING_TX, FINALISING_TX) @@ -78,5 +78,5 @@ class CashExitFlow(private val amount: Amount, } @CordaSerializable - class ExitRequest(amount: Amount, val issueRef: OpaqueBytes) : AbstractRequest(amount) + class ExitRequest(amount: Amount, val issuerRef: OpaqueBytes) : AbstractRequest(amount) } diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt index c88d2505d0..c3f0c0c173 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt @@ -141,7 +141,7 @@ class ExplorerSimulation(private val options: OptionSet) { it.startFlow(::CashIssueAndPaymentFlow, request).log(i, "${request.amount.token}Issuer") } is ExitRequest -> issuers[request.amount.token]?.let { - println("${Instant.now()} [$i] EXITING ${request.amount} with ref ${request.issueRef}") + println("${Instant.now()} [$i] EXITING ${request.amount} with ref ${request.issuerRef}") it.startFlow(::CashExitFlow, request).log(i, "${request.amount.token}Exit") } else -> throw IllegalArgumentException("Unsupported command: $request") From 01e4880947c775a562414909028f58c926a57f1e Mon Sep 17 00:00:00 2001 From: Katelyn Baker Date: Thu, 4 Jan 2018 15:49:55 +0000 Subject: [PATCH 11/19] SPELLING: updae error message in tests --- .../nodeapi/internal/serialization/amqp/ErrorMessageTests.java | 2 +- .../nodeapi/internal/serialization/amqp/ErrorMessagesTests.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/ErrorMessageTests.java b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/ErrorMessageTests.java index 2df95a808d..5e98cf9b82 100644 --- a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/ErrorMessageTests.java +++ b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/ErrorMessageTests.java @@ -10,7 +10,7 @@ public class ErrorMessageTests { private String errMsg(String property, String testname) { return "Property '" + property - + "' or it's getter is non public, this renders class 'class " + + "' or its getter is non public, this renders class 'class " + testname + "$C' unserializable -> class " + testname diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/ErrorMessagesTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/ErrorMessagesTests.kt index 775e5c3405..15f543da0d 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/ErrorMessagesTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/ErrorMessagesTests.kt @@ -10,7 +10,7 @@ class ErrorMessagesTests { } private fun errMsg(property:String, testname: String) = - "Property '$property' or it's getter is non public, this renders class 'class $testname\$C' unserializable -> class $testname\$C" + "Property '$property' or its getter is non public, this renders class 'class $testname\$C' unserializable -> class $testname\$C" @Test fun privateProperty() { From 48be562a2af5f50252a7f254edfc6d3efa575a6c Mon Sep 17 00:00:00 2001 From: Konstantinos Chalkias Date: Thu, 4 Jan 2018 17:52:31 +0000 Subject: [PATCH 12/19] ECDSA entropyToKeyPair + no idempotent signatures (#2210) --- .../kotlin/net/corda/core/crypto/Crypto.kt | 33 ++++- .../net/corda/core/crypto/CryptoUtils.kt | 9 +- .../net/corda/core/crypto/CryptoUtilsTest.kt | 116 +++++++++++++++++- .../transactions/NotaryServiceTests.kt | 2 + .../kotlin/net/corda/testing/node/MockNode.kt | 4 +- 5 files changed, 155 insertions(+), 9 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt b/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt index e736628215..1657eb2d4a 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt @@ -30,6 +30,7 @@ import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPublicKey import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter import org.bouncycastle.jce.ECNamedCurveTable import org.bouncycastle.jce.provider.BouncyCastleProvider +import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec import org.bouncycastle.jce.spec.ECParameterSpec import org.bouncycastle.jce.spec.ECPrivateKeySpec import org.bouncycastle.jce.spec.ECPublicKeySpec @@ -808,7 +809,7 @@ object Crypto { /** * Returns a key pair derived from the given [BigInteger] entropy. This is useful for unit tests * and other cases where you want hard-coded private keys. - * Currently, [EDDSA_ED25519_SHA512] is the sole scheme supported for this operation. + * Currently, the following schemes are supported: [EDDSA_ED25519_SHA512], [ECDSA_SECP256R1_SHA256] and [ECDSA_SECP256K1_SHA256]. * @param signatureScheme a supported [SignatureScheme], see [Crypto]. * @param entropy a [BigInteger] value. * @return a new [KeyPair] from an entropy input. @@ -818,6 +819,7 @@ object Crypto { fun deriveKeyPairFromEntropy(signatureScheme: SignatureScheme, entropy: BigInteger): KeyPair { return when (signatureScheme) { EDDSA_ED25519_SHA512 -> deriveEdDSAKeyPairFromEntropy(entropy) + ECDSA_SECP256R1_SHA256, ECDSA_SECP256K1_SHA256 -> deriveECDSAKeyPairFromEntropy(signatureScheme, entropy) else -> throw IllegalArgumentException("Unsupported signature scheme for fixed entropy-based key pair " + "generation: ${signatureScheme.schemeCodeName}") } @@ -832,6 +834,9 @@ object Crypto { fun deriveKeyPairFromEntropy(entropy: BigInteger): KeyPair = deriveKeyPairFromEntropy(DEFAULT_SIGNATURE_SCHEME, entropy) // Custom key pair generator from entropy. + // The BigIntenger.toByteArray() uses the two's-complement representation. + // The entropy is transformed to a byte array in big-endian byte-order and + // only the first ed25519.field.getb() / 8 bytes are used. private fun deriveEdDSAKeyPairFromEntropy(entropy: BigInteger): KeyPair { val params = EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec val bytes = entropy.toByteArray().copyOf(params.curve.field.getb() / 8) // Need to pad the entropy to the valid seed length. @@ -840,6 +845,32 @@ object Crypto { return KeyPair(EdDSAPublicKey(pub), EdDSAPrivateKey(priv)) } + // Custom key pair generator from an entropy required for various tests. It is similar to deriveKeyPairECDSA, + // but the accepted range of the input entropy is more relaxed: + // 2 <= entropy < N, where N is the order of base-point G. + private fun deriveECDSAKeyPairFromEntropy(signatureScheme: SignatureScheme, entropy: BigInteger): KeyPair { + val parameterSpec = signatureScheme.algSpec as ECNamedCurveParameterSpec + + // The entropy might be a negative number and/or out of range (e.g. PRNG output). + // In such cases we retry with hash(currentEntropy). + while (entropy < ECConstants.TWO || entropy >= parameterSpec.n) { + return deriveECDSAKeyPairFromEntropy(signatureScheme, BigInteger(1, entropy.toByteArray().sha256().bytes)) + } + + val privateKeySpec = ECPrivateKeySpec(entropy, parameterSpec) + val priv = BCECPrivateKey("EC", privateKeySpec, BouncyCastleProvider.CONFIGURATION) + + val pointQ = FixedPointCombMultiplier().multiply(parameterSpec.g, entropy) + while (pointQ.isInfinity) { + // Instead of throwing an exception, we retry with hash(entropy). + return deriveECDSAKeyPairFromEntropy(signatureScheme, BigInteger(1, entropy.toByteArray().sha256().bytes)) + } + val publicKeySpec = ECPublicKeySpec(pointQ, parameterSpec) + val pub = BCECPublicKey("EC", publicKeySpec, BouncyCastleProvider.CONFIGURATION) + + return KeyPair(pub, priv) + } + // Compute the HMAC-SHA512 using a privateKey as the MAC_key and a seed ByteArray. private fun deriveHMAC(privateKey: PrivateKey, seed: ByteArray): ByteArray { // Compute hmac(privateKey, seed). diff --git a/core/src/main/kotlin/net/corda/core/crypto/CryptoUtils.kt b/core/src/main/kotlin/net/corda/core/crypto/CryptoUtils.kt index 10e7c2f69c..96f0c85cc2 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/CryptoUtils.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/CryptoUtils.kt @@ -117,18 +117,19 @@ fun Iterable.byKeys() = map { it.by }.toSet() // Allow Kotlin destructuring: // val (private, public) = keyPair -/* The [PrivateKey] of this [KeyPair] .*/ +/* The [PrivateKey] of this [KeyPair]. */ operator fun KeyPair.component1(): PrivateKey = this.private -/* The [PublicKey] of this [KeyPair] .*/ +/* The [PublicKey] of this [KeyPair]. */ operator fun KeyPair.component2(): PublicKey = this.public -/** A simple wrapper that will make it easier to swap out the EC algorithm we use in future. */ +/** A simple wrapper that will make it easier to swap out the signature algorithm we use in future. */ fun generateKeyPair(): KeyPair = Crypto.generateKeyPair() /** * Returns a key pair derived from the given private key entropy. This is useful for unit tests and other cases where * you want hard-coded private keys. - * This currently works for the default signature scheme EdDSA ed25519 only. + * @param entropy a [BigInteger] value. + * @return a deterministically generated [KeyPair] for the [Crypto.DEFAULT_SIGNATURE_SCHEME]. */ fun entropyToKeyPair(entropy: BigInteger): KeyPair = Crypto.deriveKeyPairFromEntropy(entropy) diff --git a/core/src/test/kotlin/net/corda/core/crypto/CryptoUtilsTest.kt b/core/src/test/kotlin/net/corda/core/crypto/CryptoUtilsTest.kt index d10944bdb8..587700fc32 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/CryptoUtilsTest.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/CryptoUtilsTest.kt @@ -14,21 +14,22 @@ import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey import org.bouncycastle.jce.ECNamedCurveTable import org.bouncycastle.jce.interfaces.ECKey +import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PrivateKey import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PublicKey import org.junit.Assert.assertNotEquals import org.junit.Test +import java.math.BigInteger import java.security.KeyPairGenerator import java.util.* import kotlin.test.* /** - * Run tests for cryptographic algorithms + * Run tests for cryptographic algorithms. */ class CryptoUtilsTest { - private val testString = "Hello World" - private val testBytes = testString.toByteArray() + private val testBytes = "Hello World".toByteArray() // key generation test @Test @@ -781,4 +782,113 @@ class CryptoUtilsTest { assertEquals(dpriv2, dpriv_2) assertEquals(dpub2, dpub_2) } + + @Test + fun `EdDSA ed25519 keyPair from entropy`() { + val keyPairPositive = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("10")) + assertEquals("DLBL3iHCp9uRReWhhCGfCsrxZZpfAm9h9GLbfN8ijqXTq", keyPairPositive.public.toStringShort()) + + val keyPairNegative = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("-10")) + assertEquals("DLC5HXnYsJAFqmM9hgPj5G8whQ4TpyE9WMBssqCayLBwA2", keyPairNegative.public.toStringShort()) + + val keyPairZero = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("0")) + assertEquals("DL4UVhGh4tqu1G86UVoGNaDDNCMsBtNHzE6BSZuNNJN7W2", keyPairZero.public.toStringShort()) + + val keyPairOne = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("1")) + assertEquals("DL8EZUdHixovcCynKMQzrMWBnXQAcbVDHi6ArPphqwJVzq", keyPairOne.public.toStringShort()) + + val keyPairBiggerThan256bits = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("2").pow(258).minus(BigInteger.TEN)) + assertEquals("DLB9K1UiBrWonn481z6NzkqoWHjMBXpfDeaet3wiwRNWSU", keyPairBiggerThan256bits.public.toStringShort()) + // The underlying implementation uses the first 256 bytes of the entropy. Thus, 2^258-10 and 2^258-50 and 2^514-10 have the same impact. + val keyPairBiggerThan256bitsV2 = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("2").pow(258).minus(BigInteger("50"))) + assertEquals("DLB9K1UiBrWonn481z6NzkqoWHjMBXpfDeaet3wiwRNWSU", keyPairBiggerThan256bitsV2.public.toStringShort()) + val keyPairBiggerThan512bits = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("2").pow(514).minus(BigInteger.TEN)) + assertEquals("DLB9K1UiBrWonn481z6NzkqoWHjMBXpfDeaet3wiwRNWSU", keyPairBiggerThan512bits.public.toStringShort()) + + // Try another big number. + val keyPairBiggerThan258bits = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("2").pow(259).plus(BigInteger.ONE)) + assertEquals("DL5tEFVMXMGrzwjfCAW34JjkhsRkPfFyJ38iEnmpB6L2Z9", keyPairBiggerThan258bits.public.toStringShort()) + } + + @Test + fun `ECDSA R1 keyPair from entropy`() { + val keyPairPositive = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("10")) + assertEquals("DLHDcxuSt9J3cbjd2Dsx4rAgYYA7BAP7A8VLrFiq1tH9yy", keyPairPositive.public.toStringShort()) + // The underlying implementation uses the hash of entropy if it is out of range 2 < entropy < N, where N the order of the group. + val keyPairNegative = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("-10")) + assertEquals("DLBASmjiMZuu1g3EtdHJxfSueXE8PRoUWbkdU61Qcnpamt", keyPairNegative.public.toStringShort()) + + val keyPairZero = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("0")) + assertEquals("DLH2FEHEnsT3MpCJt2gfyNjpqRqcBxeupK4YRPXvDsVEkb", keyPairZero.public.toStringShort()) + // BigIntenger.Zero is out or range, so 1 and hash(1.toByteArray) would have the same impact. + val zeroHashed = BigInteger(1, BigInteger("0").toByteArray().sha256().bytes) + // Check oneHashed < N (order of the group), otherwise we would need an extra hash. + assertEquals(-1, zeroHashed.compareTo((Crypto.ECDSA_SECP256R1_SHA256.algSpec as ECNamedCurveParameterSpec).n)) + val keyPairZeroHashed = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, zeroHashed) + assertEquals("DLH2FEHEnsT3MpCJt2gfyNjpqRqcBxeupK4YRPXvDsVEkb", keyPairZeroHashed.public.toStringShort()) + + val keyPairOne = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("1")) + assertEquals("DLHrtKwjv6onq9HcrQDJPs8Cgtai5mZU5ZU6sb1ivJjx3z", keyPairOne.public.toStringShort()) + // BigIntenger.ONE is out or range, so 1 and hash(1.toByteArray) would have the same impact. + val oneHashed = BigInteger(1, BigInteger("1").toByteArray().sha256().bytes) + // Check oneHashed < N (order of the group), otherwise we would need an extra hash. + assertEquals(-1, oneHashed.compareTo((Crypto.ECDSA_SECP256R1_SHA256.algSpec as ECNamedCurveParameterSpec).n)) + val keyPairOneHashed = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, oneHashed) + assertEquals("DLHrtKwjv6onq9HcrQDJPs8Cgtai5mZU5ZU6sb1ivJjx3z", keyPairOneHashed.public.toStringShort()) + + // 2 is in the range. + val keyPairTwo = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2")) + assertEquals("DLFoz6txJ3vHcKNSM1vFxHJUoEQ69PorBwW64dHsAnEoZB", keyPairTwo.public.toStringShort()) + + // Try big numbers that are out of range. + val keyPairBiggerThan256bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2").pow(258).minus(BigInteger.TEN)) + assertEquals("DLBv6fZqaCTbE4L7sgjbt19biXHMgU9CzR5s8g8XBJjZ11", keyPairBiggerThan256bits.public.toStringShort()) + val keyPairBiggerThan256bitsV2 = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2").pow(258).minus(BigInteger("50"))) + assertEquals("DLANmjhGSVdLyghxcPHrn3KuGatscf6LtvqifUDxw7SGU8", keyPairBiggerThan256bitsV2.public.toStringShort()) + val keyPairBiggerThan512bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2").pow(514).minus(BigInteger.TEN)) + assertEquals("DL9sKwMExBTD3MnJN6LWGqo496Erkebs9fxZtXLVJUBY9Z", keyPairBiggerThan512bits.public.toStringShort()) + val keyPairBiggerThan258bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2").pow(259).plus(BigInteger.ONE)) + assertEquals("DLBwjWwPJSF9E7b1NWaSbEJ4oK8CF7RDGWd648TiBhZoL1", keyPairBiggerThan258bits.public.toStringShort()) + } + + @Test + fun `ECDSA K1 keyPair from entropy`() { + val keyPairPositive = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("10")) + assertEquals("DL6pYKUgH17az8MLdonvvUtUPN8TqwpCGcdgLr7vg3skCU", keyPairPositive.public.toStringShort()) + // The underlying implementation uses the hash of entropy if it is out of range 2 <= entropy < N, where N the order of the group. + val keyPairNegative = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("-10")) + assertEquals("DLnpXhxece69Nyqgm3pPt3yV7ESQYDJKoYxs1hKgfBAEu", keyPairNegative.public.toStringShort()) + + val keyPairZero = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("0")) + assertEquals("DLBC28e18T6KsYwjTFfUWJfhvHjvYVapyVf6antnqUkbgd", keyPairZero.public.toStringShort()) + // BigIntenger.Zero is out or range, so 1 and hash(1.toByteArray) would have the same impact. + val zeroHashed = BigInteger(1, BigInteger("0").toByteArray().sha256().bytes) + // Check oneHashed < N (order of the group), otherwise we would need an extra hash. + assertEquals(-1, zeroHashed.compareTo((Crypto.ECDSA_SECP256K1_SHA256.algSpec as ECNamedCurveParameterSpec).n)) + val keyPairZeroHashed = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, zeroHashed) + assertEquals("DLBC28e18T6KsYwjTFfUWJfhvHjvYVapyVf6antnqUkbgd", keyPairZeroHashed.public.toStringShort()) + + val keyPairOne = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("1")) + assertEquals("DLBimRXdEQhJUTpL6f9ri9woNdsze6mwkRrhsML13Eh7ET", keyPairOne.public.toStringShort()) + // BigIntenger.ONE is out or range, so 1 and hash(1.toByteArray) would have the same impact. + val oneHashed = BigInteger(1, BigInteger("1").toByteArray().sha256().bytes) + // Check oneHashed < N (order of the group), otherwise we would need an extra hash. + assertEquals(-1, oneHashed.compareTo((Crypto.ECDSA_SECP256K1_SHA256.algSpec as ECNamedCurveParameterSpec).n)) + val keyPairOneHashed = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, oneHashed) + assertEquals("DLBimRXdEQhJUTpL6f9ri9woNdsze6mwkRrhsML13Eh7ET", keyPairOneHashed.public.toStringShort()) + + // 2 is in the range. + val keyPairTwo = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2")) + assertEquals("DLG32UWaevGw9YY7w1Rf9mmK88biavgpDnJA9bG4GapVPs", keyPairTwo.public.toStringShort()) + + // Try big numbers that are out of range. + val keyPairBiggerThan256bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2").pow(258).minus(BigInteger.TEN)) + assertEquals("DLGHsdv2xeAuM7n3sBc6mFfiphXe6VSf3YxqvviKDU6Vbd", keyPairBiggerThan256bits.public.toStringShort()) + val keyPairBiggerThan256bitsV2 = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2").pow(258).minus(BigInteger("50"))) + assertEquals("DL9yJfiNGqteRrKPjGUkRQkeqzuQ4kwcYQWMCi5YKuUHrk", keyPairBiggerThan256bitsV2.public.toStringShort()) + val keyPairBiggerThan512bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2").pow(514).minus(BigInteger.TEN)) + assertEquals("DL3Wr5EQGrMTaKBy5XMvG8rvSfKX1AYZLCRU8kixGbxt1E", keyPairBiggerThan512bits.public.toStringShort()) + val keyPairBiggerThan258bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2").pow(259).plus(BigInteger.ONE)) + assertEquals("DL7NbssqvuuJ4cqFkkaVYu9j1MsVswESGgCfbqBS9ULwuM", keyPairBiggerThan258bits.public.toStringShort()) + } } diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt index 70cbc90835..77c5cf797b 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt @@ -24,6 +24,7 @@ import net.corda.testing.node.startFlow import org.assertj.core.api.Assertions.assertThat import org.junit.After import org.junit.Before +import org.junit.Ignore import org.junit.Test import java.time.Instant import java.util.* @@ -99,6 +100,7 @@ class NotaryServiceTests { assertThat(ex.error).isInstanceOf(NotaryError.TimeWindowInvalid::class.java) } + @Ignore("Only applies to deterministic signature schemes (e.g. EdDSA) and when deterministic metadata is attached (no timestamps or nonces)") @Test fun `should sign identical transaction multiple times (signing is idempotent)`() { val stx = run { diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt index fabb3d999a..0b8ae32540 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt @@ -6,6 +6,7 @@ import com.nhaarman.mockito_kotlin.doReturn import com.nhaarman.mockito_kotlin.whenever import net.corda.core.DoNotImplement import net.corda.core.crypto.entropyToKeyPair +import net.corda.core.crypto.Crypto import net.corda.core.crypto.random63BitValue import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party @@ -325,7 +326,8 @@ open class MockNetwork(private val cordappPackages: List, // This is not thread safe, but node construction is done on a single thread, so that should always be fine override fun generateKeyPair(): KeyPair { counter = counter.add(BigInteger.ONE) - return entropyToKeyPair(counter) + // The MockNode specifically uses EdDSA keys as they are fixed and stored in json files for some tests (e.g IRSSimulation). + return Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, counter) } /** From 00a02b30fe4915d1f0800ebcf13a281760d32062 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Thu, 4 Jan 2018 18:05:06 +0000 Subject: [PATCH 13/19] Update link for Corda API strategy documentation. (#2316) --- gradle-plugins/api-scanner/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle-plugins/api-scanner/README.md b/gradle-plugins/api-scanner/README.md index 9f8fc4f674..a0256d480b 100644 --- a/gradle-plugins/api-scanner/README.md +++ b/gradle-plugins/api-scanner/README.md @@ -6,7 +6,7 @@ Generates a text summary of Corda's public API that we can check for API-breakin $ gradlew generateApi ``` -See [here](../../docs/source/api-index.rst) for Corda's public API strategy. We will need to +See [here](../../docs/source/corda-api.rst) for Corda's public API strategy. We will need to apply this plugin to other modules in future Corda releases as those modules' APIs stabilise. Basically, this plugin will document a module's `public` and `protected` classes/methods/fields, From 133fd27222ecc1ee22ee392ad168cafa216067e3 Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Thu, 4 Jan 2018 15:06:21 +0000 Subject: [PATCH 14/19] Merge fixes and updated network-management to work with the move away from X509CertificateHolder --- .../doorman/DoormanIntegrationTest.kt | 60 ++++++------- .../hsm/SigningServiceIntegrationTest.kt | 11 ++- .../PersistentCertificateRequestStorage.kt | 27 +++--- .../corda/networkmanage/doorman/JiraCient.kt | 16 ++-- .../r3/corda/networkmanage/doorman/Main.kt | 24 ++++-- .../doorman/signer/LocalSigner.kt | 10 +-- .../hsm/signer/HsmNetworkMapSigner.kt | 5 +- .../networkmanage/hsm/utils/X509Utils.kt | 9 +- ...ersistentCertificateRequestStorageTest.kt} | 57 ++++++------ .../PersistentNetworkMapStorageTest.kt | 28 +++--- .../PersitenceNodeInfoStorageTest.kt | 29 +++++-- .../common/signer/NetworkMapSignerTest.kt | 31 +++---- .../doorman/DefaultRequestProcessorTest.kt | 15 ++-- .../doorman/NodeInfoWebServiceTest.kt | 31 ++++--- .../doorman/RegistrationWebServiceTest.kt | 86 ++++++++++++------- .../doorman/signer/CsrHandlerTest.kt | 5 +- 16 files changed, 248 insertions(+), 196 deletions(-) rename network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/{DBCertificateRequestStorageTest.kt => PersistentCertificateRequestStorageTest.kt} (71%) diff --git a/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/doorman/DoormanIntegrationTest.kt b/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/doorman/DoormanIntegrationTest.kt index 5993cef210..207a524aba 100644 --- a/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/doorman/DoormanIntegrationTest.kt +++ b/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/doorman/DoormanIntegrationTest.kt @@ -3,14 +3,11 @@ package com.r3.corda.networkmanage.doorman import com.nhaarman.mockito_kotlin.doReturn import com.nhaarman.mockito_kotlin.whenever import com.r3.corda.networkmanage.common.persistence.configureDatabase -import com.r3.corda.networkmanage.common.utils.toX509Certificate import com.r3.corda.networkmanage.doorman.signer.LocalSigner import net.corda.core.crypto.Crypto import net.corda.core.crypto.SecureHash import net.corda.core.crypto.sign -import net.corda.core.identity.CordaX500Name import net.corda.core.identity.PartyAndCertificate -import net.corda.core.internal.cert import net.corda.core.internal.createDirectories import net.corda.core.internal.div import net.corda.core.node.NodeInfo @@ -28,12 +25,13 @@ import net.corda.testing.ALICE_NAME import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.internal.rigorousMock -import org.bouncycastle.cert.X509CertificateHolder import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder import java.net.URL +import java.security.cert.X509Certificate import java.util.* +import javax.security.auth.x500.X500Principal import kotlin.test.assertEquals import kotlin.test.assertNotNull @@ -60,7 +58,7 @@ class DoormanIntegrationTest { } config.trustStoreFile.parent.createDirectories() loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also { - it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCertAndKey.certificate.cert) + it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCertAndKey.certificate) it.save(config.trustStoreFile, config.trustStorePassword) } @@ -77,18 +75,18 @@ class DoormanIntegrationTest { loadKeyStore(config.nodeKeystore, config.keyStorePassword).apply { assert(containsAlias(X509Utilities.CORDA_CLIENT_CA)) assertEquals(ALICE_NAME.x500Principal, getX509Certificate(X509Utilities.CORDA_CLIENT_CA).subjectX500Principal) - assertEquals(listOf(intermediateCACert.cert, rootCACert.cert), getCertificateChain(X509Utilities.CORDA_CLIENT_CA).drop(1).toList()) + assertEquals(listOf(intermediateCACert, rootCACert), getCertificateChain(X509Utilities.CORDA_CLIENT_CA).drop(1).toList()) } loadKeyStore(config.sslKeystore, config.keyStorePassword).apply { assert(containsAlias(X509Utilities.CORDA_CLIENT_TLS)) assertEquals(ALICE_NAME.x500Principal, getX509Certificate(X509Utilities.CORDA_CLIENT_TLS).subjectX500Principal) - assertEquals(listOf(intermediateCACert.cert, rootCACert.cert), getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).drop(2).toList()) + assertEquals(listOf(intermediateCACert, rootCACert), getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).drop(2).toList()) } loadKeyStore(config.trustStoreFile, config.trustStorePassword).apply { assert(containsAlias(X509Utilities.CORDA_ROOT_CA)) - assertEquals(rootCACert.cert.subjectX500Principal, getX509Certificate(X509Utilities.CORDA_ROOT_CA).subjectX500Principal) + assertEquals(rootCACert.subjectX500Principal, getX509Certificate(X509Utilities.CORDA_ROOT_CA).subjectX500Principal) } doorman.close() @@ -110,21 +108,26 @@ class DoormanIntegrationTest { } config.trustStoreFile.parent.createDirectories() loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also { - it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCertAndKey.certificate.cert) + it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCertAndKey.certificate) it.save(config.trustStoreFile, config.trustStorePassword) } NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.compatibilityZoneURL!!)).buildKeystore() // Publish NodeInfo - val networkMapClient = NetworkMapClient(config.compatibilityZoneURL!!, rootCertAndKey.certificate.cert) + val networkMapClient = NetworkMapClient(config.compatibilityZoneURL!!, rootCertAndKey.certificate) val keyStore = loadKeyStore(config.nodeKeystore, config.keyStorePassword) val clientCertPath = keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA) val clientCA = keyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, config.keyStorePassword) val identityKeyPair = Crypto.generateKeyPair() - val identityCert = X509Utilities.createCertificate(CertificateType.LEGAL_IDENTITY, clientCA.certificate, clientCA.keyPair, ALICE_NAME, identityKeyPair.public) - val certPath = X509CertificateFactory().generateCertPath(identityCert.cert, *clientCertPath) + val identityCert = X509Utilities.createCertificate( + CertificateType.LEGAL_IDENTITY, + clientCA.certificate, + clientCA.keyPair, + ALICE_NAME.x500Principal, + identityKeyPair.public) + val certPath = X509CertificateFactory().generateCertPath(identityCert, *clientCertPath) val nodeInfo = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(certPath)), 1, serial = 1L) val nodeInfoBytes = nodeInfo.serialize() @@ -156,23 +159,23 @@ class DoormanIntegrationTest { } -fun createDoormanIntermediateCertificateAndKeyPair(rootCertificateAndKeyPair: CertificateAndKeyPair): CertificateAndKeyPair { - val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCertificateAndKeyPair.certificate, rootCertificateAndKeyPair.keyPair, - CordaX500Name(commonName = "Integration Test Corda Node Intermediate CA", - locality = "London", - country = "GB", - organisation = "R3 Ltd"), intermediateCAKey.public) - return CertificateAndKeyPair(intermediateCACert, intermediateCAKey) +fun createDoormanIntermediateCertificateAndKeyPair(rootCa: CertificateAndKeyPair): CertificateAndKeyPair { + val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) + val intermediateCACert = X509Utilities.createCertificate( + CertificateType.INTERMEDIATE_CA, + rootCa.certificate, + rootCa.keyPair, + X500Principal("CN=Integration Test Corda Node Intermediate CA,O=R3 Ltd,L=London,C=GB"), + keyPair.public) + return CertificateAndKeyPair(intermediateCACert, keyPair) } fun createDoormanRootCertificateAndKeyPair(): CertificateAndKeyPair { - val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val rootCACert = X509Utilities.createSelfSignedCACertificate( - CordaX500Name(commonName = "Integration Test Corda Node Root CA", - organisation = "R3 Ltd", locality = "London", - country = "GB"), rootCAKey) - return CertificateAndKeyPair(rootCACert, rootCAKey) + val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) + val rootCaCert = X509Utilities.createSelfSignedCACertificate( + X500Principal("CN=Integration Test Corda Node Root CA,O=R3 Ltd,L=London,C=GB"), + keyPair) + return CertificateAndKeyPair(rootCaCert, keyPair) } fun makeTestDataSourceProperties(nodeName: String = SecureHash.randomSHA256().toString()): Properties { @@ -184,9 +187,8 @@ fun makeTestDataSourceProperties(nodeName: String = SecureHash.randomSHA256().to return props } -fun startDoorman(intermediateCACertAndKey: CertificateAndKeyPair, rootCACert: X509CertificateHolder): NetworkManagementServer { - val signer = LocalSigner(intermediateCACertAndKey.keyPair, - arrayOf(intermediateCACertAndKey.certificate.toX509Certificate(), rootCACert.toX509Certificate())) +fun startDoorman(intermediateCACertAndKey: CertificateAndKeyPair, rootCACert: X509Certificate): NetworkManagementServer { + val signer = LocalSigner(intermediateCACertAndKey.keyPair, arrayOf(intermediateCACertAndKey.certificate, rootCACert)) //Start doorman server return startDoorman(signer) } diff --git a/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/hsm/SigningServiceIntegrationTest.kt b/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/hsm/SigningServiceIntegrationTest.kt index 1c0bef94f2..b2e7298de1 100644 --- a/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/hsm/SigningServiceIntegrationTest.kt +++ b/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/hsm/SigningServiceIntegrationTest.kt @@ -10,7 +10,6 @@ import com.r3.corda.networkmanage.hsm.persistence.DBSignedCertificateRequestStor import com.r3.corda.networkmanage.hsm.persistence.SignedCertificateRequestStorage import com.r3.corda.networkmanage.hsm.signer.HsmCsrSigner import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.cert import net.corda.core.internal.createDirectories import net.corda.core.internal.div import net.corda.core.internal.uncheckedCast @@ -28,12 +27,12 @@ import net.corda.testing.CHARLIE_NAME import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.testing.internal.rigorousMock -import org.bouncycastle.cert.X509CertificateHolder import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest import org.h2.tools.Server import org.junit.* import org.junit.rules.TemporaryFolder import java.net.URL +import java.security.cert.X509Certificate import java.util.* import javax.persistence.PersistenceException import kotlin.concurrent.scheduleAtFixedRate @@ -55,7 +54,7 @@ class SigningServiceIntegrationTest { val testSerialization = SerializationEnvironmentRule(true) private lateinit var timer: Timer - private lateinit var rootCaCert: X509CertificateHolder + private lateinit var rootCaCert: X509Certificate private lateinit var intermediateCa: CertificateAndKeyPair @Before @@ -79,7 +78,7 @@ class SigningServiceIntegrationTest { for (approvedRequest in approvedRequests) { JcaPKCS10CertificationRequest(approvedRequest.request).run { val nodeCa = createDevNodeCa(intermediateCa, CordaX500Name.parse(subject.toString())) - approvedRequest.certPath = buildCertPath(nodeCa.certificate.cert, intermediateCa.certificate.cert, rootCaCert.cert) + approvedRequest.certPath = buildCertPath(nodeCa.certificate, intermediateCa.certificate, rootCaCert) } } storage.store(approvedRequests, listOf("TEST")) @@ -124,7 +123,7 @@ class SigningServiceIntegrationTest { } config.certificatesDirectory.createDirectories() loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also { - it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCaCert.cert) + it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCaCert) it.save(config.trustStoreFile, config.trustStorePassword) } NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.compatibilityZoneURL!!)).buildKeystore() @@ -168,7 +167,7 @@ class SigningServiceIntegrationTest { } config.certificatesDirectory.createDirectories() loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also { - it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCaCert.cert) + it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCaCert) it.save(config.trustStoreFile, config.trustStorePassword) } NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.compatibilityZoneURL!!)).buildKeystore() diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentCertificateRequestStorage.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentCertificateRequestStorage.kt index f134727fac..566729a239 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentCertificateRequestStorage.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentCertificateRequestStorage.kt @@ -5,15 +5,14 @@ import com.r3.corda.networkmanage.common.persistence.entity.CertificateSigningRe import com.r3.corda.networkmanage.common.utils.hashString import net.corda.core.crypto.SecureHash import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.x500Name import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseTransaction import net.corda.nodeapi.internal.persistence.TransactionIsolationLevel -import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.pkcs.PKCS10CertificationRequest import org.hibernate.Session import java.security.cert.CertPath import java.time.Instant +import javax.security.auth.x500.X500Principal /** * Database implementation of the [CertificationRequestStorage] interface. @@ -48,7 +47,7 @@ class PersistentCertificateRequestStorage(private val database: CordaPersistence val (legalName, rejectReason) = parseAndValidateLegalName(request, session) session.save(CertificateSigningRequestEntity( requestId = requestId, - legalName = legalName.toString(), + legalName = legalName, requestBytes = request.encoded, remark = rejectReason, modifiedBy = emptyList(), @@ -126,25 +125,27 @@ class PersistentCertificateRequestStorage(private val database: CordaPersistence } } - private fun parseAndValidateLegalName(request: PKCS10CertificationRequest, session: Session): Pair { + private fun parseAndValidateLegalName(request: PKCS10CertificationRequest, session: Session): Pair { + // It's important that we always use the toString() output of CordaX500Name as it standardises the string format + // to make querying possible. val legalName = try { - CordaX500Name.parse(request.subject.toString()) + CordaX500Name.build(X500Principal(request.subject.encoded)).toString() } catch (e: IllegalArgumentException) { - return Pair(request.subject, "Name validation failed with exception : ${e.message}") + return Pair(request.subject.toString(), "Name validation failed: ${e.message}") } + val query = session.criteriaBuilder.run { val criteriaQuery = createQuery(CertificateSigningRequestEntity::class.java) criteriaQuery.from(CertificateSigningRequestEntity::class.java).run { - criteriaQuery.where(equal(get(CertificateSigningRequestEntity::legalName.name), legalName.toString())) + criteriaQuery.where(equal(get(CertificateSigningRequestEntity::legalName.name), legalName)) } } + val duplicates = session.createQuery(query).resultList.filter { - it.status in setOf(RequestStatus.NEW, RequestStatus.TICKET_CREATED, RequestStatus.APPROVED) || it.certificateData?.certificateStatus == CertificateStatus.VALID - } - return if (duplicates.isEmpty()) { - Pair(legalName.x500Name, null) - } else { - Pair(legalName.x500Name, "Duplicate legal name") + it.status in setOf(RequestStatus.NEW, RequestStatus.TICKET_CREATED, RequestStatus.APPROVED) || + it.certificateData?.certificateStatus == CertificateStatus.VALID } + + return Pair(legalName, if (duplicates.isEmpty()) null else "Duplicate legal name") } } \ No newline at end of file diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/JiraCient.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/JiraCient.kt index e53a927b14..d7ebf4abc0 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/JiraCient.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/JiraCient.kt @@ -6,9 +6,7 @@ import com.atlassian.jira.rest.client.api.domain.Issue import com.atlassian.jira.rest.client.api.domain.IssueType import com.atlassian.jira.rest.client.api.domain.input.IssueInputBuilder import com.atlassian.jira.rest.client.api.domain.input.TransitionInput -import net.corda.core.internal.country -import net.corda.core.internal.locality -import net.corda.core.internal.organisation +import net.corda.core.identity.CordaX500Name import net.corda.core.utilities.loggerFor import net.corda.nodeapi.internal.crypto.X509Utilities import org.bouncycastle.asn1.x500.style.BCStyle @@ -17,6 +15,7 @@ import org.bouncycastle.pkcs.PKCS10CertificationRequest import org.bouncycastle.util.io.pem.PemObject import java.io.StringWriter import java.security.cert.CertPath +import javax.security.auth.x500.X500Principal class JiraClient(private val restClient: JiraRestClient, private val projectCode: String, private val doneTransitionCode: Int) { companion object { @@ -39,16 +38,17 @@ class JiraClient(private val restClient: JiraRestClient, private val projectCode JcaPEMWriter(request).use { it.writeObject(PemObject("CERTIFICATE REQUEST", signingRequest.encoded)) } - val organisation = signingRequest.subject.organisation - val nearestCity = signingRequest.subject.locality - val country = signingRequest.subject.country + + // TODO The subject of the signing request has already been validated and parsed into a CordaX500Name. We shouldn't + // have to do it again here. + val subject = CordaX500Name.build(X500Principal(signingRequest.subject.encoded)) val email = signingRequest.getAttributes(BCStyle.E).firstOrNull()?.attrValues?.firstOrNull()?.toString() val issue = IssueInputBuilder().setIssueTypeId(taskIssueType.id) .setProjectKey(projectCode) - .setDescription("Organisation: $organisation\nNearest City: $nearestCity\nCountry: $country\nEmail: $email\n\n{code}$request{code}") - .setSummary(organisation) + .setDescription("Organisation: ${subject.organisation}\nNearest City: ${subject.locality}\nCountry: ${subject.country}\nEmail: $email\n\n{code}$request{code}") + .setSummary(subject.organisation) .setFieldValue(requestIdField.id, requestId) // This will block until the issue is created. restClient.issueClient.createIssue(issue.build()).fail { logger.error("Exception when creating JIRA issue.", it) }.claim() diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/Main.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/Main.kt index 2a4db95100..6b5f3636f3 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/Main.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/Main.kt @@ -13,7 +13,6 @@ import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService import com.r3.corda.networkmanage.doorman.webservice.RegistrationWebService import net.corda.core.crypto.Crypto import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.cert import net.corda.core.internal.createDirectories import net.corda.core.internal.div import net.corda.core.serialization.internal.SerializationEnvironmentImpl @@ -188,8 +187,8 @@ fun generateRootKeyPair(rootStoreFile: Path, rootKeystorePass: String?, rootPriv val selfSignKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) // TODO Make the cert subject configurable val selfSignCert = X509Utilities.createSelfSignedCACertificate( - CordaX500Name(commonName = "Corda Root CA", organisation = "R3 Ltd", locality = "London", country = "GB", organisationUnit = "Corda", state = null), - selfSignKey).cert + CordaX500Name(commonName = "Corda Root CA", organisation = "R3 Ltd", locality = "London", country = "GB", organisationUnit = "Corda", state = null).x500Principal, + selfSignKey) rootStore.addOrReplaceKey(X509Utilities.CORDA_ROOT_CA, selfSignKey.private, rootPrivateKeyPassword.toCharArray(), arrayOf(selfSignCert)) rootStore.save(rootStoreFile, rootKeystorePassword) @@ -226,11 +225,20 @@ fun generateCAKeyPair(keystoreFile: Path, rootStoreFile: Path, rootKeystorePass: exitProcess(1) } - val intermediateKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val intermediateCert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootKeyAndCert.certificate, rootKeyAndCert.keyPair, - CordaX500Name(commonName = "Corda Intermediate CA", organisation = "R3 Ltd", organisationUnit = "Corda", locality = "London", country = "GB", state = null), intermediateKey.public) - keyStore.addOrReplaceKey(X509Utilities.CORDA_INTERMEDIATE_CA, intermediateKey.private, - caPrivateKeyPassword.toCharArray(), arrayOf(intermediateCert, rootKeyAndCert.certificate)) + val intermediateKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) + val intermediateCert = X509Utilities.createCertificate( + CertificateType.INTERMEDIATE_CA, + rootKeyAndCert.certificate, + rootKeyAndCert.keyPair, + CordaX500Name(commonName = "Corda Intermediate CA", organisation = "R3 Ltd", organisationUnit = "Corda", locality = "London", country = "GB", state = null).x500Principal, + intermediateKeyPair.public + ) + keyStore.addOrReplaceKey( + X509Utilities.CORDA_INTERMEDIATE_CA, + intermediateKeyPair.private, + caPrivateKeyPassword.toCharArray(), + arrayOf(intermediateCert, rootKeyAndCert.certificate) + ) keyStore.save(keystoreFile, keystorePassword) println("Intermediate CA keypair and certificate stored in $keystoreFile.") println(loadKeyStore(keystoreFile, keystorePassword).getCertificate(X509Utilities.CORDA_INTERMEDIATE_CA).publicKey) diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/signer/LocalSigner.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/signer/LocalSigner.kt index 50a7b7f971..aa7ceaa742 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/signer/LocalSigner.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/signer/LocalSigner.kt @@ -4,9 +4,6 @@ import com.r3.corda.networkmanage.common.signer.Signer import com.r3.corda.networkmanage.common.utils.buildCertPath import com.r3.corda.networkmanage.common.utils.withCert import net.corda.core.crypto.sign -import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.cert -import net.corda.core.internal.toX509CertHolder import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.network.DigitalSignatureWithCert @@ -18,6 +15,7 @@ import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest import java.security.KeyPair import java.security.cert.CertPath import java.security.cert.X509Certificate +import javax.security.auth.x500.X500Principal /** * The [LocalSigner] class signs [PKCS10CertificationRequest] using provided CA key pair and certificate path. @@ -35,12 +33,12 @@ class LocalSigner(private val caKeyPair: KeyPair, private val caCertPath: Array< arrayOf()) val nodeCaCert = X509Utilities.createCertificate( CertificateType.NODE_CA, - caCertPath.first().toX509CertHolder(), + caCertPath[0], caKeyPair, - CordaX500Name.parse(request.subject.toString()), + X500Principal(request.subject.encoded), request.publicKey, nameConstraints = nameConstraints) - return buildCertPath(nodeCaCert.cert, *caCertPath) + return buildCertPath(nodeCaCert, *caCertPath) } override fun sign(data: ByteArray): DigitalSignatureWithCert { diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/signer/HsmNetworkMapSigner.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/signer/HsmNetworkMapSigner.kt index 2fec2a234c..0465a3621a 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/signer/HsmNetworkMapSigner.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/signer/HsmNetworkMapSigner.kt @@ -9,13 +9,12 @@ import com.r3.corda.networkmanage.hsm.authentication.Authenticator import com.r3.corda.networkmanage.hsm.utils.X509Utilities.getAndInitializeKeyStore import com.r3.corda.networkmanage.hsm.utils.X509Utilities.signData import com.r3.corda.networkmanage.hsm.utils.X509Utilities.verify -import net.corda.core.internal.cert -import net.corda.core.internal.toX509CertHolder import net.corda.core.utilities.loggerFor import net.corda.core.utilities.minutes import net.corda.nodeapi.internal.network.DigitalSignatureWithCert import java.security.KeyPair import java.security.PrivateKey +import java.security.cert.X509Certificate import java.time.Duration import java.util.concurrent.Executors import java.util.concurrent.ScheduledExecutorService @@ -68,7 +67,7 @@ class HsmNetworkMapSigner(networkMapStorage: NetworkMapStorage, val caKey = keyStore.getKey(caCertificateKeyName, caPrivateKeyPass.toCharArray()) as PrivateKey val signature = signData(data, KeyPair(caCertificateChain.first().publicKey, caKey), provider) verify(data, signature, caCertificateChain.first().publicKey) - signature.withCert(caCertificateChain.first().toX509CertHolder().cert) + signature.withCert(caCertificateChain[0] as X509Certificate) } } } diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/utils/X509Utils.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/utils/X509Utils.kt index 22e94ec1b5..dc4354fe0b 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/utils/X509Utils.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/utils/X509Utils.kt @@ -3,7 +3,6 @@ package com.r3.corda.networkmanage.hsm.utils import CryptoServerJCE.CryptoServerProvider import net.corda.core.crypto.DigitalSignature import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.toX509CertHolder import net.corda.core.internal.x500Name import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.crypto.CertificateType @@ -81,7 +80,7 @@ object X509Utilities { cert.checkValidity(Date()) cert.verify(pubKey) - return CertificateAndKeyPair(cert.toX509CertHolder(), KeyPair(pubKey, keyPair.private)) + return CertificateAndKeyPair(cert, KeyPair(pubKey, keyPair.private)) } /** @@ -109,7 +108,7 @@ object X509Utilities { fun retrieveCertificateAndKeys(certificateKeyName: String, privateKeyPassword: String, keyStore: KeyStore): CertificateAndKeyPair { val privateKey = keyStore.getKey(certificateKeyName, privateKeyPassword.toCharArray()) as PrivateKey val publicKey = keyStore.getCertificate(certificateKeyName).publicKey - val certificate = keyStore.getX509Certificate(certificateKeyName).toX509CertHolder() + val certificate = keyStore.getX509Certificate(certificateKeyName) return CertificateAndKeyPair(certificate, getCleanEcdsaKeyPair(publicKey, privateKey)) } @@ -160,7 +159,7 @@ object X509Utilities { cert.checkValidity(Date()) cert.verify(certificateAuthority.keyPair.public) - return CertificateAndKeyPair(cert.toX509CertHolder(), KeyPair(pubKey, keyPair.private)) + return CertificateAndKeyPair(cert, KeyPair(pubKey, keyPair.private)) } /** @@ -186,7 +185,7 @@ object X509Utilities { val subject = CordaX500Name.parse(jcaRequest.subject.toString()).x500Name val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(jcaRequest.publicKey.encoded)) val keyPurposes = DERSequence(ASN1EncodableVector().apply { certificateType.purposes.forEach { add(it) } }) - val builder = JcaX509v3CertificateBuilder(issuerCertificate.subject, serial, validityWindow.first, validityWindow.second, subject, jcaRequest.publicKey) + val builder = JcaX509v3CertificateBuilder(issuerCertificate, serial, validityWindow.first, validityWindow.second, subject, jcaRequest.publicKey) .addExtension(Extension.subjectKeyIdentifier, false, BcX509ExtensionUtils().createSubjectKeyIdentifier(subjectPublicKeyInfo)) .addExtension(Extension.basicConstraints, certificateType.isCA, BasicConstraints(certificateType.isCA)) .addExtension(Extension.keyUsage, false, certificateType.keyUsage) diff --git a/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/DBCertificateRequestStorageTest.kt b/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentCertificateRequestStorageTest.kt similarity index 71% rename from network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/DBCertificateRequestStorageTest.kt rename to network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentCertificateRequestStorageTest.kt index 56ee6f2d26..df80ebdd6e 100644 --- a/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/DBCertificateRequestStorageTest.kt +++ b/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentCertificateRequestStorageTest.kt @@ -4,15 +4,13 @@ import com.r3.corda.networkmanage.TestBase import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage.Companion.DOORMAN_SIGNATURE import com.r3.corda.networkmanage.common.persistence.entity.CertificateSigningRequestEntity import com.r3.corda.networkmanage.common.utils.buildCertPath -import com.r3.corda.networkmanage.common.utils.toX509Certificate import net.corda.core.crypto.Crypto import net.corda.core.crypto.SecureHash import net.corda.core.identity.CordaX500Name -import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.persistence.CordaPersistence +import net.corda.testing.internal.createDevNodeCaCertPath import org.assertj.core.api.Assertions.assertThat -import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.pkcs.PKCS10CertificationRequest import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest import org.hibernate.envers.AuditReaderFactory @@ -21,9 +19,10 @@ import org.junit.Before import org.junit.Test import java.security.KeyPair import java.util.* +import javax.security.auth.x500.X500Principal import kotlin.test.* -class DBCertificateRequestStorageTest : TestBase() { +class PersistentCertificateRequestStorageTest : TestBase() { private lateinit var storage: PersistentCertificateRequestStorage private lateinit var persistence: CordaPersistence @@ -84,14 +83,15 @@ class DBCertificateRequestStorageTest : TestBase() { // New request should be empty. assertTrue(storage.getRequests(RequestStatus.NEW).isEmpty()) // Sign certificate - storage.putCertificatePath(requestId, JcaPKCS10CertificationRequest(csr).run { - val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 LTD", country = "GB"), rootCAKey) - val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, X500Name("CN=Corda Node Intermediate CA,L=London"), intermediateCAKey.public) - val ourCertificate = X509Utilities.createCertificate(CertificateType.TLS, intermediateCACert, intermediateCAKey, subject, publicKey).toX509Certificate() - buildCertPath(ourCertificate, intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate()) - }, listOf(DOORMAN_SIGNATURE)) + storage.putCertificatePath( + requestId, + JcaPKCS10CertificationRequest(csr).run { + // TODO We need a utility in InternalUtils for converting X500Name -> CordaX500Name + val (rootCa, intermediateCa, nodeCa) = createDevNodeCaCertPath(CordaX500Name.build(X500Principal(subject.encoded))) + buildCertPath(nodeCa.certificate, intermediateCa.certificate, rootCa.certificate) + }, + listOf(DOORMAN_SIGNATURE) + ) // Check request is ready assertNotNull(storage.getRequest(requestId)!!.certData) } @@ -104,25 +104,24 @@ class DBCertificateRequestStorageTest : TestBase() { // Store certificate to DB. storage.markRequestTicketCreated(requestId) storage.approveRequest(requestId, DOORMAN_SIGNATURE) - storage.putCertificatePath(requestId, JcaPKCS10CertificationRequest(csr).run { - val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 LTD", country = "GB"), rootCAKey) - val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, X500Name("CN=Corda Node Intermediate CA,L=London"), intermediateCAKey.public) - val ourCertificate = X509Utilities.createCertificate(CertificateType.TLS, intermediateCACert, intermediateCAKey, subject, publicKey).toX509Certificate() - buildCertPath(ourCertificate, intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate()) - }, listOf(DOORMAN_SIGNATURE)) + storage.putCertificatePath( + requestId, + JcaPKCS10CertificationRequest(csr).run { + val (rootCa, intermediateCa, nodeCa) = createDevNodeCaCertPath(CordaX500Name.build(X500Principal(subject.encoded))) + buildCertPath(nodeCa.certificate, intermediateCa.certificate, rootCa.certificate) + }, + listOf(DOORMAN_SIGNATURE) + ) // Sign certificate // When subsequent signature requested assertFailsWith(IllegalArgumentException::class) { - storage.putCertificatePath(requestId, JcaPKCS10CertificationRequest(csr).run { - val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 LTD", country = "GB"), rootCAKey) - val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, X500Name("CN=Corda Node Intermediate CA,L=London"), intermediateCAKey.public) - val ourCertificate = X509Utilities.createCertificate(CertificateType.TLS, intermediateCACert, intermediateCAKey, subject, publicKey).toX509Certificate() - buildCertPath(ourCertificate, intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate()) - }, listOf(DOORMAN_SIGNATURE)) + storage.putCertificatePath( + requestId, + JcaPKCS10CertificationRequest(csr).run { + val (rootCa, intermediateCa, nodeCa) = createDevNodeCaCertPath(CordaX500Name.build(X500Principal(subject.encoded))) + buildCertPath(nodeCa.certificate, intermediateCa.certificate, rootCa.certificate) + }, + listOf(DOORMAN_SIGNATURE)) } } @@ -207,6 +206,6 @@ class DBCertificateRequestStorageTest : TestBase() { internal fun createRequest(organisation: String): Pair { val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val request = X509Utilities.createCertificateSigningRequest(CordaX500Name(organisation = organisation, locality = "London", country = "GB"), "my@mail.com", keyPair) + val request = X509Utilities.createCertificateSigningRequest(X500Principal("O=$organisation,L=London,C=GB"), "my@mail.com", keyPair) return Pair(request, keyPair) } diff --git a/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNetworkMapStorageTest.kt b/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNetworkMapStorageTest.kt index 832c124d79..de5ef7e27b 100644 --- a/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNetworkMapStorageTest.kt +++ b/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNetworkMapStorageTest.kt @@ -3,25 +3,24 @@ package com.r3.corda.networkmanage.common.persistence import com.r3.corda.networkmanage.TestBase import com.r3.corda.networkmanage.common.utils.withCert import com.r3.corda.networkmanage.doorman.signer.LocalSigner -import net.corda.core.crypto.Crypto import net.corda.core.crypto.sign import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.cert import net.corda.core.serialization.serialize import net.corda.nodeapi.internal.SignedNodeInfo -import net.corda.nodeapi.internal.crypto.CertificateType +import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.crypto.X509CertificateFactory -import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.network.NetworkMap import net.corda.nodeapi.internal.network.SignedNetworkMap import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.internal.TestNodeInfoBuilder +import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import org.assertj.core.api.Assertions.assertThat import org.junit.After import org.junit.Before import org.junit.Test +import java.security.cert.X509Certificate import kotlin.test.assertEquals class PersistentNetworkMapStorageTest : TestBase() { @@ -30,15 +29,16 @@ class PersistentNetworkMapStorageTest : TestBase() { private lateinit var nodeInfoStorage: PersistentNodeInfoStorage private lateinit var requestStorage: PersistentCertificateRequestStorage - private val rootCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - private val rootCaCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name("Corda Node Root CA", "R3 LTD", "London", "GB"), rootCaKeyPair) - private val intermediateCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - private val intermediateCaCert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCaCert, rootCaKeyPair, CordaX500Name("Corda Node Intermediate CA", "R3 LTD", "London", "GB"), intermediateCaKeyPair.public) + private lateinit var rootCaCert: X509Certificate + private lateinit var intermediateCa: CertificateAndKeyPair @Before fun startDb() { + val (rootCa, intermediateCa) = createDevIntermediateCaCertPath() + rootCaCert = rootCa.certificate + this.intermediateCa = intermediateCa persistence = configureDatabase(makeTestDataSourceProperties()) - networkMapStorage = PersistentNetworkMapStorage(persistence, LocalSigner(intermediateCaKeyPair, arrayOf(intermediateCaCert.cert, rootCaCert.cert))) + networkMapStorage = PersistentNetworkMapStorage(persistence, LocalSigner(intermediateCa.keyPair, arrayOf(intermediateCa.certificate, rootCaCert))) nodeInfoStorage = PersistentNodeInfoStorage(persistence) requestStorage = PersistentCertificateRequestStorage(persistence) } @@ -60,7 +60,7 @@ class PersistentNetworkMapStorageTest : TestBase() { val networkMap = NetworkMap(listOf(nodeInfoHash), networkParametersHash) val serializedNetworkMap = networkMap.serialize() - val signatureData = intermediateCaKeyPair.sign(serializedNetworkMap).withCert(intermediateCaCert.cert) + val signatureData = intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate) val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, signatureData) // when @@ -70,7 +70,7 @@ class PersistentNetworkMapStorageTest : TestBase() { val persistedSignedNetworkMap = networkMapStorage.getCurrentNetworkMap() assertEquals(signedNetworkMap.signature, persistedSignedNetworkMap?.signature) - assertEquals(signedNetworkMap.verified(rootCaCert.cert), persistedSignedNetworkMap?.verified(rootCaCert.cert)) + assertEquals(signedNetworkMap.verified(rootCaCert), persistedSignedNetworkMap?.verified(rootCaCert)) } @Test @@ -96,7 +96,7 @@ class PersistentNetworkMapStorageTest : TestBase() { // Sign network map making it current network map val networkMap = NetworkMap(emptyList(), networkParametersHash) val serializedNetworkMap = networkMap.serialize() - val signatureData = intermediateCaKeyPair.sign(serializedNetworkMap).withCert(intermediateCaCert.cert) + val signatureData = intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate) val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, signatureData) networkMapStorage.saveNetworkMap(signedNetworkMap) @@ -117,7 +117,7 @@ class PersistentNetworkMapStorageTest : TestBase() { val netParamsHash = networkMapStorage.saveNetworkParameters(netParams) val signedNetParams = networkMapStorage.getSignedNetworkParameters(netParamsHash) assertThat(signedNetParams?.verified()).isEqualTo(netParams) - assertThat(signedNetParams?.sig?.by).isEqualTo(intermediateCaKeyPair.public) + assertThat(signedNetParams?.sig?.by).isEqualTo(intermediateCa.keyPair.public) } @Test @@ -135,7 +135,7 @@ class PersistentNetworkMapStorageTest : TestBase() { val networkParametersHash = networkMapStorage.saveNetworkParameters(testNetworkParameters(emptyList())) val networkMap = NetworkMap(listOf(nodeInfoHashA), networkParametersHash) val serializedNetworkMap = networkMap.serialize() - val signatureData = intermediateCaKeyPair.sign(serializedNetworkMap).withCert(intermediateCaCert.cert) + val signatureData = intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate) val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, signatureData) // Sign network map diff --git a/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersitenceNodeInfoStorageTest.kt b/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersitenceNodeInfoStorageTest.kt index e5b2e37699..c8df5a3a3d 100644 --- a/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersitenceNodeInfoStorageTest.kt +++ b/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersitenceNodeInfoStorageTest.kt @@ -6,15 +6,16 @@ import com.r3.corda.networkmanage.common.utils.hashString import net.corda.core.crypto.Crypto import net.corda.core.crypto.SecureHash import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.cert import net.corda.core.node.NodeInfo import net.corda.core.serialization.serialize import net.corda.nodeapi.internal.SignedNodeInfo +import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.testing.internal.TestNodeInfoBuilder +import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.testing.internal.signWith import net.corda.testing.node.MockServices import org.assertj.core.api.Assertions.assertThat @@ -22,6 +23,7 @@ import org.junit.After import org.junit.Before import org.junit.Test import java.security.PrivateKey +import java.security.cert.X509Certificate import kotlin.test.assertEquals import kotlin.test.assertNotNull import kotlin.test.assertNull @@ -31,13 +33,14 @@ class PersitenceNodeInfoStorageTest : TestBase() { private lateinit var nodeInfoStorage: PersistentNodeInfoStorage private lateinit var persistence: CordaPersistence - private val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - private val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 LTD", country = "GB"), rootCAKey) - private val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - private val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, CordaX500Name(commonName = "Corda Node Intermediate CA", locality = "London", organisation = "R3 LTD", country = "GB"), intermediateCAKey.public) + private lateinit var rootCaCert: X509Certificate + private lateinit var intermediateCa: CertificateAndKeyPair @Before fun startDb() { + val (rootCa, intermediateCa) = createDevIntermediateCaCertPath() + rootCaCert = rootCa.certificate + this.intermediateCa = intermediateCa persistence = configureDatabase(MockServices.makeTestDataSourceProperties()) nodeInfoStorage = PersistentNodeInfoStorage(persistence) requestStorage = PersistentCertificateRequestStorage(persistence) @@ -53,9 +56,14 @@ class PersitenceNodeInfoStorageTest : TestBase() { // Create node info. val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val name = CordaX500Name(organisation = "Test", locality = "London", country = "GB") - val nodeCaCert = X509Utilities.createCertificate(CertificateType.NODE_CA, intermediateCACert, intermediateCAKey, name, keyPair.public) + val nodeCaCert = X509Utilities.createCertificate( + CertificateType.NODE_CA, + intermediateCa.certificate, + intermediateCa.keyPair, + name.x500Principal, + keyPair.public) - val request = X509Utilities.createCertificateSigningRequest(name, "my@mail.com", keyPair) + val request = X509Utilities.createCertificateSigningRequest(name.x500Principal, "my@mail.com", keyPair) val requestId = requestStorage.saveRequest(request) requestStorage.markRequestTicketCreated(requestId) @@ -63,12 +71,15 @@ class PersitenceNodeInfoStorageTest : TestBase() { assertNull(nodeInfoStorage.getCertificatePath(SecureHash.parse(keyPair.public.hashString()))) - requestStorage.putCertificatePath(requestId, buildCertPath(nodeCaCert.cert, intermediateCACert.cert, rootCACert.cert), listOf(CertificationRequestStorage.DOORMAN_SIGNATURE)) + requestStorage.putCertificatePath( + requestId, + buildCertPath(nodeCaCert, intermediateCa.certificate, rootCaCert), + listOf(CertificationRequestStorage.DOORMAN_SIGNATURE)) val storedCertPath = nodeInfoStorage.getCertificatePath(SecureHash.parse(keyPair.public.hashString())) assertNotNull(storedCertPath) - assertEquals(nodeCaCert.cert, storedCertPath!!.certificates.first()) + assertEquals(nodeCaCert, storedCertPath!!.certificates.first()) } @Test diff --git a/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/signer/NetworkMapSignerTest.kt b/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/signer/NetworkMapSignerTest.kt index 92b17f107f..71926c7fac 100644 --- a/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/signer/NetworkMapSignerTest.kt +++ b/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/signer/NetworkMapSignerTest.kt @@ -4,20 +4,18 @@ import com.nhaarman.mockito_kotlin.* import com.r3.corda.networkmanage.TestBase import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage import com.r3.corda.networkmanage.common.utils.withCert -import net.corda.core.crypto.Crypto import net.corda.core.crypto.SecureHash import net.corda.core.crypto.sha256 import net.corda.core.crypto.sign -import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.cert import net.corda.core.serialization.serialize -import net.corda.nodeapi.internal.crypto.CertificateType -import net.corda.nodeapi.internal.crypto.X509Utilities +import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.network.NetworkMap import net.corda.nodeapi.internal.network.SignedNetworkMap import net.corda.testing.common.internal.testNetworkParameters +import net.corda.testing.internal.createDevIntermediateCaCertPath import org.junit.Before import org.junit.Test +import java.security.cert.X509Certificate import kotlin.test.assertEquals import kotlin.test.assertTrue @@ -25,12 +23,15 @@ class NetworkMapSignerTest : TestBase() { private lateinit var signer: Signer private lateinit var networkMapStorage: NetworkMapStorage private lateinit var networkMapSigner: NetworkMapSigner - private val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - private val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 LTD", country = "GB"), rootCAKey) - private val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - private val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, CordaX500Name(commonName = "Corda Node Intermediate CA", locality = "London", organisation = "R3 LTD", country = "GB"), intermediateCAKey.public) + + private lateinit var rootCaCert: X509Certificate + private lateinit var intermediateCa: CertificateAndKeyPair + @Before fun setUp() { + val (rootCa, intermediateCa) = createDevIntermediateCaCertPath() + rootCaCert = rootCa.certificate + this.intermediateCa = intermediateCa signer = mock() networkMapStorage = mock() networkMapSigner = NetworkMapSigner(networkMapStorage, signer) @@ -43,11 +44,11 @@ class NetworkMapSignerTest : TestBase() { val networkParameters = testNetworkParameters(emptyList()) val serializedNetworkMap = NetworkMap(signedNodeInfoHashes, SecureHash.randomSHA256()).serialize() whenever(networkMapStorage.getCurrentNetworkMap()) - .thenReturn(SignedNetworkMap(serializedNetworkMap, intermediateCAKey.sign(serializedNetworkMap).withCert(intermediateCACert.cert))) + .thenReturn(SignedNetworkMap(serializedNetworkMap, intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate))) whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(signedNodeInfoHashes) whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters) whenever(signer.sign(any())).then { - intermediateCAKey.sign(it.arguments.first() as ByteArray).withCert(intermediateCACert.cert) + intermediateCa.keyPair.sign(it.arguments[0] as ByteArray).withCert(intermediateCa.certificate) } // when @@ -59,7 +60,7 @@ class NetworkMapSignerTest : TestBase() { verify(networkMapStorage).getLatestNetworkParameters() argumentCaptor().apply { verify(networkMapStorage).saveNetworkMap(capture()) - val networkMap = firstValue.verified(rootCACert.cert) + val networkMap = firstValue.verified(rootCaCert) assertEquals(networkParameters.serialize().hash, networkMap.networkParameterHash) assertEquals(signedNodeInfoHashes.size, networkMap.nodeInfoHashes.size) assertTrue(networkMap.nodeInfoHashes.containsAll(signedNodeInfoHashes)) @@ -73,7 +74,7 @@ class NetworkMapSignerTest : TestBase() { val networkMapParametersHash = networkParameters.serialize().bytes.sha256() val networkMap = NetworkMap(emptyList(), networkMapParametersHash) val serializedNetworkMap = networkMap.serialize() - val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, intermediateCAKey.sign(serializedNetworkMap).withCert(intermediateCACert.cert)) + val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate)) whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(signedNetworkMap) whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList()) whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters) @@ -94,7 +95,7 @@ class NetworkMapSignerTest : TestBase() { whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList()) whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters) whenever(signer.sign(any())).then { - intermediateCAKey.sign(it.arguments.first() as ByteArray).withCert(intermediateCACert.cert) + intermediateCa.keyPair.sign(it.arguments[0] as ByteArray).withCert(intermediateCa.certificate) } // when networkMapSigner.signNetworkMap() @@ -105,7 +106,7 @@ class NetworkMapSignerTest : TestBase() { verify(networkMapStorage).getLatestNetworkParameters() argumentCaptor().apply { verify(networkMapStorage).saveNetworkMap(capture()) - val networkMap = firstValue.verified(rootCACert.cert) + val networkMap = firstValue.verified(rootCaCert) assertEquals(networkParameters.serialize().hash, networkMap.networkParameterHash) } } diff --git a/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/DefaultRequestProcessorTest.kt b/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/DefaultRequestProcessorTest.kt index 3029723e79..b0091a4cf6 100644 --- a/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/DefaultRequestProcessorTest.kt +++ b/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/DefaultRequestProcessorTest.kt @@ -10,24 +10,23 @@ import com.r3.corda.networkmanage.common.persistence.CertificateStatus import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage import com.r3.corda.networkmanage.common.persistence.RequestStatus import com.r3.corda.networkmanage.common.utils.buildCertPath -import com.r3.corda.networkmanage.common.utils.toX509Certificate import com.r3.corda.networkmanage.doorman.signer.DefaultCsrHandler import com.r3.corda.networkmanage.doorman.signer.LocalSigner import net.corda.core.crypto.Crypto -import net.corda.core.identity.CordaX500Name import net.corda.nodeapi.internal.crypto.X509Utilities import org.junit.Test +import javax.security.auth.x500.X500Principal import kotlin.test.assertEquals class DefaultRequestProcessorTest : TestBase() { @Test fun `get response`() { val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val cert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(locality = "London", organisation = "Test", country = "GB"), keyPair) + val cert = X509Utilities.createSelfSignedCACertificate(X500Principal("O=Test,L=London,C=GB"), keyPair) val requestStorage: CertificationRequestStorage = mock { on { getRequest("New") }.thenReturn(certificateSigningRequest()) - on { getRequest("Signed") }.thenReturn(certificateSigningRequest(status = RequestStatus.SIGNED, certData = certificateData("", CertificateStatus.VALID, buildCertPath(cert.toX509Certificate())))) + on { getRequest("Signed") }.thenReturn(certificateSigningRequest(status = RequestStatus.SIGNED, certData = certificateData("", CertificateStatus.VALID, buildCertPath(cert)))) on { getRequest("Rejected") }.thenReturn(certificateSigningRequest(status = RequestStatus.REJECTED, remark = "Random reason")) } val signer: LocalSigner = mock() @@ -35,15 +34,15 @@ class DefaultRequestProcessorTest : TestBase() { assertEquals(CertificateResponse.NotReady, requestProcessor.getResponse("random")) assertEquals(CertificateResponse.NotReady, requestProcessor.getResponse("New")) - assertEquals(CertificateResponse.Ready(buildCertPath(cert.toX509Certificate())), requestProcessor.getResponse("Signed")) + assertEquals(CertificateResponse.Ready(buildCertPath(cert)), requestProcessor.getResponse("Signed")) assertEquals(CertificateResponse.Unauthorised("Random reason"), requestProcessor.getResponse("Rejected")) } @Test fun `process request`() { - val request1 = X509Utilities.createCertificateSigningRequest(CordaX500Name(locality = "London", organisation = "Test1", country = "GB"), "my@email.com", Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) - val request2 = X509Utilities.createCertificateSigningRequest(CordaX500Name(locality = "London", organisation = "Test2", country = "GB"), "my@email.com", Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) - val request3 = X509Utilities.createCertificateSigningRequest(CordaX500Name(locality = "London", organisation = "Test3", country = "GB"), "my@email.com", Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) + val (request1, request2, request3) = (1..3).map { + X509Utilities.createCertificateSigningRequest(X500Principal("O=Test1,L=London,C=GB"), "my@email.com", Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) + } val requestStorage: CertificationRequestStorage = mock { on { getRequests(RequestStatus.APPROVED) }.thenReturn(listOf( diff --git a/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/NodeInfoWebServiceTest.kt b/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/NodeInfoWebServiceTest.kt index 939c901c65..b376833aa1 100644 --- a/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/NodeInfoWebServiceTest.kt +++ b/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/NodeInfoWebServiceTest.kt @@ -7,33 +7,32 @@ import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage import com.r3.corda.networkmanage.common.persistence.NodeInfoStorage import com.r3.corda.networkmanage.common.utils.withCert import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService -import net.corda.core.crypto.Crypto import net.corda.core.crypto.SecureHash.Companion.randomSHA256 import net.corda.core.crypto.SignedData import net.corda.core.crypto.sign import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.cert import net.corda.core.internal.openHttpConnection import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.seconds import net.corda.nodeapi.internal.SignedNodeInfo -import net.corda.nodeapi.internal.crypto.CertificateType -import net.corda.nodeapi.internal.crypto.X509Utilities +import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.network.NetworkMap import net.corda.nodeapi.internal.network.NetworkParameters import net.corda.nodeapi.internal.network.SignedNetworkMap import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.common.internal.testNetworkParameters +import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.testing.internal.createNodeInfoAndSigned import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatExceptionOfType -import org.bouncycastle.asn1.x500.X500Name +import org.junit.Before import org.junit.Rule import org.junit.Test import java.io.FileNotFoundException import java.net.URL +import java.security.cert.X509Certificate import javax.ws.rs.core.MediaType import kotlin.test.assertEquals @@ -42,12 +41,18 @@ class NodeInfoWebServiceTest { @JvmField val testSerialization = SerializationEnvironmentRule(true) - private val rootCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - private val rootCaCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name("Corda Node Root CA", "R3 LTD", "London", "GB"), rootCaKeyPair) - private val intermediateCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - private val intermediateCaCert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCaCert, rootCaKeyPair, X500Name("CN=Corda Node Intermediate CA,L=London"), intermediateCaKeyPair.public) + private lateinit var rootCaCert: X509Certificate + private lateinit var intermediateCa: CertificateAndKeyPair + private val testNetworkMapConfig = NetworkMapConfig(10.seconds.toMillis(), 10.seconds.toMillis()) + @Before + fun init() { + val (rootCa, intermediateCa) = createDevIntermediateCaCertPath() + rootCaCert = rootCa.certificate + this.intermediateCa = intermediateCa + } + @Test fun `submit nodeInfo`() { // Create node info. @@ -65,7 +70,7 @@ class NodeInfoWebServiceTest { fun `get network map`() { val networkMap = NetworkMap(listOf(randomSHA256(), randomSHA256()), randomSHA256()) val serializedNetworkMap = networkMap.serialize() - val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, intermediateCaKeyPair.sign(serializedNetworkMap).withCert(intermediateCaCert.cert)) + val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate)) val networkMapStorage: NetworkMapStorage = mock { on { getCurrentNetworkMap() }.thenReturn(signedNetworkMap) @@ -75,7 +80,7 @@ class NodeInfoWebServiceTest { it.start() val signedNetworkMapResponse = it.doGet("") verify(networkMapStorage, times(1)).getCurrentNetworkMap() - assertEquals(signedNetworkMapResponse.verified(rootCaCert.cert), networkMap) + assertEquals(signedNetworkMapResponse.verified(rootCaCert), networkMap) } } @@ -104,7 +109,7 @@ class NodeInfoWebServiceTest { fun `get network parameters`() { val netParams = testNetworkParameters(emptyList()) val serializedNetParams = netParams.serialize() - val signedNetParams = SignedData(serializedNetParams, intermediateCaKeyPair.sign(serializedNetParams)) + val signedNetParams = SignedData(serializedNetParams, intermediateCa.keyPair.sign(serializedNetParams)) val netParamsHash = serializedNetParams.hash val networkMapStorage: NetworkMapStorage = mock { @@ -116,7 +121,7 @@ class NodeInfoWebServiceTest { val netParamsResponse = it.doGet>("network-parameter/$netParamsHash") verify(networkMapStorage, times(1)).getSignedNetworkParameters(netParamsHash) assertThat(netParamsResponse.verified()).isEqualTo(netParams) - assertThat(netParamsResponse.sig.by).isEqualTo(intermediateCaKeyPair.public) + assertThat(netParamsResponse.sig.by).isEqualTo(intermediateCa.keyPair.public) assertThatExceptionOfType(FileNotFoundException::class.java).isThrownBy { it.doGet>("network-parameter/${randomSHA256()}") diff --git a/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/RegistrationWebServiceTest.kt b/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/RegistrationWebServiceTest.kt index 0f5bdec26b..39a6d58630 100644 --- a/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/RegistrationWebServiceTest.kt +++ b/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/RegistrationWebServiceTest.kt @@ -4,46 +4,54 @@ import com.nhaarman.mockito_kotlin.* import com.r3.corda.networkmanage.TestBase import com.r3.corda.networkmanage.common.persistence.CertificateResponse import com.r3.corda.networkmanage.common.utils.buildCertPath -import com.r3.corda.networkmanage.common.utils.toX509Certificate import com.r3.corda.networkmanage.doorman.signer.CsrHandler import com.r3.corda.networkmanage.doorman.webservice.RegistrationWebService import net.corda.core.crypto.Crypto import net.corda.core.crypto.SecureHash import net.corda.core.identity.CordaX500Name import net.corda.core.utilities.NetworkHostAndPort +import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME +import net.corda.testing.internal.createDevIntermediateCaCertPath import org.apache.commons.io.IOUtils import org.assertj.core.api.Assertions.assertThat import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x509.GeneralName import org.bouncycastle.asn1.x509.GeneralSubtree import org.bouncycastle.asn1.x509.NameConstraints -import org.bouncycastle.cert.X509CertificateHolder import org.bouncycastle.pkcs.PKCS10CertificationRequest import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest import org.junit.After +import org.junit.Before import org.junit.Test import java.io.IOException import java.net.HttpURLConnection import java.net.HttpURLConnection.* import java.net.URL +import java.nio.charset.StandardCharsets.UTF_8 import java.security.cert.CertPath import java.security.cert.X509Certificate import java.util.* import java.util.zip.ZipInputStream +import javax.security.auth.x500.X500Principal import javax.ws.rs.core.MediaType import kotlin.test.assertEquals class RegistrationWebServiceTest : TestBase() { - private val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - private val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 Ltd", country = "GB"), rootCAKey) - private val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - private val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, X500Name("CN=Corda Node Intermediate CA,L=London"), intermediateCAKey.public) + private lateinit var rootCaCert: X509Certificate + private lateinit var intermediateCa: CertificateAndKeyPair private lateinit var webServer: NetworkManagementWebServer + @Before + fun init() { + val (rootCa, intermediateCa) = createDevIntermediateCaCertPath() + rootCaCert = rootCa.certificate + this.intermediateCa = intermediateCa + } + private fun startSigningServer(csrHandler: CsrHandler) { webServer = NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), RegistrationWebService(csrHandler)) webServer.start() @@ -65,7 +73,10 @@ class RegistrationWebServiceTest : TestBase() { startSigningServer(requestProcessor) val keyPair = Crypto.generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME) - val request = X509Utilities.createCertificateSigningRequest(CordaX500Name(locality = "London", organisation = "Legal Name", country = "GB"), "my@mail.com", keyPair) + val request = X509Utilities.createCertificateSigningRequest( + CordaX500Name(locality = "London", organisation = "Legal Name", country = "GB").x500Principal, + "my@mail.com", + keyPair) // Post request to signing server via http. assertEquals(id, submitRequest(request)) @@ -79,6 +90,8 @@ class RegistrationWebServiceTest : TestBase() { val keyPair = Crypto.generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME) val id = SecureHash.randomSHA256().toString() + val subject = CordaX500Name(locality = "London", organisation = "LegalName", country = "GB").x500Principal + // Mock Storage behaviour. val certificateStore = mutableMapOf() val requestProcessor = mock { @@ -88,10 +101,15 @@ class RegistrationWebServiceTest : TestBase() { } ?: CertificateResponse.NotReady } on { processApprovedRequests() }.then { - val request = X509Utilities.createCertificateSigningRequest(CordaX500Name(locality = "London", organisation = "LegalName", country = "GB"), "my@mail.com", keyPair) + val request = X509Utilities.createCertificateSigningRequest(subject, "my@mail.com", keyPair) certificateStore[id] = JcaPKCS10CertificationRequest(request).run { - val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, intermediateCACert, intermediateCAKey, subject, publicKey).toX509Certificate() - buildCertPath(tlsCert, intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate()) + val tlsCert = X509Utilities.createCertificate( + CertificateType.TLS, + intermediateCa.certificate, + intermediateCa.keyPair, + X500Principal(subject.encoded), + publicKey) + buildCertPath(tlsCert, intermediateCa.certificate, rootCaCert) } null } @@ -104,22 +122,15 @@ class RegistrationWebServiceTest : TestBase() { val certificates = (pollForResponse(id) as PollResponse.Ready).certChain verify(requestProcessor, times(2)).getResponse(any()) - assertEquals(3, certificates.size) - certificates.first().run { - assertThat(subjectDN.name).contains("O=LegalName") - assertThat(subjectDN.name).contains("L=London") - } - - certificates.last().run { - assertThat(subjectDN.name).contains("CN=Corda Node Root CA") - assertThat(subjectDN.name).contains("L=London") - } + assertThat(certificates).hasSize(3) + assertThat(certificates[0].subjectX500Principal).isEqualTo(subject) + assertThat(certificates).endsWith(intermediateCa.certificate, rootCaCert) } @Test fun `retrieve certificate and create valid TLS certificate`() { - val keyPair = Crypto.generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME) + val nodeCaKeyPair = Crypto.generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME) val id = SecureHash.randomSHA256().toString() // Mock Storage behaviour. @@ -131,11 +142,22 @@ class RegistrationWebServiceTest : TestBase() { } ?: CertificateResponse.NotReady } on { processApprovedRequests() }.then { - val request = X509Utilities.createCertificateSigningRequest(CordaX500Name(locality = "London", organisation = "Legal Name", country = "GB"), "my@mail.com", keyPair) + val request = X509Utilities.createCertificateSigningRequest( + CordaX500Name(locality = "London", organisation = "Legal Name", country = "GB").x500Principal, + "my@mail.com", + nodeCaKeyPair) certificateStore[id] = JcaPKCS10CertificationRequest(request).run { - val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, X500Name("CN=LegalName, L=London")))), arrayOf()) - val clientCert = X509Utilities.createCertificate(CertificateType.NODE_CA, intermediateCACert, intermediateCAKey, subject, publicKey, nameConstraints = nameConstraints).toX509Certificate() - buildCertPath(clientCert, intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate()) + val nameConstraints = NameConstraints( + arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, X500Name("CN=LegalName, L=London")))), + arrayOf()) + val clientCert = X509Utilities.createCertificate( + CertificateType.NODE_CA, + intermediateCa.certificate, + intermediateCa.keyPair, + X500Principal(subject.encoded), + publicKey, + nameConstraints = nameConstraints) + buildCertPath(clientCert, intermediateCa.certificate, rootCaCert) } true } @@ -149,11 +171,17 @@ class RegistrationWebServiceTest : TestBase() { verify(storage, times(2)).getResponse(any()) assertEquals(3, certificates.size) - val sslKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val sslCert = X509Utilities.createCertificate(CertificateType.TLS, X509CertificateHolder(certificates.first().encoded), keyPair, X500Name("CN=LegalName,L=London"), sslKey.public).toX509Certificate() + val sslKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) + val sslCert = X509Utilities.createCertificate( + CertificateType.TLS, + certificates[0], + nodeCaKeyPair, + // TODO Investigate why X500Principal("CN=LegalName, L=London") results in a name constraints violation + X500Principal(X500Name("CN=LegalName, L=London").encoded), + sslKeyPair.public) // TODO: This is temporary solution, remove all certificate re-shaping after identity refactoring is done. - X509Utilities.validateCertificateChain(certificates.last(), sslCert, *certificates.toTypedArray()) + X509Utilities.validateCertificateChain(rootCaCert, sslCert, *certificates.toTypedArray()) } @Test @@ -192,7 +220,7 @@ class RegistrationWebServiceTest : TestBase() { PollResponse.Ready(certificates) } HTTP_NO_CONTENT -> PollResponse.NotReady - HTTP_UNAUTHORIZED -> PollResponse.Unauthorised(IOUtils.toString(conn.errorStream)) + HTTP_UNAUTHORIZED -> PollResponse.Unauthorised(IOUtils.toString(conn.errorStream, UTF_8)) else -> throw IOException("Cannot connect to Certificate Signing Server, HTTP response code : ${conn.responseCode}") } } diff --git a/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/signer/CsrHandlerTest.kt b/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/signer/CsrHandlerTest.kt index 21d3ed1375..42314f4b38 100644 --- a/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/signer/CsrHandlerTest.kt +++ b/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/signer/CsrHandlerTest.kt @@ -39,7 +39,10 @@ class JiraCsrHandlerTest { private lateinit var certificateResponse: CertificateResponse.Ready private val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - private val pkcS10CertificationRequest = X509Utilities.createCertificateSigningRequest(CordaX500Name(locality = "London", organisation = "LegalName", country = "GB"), "my@mail.com", keyPair) + private val pkcS10CertificationRequest = X509Utilities.createCertificateSigningRequest( + CordaX500Name(locality = "London", organisation = "LegalName", country = "GB").x500Principal, + "my@mail.com", + keyPair) @Before fun setup() { From 1e9e1a61d90aa574eac2f0ec807ebb366140254c Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Thu, 4 Jan 2018 22:08:16 +0000 Subject: [PATCH 15/19] Moved IntegrationTest class into internal package --- .../kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt | 5 ++++- .../net/corda/client/rpc/CordaRPCJavaClientTest.java | 5 ++--- .../net/corda/client/rpc/BlacklistKotlinClosureTest.kt | 3 +++ .../kotlin/net/corda/client/rpc/CordaRPCClientTest.kt | 2 ++ .../kotlin/net/corda/client/rpc/RPCStabilityTests.kt | 5 ++++- .../kotlin/net/corda/docs/IntegrationTestingTutorial.kt | 3 +++ .../net/corda/finance/flows/CashConfigDataFlowTest.kt | 5 ++++- .../integration-test/kotlin/net/corda/node/BootTests.kt | 5 ++++- .../kotlin/net/corda/node/CordappScanningDriverTest.kt | 3 +++ .../kotlin/net/corda/node/NodeKeystoreCheckTest.kt | 9 +++------ .../kotlin/net/corda/node/NodePerformanceTests.kt | 4 ++++ .../kotlin/net/corda/node/NodeStartupPerformanceTests.kt | 3 +++ .../kotlin/net/corda/node/SSHServerTest.kt | 6 +++--- .../net/corda/node/services/AttachmentLoadingTests.kt | 3 +-- .../net/corda/node/services/BFTNotaryServiceTests.kt | 4 ++-- .../net/corda/node/services/DistributedServiceTests.kt | 5 ++++- .../net/corda/node/services/MySQLNotaryServiceTests.kt | 2 ++ .../net/corda/node/services/RaftNotaryServiceTests.kt | 5 ++++- .../net/corda/node/services/UserAuthServiceTests.kt | 4 ++-- .../net/corda/node/services/network/NetworkMapTest.kt | 3 +++ .../services/network/PersistentNetworkMapCacheTest.kt | 2 ++ .../node/services/statemachine/FlowVersioningTest.kt | 2 ++ .../node/services/statemachine/LargeTransactionsTest.kt | 3 +++ .../node/services/vault/VaultQueryIntegrationTests.kt | 3 +++ .../node/utilities/registration/NodeRegistrationTest.kt | 2 ++ .../net/corda/services/messaging/MQSecurityTest.kt | 2 ++ .../net/corda/services/messaging/P2PMessagingTest.kt | 6 +++--- .../net/corda/test/node/NodeStatePersistenceTests.kt | 6 +++++- .../net/corda/attachmentdemo/AttachmentDemoTest.kt | 3 +++ .../kotlin/net/corda/bank/BankOfCordaCordformTest.kt | 3 ++- .../kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt | 3 +++ .../integration-test/kotlin/net/corda/irs/IRSDemoTest.kt | 3 +++ .../kotlin/net/corda/vega/SimmValuationTest.kt | 3 +++ .../kotlin/net/corda/traderdemo/TraderDemoTest.kt | 3 +++ .../kotlin/net/corda/testing/driver/DriverTests.kt | 3 +++ .../net/corda/testing/node/internal/NodeBasedTest.kt | 2 +- .../net/corda/testing/{ => internal}/IntegrationTest.kt | 3 +-- .../kotlin/net/corda/verifier/VerifierTests.kt | 3 +++ .../kotlin/net/corda/webserver/WebserverDriverTests.kt | 6 +++--- 39 files changed, 110 insertions(+), 35 deletions(-) rename testing/test-utils/src/main/kotlin/net/corda/testing/{ => internal}/IntegrationTest.kt (97%) diff --git a/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt b/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt index 4a22ee467d..be24fe66ad 100644 --- a/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt +++ b/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt @@ -31,6 +31,9 @@ import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.internal.config.User import net.corda.testing.* import net.corda.testing.driver.driver +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaNames import org.junit.ClassRule import org.junit.Test import rx.Observable @@ -54,7 +57,7 @@ class NodeMonitorModelTest : IntegrationTest() { companion object { @ClassRule @JvmField val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, CHARLIE_NAME, DUMMY_NOTARY_NAME) - .map { it.toDatabaseSchemaNames("","_10000","_10003") }.flatten().toTypedArray()) + .map { it.toDatabaseSchemaNames("", "_10000", "_10003") }.flatten().toTypedArray()) } private fun setup(runTest: () -> Unit) { diff --git a/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java b/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java index b0bce0c0b4..0b3646493b 100644 --- a/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java +++ b/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java @@ -12,8 +12,8 @@ import net.corda.node.internal.Node; import net.corda.node.internal.StartedNode; import net.corda.nodeapi.internal.config.User; import net.corda.testing.CoreTestUtils; -import net.corda.testing.IntegrationTestKt; -import net.corda.testing.IntegrationTestSchemas; +import net.corda.testing.internal.IntegrationTestKt; +import net.corda.testing.internal.IntegrationTestSchemas; import net.corda.testing.node.internal.NodeBasedTest; import org.junit.After; import org.junit.Before; @@ -32,7 +32,6 @@ import static net.corda.finance.contracts.GetBalances.getCashBalance; import static net.corda.node.services.Permissions.invokeRpc; import static net.corda.node.services.Permissions.startFlow; import static net.corda.testing.TestConstants.ALICE_NAME; -import static net.corda.testing.TestConstants.DUMMY_NOTARY_NAME; public class CordaRPCJavaClientTest extends NodeBasedTest { public CordaRPCJavaClientTest() { diff --git a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/BlacklistKotlinClosureTest.kt b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/BlacklistKotlinClosureTest.kt index 12372dbf5f..6afba56f62 100644 --- a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/BlacklistKotlinClosureTest.kt +++ b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/BlacklistKotlinClosureTest.kt @@ -9,6 +9,9 @@ import net.corda.core.serialization.CordaSerializable import net.corda.core.utilities.getOrThrow import net.corda.testing.* import net.corda.testing.driver.driver +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import org.assertj.core.api.Assertions.assertThatExceptionOfType import org.junit.ClassRule import org.junit.Test diff --git a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt index bef0cde9c4..a5a3b81c40 100644 --- a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt +++ b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt @@ -22,6 +22,8 @@ import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.internal.config.User import net.corda.testing.* +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import net.corda.testing.node.internal.NodeBasedTest import org.apache.activemq.artemis.api.core.ActiveMQSecurityException import org.assertj.core.api.Assertions.assertThat diff --git a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/RPCStabilityTests.kt b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/RPCStabilityTests.kt index dfc2950471..a3f6b9f9b7 100644 --- a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/RPCStabilityTests.kt +++ b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/RPCStabilityTests.kt @@ -13,7 +13,10 @@ import net.corda.core.utilities.* import net.corda.node.services.messaging.RPCServerConfiguration import net.corda.nodeapi.RPCApi import net.corda.testing.* +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.internal.testThreadFactory +import net.corda.testing.internal.toDatabaseSchemaNames import net.corda.testing.node.internal.* import org.apache.activemq.artemis.api.core.SimpleString import org.junit.After @@ -45,7 +48,7 @@ class RPCStabilityTests : IntegrationTest() { companion object { @ClassRule @JvmField val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, DUMMY_BANK_A_NAME) - .map { it.toDatabaseSchemaNames("","_10000","_10003","_10012") }.flatten().toTypedArray()) + .map { it.toDatabaseSchemaNames("", "_10000", "_10003", "_10012") }.flatten().toTypedArray()) } object DummyOps : RPCOps { diff --git a/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt b/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt index 745acb6605..377b1858c4 100644 --- a/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt +++ b/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt @@ -16,6 +16,9 @@ import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.internal.config.User import net.corda.testing.* import net.corda.testing.driver.driver +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import org.junit.ClassRule import org.junit.Test import kotlin.test.assertEquals diff --git a/finance/src/integration-test/kotlin/net/corda/finance/flows/CashConfigDataFlowTest.kt b/finance/src/integration-test/kotlin/net/corda/finance/flows/CashConfigDataFlowTest.kt index ca3f20e6a8..1fa2459d8d 100644 --- a/finance/src/integration-test/kotlin/net/corda/finance/flows/CashConfigDataFlowTest.kt +++ b/finance/src/integration-test/kotlin/net/corda/finance/flows/CashConfigDataFlowTest.kt @@ -6,6 +6,9 @@ import net.corda.finance.EUR import net.corda.finance.USD import net.corda.testing.* import net.corda.testing.driver.driver +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaNames import org.assertj.core.api.Assertions.assertThat import org.junit.ClassRule import org.junit.Test @@ -14,7 +17,7 @@ class CashConfigDataFlowTest : IntegrationTest() { companion object { @ClassRule @JvmField val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, DUMMY_BANK_A_NAME) - .map { it.toDatabaseSchemaNames("","_10000","_10003") }.flatten().toTypedArray()) + .map { it.toDatabaseSchemaNames("", "_10000", "_10003") }.flatten().toTypedArray()) } @Test fun `issuable currencies are read in from node config`() { diff --git a/node/src/integration-test/kotlin/net/corda/node/BootTests.kt b/node/src/integration-test/kotlin/net/corda/node/BootTests.kt index 6ad9235002..f32b573259 100644 --- a/node/src/integration-test/kotlin/net/corda/node/BootTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/BootTests.kt @@ -12,6 +12,9 @@ import net.corda.nodeapi.internal.config.User import net.corda.testing.* import net.corda.testing.common.internal.ProjectStructure.projectRootDir import net.corda.testing.driver.driver +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaNames import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.ClassRule @@ -24,7 +27,7 @@ class BootTests : IntegrationTest() { companion object { @ClassRule @JvmField val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, DUMMY_BANK_A_NAME) - .map { it.toDatabaseSchemaNames("", "_10000","_10003") }.flatten().toTypedArray()) + .map { it.toDatabaseSchemaNames("", "_10000", "_10003") }.flatten().toTypedArray()) } @Test diff --git a/node/src/integration-test/kotlin/net/corda/node/CordappScanningDriverTest.kt b/node/src/integration-test/kotlin/net/corda/node/CordappScanningDriverTest.kt index 99e08cea01..de34d975e2 100644 --- a/node/src/integration-test/kotlin/net/corda/node/CordappScanningDriverTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/CordappScanningDriverTest.kt @@ -11,6 +11,9 @@ import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.internal.config.User import net.corda.testing.* import net.corda.testing.driver.driver +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import org.assertj.core.api.Assertions.assertThat import org.junit.ClassRule import org.junit.Test diff --git a/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt b/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt index dbe469e9cf..25e41028ad 100644 --- a/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt @@ -7,18 +7,15 @@ import net.corda.node.services.config.configureDevKeyAndTrustStores import net.corda.nodeapi.internal.config.SSLConfiguration import net.corda.nodeapi.internal.crypto.* import net.corda.testing.ALICE_NAME -import net.corda.testing.IntegrationTest -import net.corda.testing.IntegrationTestSchemas +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.driver.driver -import net.corda.testing.toDatabaseSchemaName +import net.corda.testing.internal.toDatabaseSchemaName import org.junit.ClassRule import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Test import java.nio.file.Path import javax.security.auth.x500.X500Principal -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertTrue class NodeKeystoreCheckTest : IntegrationTest() { companion object { diff --git a/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt b/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt index 628bea91ed..8e7e8f547f 100644 --- a/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt @@ -18,7 +18,11 @@ import net.corda.testing.* import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.PortAllocation import net.corda.testing.driver.driver +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.internal.performance.div +import net.corda.testing.internal.toDatabaseSchemaName +import net.corda.testing.internal.toDatabaseSchemaNames import net.corda.testing.node.NotarySpec import net.corda.testing.node.internal.InternalDriverDSL import net.corda.testing.node.internal.performance.startPublishingFixedRateInjector diff --git a/node/src/integration-test/kotlin/net/corda/node/NodeStartupPerformanceTests.kt b/node/src/integration-test/kotlin/net/corda/node/NodeStartupPerformanceTests.kt index bcd5321713..3020c7f731 100644 --- a/node/src/integration-test/kotlin/net/corda/node/NodeStartupPerformanceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/NodeStartupPerformanceTests.kt @@ -3,6 +3,9 @@ package net.corda.node import com.google.common.base.Stopwatch import net.corda.testing.* import net.corda.testing.driver.driver +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import org.junit.ClassRule import org.junit.Ignore import org.junit.Test diff --git a/node/src/integration-test/kotlin/net/corda/node/SSHServerTest.kt b/node/src/integration-test/kotlin/net/corda/node/SSHServerTest.kt index bbfe09b159..30d61929e9 100644 --- a/node/src/integration-test/kotlin/net/corda/node/SSHServerTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/SSHServerTest.kt @@ -14,10 +14,10 @@ import net.corda.core.utilities.unwrap import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.internal.config.User import net.corda.testing.ALICE_NAME -import net.corda.testing.IntegrationTest -import net.corda.testing.IntegrationTestSchemas +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.driver.driver -import net.corda.testing.toDatabaseSchemaName +import net.corda.testing.internal.toDatabaseSchemaName import org.assertj.core.api.Assertions.assertThat import org.bouncycastle.util.io.Streams import org.junit.ClassRule diff --git a/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt index cd6c10c3eb..a9d190355a 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt @@ -24,8 +24,7 @@ import net.corda.testing.* import net.corda.testing.driver.DriverDSL import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver -import net.corda.testing.internal.rigorousMock -import net.corda.testing.internal.withoutTestSerialization +import net.corda.testing.internal.* import net.corda.testing.services.MockAttachmentStorage import org.junit.Assert.assertEquals import org.junit.ClassRule 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 ea05e0c223..00842db789 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 @@ -26,8 +26,8 @@ import net.corda.node.services.transactions.minCorrectReplicas import net.corda.nodeapi.internal.DevIdentityGenerator import net.corda.nodeapi.internal.network.NetworkParametersCopier import net.corda.nodeapi.internal.network.NotaryInfo -import net.corda.testing.IntegrationTest -import net.corda.testing.IntegrationTestSchemas +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.chooseIdentity import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.contracts.DummyContract diff --git a/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt index aa62f67cfe..478fcfc96d 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt @@ -11,13 +11,16 @@ import net.corda.core.utilities.getOrThrow import net.corda.finance.POUNDS import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow -import net.corda.finance.schemas.CashSchemaV1 import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.internal.config.User import net.corda.testing.* import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName +import net.corda.testing.internal.toDatabaseSchemaNames import net.corda.testing.node.ClusterSpec import net.corda.testing.node.NotarySpec import org.assertj.core.api.Assertions.assertThat diff --git a/node/src/integration-test/kotlin/net/corda/node/services/MySQLNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/MySQLNotaryServiceTests.kt index 832514e770..a7fd1c2ca5 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/MySQLNotaryServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/MySQLNotaryServiceTests.kt @@ -20,6 +20,8 @@ import net.corda.nodeapi.internal.network.NotaryInfo import net.corda.testing.* import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.contracts.DummyContract +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNodeParameters import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties diff --git a/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt index 8110118362..ecd44ade27 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt @@ -15,6 +15,9 @@ import net.corda.testing.* import net.corda.testing.contracts.DummyContract import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import net.corda.testing.node.ClusterSpec import net.corda.testing.node.NotarySpec import net.corda.testing.node.startFlow @@ -27,7 +30,7 @@ import kotlin.test.assertFailsWith class RaftNotaryServiceTests : IntegrationTest() { companion object { @ClassRule @JvmField - val databaseSchemas = IntegrationTestSchemas( "RAFTNotaryService_0", "RAFTNotaryService_1", "RAFTNotaryService_2", + val databaseSchemas = IntegrationTestSchemas("RAFTNotaryService_0", "RAFTNotaryService_1", "RAFTNotaryService_2", DUMMY_BANK_A_NAME.toDatabaseSchemaName()) } private val notaryName = CordaX500Name("RAFT Notary Service", "London", "GB") diff --git a/node/src/integration-test/kotlin/net/corda/node/services/UserAuthServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/UserAuthServiceTests.kt index de631de7a4..5ea81d76d0 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/UserAuthServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/UserAuthServiceTests.kt @@ -17,9 +17,9 @@ import net.corda.node.services.config.SecurityConfiguration import net.corda.nodeapi.internal.config.User import net.corda.nodeapi.internal.config.toConfig import net.corda.testing.ALICE_NAME -import net.corda.testing.IntegrationTestSchemas +import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.node.internal.NodeBasedTest -import net.corda.testing.toDatabaseSchemaName +import net.corda.testing.internal.toDatabaseSchemaName import org.apache.activemq.artemis.api.core.ActiveMQSecurityException import org.junit.After import org.junit.Before diff --git a/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt index 09dff74761..93d7b363eb 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt @@ -21,6 +21,9 @@ import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.PortAllocation +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import net.corda.testing.node.internal.CompatibilityZoneParams import net.corda.testing.node.internal.internalDriver import net.corda.testing.node.internal.network.NetworkMapServer diff --git a/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt index bf44322ee0..7b82b44b22 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt @@ -7,6 +7,8 @@ import net.corda.core.utilities.NetworkHostAndPort import net.corda.node.internal.Node import net.corda.node.internal.StartedNode import net.corda.testing.* +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import net.corda.testing.node.internal.NodeBasedTest import org.junit.Before import org.junit.ClassRule diff --git a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt index bcb2aefb9b..934645ba84 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt @@ -8,6 +8,8 @@ import net.corda.core.identity.Party import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.unwrap import net.corda.testing.* +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import net.corda.testing.node.internal.NodeBasedTest import net.corda.testing.node.startFlow import org.assertj.core.api.Assertions.assertThat diff --git a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt index 4ebed07cee..156fd71f0d 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt @@ -13,6 +13,9 @@ import net.corda.testing.* import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyState import net.corda.testing.driver.driver +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import org.junit.ClassRule import org.junit.Test import kotlin.test.assertEquals diff --git a/node/src/integration-test/kotlin/net/corda/node/services/vault/VaultQueryIntegrationTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/vault/VaultQueryIntegrationTests.kt index 0543085978..641c49a5af 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/vault/VaultQueryIntegrationTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/vault/VaultQueryIntegrationTests.kt @@ -3,6 +3,9 @@ package net.corda.node.services.vault import net.corda.core.identity.CordaX500Name import net.corda.testing.* +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import org.junit.* class VaultQueryIntegrationTests : VaultQueryTests() { diff --git a/node/src/integration-test/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt b/node/src/integration-test/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt index 8c6afec769..7ac559b8d3 100644 --- a/node/src/integration-test/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt @@ -20,6 +20,8 @@ import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA import net.corda.testing.* import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.driver.PortAllocation +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.node.NotarySpec import net.corda.testing.node.internal.CompatibilityZoneParams import net.corda.testing.node.internal.internalDriver diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt index 5f6d32e69d..201b9fa506 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt @@ -26,7 +26,9 @@ import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEERS_PREF import net.corda.nodeapi.internal.config.SSLConfiguration import net.corda.nodeapi.internal.config.User import net.corda.testing.* +import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.internal.configureTestSSL +import net.corda.testing.internal.toDatabaseSchemaName import net.corda.testing.node.internal.NodeBasedTest import net.corda.testing.node.startFlow import org.apache.activemq.artemis.api.core.ActiveMQNonExistentQueueException 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 189506d02b..b6bf5c02b7 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 @@ -17,14 +17,14 @@ import net.corda.node.internal.StartedNode import net.corda.node.services.messaging.MessagingService import net.corda.node.services.messaging.ReceivedMessage import net.corda.node.services.messaging.send -import net.corda.node.services.transactions.RaftValidatingNotaryService -import net.corda.testing.* -import net.corda.node.services.messaging.* import net.corda.testing.ALICE_NAME import net.corda.testing.chooseIdentity import net.corda.testing.driver.DriverDSL import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import net.corda.testing.node.ClusterSpec import net.corda.testing.node.NotarySpec import org.assertj.core.api.Assertions.assertThat diff --git a/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt b/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt index 3d072aaad3..3021f4a699 100644 --- a/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt @@ -22,6 +22,10 @@ import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.internal.config.User import net.corda.testing.* import net.corda.testing.driver.driver +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName +import net.corda.testing.internal.toDatabaseSchemaNames import org.junit.Assume.assumeFalse import org.junit.ClassRule import org.junit.Test @@ -36,7 +40,7 @@ class NodeStatePersistenceTests : IntegrationTest() { companion object { @ClassRule @JvmField val databaseSchemas = IntegrationTestSchemas(*listOf(ALICE_NAME, BOB_NAME, DUMMY_BANK_A_NAME) - .map { it.toDatabaseSchemaNames("", "_10000","_10003","_10006") }.flatten().toTypedArray(), + .map { it.toDatabaseSchemaNames("", "_10000", "_10003", "_10006") }.flatten().toTypedArray(), DUMMY_NOTARY_NAME.toDatabaseSchemaName()) } @Test diff --git a/samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt b/samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt index 561dcf514a..b65cbb870f 100644 --- a/samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt +++ b/samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt @@ -8,6 +8,9 @@ import net.corda.nodeapi.internal.config.User import net.corda.testing.* import net.corda.testing.driver.PortAllocation import net.corda.testing.driver.driver +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import org.junit.ClassRule import org.junit.Test import java.util.concurrent.CompletableFuture.supplyAsync diff --git a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaCordformTest.kt b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaCordformTest.kt index 90bb37a6b7..43870199c4 100644 --- a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaCordformTest.kt +++ b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaCordformTest.kt @@ -2,7 +2,8 @@ package net.corda.bank import net.corda.finance.DOLLARS import net.corda.finance.POUNDS -import net.corda.testing.* +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.node.internal.demorun.deployNodesThen import org.junit.ClassRule import org.junit.Test diff --git a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt index ce4f4dc62a..ab1395dd00 100644 --- a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt +++ b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt @@ -14,6 +14,9 @@ import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.internal.config.User import net.corda.testing.* import net.corda.testing.driver.driver +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import org.junit.ClassRule import org.junit.Test diff --git a/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt b/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt index 90b689cb83..f981389c6b 100644 --- a/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt +++ b/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt @@ -25,6 +25,9 @@ import net.corda.nodeapi.internal.config.User import net.corda.test.spring.springDriver import net.corda.testing.* import net.corda.testing.http.HttpApi +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import net.corda.testing.node.NotarySpec import org.apache.commons.io.IOUtils import org.assertj.core.api.Assertions.assertThat diff --git a/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt b/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt index b95d2f5c4b..afbab2b339 100644 --- a/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt +++ b/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt @@ -6,6 +6,9 @@ import net.corda.core.utilities.getOrThrow import net.corda.testing.* import net.corda.testing.driver.driver import net.corda.testing.http.HttpApi +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import net.corda.vega.api.PortfolioApi import net.corda.vega.api.PortfolioApiUtils import net.corda.vega.api.SwapDataModel diff --git a/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt b/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt index d3c40a7062..594cffa8e4 100644 --- a/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt +++ b/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt @@ -12,6 +12,9 @@ import net.corda.nodeapi.internal.config.User import net.corda.testing.* import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import net.corda.testing.node.internal.poll import net.corda.traderdemo.flow.BuyerFlow import net.corda.traderdemo.flow.CommercialPaperIssueFlow diff --git a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt index c1c29b9de7..43a7762bf1 100644 --- a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt +++ b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt @@ -12,6 +12,9 @@ import net.corda.node.internal.NodeStartup import net.corda.testing.* import net.corda.testing.common.internal.ProjectStructure.projectRootDir import net.corda.testing.http.HttpApi +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import net.corda.testing.node.NotarySpec import net.corda.testing.node.internal.addressMustBeBound import net.corda.testing.node.internal.addressMustNotBeBound diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt index 4464724829..c7117e3f96 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt @@ -13,7 +13,7 @@ import net.corda.node.internal.StartedNode import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.services.config.* import net.corda.nodeapi.internal.config.User -import net.corda.testing.IntegrationTest +import net.corda.testing.internal.IntegrationTest import net.corda.testing.SerializationEnvironmentRule import net.corda.nodeapi.internal.network.NetworkParametersCopier import net.corda.testing.common.internal.testNetworkParameters diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/IntegrationTest.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/IntegrationTest.kt similarity index 97% rename from testing/test-utils/src/main/kotlin/net/corda/testing/IntegrationTest.kt rename to testing/test-utils/src/main/kotlin/net/corda/testing/internal/IntegrationTest.kt index 79df695f84..66a0e691a5 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/IntegrationTest.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/IntegrationTest.kt @@ -1,7 +1,6 @@ -package net.corda.testing +package net.corda.testing.internal import net.corda.core.identity.CordaX500Name -import net.corda.core.identity.Party import net.corda.testing.database.DbScriptRunner.runDbScript import org.junit.After import org.junit.AfterClass diff --git a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt index a7383cfce1..647796fec4 100644 --- a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt +++ b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt @@ -13,6 +13,9 @@ import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.config.VerifierType import net.corda.testing.* +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas +import net.corda.testing.internal.toDatabaseSchemaName import net.corda.testing.node.NotarySpec import org.junit.ClassRule import org.junit.Rule diff --git a/webserver/src/integration-test/kotlin/net/corda/webserver/WebserverDriverTests.kt b/webserver/src/integration-test/kotlin/net/corda/webserver/WebserverDriverTests.kt index c22f8f30a4..9163f06199 100644 --- a/webserver/src/integration-test/kotlin/net/corda/webserver/WebserverDriverTests.kt +++ b/webserver/src/integration-test/kotlin/net/corda/webserver/WebserverDriverTests.kt @@ -3,13 +3,13 @@ package net.corda.webserver import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.getOrThrow import net.corda.testing.DUMMY_BANK_A_NAME -import net.corda.testing.IntegrationTest -import net.corda.testing.IntegrationTestSchemas +import net.corda.testing.internal.IntegrationTest +import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.driver.WebserverHandle import net.corda.testing.driver.driver import net.corda.testing.node.internal.addressMustBeBound import net.corda.testing.node.internal.addressMustNotBeBound -import net.corda.testing.toDatabaseSchemaName +import net.corda.testing.internal.toDatabaseSchemaName import org.junit.ClassRule import org.junit.Test import java.util.concurrent.Executors From 1661cea816b7ed3ec432d713028188a998b45425 Mon Sep 17 00:00:00 2001 From: Anthony Keenan <34482776+anthonykr3@users.noreply.github.com> Date: Fri, 5 Jan 2018 09:21:59 +0000 Subject: [PATCH 16/19] CORDA-892: Make cordform test use new network bootstrapper logic (#2307) * Make cordform test use new network bootstrapper logic * Fixing review comments * Fix issue with backwards compatibility * Fix issue with setup not being called from CordformDefinitions * Make sure node dir is created (as CordformDefinition uses it directly if setup is overridden Make sure tmp dir is created * Don't crash if node dir is already created * Stop overwriting errors --- constants.properties | 2 +- .../main/kotlin/net/corda/plugins/Cordform.kt | 22 ++++- .../kotlin/net/corda/plugins/Cordformation.kt | 25 ++++- .../src/main/kotlin/net/corda/plugins/Node.kt | 96 +++++++------------ .../internal/network/NetworkBootstrapper.kt | 12 ++- 5 files changed, 85 insertions(+), 72 deletions(-) diff --git a/constants.properties b/constants.properties index 5613b91279..019e9d8e1a 100644 --- a/constants.properties +++ b/constants.properties @@ -1,4 +1,4 @@ -gradlePluginsVersion=3.0.2 +gradlePluginsVersion=3.0.3 kotlinVersion=1.1.60 platformVersion=2 guavaVersion=21.0 diff --git a/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Cordform.kt b/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Cordform.kt index dc131cc8b5..1d44484eb2 100644 --- a/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Cordform.kt +++ b/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Cordform.kt @@ -22,6 +22,7 @@ import java.util.jar.JarInputStream @Suppress("unused") open class Cordform : DefaultTask() { private companion object { + val nodeJarName = "corda.jar" private val defaultDirectory: Path = Paths.get("build", "nodes") } @@ -132,9 +133,26 @@ open class Cordform : DefaultTask() { fun build() { project.logger.info("Running Cordform task") initializeConfiguration() + nodes.forEach(Node::installConfig) + installCordaJar() installRunScript() - nodes.forEach(Node::build) bootstrapNetwork() + nodes.forEach(Node::build) + } + + /** + * Installs the corda fat JAR to the root directory, for the network bootstrapper to use. + */ + private fun installCordaJar() { + val cordaJar = Cordformation.verifyAndGetRuntimeJar(project, "corda") + project.copy { + it.apply { + from(cordaJar) + into(directory) + rename(cordaJar.name, nodeJarName) + fileMode = Cordformation.executableFileMode + } + } } private fun initializeConfiguration() { @@ -150,8 +168,8 @@ open class Cordform : DefaultTask() { cd.nodeConfigurers.forEach { val node = node { } it.accept(node) + node.additionalCordapps.addAll(cordapps) node.rootDir(directory) - node.installCordapps(cordapps) } cd.setup { nodeName -> project.projectDir.toPath().resolve(getNodeByName(nodeName)!!.nodeDir.toPath()) } } else { diff --git a/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Cordformation.kt b/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Cordformation.kt index 4b722a7f03..26878fa357 100644 --- a/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Cordformation.kt +++ b/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Cordformation.kt @@ -20,14 +20,35 @@ class Cordformation : Plugin { * @return A file handle to the file in the JAR. */ fun getPluginFile(project: Project, filePathInJar: String): File { - val archive: File? = project.rootProject.buildscript.configurations + val archive = project.rootProject.buildscript.configurations .single { it.name == "classpath" } - .find { it.name.contains("cordformation") } + .first { it.name.contains("cordformation") } return project.rootProject.resources.text .fromArchiveEntry(archive, filePathInJar) .asFile() } + /** + * Gets a current built corda jar file + * + * @param project The project environment this plugin executes in. + * @param jarName The name of the JAR you wish to access. + * @return A file handle to the file in the JAR. + */ + fun verifyAndGetRuntimeJar(project: Project, jarName: String): File { + val releaseVersion = project.rootProject.ext("corda_release_version") + val maybeJar = project.configuration("runtime").filter { + "$jarName-$releaseVersion.jar" in it.toString() || "$jarName-enterprise-$releaseVersion.jar" in it.toString() + } + if (maybeJar.isEmpty) { + throw IllegalStateException("No $jarName JAR found. Have you deployed the Corda project to Maven? Looked for \"$jarName-$releaseVersion.jar\"") + } else { + val jar = maybeJar.singleFile + require(jar.isFile) + return jar + } + } + val executableFileMode = "0755".toInt(8) } diff --git a/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Node.kt b/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Node.kt index bd3842d3fa..bb16a654f8 100644 --- a/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Node.kt +++ b/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Node.kt @@ -15,8 +15,6 @@ import java.nio.file.Path */ class Node(private val project: Project) : CordformNode() { companion object { - @JvmStatic - val nodeJarName = "corda.jar" @JvmStatic val webJarName = "corda-webserver.jar" private val configFileProperty = "configFile" @@ -30,10 +28,11 @@ class Node(private val project: Project) : CordformNode() { * @note Type is any due to gradle's use of "GStrings" - each value will have "toString" called on it */ var cordapps = mutableListOf() - - private val releaseVersion = project.rootProject.ext("corda_release_version") + var additionalCordapps = mutableListOf() internal lateinit var nodeDir: File private set + internal lateinit var rootDir: File + private set /** * Sets whether this node will use HTTPS communication. @@ -65,16 +64,12 @@ class Node(private val project: Project) : CordformNode() { } internal fun build() { - configureProperties() - installCordaJar() if (config.hasPath("webAddress")) { installWebserverJar() } installAgentJar() installBuiltCordapp() installCordapps() - installConfig() - appendOptionalConfig() } internal fun rootDir(rootDir: Path) { @@ -86,7 +81,9 @@ class Node(private val project: Project) : CordformNode() { // with loading our custom X509EdDSAEngine. val organizationName = name.trim().split(",").firstOrNull { it.startsWith("O=") }?.substringAfter("=") val dirName = organizationName ?: name - nodeDir = File(rootDir.toFile(), dirName) + this.rootDir = rootDir.toFile() + nodeDir = File(this.rootDir, dirName) + Files.createDirectories(nodeDir.toPath()) } private fun configureProperties() { @@ -99,26 +96,11 @@ class Node(private val project: Project) : CordformNode() { } } - /** - * Installs the corda fat JAR to the node directory. - */ - private fun installCordaJar() { - val cordaJar = verifyAndGetRuntimeJar("corda") - project.copy { - it.apply { - from(cordaJar) - into(nodeDir) - rename(cordaJar.name, nodeJarName) - fileMode = Cordformation.executableFileMode - } - } - } - /** * Installs the corda webserver JAR to the node directory */ private fun installWebserverJar() { - val webJar = verifyAndGetRuntimeJar("corda-webserver") + val webJar = Cordformation.verifyAndGetRuntimeJar(project, "corda-webserver") project.copy { it.apply { from(webJar) @@ -141,19 +123,6 @@ class Node(private val project: Project) : CordformNode() { } } - /** - * Installs other cordapps to this node's cordapps directory. - */ - internal fun installCordapps(cordapps: Collection = getCordappList()) { - val cordappsDir = File(nodeDir, "cordapps") - project.copy { - it.apply { - from(cordapps) - into(cordappsDir) - } - } - } - /** * Installs the jolokia monitoring agent JAR to the node/drivers directory */ @@ -161,8 +130,8 @@ class Node(private val project: Project) : CordformNode() { val jolokiaVersion = project.rootProject.ext("jolokia_version") val agentJar = project.configuration("runtime").files { (it.group == "org.jolokia") && - (it.name == "jolokia-jvm") && - (it.version == jolokiaVersion) + (it.name == "jolokia-jvm") && + (it.version == jolokiaVersion) // TODO: revisit when classifier attribute is added. eg && (it.classifier = "agent") }.first() // should always be the jolokia agent fat jar: eg. jolokia-jvm-1.3.7-agent.jar project.logger.info("Jolokia agent jar: $agentJar") @@ -177,10 +146,7 @@ class Node(private val project: Project) : CordformNode() { } } - /** - * Installs the configuration file to this node's directory and detokenises it. - */ - private fun installConfig() { + private fun createTempConfigFile(): File { val options = ConfigRenderOptions .defaults() .setOriginComments(false) @@ -188,16 +154,26 @@ class Node(private val project: Project) : CordformNode() { .setFormatted(true) .setJson(false) val configFileText = config.root().render(options).split("\n").toList() - // Need to write a temporary file first to use the project.copy, which resolves directories correctly. val tmpDir = File(project.buildDir, "tmp") - val tmpConfFile = File(tmpDir, "node.conf") + Files.createDirectories(tmpDir.toPath()) + var fileName = "${nodeDir.getName()}.conf" + val tmpConfFile = File(tmpDir, fileName) Files.write(tmpConfFile.toPath(), configFileText, StandardCharsets.UTF_8) + return tmpConfFile + } + /** + * Installs the configuration file to the root directory and detokenises it. + */ + internal fun installConfig() { + configureProperties() + val tmpConfFile = createTempConfigFile() + appendOptionalConfig(tmpConfFile) project.copy { it.apply { from(tmpConfFile) - into(nodeDir) + into(rootDir) } } } @@ -205,7 +181,7 @@ class Node(private val project: Project) : CordformNode() { /** * Appends installed config file with properties from an optional file. */ - private fun appendOptionalConfig() { + private fun appendOptionalConfig(confFile: File) { val optionalConfig: File? = when { project.findProperty(configFileProperty) != null -> //provided by -PconfigFile command line property when running Gradle task File(project.findProperty(configFileProperty) as String) @@ -217,28 +193,22 @@ class Node(private val project: Project) : CordformNode() { if (!optionalConfig.exists()) { project.logger.error("$configFileProperty '$optionalConfig' not found") } else { - val confFile = File(project.buildDir.path + "/../" + nodeDir, "node.conf") confFile.appendBytes(optionalConfig.readBytes()) } } } /** - * Find the given JAR amongst the dependencies - * @param jarName JAR name without the version part, for example for corda-2.0-SNAPSHOT.jar provide only "corda" as jarName - * - * @return A file representing found JAR + * Installs other cordapps to this node's cordapps directory. */ - private fun verifyAndGetRuntimeJar(jarName: String): File { - val maybeJar = project.configuration("runtime").filter { - "$jarName-$releaseVersion.jar" in it.toString() || "$jarName-enterprise-$releaseVersion.jar" in it.toString() - } - if (maybeJar.isEmpty) { - throw IllegalStateException("No $jarName JAR found. Have you deployed the Corda project to Maven? Looked for \"$jarName-$releaseVersion.jar\"") - } else { - val jar = maybeJar.singleFile - require(jar.isFile) - return jar + internal fun installCordapps() { + additionalCordapps.addAll(getCordappList()) + val cordappsDir = File(nodeDir, "cordapps") + project.copy { + it.apply { + from(additionalCordapps) + into(cordappsDir) + } } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt index af625adae5..a9b16f61e9 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt @@ -83,16 +83,20 @@ class NetworkBootstrapper { for (confFile in confFiles) { val nodeName = confFile.fileName.toString().removeSuffix(".conf") println("Generating directory for $nodeName") - val nodeDir = (directory / nodeName).createDirectory() - confFile.moveTo(nodeDir / "node.conf") - Files.copy(cordaJar, (nodeDir / "corda.jar")) + val nodeDir = (directory / nodeName) + if (!nodeDir.exists()) { nodeDir.createDirectory() } + confFile.moveTo(nodeDir / "node.conf", StandardCopyOption.REPLACE_EXISTING) + Files.copy(cordaJar, (nodeDir / "corda.jar"), StandardCopyOption.REPLACE_EXISTING) } Files.delete(cordaJar) } private fun extractCordaJarTo(directory: Path): Path { val cordaJarPath = (directory / "corda.jar") - Thread.currentThread().contextClassLoader.getResourceAsStream("corda.jar").copyTo(cordaJarPath) + if (!cordaJarPath.exists()) { + println("No corda jar found in root directory. Extracting from jar") + Thread.currentThread().contextClassLoader.getResourceAsStream("corda.jar").copyTo(cordaJarPath) + } return cordaJarPath } From 00b570df29112d6f1a09873493f06b2507da88f0 Mon Sep 17 00:00:00 2001 From: igor nitto Date: Fri, 5 Jan 2018 11:50:21 +0000 Subject: [PATCH 17/19] Improve RPC security test coverage [CORDA-827] (#2320) * Added test cases covering encrypted password usage * Renamed UserAuthServiceTests as AuthDBTests: the integration tests checking user credentials loaded from external database (still limited to H2 in-memory for now). * Some internal renamings --- ...UserAuthServiceTests.kt => AuthDBTests.kt} | 203 ++++++++++-------- .../node/services/config/NodeConfiguration.kt | 25 ++- .../node/services/RPCSecurityManagerTest.kt | 15 +- 3 files changed, 136 insertions(+), 107 deletions(-) rename node/src/integration-test/kotlin/net/corda/node/{services/UserAuthServiceTests.kt => AuthDBTests.kt} (61%) diff --git a/node/src/integration-test/kotlin/net/corda/node/services/UserAuthServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt similarity index 61% rename from node/src/integration-test/kotlin/net/corda/node/services/UserAuthServiceTests.kt rename to node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt index 8805144b56..4015265da8 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/UserAuthServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt @@ -1,4 +1,4 @@ -package net.corda.node.services +package net.corda.node import co.paralleluniverse.fibers.Suspendable import net.corda.client.rpc.CordaRPCClient @@ -11,26 +11,89 @@ import net.corda.core.messaging.startFlow import net.corda.finance.flows.CashIssueFlow import net.corda.node.internal.Node import net.corda.node.internal.StartedNode +import net.corda.node.services.Permissions import net.corda.node.services.config.PasswordEncryption -import net.corda.node.services.config.SecurityConfiguration -import net.corda.node.services.config.AuthDataSourceType -import net.corda.nodeapi.internal.config.User -import net.corda.nodeapi.internal.config.toConfig import net.corda.testing.node.internal.NodeBasedTest import net.corda.testing.* import org.apache.activemq.artemis.api.core.ActiveMQSecurityException +import org.apache.shiro.authc.credential.DefaultPasswordService import org.junit.After import org.junit.Before import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized import java.sql.DriverManager import java.sql.Statement -import java.util.* import kotlin.test.assertFailsWith -abstract class UserAuthServiceTest : NodeBasedTest() { +/* + * Starts Node's instance configured to load clients credentials and permissions from an external DB, then + * check authentication/authorization of RPC connections. + */ +@RunWith(Parameterized::class) +class AuthDBTests : NodeBasedTest() { - protected lateinit var node: StartedNode - protected lateinit var client: CordaRPCClient + private lateinit var node: StartedNode + private lateinit var client: CordaRPCClient + private lateinit var db: UsersDB + + companion object { + private val cacheExpireAfterSecs: Long = 1 + + @JvmStatic + @Parameterized.Parameters(name = "password encryption format = {0}") + fun encFormats() = arrayOf(PasswordEncryption.NONE, PasswordEncryption.SHIRO_1_CRYPT) + } + + @Parameterized.Parameter + lateinit var passwordEncryption: PasswordEncryption + + @Before + fun setup() { + db = UsersDB( + name = "SecurityDataSourceTestDB", + users = listOf(UserAndRoles(username = "user", + password = encodePassword("foo", passwordEncryption), + roles = listOf("default"))), + roleAndPermissions = listOf( + RoleAndPermissions( + role = "default", + permissions = listOf( + Permissions.startFlow(), + Permissions.invokeRpc("vaultQueryBy"), + Permissions.invokeRpc(CordaRPCOps::stateMachinesFeed), + Permissions.invokeRpc("vaultQueryByCriteria"))), + RoleAndPermissions( + role = "admin", + permissions = listOf("ALL") + ))) + + val securityConfig = mapOf( + "security" to mapOf( + "authService" to mapOf( + "dataSource" to mapOf( + "type" to "DB", + "passwordEncryption" to passwordEncryption.toString(), + "connection" to mapOf( + "jdbcUrl" to db.jdbcUrl, + "username" to "", + "password" to "", + "driverClassName" to "org.h2.Driver" + ) + ) + ), + "options" to mapOf( + "cache" to mapOf( + "expireAfterSecs" to cacheExpireAfterSecs, + "maxEntries" to 50 + ) + ) + ) + ) + + node = startNode(ALICE_NAME, rpcUsers = emptyList(), configOverrides = securityConfig) + client = CordaRPCClient(node.internals.configuration.rpcAddress!!) + } @Test fun `login with correct credentials`() { @@ -58,7 +121,7 @@ abstract class UserAuthServiceTest : NodeBasedTest() { val proxy = it.proxy proxy.startFlowDynamic(DummyFlow::class.java) proxy.startTrackedFlowDynamic(DummyFlow::class.java) - proxy.startFlow(::DummyFlow) + proxy.startFlow(AuthDBTests::DummyFlow) assertFailsWith( PermissionException::class, "This user should not be authorized to start flow `CashIssueFlow`") { @@ -85,77 +148,8 @@ abstract class UserAuthServiceTest : NodeBasedTest() { } } - @StartableByRPC - @InitiatingFlow - class DummyFlow : FlowLogic() { - @Suspendable - override fun call() = Unit - } -} - -class UserAuthServiceEmbedded : UserAuthServiceTest() { - - private val rpcUser = User("user", "foo", permissions = setOf( - Permissions.startFlow(), - Permissions.invokeRpc("vaultQueryBy"), - Permissions.invokeRpc(CordaRPCOps::stateMachinesFeed), - Permissions.invokeRpc("vaultQueryByCriteria"))) - - @Before - fun setup() { - val securityConfig = SecurityConfiguration( - authService = SecurityConfiguration.AuthService.fromUsers(listOf(rpcUser))) - - val configOverrides = mapOf("security" to securityConfig.toConfig().root().unwrapped()) - node = startNode(ALICE_NAME, rpcUsers = emptyList(), configOverrides = configOverrides) - client = CordaRPCClient(node.internals.configuration.rpcAddress!!) - } -} - -class UserAuthServiceTestsJDBC : UserAuthServiceTest() { - - private val db = UsersDB( - name = "SecurityDataSourceTestDB", - users = listOf(UserAndRoles(username = "user", - password = "foo", - roles = listOf("default"))), - roleAndPermissions = listOf( - RoleAndPermissions( - role = "default", - permissions = listOf( - Permissions.startFlow(), - Permissions.invokeRpc("vaultQueryBy"), - Permissions.invokeRpc(CordaRPCOps::stateMachinesFeed), - Permissions.invokeRpc("vaultQueryByCriteria"))), - RoleAndPermissions( - role = "admin", - permissions = listOf("ALL") - ))) - - @Before - fun setup() { - val securityConfig = SecurityConfiguration( - authService = SecurityConfiguration.AuthService( - dataSource = SecurityConfiguration.AuthService.DataSource( - type = AuthDataSourceType.DB, - passwordEncryption = PasswordEncryption.NONE, - connection = Properties().apply { - setProperty("jdbcUrl", db.jdbcUrl) - setProperty("username", "") - setProperty("password", "") - setProperty("driverClassName", "org.h2.Driver") - } - ) - ) - ) - - val configOverrides = mapOf("security" to securityConfig.toConfig().root().unwrapped()) - node = startNode(ALICE_NAME, rpcUsers = emptyList(), configOverrides = configOverrides) - client = CordaRPCClient(node.internals.configuration.rpcAddress!!) - } - @Test - fun `Add new users on-the-fly`() { + fun `Add new users dynamically`() { assertFailsWith( ActiveMQSecurityException::class, "Login with incorrect password should fail") { @@ -164,7 +158,7 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() { db.insert(UserAndRoles( username = "user2", - password = "bar", + password = encodePassword("bar"), roles = listOf("default"))) client.start("user2", "bar") @@ -174,10 +168,9 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() { fun `Modify user permissions during RPC session`() { db.insert(UserAndRoles( username = "user3", - password = "bar", + password = encodePassword("bar"), roles = emptyList())) - client.start("user3", "bar").use { val proxy = it.proxy assertFailsWith( @@ -186,6 +179,7 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() { proxy.stateMachinesFeed() } db.addRoleToUser("user3", "default") + Thread.sleep(1500) proxy.stateMachinesFeed() } } @@ -194,13 +188,14 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() { fun `Revoke user permissions during RPC session`() { db.insert(UserAndRoles( username = "user4", - password = "test", + password = encodePassword("test"), roles = listOf("default"))) client.start("user4", "test").use { val proxy = it.proxy proxy.stateMachinesFeed() db.deleteUser("user4") + Thread.sleep(1500) assertFailsWith( PermissionException::class, "This user should not be authorized to call 'nodeInfo'") { @@ -209,15 +204,27 @@ class UserAuthServiceTestsJDBC : UserAuthServiceTest() { } } + @StartableByRPC + @InitiatingFlow + class DummyFlow : FlowLogic() { + @Suspendable + override fun call() = Unit + } + @After fun tearDown() { - db.close() + db.close() } + + private fun encodePassword(s: String) = encodePassword(s, passwordEncryption) } private data class UserAndRoles(val username: String, val password: String, val roles: List) private data class RoleAndPermissions(val role: String, val permissions: List) +/* + * Manage in-memory DB mocking a users database with the schema expected by Node's security manager + */ private class UsersDB : AutoCloseable { val jdbcUrl: String @@ -254,12 +261,6 @@ private class UsersDB : AutoCloseable { } } - fun deleteRole(role: String) { - session { - it.execute("DELETE FROM role_permissions WHERE role_name = '$role'") - } - } - fun deleteUser(username: String) { session { it.execute("DELETE FROM users WHERE username = '$username'") @@ -300,4 +301,22 @@ private class UsersDB : AutoCloseable { } } } -} \ No newline at end of file +} + +/* + * Sample of hardcoded hashes to watch for format backward compatibility + */ +private val hashedPasswords = mapOf( + PasswordEncryption.SHIRO_1_CRYPT to mapOf( + "foo" to "\$shiro1\$SHA-256$500000\$WSiEVj6q8d02sFcCk1dkoA==\$MBkU/ghdD9ovoDerdzNfkXdP9Bdhmok7tidvVIqGzcA=", + "bar" to "\$shiro1\$SHA-256$500000\$Q6dmdY1uVMm0LYAWaOHtCA==\$u7NbFaj9tHf2RTW54jedLPiOiGjJv0RVEPIjVquJuYY=", + "test" to "\$shiro1\$SHA-256$500000\$F6CWSFDDxGTlzvREwih8Gw==\$DQhyAPoUw3RdvNYJ1aubCnzEIXm+szGQ3HplaG+euz8=")) + +/* + * A functional object for producing password encoded according to the given scheme. + */ +private fun encodePassword(s: String, format: PasswordEncryption) = when (format) { + PasswordEncryption.NONE -> s + PasswordEncryption.SHIRO_1_CRYPT -> hashedPasswords[format]!![s] ?: + DefaultPasswordService().encryptPassword(s.toCharArray()) + } \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt index 60acff95d1..f549aa8bf9 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt @@ -198,8 +198,16 @@ data class SecurityConfiguration(val authService: SecurityConfiguration.AuthServ data class Options(val cache: Options.Cache?) { // Cache parameters - data class Cache(val expireAfterSecs: Long, val maxEntries: Long) - + data class Cache(val expireAfterSecs: Long, val maxEntries: Long) { + init { + require(expireAfterSecs >= 0) { + "Expected positive value for 'cache.expireAfterSecs'" + } + require(maxEntries > 0) { + "Expected positive value for 'cache.maxEntries'" + } + } + } } // Provider of users credentials and permissions data @@ -223,12 +231,13 @@ data class SecurityConfiguration(val authService: SecurityConfiguration.AuthServ AuthDataSourceType.DB -> AuthServiceId("REMOTE_DATABASE") } - fun fromUsers(users: List) = AuthService( - dataSource = DataSource( - type = AuthDataSourceType.INMEMORY, - users = users, - passwordEncryption = PasswordEncryption.NONE), - id = AuthServiceId("NODE_CONFIG")) + fun fromUsers(users: List, encryption: PasswordEncryption = PasswordEncryption.NONE) = + AuthService( + dataSource = DataSource( + type = AuthDataSourceType.INMEMORY, + users = users, + passwordEncryption = encryption), + id = AuthServiceId("NODE_CONFIG")) } } } \ No newline at end of file diff --git a/node/src/test/kotlin/net/corda/node/services/RPCSecurityManagerTest.kt b/node/src/test/kotlin/net/corda/node/services/RPCSecurityManagerTest.kt index be0d40ae8e..230170f0a7 100644 --- a/node/src/test/kotlin/net/corda/node/services/RPCSecurityManagerTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/RPCSecurityManagerTest.kt @@ -7,6 +7,7 @@ import net.corda.node.internal.security.Password import net.corda.node.internal.security.RPCSecurityManagerImpl import net.corda.node.internal.security.tryAuthenticate import net.corda.nodeapi.internal.config.User +import net.corda.node.services.config.SecurityConfiguration import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Test import javax.security.auth.login.FailedLoginException @@ -26,7 +27,7 @@ class RPCSecurityManagerTest { @Test fun `Generic RPC call authorization`() { - checkUserPermissions( + checkUserActions( permitted = setOf(arrayListOf("nodeInfo"), arrayListOf("notaryIdentities")), permissions = setOf( Permissions.invokeRpc(CordaRPCOps::nodeInfo), @@ -35,7 +36,7 @@ class RPCSecurityManagerTest { @Test fun `Flow invocation authorization`() { - checkUserPermissions( + checkUserActions( permissions = setOf(Permissions.startFlow()), permitted = setOf( arrayListOf("startTrackedFlowDynamic", "net.corda.node.services.RPCSecurityManagerTest\$DummyFlow"), @@ -44,21 +45,21 @@ class RPCSecurityManagerTest { @Test fun `Check startFlow RPC permission implies startFlowDynamic`() { - checkUserPermissions( + checkUserActions( permissions = setOf(Permissions.invokeRpc("startFlow")), permitted = setOf(arrayListOf("startFlow"), arrayListOf("startFlowDynamic"))) } @Test fun `Check startTrackedFlow RPC permission implies startTrackedFlowDynamic`() { - checkUserPermissions( + checkUserActions( permitted = setOf(arrayListOf("startTrackedFlow"), arrayListOf("startTrackedFlowDynamic")), permissions = setOf(Permissions.invokeRpc("startTrackedFlow"))) } @Test fun `Admin authorization`() { - checkUserPermissions( + checkUserActions( permissions = setOf("all"), permitted = allActions.map { arrayListOf(it) }.toSet()) } @@ -118,9 +119,9 @@ class RPCSecurityManagerTest { users = listOf(User(username, "password", setOf())), id = AuthServiceId("TEST")) } - private fun checkUserPermissions(permissions: Set, permitted: Set>) { + private fun checkUserActions(permissions: Set, permitted: Set>) { val user = User(username = "user", password = "password", permissions = permissions) - val userRealms = RPCSecurityManagerImpl.fromUserList(users = listOf(user), id = AuthServiceId("TEST")) + val userRealms = RPCSecurityManagerImpl(SecurityConfiguration.AuthService.fromUsers(listOf(user))) val disabled = allActions.filter { !permitted.contains(listOf(it)) } for (subject in listOf( userRealms.authenticate("user", Password("password")), From 694721e8ba693d83419feaad2582d3cf6b17d517 Mon Sep 17 00:00:00 2001 From: Katelyn Baker Date: Fri, 5 Jan 2018 14:07:55 +0000 Subject: [PATCH 18/19] TEMP removal of SSH tests till they're made determinsitic (#2325) --- .../integration-test/kotlin/net/corda/node/SSHServerTest.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/node/src/integration-test/kotlin/net/corda/node/SSHServerTest.kt b/node/src/integration-test/kotlin/net/corda/node/SSHServerTest.kt index 4f597a016c..78e80c4913 100644 --- a/node/src/integration-test/kotlin/net/corda/node/SSHServerTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/SSHServerTest.kt @@ -19,10 +19,12 @@ import java.net.ConnectException import kotlin.test.assertTrue import kotlin.test.fail import org.assertj.core.api.Assertions.assertThat +import org.junit.Ignore import java.util.regex.Pattern class SSHServerTest { + @Ignore("Test has undeterministic capacity to hang, ignore till fixed") @Test() fun `ssh server does not start be default`() { val user = User("u", "p", setOf()) @@ -44,6 +46,7 @@ class SSHServerTest { } } + @Ignore("Test has undeterministic capacity to hang, ignore till fixed") @Test fun `ssh server starts when configured`() { val user = User("u", "p", setOf()) @@ -64,6 +67,7 @@ class SSHServerTest { } + @Ignore("Test has undeterministic capacity to hang, ignore till fixed") @Test fun `ssh server verify credentials`() { val user = User("u", "p", setOf()) @@ -87,6 +91,7 @@ class SSHServerTest { } } + @Ignore("Test has undeterministic capacity to hang, ignore till fixed") @Test fun `ssh respects permissions`() { val user = User("u", "p", setOf(startFlow())) @@ -117,6 +122,7 @@ class SSHServerTest { } } + @Ignore("Test has undeterministic capacity to hang, ignore till fixed") @Test fun `ssh runs flows`() { val user = User("u", "p", setOf(startFlow())) From 2cb71ff64911626104571dde443437f388067e74 Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Fri, 5 Jan 2018 14:38:53 +0000 Subject: [PATCH 19/19] Merge fixes --- .../kotlin/net/corda/node/AuthDBTests.kt | 16 +++++----------- .../kotlin/net/corda/node/SSHServerTest.kt | 6 ++---- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt b/node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt index d7810db393..c05824f972 100644 --- a/node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt @@ -11,16 +11,12 @@ import net.corda.core.messaging.startFlow import net.corda.finance.flows.CashIssueFlow import net.corda.node.internal.Node import net.corda.node.internal.StartedNode -import net.corda.node.services.config.AuthDataSourceType import net.corda.node.services.Permissions import net.corda.node.services.config.PasswordEncryption -import net.corda.node.services.config.SecurityConfiguration -import net.corda.nodeapi.internal.config.User -import net.corda.nodeapi.internal.config.toConfig import net.corda.testing.ALICE_NAME import net.corda.testing.internal.IntegrationTestSchemas -import net.corda.testing.node.internal.NodeBasedTest import net.corda.testing.internal.toDatabaseSchemaName +import net.corda.testing.node.internal.NodeBasedTest import org.apache.activemq.artemis.api.core.ActiveMQSecurityException import org.apache.shiro.authc.credential.DefaultPasswordService import org.junit.After @@ -42,13 +38,7 @@ class AuthDBTests : NodeBasedTest() { companion object { @ClassRule @JvmField val databaseSchemas = IntegrationTestSchemas(ALICE_NAME.toDatabaseSchemaName()) - } - private lateinit var node: StartedNode - private lateinit var client: CordaRPCClient - private lateinit var db: UsersDB - - companion object { private val cacheExpireAfterSecs: Long = 1 @JvmStatic @@ -56,6 +46,10 @@ class AuthDBTests : NodeBasedTest() { fun encFormats() = arrayOf(PasswordEncryption.NONE, PasswordEncryption.SHIRO_1_CRYPT) } + private lateinit var node: StartedNode + private lateinit var client: CordaRPCClient + private lateinit var db: UsersDB + @Parameterized.Parameter lateinit var passwordEncryption: PasswordEncryption diff --git a/node/src/integration-test/kotlin/net/corda/node/SSHServerTest.kt b/node/src/integration-test/kotlin/net/corda/node/SSHServerTest.kt index d32a24fabd..3ec641e49f 100644 --- a/node/src/integration-test/kotlin/net/corda/node/SSHServerTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/SSHServerTest.kt @@ -14,21 +14,19 @@ import net.corda.core.utilities.unwrap import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.internal.config.User import net.corda.testing.ALICE_NAME +import net.corda.testing.driver.driver import net.corda.testing.internal.IntegrationTest import net.corda.testing.internal.IntegrationTestSchemas -import net.corda.testing.driver.driver import net.corda.testing.internal.toDatabaseSchemaName import org.assertj.core.api.Assertions.assertThat import org.bouncycastle.util.io.Streams import org.junit.ClassRule +import org.junit.Ignore import org.junit.Test import java.net.ConnectException import java.util.regex.Pattern import kotlin.test.assertTrue import kotlin.test.fail -import org.assertj.core.api.Assertions.assertThat -import org.junit.Ignore -import java.util.regex.Pattern class SSHServerTest : IntegrationTest() { companion object {