diff --git a/network-management/README.md b/network-management/README.md index 33929abdd0..4f278c83df 100644 --- a/network-management/README.md +++ b/network-management/README.md @@ -134,8 +134,8 @@ networkMapConfig { ### 1. Create keystore for local signer - If local signer is enabled, the server will look for keystores in the certificate folder on start up. - The keystores can be created using `--mode` flag. + If local signer is enabled, the server will look for key stores in the certificate folder on start up. + The key stores can be created using `--mode` flag. ``` java -jar doorman-.jar --mode ROOT_KEYGEN ``` @@ -180,8 +180,8 @@ networkMapConfig { Save the parameters to `network-parameters.conf` ### 5. Load initial network parameters file for network map service -A network parameters file is required to start the network map service for the first time. The initial network parameters file can be loaded using the `--update-network-parameter` flag. +A network parameters file is required to start the network map service for the first time. The initial network parameters file can be loaded using the `--update-network-parameters` flag. We can now restart the network management server with both doorman and network map service. ``` -java -jar doorman-.jar --update-network-parameter network-parameters.conf +java -jar doorman-.jar --update-network-parameters network-parameters.conf ``` diff --git a/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/doorman/NodeRegistrationTest.kt b/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/doorman/NodeRegistrationTest.kt index 7c6ef580f1..4ece2a3a21 100644 --- a/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/doorman/NodeRegistrationTest.kt +++ b/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/doorman/NodeRegistrationTest.kt @@ -15,6 +15,7 @@ import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.seconds import net.corda.finance.DOLLARS import net.corda.finance.flows.CashIssueAndPaymentFlow +import net.corda.nodeapi.internal.createDevNetworkMapCa import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.network.NetworkParameters import net.corda.nodeapi.internal.persistence.DatabaseConfig @@ -60,15 +61,17 @@ class NodeRegistrationTest : IntegrationTest() { private val dbId = random63BitValue().toString() private lateinit var rootCaCert: X509Certificate - private lateinit var intermediateCa: CertificateAndKeyPair + private lateinit var csrCa: CertificateAndKeyPair + private lateinit var networkMapCa: CertificateAndKeyPair private var server: NetworkManagementServer? = null @Before fun init() { - val (rootCa, intermediateCa) = createDevIntermediateCaCertPath() + val (rootCa, doormanCa) = createDevIntermediateCaCertPath() rootCaCert = rootCa.certificate - this.intermediateCa = intermediateCa + this.csrCa = doormanCa + networkMapCa = createDevNetworkMapCa(rootCa) } @After @@ -138,10 +141,15 @@ class NodeRegistrationTest : IntegrationTest() { start( serverAddress, configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true)), - LocalSigner(intermediateCa.keyPair, arrayOf(intermediateCa.certificate, rootCaCert)), - networkParameters, - networkParameters?.let { NetworkMapConfig(cacheTimeout = timeoutMillis, signInterval = timeoutMillis) }, - DoormanConfig(approveAll = true, jiraConfig = null, approveInterval = timeoutMillis) + LocalSigner(csrCa.keyPair, arrayOf(csrCa.certificate, rootCaCert)), + DoormanConfig(approveAll = true, jiraConfig = null, approveInterval = timeoutMillis), + networkParameters?.let { + NetworkMapStartParams( + LocalSigner(networkMapCa.keyPair, arrayOf(networkMapCa.certificate, rootCaCert)), + networkParameters, + NetworkMapConfig(cacheTimeout = timeoutMillis, signInterval = timeoutMillis) + ) + } ) } } diff --git a/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/hsm/DemoMain.kt b/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/hsm/DemoMain.kt deleted file mode 100644 index bfc4366eeb..0000000000 --- a/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/hsm/DemoMain.kt +++ /dev/null @@ -1,43 +0,0 @@ -package com.r3.corda.networkmanage.hsm - -import com.r3.corda.networkmanage.hsm.SigningServiceIntegrationTest.Companion.DB_NAME -import com.r3.corda.networkmanage.hsm.SigningServiceIntegrationTest.Companion.H2_TCP_PORT -import com.r3.corda.networkmanage.hsm.SigningServiceIntegrationTest.Companion.HOST -import com.r3.corda.networkmanage.hsm.configuration.Parameters -import java.util.* - -/** - * The main method for an interactive HSM signing service test/demo. It is supposed to be executed with the - * `DEMO - Create CSR and poll` method located in the [SigningServiceIntegrationTest], which is responsible for simulating - * CSR creation on the Doorman side. - * Execution instructions: - * 1) It is assumed that the HSM simulator is installed locally (or via means of the VM) and accessible under the address - * configured under the 'device' parameter (defaults to 3001@127.0.0.1). If that is not the case please specify - * a correct 'device' parameter value. Also, it is assumed that the HSM setup consists of a cryptographic user eligible to - * sign the CSRs (and potentially to generate new root and intermediate certificates). - * 2) Run the `DEMO - Create CSR and poll` as a regular test from your IntelliJ. - * The method starts the doorman, creates 3 CSRs for ALICE, BOB and CHARLIE - * and then polls the doorman until all 3 requests are signed. - * 3) Once the `DEMO - Create CSR and poll` is started, execute the following main method - * and interact with console menu options presented. - */ -fun main(args: Array) { - run(Parameters( - dataSourceProperties = makeTestDataSourceProperties(), - databaseConfig = makeNotInitialisingTestDatabaseProperties(), - csrPrivateKeyPassword = "", - networkMapPrivateKeyPassword = "", - rootPrivateKeyPassword = "", - keyGroup = "DEV.DOORMAN", - validDays = 3650 - )) -} - -private fun makeTestDataSourceProperties(): Properties { - val props = Properties() - props.setProperty("dataSourceClassName", "org.h2.jdbcx.JdbcDataSource") - props.setProperty("dataSource.url", "jdbc:h2:tcp://$HOST:$H2_TCP_PORT/mem:$DB_NAME;DB_CLOSE_DELAY=-1") - props.setProperty("dataSource.user", "sa") - props.setProperty("dataSource.password", "") - return props -} 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 bcbb91fec9..765d47d5f0 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 @@ -22,27 +22,25 @@ import net.corda.nodeapi.internal.createDevNodeCa import net.corda.nodeapi.internal.crypto.* import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.testing.ALICE_NAME -import net.corda.testing.BOB_NAME -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.pkcs.jcajce.JcaPKCS10CertificationRequest -import org.h2.tools.Server -import org.junit.* +import org.junit.After +import org.junit.Before +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.persistence.PersistenceException import kotlin.concurrent.scheduleAtFixedRate -import kotlin.concurrent.thread class SigningServiceIntegrationTest { companion object { - val H2_TCP_PORT = "8092" - val HOST = "localhost" - val DB_NAME = "test_db" + private val HOST = "localhost" + private val DB_NAME = "test_db" } @Rule @@ -92,7 +90,7 @@ class SigningServiceIntegrationTest { val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true)) NetworkManagementServer().use { server -> - server.start(NetworkHostAndPort(HOST, 0), database, networkMapServiceParameter = null, doormanServiceParameter = DoormanConfig(approveAll = true, approveInterval = 2.seconds.toMillis(), jiraConfig = null), updateNetworkParameters = null) + server.start(NetworkHostAndPort(HOST, 0), database, doormanServiceParameter = DoormanConfig(approveAll = true, approveInterval = 2.seconds.toMillis(), jiraConfig = null), startNetworkMap = null) val doormanHostAndPort = server.hostAndPort // Start Corda network registration. val config = createConfig().also { @@ -131,51 +129,6 @@ class SigningServiceIntegrationTest { } } - /* - * Piece of code is purely for demo purposes and should not be considered as actual test (therefore it is ignored). - * Its purpose is to produce 3 CSRs and wait (polling Doorman) for external signature. - * The use of the jUnit testing framework was chosen due to the convenience reasons: mocking, tempFolder storage. - * It is meant to be run together with the [DemoMain.main] method, which executes HSM signing service. - * The split is done due to the limited console support while executing tests and inability to capture user's input there. - * - */ - @Ignore - @Test - fun `DEMO - Create CSR and poll`() { - //Start doorman server - val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true)) - - NetworkManagementServer().use { server -> - server.start(NetworkHostAndPort(HOST, 0), database, networkMapServiceParameter = null, doormanServiceParameter = DoormanConfig(approveAll = true, approveInterval = 2.seconds.toMillis(), jiraConfig = null), updateNetworkParameters = null) - thread(start = true, isDaemon = true) { - val h2ServerArgs = arrayOf("-tcpPort", H2_TCP_PORT, "-tcpAllowOthers") - Server.createTcpServer(*h2ServerArgs).start() - } - - // Start Corda network registration. - (1..3).map { num -> - thread(start = true) { - // Start Corda network registration. - val config = createConfig().also { - doReturn(when (num) { - 1 -> ALICE_NAME - 2 -> BOB_NAME - 3 -> CHARLIE_NAME - else -> throw IllegalArgumentException("Unrecognised option") - }).whenever(it).myLegalName - doReturn(URL("http://$HOST:${server.hostAndPort.port}")).whenever(it).compatibilityZoneURL - } - config.certificatesDirectory.createDirectories() - loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also { - it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCaCert) - it.save(config.trustStoreFile, config.trustStorePassword) - } - NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.compatibilityZoneURL!!)).buildKeystore() - } - }.map { it.join() } - } - } - private fun createConfig(): NodeConfiguration { return rigorousMock().also { doReturn(tempFolder.root.toPath()).whenever(it).baseDirectory @@ -198,5 +151,3 @@ class SigningServiceIntegrationTest { return props } } - -internal fun makeNotInitialisingTestDatabaseProperties() = DatabaseConfig(runMigration = false) diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/NetworkMapStorage.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/NetworkMapStorage.kt index 0dea37f7b8..732f049443 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/NetworkMapStorage.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/NetworkMapStorage.kt @@ -1,9 +1,9 @@ package com.r3.corda.networkmanage.common.persistence +import com.r3.corda.networkmanage.common.utils.SignedNetworkMap +import com.r3.corda.networkmanage.common.utils.SignedNetworkParameters import net.corda.core.crypto.SecureHash -import net.corda.core.crypto.SignedData import net.corda.nodeapi.internal.network.NetworkParameters -import net.corda.nodeapi.internal.network.SignedNetworkMap /** * Data access object interface for NetworkMap persistence layer @@ -34,7 +34,7 @@ interface NetworkMapStorage { * Return the signed network parameters object which matches the given hash. The hash is that of the underlying * [NetworkParameters] object and not the `SignedData` object that's returned. */ - fun getSignedNetworkParameters(hash: SecureHash): SignedData? + fun getSignedNetworkParameters(hash: SecureHash): SignedNetworkParameters? /** * Retrieve network map parameters. diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNetworkMapStorage.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNetworkMapStorage.kt index 314d2b9f83..740885dee0 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNetworkMapStorage.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNetworkMapStorage.kt @@ -1,17 +1,16 @@ package com.r3.corda.networkmanage.common.persistence import com.r3.corda.networkmanage.common.persistence.entity.* +import com.r3.corda.networkmanage.common.utils.SignedNetworkMap +import com.r3.corda.networkmanage.common.utils.SignedNetworkParameters import com.r3.corda.networkmanage.doorman.signer.LocalSigner -import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.SecureHash -import net.corda.core.crypto.SignedData import net.corda.core.crypto.sha256 import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.nodeapi.internal.network.NetworkMap import net.corda.nodeapi.internal.network.NetworkParameters -import net.corda.nodeapi.internal.network.SignedNetworkMap import net.corda.nodeapi.internal.persistence.CordaPersistence /** @@ -40,8 +39,8 @@ class PersistentNetworkMapStorage(private val database: CordaPersistence, privat database.transaction { val networkMapEntity = NetworkMapEntity( networkMap = signedNetworkMap.raw.bytes, - signature = signedNetworkMap.signature.signatureBytes, - certificate = signedNetworkMap.signature.by.encoded + signature = signedNetworkMap.sig.bytes, + certificate = signedNetworkMap.sig.by.encoded ) session.save(networkMapEntity) } @@ -49,10 +48,10 @@ class PersistentNetworkMapStorage(private val database: CordaPersistence, privat // TODO The signing cannot occur here as it won't work with an HSM. The signed network parameters needs to be persisted // into the database. - override fun getSignedNetworkParameters(hash: SecureHash): SignedData? { + override fun getSignedNetworkParameters(hash: SecureHash): SignedNetworkParameters? { val netParamsBytes = getNetworkParametersEntity(hash.toString())?.parametersBytes ?: return null - val sigWithCert = localSigner!!.sign(netParamsBytes) - return SignedData(SerializedBytes(netParamsBytes), DigitalSignature.WithKey(sigWithCert.by.publicKey, sigWithCert.signatureBytes)) + val sigWithCert = localSigner!!.signBytes(netParamsBytes) + return SignedNetworkParameters(SerializedBytes(netParamsBytes), sigWithCert) } override fun getNodeInfoHashes(certificateStatus: CertificateStatus): List { diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/NetworkMapEntity.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/NetworkMapEntity.kt index 019bed926e..f71a252213 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/NetworkMapEntity.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/NetworkMapEntity.kt @@ -1,7 +1,7 @@ package com.r3.corda.networkmanage.common.persistence.entity +import net.corda.core.internal.DigitalSignatureWithCert import net.corda.nodeapi.internal.crypto.X509CertificateFactory -import net.corda.nodeapi.internal.network.DigitalSignatureWithCert import javax.persistence.* @Entity @@ -24,7 +24,7 @@ class NetworkMapEntity( val certificate: ByteArray ) { /** - * Deserializes NetworkMapEntity.signatureBytes into the [SignatureAndCertPath] instance + * Deserializes NetworkMapEntity.signatureBytes into the [DigitalSignatureWithCert] instance */ fun signatureAndCertificate(): DigitalSignatureWithCert { return DigitalSignatureWithCert(X509CertificateFactory().generateCertificate(certificate.inputStream()), signature) diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/signer/NetworkMapSigner.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/signer/NetworkMapSigner.kt index 05f0111d42..cafc5e7469 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/signer/NetworkMapSigner.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/signer/NetworkMapSigner.kt @@ -2,9 +2,9 @@ package com.r3.corda.networkmanage.common.signer import com.r3.corda.networkmanage.common.persistence.CertificateStatus import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage +import net.corda.core.internal.SignedDataWithCert import net.corda.core.serialization.serialize import net.corda.nodeapi.internal.network.NetworkMap -import net.corda.nodeapi.internal.network.SignedNetworkMap class NetworkMapSigner(private val networkMapStorage: NetworkMapStorage, private val signer: Signer) { /** @@ -14,12 +14,10 @@ class NetworkMapSigner(private val networkMapStorage: NetworkMapStorage, private val currentSignedNetworkMap = networkMapStorage.getCurrentNetworkMap() val nodeInfoHashes = networkMapStorage.getNodeInfoHashes(CertificateStatus.VALID) val networkParameters = networkMapStorage.getLatestNetworkParameters() - val networkMap = NetworkMap(nodeInfoHashes, networkParameters.serialize().hash) - // We wan only check if the data structure is same. - if (networkMap != currentSignedNetworkMap?.verified(null)) { - val digitalSignature = signer.sign(networkMap.serialize().bytes) - val signedHashedNetworkMap = SignedNetworkMap(networkMap.serialize(), digitalSignature) - networkMapStorage.saveNetworkMap(signedHashedNetworkMap) + val serialisedNetworkMap = NetworkMap(nodeInfoHashes, networkParameters.serialize().hash).serialize() + if (serialisedNetworkMap != currentSignedNetworkMap?.raw) { + val newSignedNetworkMap = SignedDataWithCert(serialisedNetworkMap, signer.signBytes(serialisedNetworkMap.bytes)) + networkMapStorage.saveNetworkMap(newSignedNetworkMap) } } } \ No newline at end of file diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/signer/Signer.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/signer/Signer.kt index e00ee54335..40a030c2b0 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/signer/Signer.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/signer/Signer.kt @@ -1,17 +1,24 @@ package com.r3.corda.networkmanage.common.signer -import net.corda.nodeapi.internal.network.DigitalSignatureWithCert +import net.corda.core.internal.DigitalSignatureWithCert +import net.corda.core.internal.SignedDataWithCert +import net.corda.core.serialization.serialize /** * An interface for arbitrary data signing functionality. */ interface Signer { /** - * Signs given [data]. The signing key selction strategy is left to the implementing class. - * @return [SignatureAndCertPath] that encapsulates the signature and the certificate path used in the signing process. + * Signs given bytes. The signing key selction strategy is left to the implementing class. + * @return [DigitalSignatureWithCert] that encapsulates the signature and the certificate path used in the signing process. * @throws [AuthenticationException] if fails authentication */ - fun sign(data: ByteArray): DigitalSignatureWithCert + fun signBytes(data: ByteArray): DigitalSignatureWithCert + + fun signObject(obj: T): SignedDataWithCert { + val serialised = obj.serialize() + return SignedDataWithCert(serialised, signBytes(serialised.bytes)) + } } class AuthenticationException : Exception() diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/utils/Utils.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/utils/Utils.kt index f1647de6bd..8ac987e03a 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/utils/Utils.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/utils/Utils.kt @@ -5,15 +5,18 @@ import com.typesafe.config.Config import com.typesafe.config.ConfigFactory import joptsimple.ArgumentAcceptingOptionSpec import joptsimple.OptionParser -import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.sha256 +import net.corda.core.internal.SignedDataWithCert import net.corda.nodeapi.internal.crypto.X509CertificateFactory -import net.corda.nodeapi.internal.network.DigitalSignatureWithCert -import org.bouncycastle.cert.X509CertificateHolder +import net.corda.nodeapi.internal.network.NetworkMap +import net.corda.nodeapi.internal.network.NetworkParameters import java.security.PublicKey import java.security.cert.CertPath import java.security.cert.Certificate -import java.security.cert.X509Certificate + +// TODO These should be defined in node-api +typealias SignedNetworkParameters = SignedDataWithCert +typealias SignedNetworkMap = SignedDataWithCert // TODO: replace this with Crypto.hash when its available. /** @@ -39,15 +42,10 @@ fun Array.toConfigWithOptions(registerOptions: OptionParser.() -> Un class ShowHelpException(val parser: OptionParser, val errorMessage: String? = null) : Exception() -// TODO Remove this as we already have InternalUtils.cert -fun X509CertificateHolder.toX509Certificate(): X509Certificate = X509CertificateFactory().generateCertificate(encoded.inputStream()) - fun buildCertPath(vararg certificates: Certificate): CertPath = X509CertificateFactory().delegate.generateCertPath(certificates.asList()) fun buildCertPath(certPathBytes: ByteArray): CertPath = X509CertificateFactory().delegate.generateCertPath(certPathBytes.inputStream()) -fun DigitalSignature.WithKey.withCert(cert: X509Certificate): DigitalSignatureWithCert = DigitalSignatureWithCert(cert, bytes) - private fun String.toCamelcase(): String { return if (contains('_') || contains('-')) { CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, this.replace("-", "_")) diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/DoormanParameters.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/DoormanParameters.kt index 3d4b47a107..4ffaf397ce 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/DoormanParameters.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/DoormanParameters.kt @@ -53,10 +53,11 @@ data class DoormanConfig(val approveAll: Boolean = false, val approveInterval: Long = NetworkManagementServerParameters.DEFAULT_APPROVE_INTERVAL.toMillis()) data class NetworkMapConfig(val cacheTimeout: Long, - // TODO: Move signing to signing server. + // TODO: Move signing to signing server. val signInterval: Long = NetworkManagementServerParameters.DEFAULT_SIGN_INTERVAL.toMillis()) enum class Mode { + // TODO CA_KEYGEN now also generates the nework map cert, so it should be renamed. DOORMAN, CA_KEYGEN, ROOT_KEYGEN } 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 6b5f3636f3..9cce45f4ec 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 @@ -11,7 +11,10 @@ import com.r3.corda.networkmanage.doorman.signer.LocalSigner import com.r3.corda.networkmanage.doorman.webservice.MonitoringWebService import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService import com.r3.corda.networkmanage.doorman.webservice.RegistrationWebService +import com.r3.corda.networkmanage.hsm.configuration.Parameters.Companion.DEFAULT_CSR_CERTIFICATE_NAME +import com.r3.corda.networkmanage.hsm.configuration.Parameters.Companion.DEFAULT_NETWORK_MAP_CERTIFICATE_NAME import net.corda.core.crypto.Crypto +import net.corda.core.crypto.SignatureScheme import net.corda.core.identity.CordaX500Name import net.corda.core.internal.createDirectories import net.corda.core.internal.div @@ -34,6 +37,7 @@ import java.time.Instant import java.util.* import java.util.concurrent.Executors import java.util.concurrent.TimeUnit +import javax.security.auth.x500.X500Principal import kotlin.concurrent.thread import kotlin.system.exitProcess @@ -130,17 +134,16 @@ class NetworkManagementServer : Closeable { fun start(hostAndPort: NetworkHostAndPort, database: CordaPersistence, - signer: LocalSigner? = null, - updateNetworkParameters: NetworkParameters?, - networkMapServiceParameter: NetworkMapConfig?, - doormanServiceParameter: DoormanConfig?) { - + doormanSigner: LocalSigner? = null, + doormanServiceParameter: DoormanConfig?, // TODO Doorman config shouldn't be optional as the doorman is always required to run + startNetworkMap: NetworkMapStartParams? + ) { val services = mutableListOf() val serverStatus = NetworkManagementServerStatus() // TODO: move signing to signing server. - networkMapServiceParameter?.let { services += getNetworkMapService(it, database, signer, updateNetworkParameters) } - doormanServiceParameter?.let { services += getDoormanService(it, database, signer, serverStatus) } + startNetworkMap?.let { services += getNetworkMapService(it.config, database, it.signer, it.updateNetworkParameters) } + doormanServiceParameter?.let { services += getDoormanService(it, database, doormanSigner, serverStatus) } require(services.isNotEmpty()) { "No service created, please provide at least one service config." } @@ -150,11 +153,13 @@ class NetworkManagementServer : Closeable { val webServer = NetworkManagementWebServer(hostAndPort, *services.toTypedArray()) webServer.start() - doOnClose += { webServer.close() } + doOnClose += webServer::close this.hostAndPort = webServer.hostAndPort } } +data class NetworkMapStartParams(val signer: LocalSigner?, val updateNetworkParameters: NetworkParameters?, val config: NetworkMapConfig) + data class NetworkManagementServerStatus(var serverStartTime: Instant = Instant.now(), var lastRequestCheckTime: Instant? = null) /** Read password from console, do a readLine instead if console is null (e.g. when debugging in IDE). */ @@ -203,58 +208,77 @@ fun generateRootKeyPair(rootStoreFile: Path, rootKeystorePass: String?, rootPriv println(loadKeyStore(rootStoreFile, rootKeystorePassword).getCertificate(X509Utilities.CORDA_ROOT_CA).publicKey) } -fun generateCAKeyPair(keystoreFile: Path, rootStoreFile: Path, rootKeystorePass: String?, rootPrivateKeyPass: String?, keystorePass: String?, caPrivateKeyPass: String?) { - println("Generating Intermediate CA keypair and certificate using root keystore $rootStoreFile.") +fun generateSigningKeyPairs(keystoreFile: Path, rootStoreFile: Path, rootKeystorePass: String?, rootPrivateKeyPass: String?, keystorePass: String?, caPrivateKeyPass: String?) { + println("Generating intermediate and network map key pairs and certificates using root key store $rootStoreFile.") // Get password from console if not in config. - val rootKeystorePassword = rootKeystorePass ?: readPassword("Root Keystore Password: ") - val rootPrivateKeyPassword = rootPrivateKeyPass ?: readPassword("Root Private Key Password: ") + val rootKeystorePassword = rootKeystorePass ?: readPassword("Root key store password: ") + val rootPrivateKeyPassword = rootPrivateKeyPass ?: readPassword("Root private key password: ") val rootKeyStore = loadKeyStore(rootStoreFile, rootKeystorePassword) - val rootKeyAndCert = rootKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_ROOT_CA, rootPrivateKeyPassword) + val rootKeyPairAndCert = rootKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_ROOT_CA, rootPrivateKeyPassword) - val keystorePassword = keystorePass ?: readPassword("Keystore Password: ") - val caPrivateKeyPassword = caPrivateKeyPass ?: readPassword("CA Private Key Password: ") + val keyStorePassword = keystorePass ?: readPassword("Key store Password: ") + val privateKeyPassword = caPrivateKeyPass ?: readPassword("Private key Password: ") // Ensure folder exists. keystoreFile.parent.createDirectories() - val keyStore = loadOrCreateKeyStore(keystoreFile, keystorePassword) + val keyStore = loadOrCreateKeyStore(keystoreFile, keyStorePassword) - if (keyStore.containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA)) { - val oldKey = loadOrCreateKeyStore(keystoreFile, rootKeystorePassword).getCertificate(X509Utilities.CORDA_INTERMEDIATE_CA).publicKey - println("Key ${X509Utilities.CORDA_INTERMEDIATE_CA} already exists in keystore, process will now terminate.") - println(oldKey) - exitProcess(1) + fun storeCertIfAbsent(alias: String, certificateType: CertificateType, subject: X500Principal, signatureScheme: SignatureScheme) { + if (keyStore.containsAlias(alias)) { + println("$alias already exists in keystore:") + println(keyStore.getCertificate(alias)) + return + } + + val keyPair = Crypto.generateKeyPair(signatureScheme) + val cert = X509Utilities.createCertificate( + certificateType, + rootKeyPairAndCert.certificate, + rootKeyPairAndCert.keyPair, + subject, + keyPair.public + ) + keyStore.addOrReplaceKey( + alias, + keyPair.private, + privateKeyPassword.toCharArray(), + arrayOf(cert, rootKeyPairAndCert.certificate) + ) + keyStore.save(keystoreFile, keyStorePassword) + + println("$certificateType key pair and certificate stored in $keystoreFile.") + println(cert) } - val intermediateKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val intermediateCert = X509Utilities.createCertificate( + storeCertIfAbsent( + DEFAULT_CSR_CERTIFICATE_NAME, 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) + X500Principal("CN=Corda Intermediate CA,OU=Corda,O=R3 Ltd,L=London,C=GB"), + X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) + + storeCertIfAbsent( + DEFAULT_NETWORK_MAP_CERTIFICATE_NAME, + CertificateType.NETWORK_MAP, + X500Principal("CN=Corda Network Map,OU=Corda,O=R3 Ltd,L=London,C=GB"), + Crypto.EDDSA_ED25519_SHA512) } -private fun buildLocalSigner(parameters: NetworkManagementServerParameters): LocalSigner? { - return parameters.keystorePath?.let { - // Get password from console if not in config. - val keystorePassword = parameters.keystorePassword ?: readPassword("Keystore Password: ") - val caPrivateKeyPassword = parameters.caPrivateKeyPassword ?: readPassword("CA Private Key Password: ") - val keystore = loadOrCreateKeyStore(parameters.keystorePath, keystorePassword) - val caKeyPair = keystore.getKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, caPrivateKeyPassword) - val caCertPath = keystore.getCertificateChain(X509Utilities.CORDA_INTERMEDIATE_CA).map { it as X509Certificate } - LocalSigner(caKeyPair, caCertPath.toTypedArray()) +private fun buildLocalSigners(parameters: NetworkManagementServerParameters): Pair? { + if (parameters.keystorePath == null) return null + + // Get password from console if not in config. + val keyStorePassword = parameters.keystorePassword ?: readPassword("Key store password: ") + val privateKeyPassword = parameters.caPrivateKeyPassword ?: readPassword("Private key password: ") + val keyStore = loadOrCreateKeyStore(parameters.keystorePath, keyStorePassword) + + val (doormanSigner, networkMapSigner) = listOf(DEFAULT_CSR_CERTIFICATE_NAME, DEFAULT_NETWORK_MAP_CERTIFICATE_NAME).map { + val keyPair = keyStore.getKeyPair(it, privateKeyPassword) + val certPath = keyStore.getCertificateChain(it).map { it as X509Certificate } + LocalSigner(keyPair, certPath.toTypedArray()) } + + return Pair(doormanSigner, networkMapSigner) } /** @@ -278,7 +302,7 @@ fun main(args: Array) { rootStorePath ?: throw IllegalArgumentException("The 'rootStorePath' parameter must be specified when generating keys!"), rootKeystorePassword, rootPrivateKeyPassword) - Mode.CA_KEYGEN -> generateCAKeyPair( + Mode.CA_KEYGEN -> generateSigningKeyPairs( keystorePath ?: throw IllegalArgumentException("The 'keystorePath' parameter must be specified when generating keys!"), rootStorePath ?: throw IllegalArgumentException("The 'rootStorePath' parameter must be specified when generating keys!"), rootKeystorePassword, @@ -289,18 +313,24 @@ fun main(args: Array) { initialiseSerialization() val database = configureDatabase(dataSourceProperties) // TODO: move signing to signing server. - val signer = buildLocalSigner(this) + val localSigners = buildLocalSigners(this) - if (signer != null) { - println("Starting network management services with local signer.") + if (localSigners != null) { + println("Starting network management services with local signing") } val networkManagementServer = NetworkManagementServer() - val networkParameter = updateNetworkParameters?.let { - println("Parsing network parameter from '${it.fileName}'...") + val networkParameters = updateNetworkParameters?.let { + // TODO This check shouldn't be needed. Fix up the config design. + requireNotNull(networkMapConfig) { "'networkMapConfig' config is required for applying network parameters" } + println("Parsing network parameters from '${it.toAbsolutePath()}'...") parseNetworkParametersFrom(it) } - networkManagementServer.start(NetworkHostAndPort(host, port), database, signer, networkParameter, networkMapConfig, doormanConfig) + val networkMapStartParams = networkMapConfig?.let { + NetworkMapStartParams(localSigners?.second, networkParameters, it) + } + + networkManagementServer.start(NetworkHostAndPort(host, port), database, localSigners?.first, doormanConfig, networkMapStartParams) Runtime.getRuntime().addShutdownHook(thread(start = false) { networkManagementServer.close() 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 aa7ceaa742..18343d7e44 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 @@ -2,11 +2,10 @@ package com.r3.corda.networkmanage.doorman.signer 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.crypto.Crypto +import net.corda.core.internal.DigitalSignatureWithCert import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509Utilities -import net.corda.nodeapi.internal.network.DigitalSignatureWithCert import org.bouncycastle.asn1.x509.GeneralName import org.bouncycastle.asn1.x509.GeneralSubtree import org.bouncycastle.asn1.x509.NameConstraints @@ -21,7 +20,9 @@ import javax.security.auth.x500.X500Principal * The [LocalSigner] class signs [PKCS10CertificationRequest] using provided CA key pair and certificate path. * This is intended to be used in testing environment where hardware signing module is not available. */ -class LocalSigner(private val caKeyPair: KeyPair, private val caCertPath: Array) : Signer { +//TODO Use a list instead of array +class LocalSigner(private val signingKeyPair: KeyPair, private val signingCertPath: Array) : Signer { + // TODO This doesn't belong in this class fun createSignedClientCertificate(certificationRequest: PKCS10CertificationRequest): CertPath { // The sub certs issued by the client must satisfy this directory name (or legal name in Corda) constraints, sub certs' directory name must be within client CA's name's subtree, // please see [sun.security.x509.X500Name.isWithinSubtree()] for more information. @@ -33,15 +34,15 @@ class LocalSigner(private val caKeyPair: KeyPair, private val caCertPath: Array< arrayOf()) val nodeCaCert = X509Utilities.createCertificate( CertificateType.NODE_CA, - caCertPath[0], - caKeyPair, + signingCertPath[0], + signingKeyPair, X500Principal(request.subject.encoded), request.publicKey, nameConstraints = nameConstraints) - return buildCertPath(nodeCaCert, *caCertPath) + return buildCertPath(nodeCaCert, *signingCertPath) } - override fun sign(data: ByteArray): DigitalSignatureWithCert { - return caKeyPair.sign(data).withCert(caCertPath.first()) + override fun signBytes(data: ByteArray): DigitalSignatureWithCert { + return DigitalSignatureWithCert(signingCertPath[0], Crypto.doSign(signingKeyPair.private, data)) } } diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/webservice/NodeInfoWebService.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/webservice/NodeInfoWebService.kt index 2ac26743e2..0b1f1e57f6 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/webservice/NodeInfoWebService.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/webservice/NodeInfoWebService.kt @@ -6,6 +6,7 @@ import com.google.common.cache.LoadingCache import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage import com.r3.corda.networkmanage.common.persistence.NodeInfoStorage import com.r3.corda.networkmanage.common.persistence.NodeInfoWithSigned +import com.r3.corda.networkmanage.common.utils.SignedNetworkMap import com.r3.corda.networkmanage.doorman.NetworkMapConfig import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService.Companion.NETWORK_MAP_PATH import net.corda.core.crypto.SecureHash @@ -15,7 +16,6 @@ import net.corda.core.serialization.serialize import net.corda.core.utilities.contextLogger import net.corda.nodeapi.internal.SignedNodeInfo import net.corda.nodeapi.internal.network.NetworkParameters -import net.corda.nodeapi.internal.network.SignedNetworkMap import java.io.InputStream import java.security.InvalidKeyException import java.security.SignatureException @@ -77,9 +77,9 @@ class NodeInfoWebService(private val nodeInfoStorage: NodeInfoStorage, } @GET - @Path("network-parameter/{netParamsHash}") // TODO Fix path to be /network-parameters - fun getNetworkParameters(@PathParam("netParamsHash") netParamsHash: String): Response { - val signedNetParams = networkMapStorage.getSignedNetworkParameters(SecureHash.parse(netParamsHash)) + @Path("network-parameters/{hash}") + fun getNetworkParameters(@PathParam("hash") hash: String): Response { + val signedNetParams = networkMapStorage.getSignedNetworkParameters(SecureHash.parse(hash)) return createResponse(signedNetParams) } diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/configuration/Configuration.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/configuration/Configuration.kt index 6986f3a862..09475b4910 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/configuration/Configuration.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/hsm/configuration/Configuration.kt @@ -48,7 +48,7 @@ data class Parameters(val dataSourceProperties: Properties, val DEFAULT_KEY_FILE_PATH: Path? = null //Paths.get("/Users/michalkit/WinDev1706Eval/Shared/TEST4.key") val DEFAULT_KEY_FILE_PASSWORD: String? = null val DEFAULT_AUTO_USERNAME: String? = null - val DEFAULT_NETWORK_MAP_CERTIFICATE_NAME = "cordaintermediateca_nm" + val DEFAULT_NETWORK_MAP_CERTIFICATE_NAME = "cordaintermediateca_nm" // TODO Change the value to "cordanetworkmap" since this is not a CA val DEFAULT_SIGN_INTERVAL = 600L // in seconds (10 minutes) } } 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 0465a3621a..322233220f 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 @@ -4,16 +4,15 @@ import com.google.common.util.concurrent.MoreExecutors import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage import com.r3.corda.networkmanage.common.signer.NetworkMapSigner import com.r3.corda.networkmanage.common.signer.Signer -import com.r3.corda.networkmanage.common.utils.withCert import com.r3.corda.networkmanage.hsm.authentication.Authenticator +import com.r3.corda.networkmanage.hsm.utils.X509Utilities 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.DigitalSignatureWithCert 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.Signature import java.security.cert.X509Certificate import java.time.Duration import java.util.concurrent.Executors @@ -24,6 +23,7 @@ import java.util.concurrent.TimeUnit * Encapsulates logic for periodic network map signing execution. * It uses HSM as the signing entity with keys and certificates specified at the construction time. */ +// TODO Rename this to HsmSigner class HsmNetworkMapSigner(networkMapStorage: NetworkMapStorage, private val caCertificateKeyName: String, private val caPrivateKeyPass: String, @@ -40,6 +40,7 @@ class HsmNetworkMapSigner(networkMapStorage: NetworkMapStorage, private val networkMapSigner = NetworkMapSigner(networkMapStorage, this) private lateinit var scheduledExecutor: ScheduledExecutorService + // TODO This doesn't belong in this class fun start(): HsmNetworkMapSigner { val signingPeriodMillis = signingPeriod.toMillis() scheduledExecutor = Executors.newSingleThreadScheduledExecutor() @@ -60,14 +61,18 @@ class HsmNetworkMapSigner(networkMapStorage: NetworkMapStorage, /** * Signs given data using [CryptoServerJCE.CryptoServerProvider], which connects to the underlying HSM. */ - override fun sign(data: ByteArray): DigitalSignatureWithCert { + override fun signBytes(data: ByteArray): DigitalSignatureWithCert { return authenticator.connectAndAuthenticate { provider, _ -> val keyStore = getAndInitializeKeyStore(provider) val caCertificateChain = keyStore.getCertificateChain(caCertificateKeyName) 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[0] as X509Certificate) + val signature = Signature.getInstance(X509Utilities.SIGNATURE_ALGORITHM, provider).run { + initSign(caKey) + update(data) + sign() + } + verify(data, signature, caCertificateChain[0].publicKey) + DigitalSignatureWithCert(caCertificateChain[0] as X509Certificate, signature) } } } 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 dc4354fe0b..9bdd397e46 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 @@ -1,7 +1,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.x500Name import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair @@ -268,27 +267,14 @@ object X509Utilities { } } - /** - * Sign data with the given private key - */ - fun signData(data: ByteArray, - keyPair: KeyPair, - provider: Provider, - signatureAlgorithm: String = SIGNATURE_ALGORITHM): DigitalSignature.WithKey { - val signer = Signature.getInstance(signatureAlgorithm, provider) - signer.initSign(keyPair.private) - signer.update(data) - return DigitalSignature.WithKey(keyPair.public, signer.sign()) - } - fun verify(data: ByteArray, - signature: DigitalSignature, + signature: ByteArray, publicKey: PublicKey, signatureAlgorithm: String = SIGNATURE_ALGORITHM) { val verify = Signature.getInstance(signatureAlgorithm) verify.initVerify(publicKey) verify.update(data) - require(verify.verify(signature.bytes)) { "Signature didn't independently verify" } + require(verify.verify(signature)) { "Signature didn't independently verify" } } /** 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 1ca4abd931..01e95a7277 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 @@ -1,16 +1,14 @@ 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.sign import net.corda.core.identity.CordaX500Name -import net.corda.core.serialization.serialize -import net.corda.nodeapi.internal.SignedNodeInfo +import net.corda.core.internal.signWithCert +import net.corda.nodeapi.internal.createDevNetworkMapCa import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.network.NetworkMap -import net.corda.nodeapi.internal.network.SignedNetworkMap +import net.corda.nodeapi.internal.network.verifiedNetworkMapCert import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.testing.common.internal.testNetworkParameters @@ -31,15 +29,15 @@ class PersistentNetworkMapStorageTest : TestBase() { private lateinit var requestStorage: PersistentCertificateRequestStorage private lateinit var rootCaCert: X509Certificate - private lateinit var intermediateCa: CertificateAndKeyPair + private lateinit var networkMapCa: CertificateAndKeyPair @Before fun startDb() { - val (rootCa, intermediateCa) = createDevIntermediateCaCertPath() + val (rootCa) = createDevIntermediateCaCertPath() rootCaCert = rootCa.certificate - this.intermediateCa = intermediateCa + networkMapCa = createDevNetworkMapCa(rootCa) persistence = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true)) - networkMapStorage = PersistentNetworkMapStorage(persistence, LocalSigner(intermediateCa.keyPair, arrayOf(intermediateCa.certificate, rootCaCert))) + networkMapStorage = PersistentNetworkMapStorage(persistence, LocalSigner(networkMapCa.keyPair, arrayOf(networkMapCa.certificate, rootCaCert))) nodeInfoStorage = PersistentNodeInfoStorage(persistence) requestStorage = PersistentCertificateRequestStorage(persistence) } @@ -60,9 +58,7 @@ class PersistentNetworkMapStorageTest : TestBase() { val networkParametersHash = networkMapStorage.saveNetworkParameters(testNetworkParameters(emptyList())) val networkMap = NetworkMap(listOf(nodeInfoHash), networkParametersHash) - val serializedNetworkMap = networkMap.serialize() - val signatureData = intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate) - val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, signatureData) + val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate) // when networkMapStorage.saveNetworkMap(signedNetworkMap) @@ -70,8 +66,8 @@ class PersistentNetworkMapStorageTest : TestBase() { // then val persistedSignedNetworkMap = networkMapStorage.getCurrentNetworkMap() - assertEquals(signedNetworkMap.signature, persistedSignedNetworkMap?.signature) - assertEquals(signedNetworkMap.verified(rootCaCert), persistedSignedNetworkMap?.verified(rootCaCert)) + assertEquals(signedNetworkMap.sig, persistedSignedNetworkMap?.sig) + assertEquals(signedNetworkMap.verifiedNetworkMapCert(rootCaCert), persistedSignedNetworkMap?.verifiedNetworkMapCert(rootCaCert)) } @Test @@ -96,9 +92,7 @@ class PersistentNetworkMapStorageTest : TestBase() { // Sign network map making it current network map val networkMap = NetworkMap(emptyList(), networkParametersHash) - val serializedNetworkMap = networkMap.serialize() - val signatureData = intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate) - val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, signatureData) + val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate) networkMapStorage.saveNetworkMap(signedNetworkMap) // Create new network parameters @@ -114,11 +108,11 @@ class PersistentNetworkMapStorageTest : TestBase() { // This test will probably won't be needed when we remove the explicit use of LocalSigner @Test fun `getSignedNetworkParameters uses the local signer to return a signed object`() { - val netParams = testNetworkParameters(emptyList()) - val netParamsHash = networkMapStorage.saveNetworkParameters(netParams) - val signedNetParams = networkMapStorage.getSignedNetworkParameters(netParamsHash) - assertThat(signedNetParams?.verified()).isEqualTo(netParams) - assertThat(signedNetParams?.sig?.by).isEqualTo(intermediateCa.keyPair.public) + val networkParameters = testNetworkParameters(emptyList()) + val netParamsHash = networkMapStorage.saveNetworkParameters(networkParameters) + val signedNetworkParameters = networkMapStorage.getSignedNetworkParameters(netParamsHash) + assertThat(signedNetworkParameters?.verifiedNetworkMapCert(rootCaCert)).isEqualTo(networkParameters) + assertThat(signedNetworkParameters?.sig?.by).isEqualTo(networkMapCa.certificate) } @Test @@ -135,9 +129,7 @@ class PersistentNetworkMapStorageTest : TestBase() { // Create network parameters val networkParametersHash = networkMapStorage.saveNetworkParameters(testNetworkParameters(emptyList())) val networkMap = NetworkMap(listOf(nodeInfoHashA), networkParametersHash) - val serializedNetworkMap = networkMap.serialize() - val signatureData = intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate) - val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, signatureData) + val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate) // Sign network map networkMapStorage.saveNetworkMap(signedNetworkMap) 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 71926c7fac..014fd0895e 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 @@ -3,21 +3,24 @@ package com.r3.corda.networkmanage.common.signer 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 com.r3.corda.networkmanage.common.utils.SignedNetworkMap +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.internal.DigitalSignatureWithCert +import net.corda.core.internal.signWithCert import net.corda.core.serialization.serialize +import net.corda.nodeapi.internal.createDevNetworkMapCa import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.network.NetworkMap -import net.corda.nodeapi.internal.network.SignedNetworkMap +import net.corda.nodeapi.internal.network.verifiedNetworkMapCert import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.internal.createDevIntermediateCaCertPath +import org.assertj.core.api.Assertions.assertThat import org.junit.Before import org.junit.Test import java.security.cert.X509Certificate import kotlin.test.assertEquals -import kotlin.test.assertTrue class NetworkMapSignerTest : TestBase() { private lateinit var signer: Signer @@ -25,13 +28,13 @@ class NetworkMapSignerTest : TestBase() { private lateinit var networkMapSigner: NetworkMapSigner private lateinit var rootCaCert: X509Certificate - private lateinit var intermediateCa: CertificateAndKeyPair + private lateinit var networkMapCa: CertificateAndKeyPair @Before fun setUp() { - val (rootCa, intermediateCa) = createDevIntermediateCaCertPath() + val (rootCa) = createDevIntermediateCaCertPath() rootCaCert = rootCa.certificate - this.intermediateCa = intermediateCa + networkMapCa = createDevNetworkMapCa(rootCa) signer = mock() networkMapStorage = mock() networkMapSigner = NetworkMapSigner(networkMapStorage, signer) @@ -42,13 +45,13 @@ class NetworkMapSignerTest : TestBase() { // given val signedNodeInfoHashes = listOf(SecureHash.randomSHA256(), SecureHash.randomSHA256()) val networkParameters = testNetworkParameters(emptyList()) - val serializedNetworkMap = NetworkMap(signedNodeInfoHashes, SecureHash.randomSHA256()).serialize() - whenever(networkMapStorage.getCurrentNetworkMap()) - .thenReturn(SignedNetworkMap(serializedNetworkMap, intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate))) + val networkMap = NetworkMap(signedNodeInfoHashes, SecureHash.randomSHA256()) + val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate) + whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(signedNetworkMap) whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(signedNodeInfoHashes) whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters) - whenever(signer.sign(any())).then { - intermediateCa.keyPair.sign(it.arguments[0] as ByteArray).withCert(intermediateCa.certificate) + whenever(signer.signBytes(any())).then { + DigitalSignatureWithCert(networkMapCa.certificate, Crypto.doSign(networkMapCa.keyPair.private, it.arguments[0] as ByteArray)) } // when @@ -60,10 +63,10 @@ class NetworkMapSignerTest : TestBase() { verify(networkMapStorage).getLatestNetworkParameters() argumentCaptor().apply { verify(networkMapStorage).saveNetworkMap(capture()) - val networkMap = firstValue.verified(rootCaCert) - assertEquals(networkParameters.serialize().hash, networkMap.networkParameterHash) - assertEquals(signedNodeInfoHashes.size, networkMap.nodeInfoHashes.size) - assertTrue(networkMap.nodeInfoHashes.containsAll(signedNodeInfoHashes)) + val capturedNetworkMap = firstValue.verifiedNetworkMapCert(rootCaCert) + assertEquals(networkParameters.serialize().hash, capturedNetworkMap.networkParameterHash) + assertEquals(signedNodeInfoHashes.size, capturedNetworkMap.nodeInfoHashes.size) + assertThat(capturedNetworkMap.nodeInfoHashes).containsAll(signedNodeInfoHashes) } } @@ -73,8 +76,7 @@ class NetworkMapSignerTest : TestBase() { val networkParameters = testNetworkParameters(emptyList()) val networkMapParametersHash = networkParameters.serialize().bytes.sha256() val networkMap = NetworkMap(emptyList(), networkMapParametersHash) - val serializedNetworkMap = networkMap.serialize() - val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate)) + val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate) whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(signedNetworkMap) whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList()) whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters) @@ -94,8 +96,8 @@ class NetworkMapSignerTest : TestBase() { whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(null) whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList()) whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters) - whenever(signer.sign(any())).then { - intermediateCa.keyPair.sign(it.arguments[0] as ByteArray).withCert(intermediateCa.certificate) + whenever(signer.signBytes(any())).then { + DigitalSignatureWithCert(networkMapCa.certificate, Crypto.doSign(networkMapCa.keyPair.private, it.arguments[0] as ByteArray)) } // when networkMapSigner.signNetworkMap() @@ -106,7 +108,7 @@ class NetworkMapSignerTest : TestBase() { verify(networkMapStorage).getLatestNetworkParameters() argumentCaptor().apply { verify(networkMapStorage).saveNetworkMap(capture()) - val networkMap = firstValue.verified(rootCaCert) + val networkMap = firstValue.verifiedNetworkMapCert(rootCaCert) assertEquals(networkParameters.serialize().hash, networkMap.networkParameterHash) } } 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 b1f0e320db..f615d8d7ff 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 @@ -5,35 +5,33 @@ import com.nhaarman.mockito_kotlin.times import com.nhaarman.mockito_kotlin.verify 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.common.utils.SignedNetworkMap +import com.r3.corda.networkmanage.common.utils.SignedNetworkParameters import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService 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.checkOkResponse import net.corda.core.internal.openHttpConnection -import net.corda.core.serialization.deserialize +import net.corda.core.internal.responseAs +import net.corda.core.internal.signWithCert 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.createDevNetworkMapCa 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.nodeapi.internal.network.verifiedNetworkMapCert 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.apache.commons.io.IOUtils import org.assertj.core.api.Assertions.* import org.junit.Before import org.junit.Rule import org.junit.Test -import java.io.FileNotFoundException import java.io.IOException import java.net.URL -import java.nio.charset.Charset import java.security.cert.X509Certificate import javax.ws.rs.core.MediaType import kotlin.test.assertEquals @@ -44,15 +42,15 @@ class NodeInfoWebServiceTest { val testSerialization = SerializationEnvironmentRule(true) private lateinit var rootCaCert: X509Certificate - private lateinit var intermediateCa: CertificateAndKeyPair + private lateinit var networkMapCa: CertificateAndKeyPair private val testNetworkMapConfig = NetworkMapConfig(10.seconds.toMillis(), 10.seconds.toMillis()) @Before fun init() { - val (rootCa, intermediateCa) = createDevIntermediateCaCertPath() + val (rootCa) = createDevIntermediateCaCertPath() rootCaCert = rootCa.certificate - this.intermediateCa = intermediateCa + networkMapCa = createDevNetworkMapCa(rootCa) } @Test @@ -106,8 +104,7 @@ class NodeInfoWebServiceTest { @Test fun `get network map`() { val networkMap = NetworkMap(listOf(randomSHA256(), randomSHA256()), randomSHA256()) - val serializedNetworkMap = networkMap.serialize() - val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate)) + val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate) val networkMapStorage: NetworkMapStorage = mock { on { getCurrentNetworkMap() }.thenReturn(signedNetworkMap) @@ -117,7 +114,7 @@ class NodeInfoWebServiceTest { it.start() val signedNetworkMapResponse = it.doGet("") verify(networkMapStorage, times(1)).getCurrentNetworkMap() - assertEquals(signedNetworkMapResponse.verified(rootCaCert), networkMap) + assertEquals(signedNetworkMapResponse.verifiedNetworkMapCert(rootCaCert), networkMap) } } @@ -136,33 +133,32 @@ class NodeInfoWebServiceTest { verify(nodeInfoStorage, times(1)).getNodeInfo(nodeInfoHash) assertEquals(nodeInfo, nodeInfoResponse.verified()) - assertThatExceptionOfType(FileNotFoundException::class.java).isThrownBy { - it.doGet("node-info/${randomSHA256()}") - } + assertThatExceptionOfType(IOException::class.java) + .isThrownBy { it.doGet("node-info/${randomSHA256()}") } + .withMessageContaining("404") } } @Test fun `get network parameters`() { - val netParams = testNetworkParameters(emptyList()) - val serializedNetParams = netParams.serialize() - val signedNetParams = SignedData(serializedNetParams, intermediateCa.keyPair.sign(serializedNetParams)) - val netParamsHash = serializedNetParams.hash + val networkParameters = testNetworkParameters(emptyList()) + val signedNetworkParameters = networkParameters.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate) + val networkParametersHash = signedNetworkParameters.raw.hash val networkMapStorage: NetworkMapStorage = mock { - on { getSignedNetworkParameters(netParamsHash) }.thenReturn(signedNetParams) + on { getSignedNetworkParameters(networkParametersHash) }.thenReturn(signedNetworkParameters) } NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), NodeInfoWebService(mock(), networkMapStorage, testNetworkMapConfig)).use { it.start() - val netParamsResponse = it.doGet>("network-parameter/$netParamsHash") - verify(networkMapStorage, times(1)).getSignedNetworkParameters(netParamsHash) - assertThat(netParamsResponse.verified()).isEqualTo(netParams) - assertThat(netParamsResponse.sig.by).isEqualTo(intermediateCa.keyPair.public) + val netParamsResponse = it.doGet("network-parameters/$networkParametersHash") + verify(networkMapStorage, times(1)).getSignedNetworkParameters(networkParametersHash) + assertThat(netParamsResponse.verified()).isEqualTo(networkParameters) + assertThat(netParamsResponse.sig.by).isEqualTo(networkMapCa.certificate) - assertThatExceptionOfType(FileNotFoundException::class.java).isThrownBy { - it.doGet>("network-parameter/${randomSHA256()}") - } + assertThatExceptionOfType(IOException::class.java) + .isThrownBy { it.doGet("network-parameters/${randomSHA256()}") } + .withMessageContaining("404") } } @@ -173,15 +169,11 @@ class NodeInfoWebServiceTest { requestMethod = "POST" setRequestProperty("Content-Type", MediaType.APPLICATION_OCTET_STREAM) outputStream.write(payload) - if (responseCode != 200) { - throw IOException("Response Code $responseCode: ${IOUtils.toString(errorStream, Charset.defaultCharset())}") - } - inputStream.close() + checkOkResponse() } } private inline fun NetworkManagementWebServer.doGet(path: String): T { - val url = URL("http://$hostAndPort/network-map/$path") - return url.openHttpConnection().inputStream.use { it.readBytes().deserialize() } + return URL("http://$hostAndPort/network-map/$path").openHttpConnection().responseAs() } } \ No newline at end of file