Updating network-management to use DigitalSignatureWithCert, network map certs and other relevant changes from O/S.

This commit is contained in:
Shams Asari 2018-01-12 15:15:38 +00:00
parent c71ef3139b
commit b1509607cb
20 changed files with 244 additions and 317 deletions

View File

@ -134,8 +134,8 @@ networkMapConfig {
### 1. Create keystore for local signer ### 1. Create keystore for local signer
If local signer is enabled, the server will look for keystores in the certificate folder on start up. If local signer is enabled, the server will look for key stores in the certificate folder on start up.
The keystores can be created using `--mode` flag. The key stores can be created using `--mode` flag.
``` ```
java -jar doorman-<version>.jar --mode ROOT_KEYGEN java -jar doorman-<version>.jar --mode ROOT_KEYGEN
``` ```
@ -180,8 +180,8 @@ networkMapConfig {
Save the parameters to `network-parameters.conf` Save the parameters to `network-parameters.conf`
### 5. Load initial network parameters file for network map service ### 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. We can now restart the network management server with both doorman and network map service.
``` ```
java -jar doorman-<version>.jar --update-network-parameter network-parameters.conf java -jar doorman-<version>.jar --update-network-parameters network-parameters.conf
``` ```

View File

@ -15,6 +15,7 @@ import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.seconds import net.corda.core.utilities.seconds
import net.corda.finance.DOLLARS import net.corda.finance.DOLLARS
import net.corda.finance.flows.CashIssueAndPaymentFlow import net.corda.finance.flows.CashIssueAndPaymentFlow
import net.corda.nodeapi.internal.createDevNetworkMapCa
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
import net.corda.nodeapi.internal.network.NetworkParameters import net.corda.nodeapi.internal.network.NetworkParameters
import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.nodeapi.internal.persistence.DatabaseConfig
@ -60,15 +61,17 @@ class NodeRegistrationTest : IntegrationTest() {
private val dbId = random63BitValue().toString() private val dbId = random63BitValue().toString()
private lateinit var rootCaCert: X509Certificate 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 private var server: NetworkManagementServer? = null
@Before @Before
fun init() { fun init() {
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath() val (rootCa, doormanCa) = createDevIntermediateCaCertPath()
rootCaCert = rootCa.certificate rootCaCert = rootCa.certificate
this.intermediateCa = intermediateCa this.csrCa = doormanCa
networkMapCa = createDevNetworkMapCa(rootCa)
} }
@After @After
@ -138,10 +141,15 @@ class NodeRegistrationTest : IntegrationTest() {
start( start(
serverAddress, serverAddress,
configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true)), configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true)),
LocalSigner(intermediateCa.keyPair, arrayOf(intermediateCa.certificate, rootCaCert)), LocalSigner(csrCa.keyPair, arrayOf(csrCa.certificate, rootCaCert)),
networkParameters, DoormanConfig(approveAll = true, jiraConfig = null, approveInterval = timeoutMillis),
networkParameters?.let { NetworkMapConfig(cacheTimeout = timeoutMillis, signInterval = timeoutMillis) }, networkParameters?.let {
DoormanConfig(approveAll = true, jiraConfig = null, approveInterval = timeoutMillis) NetworkMapStartParams(
LocalSigner(networkMapCa.keyPair, arrayOf(networkMapCa.certificate, rootCaCert)),
networkParameters,
NetworkMapConfig(cacheTimeout = timeoutMillis, signInterval = timeoutMillis)
)
}
) )
} }
} }

View File

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

View File

@ -22,27 +22,25 @@ import net.corda.nodeapi.internal.createDevNodeCa
import net.corda.nodeapi.internal.crypto.* import net.corda.nodeapi.internal.crypto.*
import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.nodeapi.internal.persistence.DatabaseConfig
import net.corda.testing.ALICE_NAME 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.SerializationEnvironmentRule
import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.testing.internal.createDevIntermediateCaCertPath
import net.corda.testing.internal.rigorousMock import net.corda.testing.internal.rigorousMock
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
import org.h2.tools.Server import org.junit.After
import org.junit.* import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder import org.junit.rules.TemporaryFolder
import java.net.URL import java.net.URL
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import java.util.* import java.util.*
import javax.persistence.PersistenceException import javax.persistence.PersistenceException
import kotlin.concurrent.scheduleAtFixedRate import kotlin.concurrent.scheduleAtFixedRate
import kotlin.concurrent.thread
class SigningServiceIntegrationTest { class SigningServiceIntegrationTest {
companion object { companion object {
val H2_TCP_PORT = "8092" private val HOST = "localhost"
val HOST = "localhost" private val DB_NAME = "test_db"
val DB_NAME = "test_db"
} }
@Rule @Rule
@ -92,7 +90,7 @@ class SigningServiceIntegrationTest {
val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true)) val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true))
NetworkManagementServer().use { server -> 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 val doormanHostAndPort = server.hostAndPort
// Start Corda network registration. // Start Corda network registration.
val config = createConfig().also { 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 { private fun createConfig(): NodeConfiguration {
return rigorousMock<NodeConfiguration>().also { return rigorousMock<NodeConfiguration>().also {
doReturn(tempFolder.root.toPath()).whenever(it).baseDirectory doReturn(tempFolder.root.toPath()).whenever(it).baseDirectory
@ -198,5 +151,3 @@ class SigningServiceIntegrationTest {
return props return props
} }
} }
internal fun makeNotInitialisingTestDatabaseProperties() = DatabaseConfig(runMigration = false)

View File

@ -1,9 +1,9 @@
package com.r3.corda.networkmanage.common.persistence 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.SecureHash
import net.corda.core.crypto.SignedData
import net.corda.nodeapi.internal.network.NetworkParameters import net.corda.nodeapi.internal.network.NetworkParameters
import net.corda.nodeapi.internal.network.SignedNetworkMap
/** /**
* Data access object interface for NetworkMap persistence layer * 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 * Return the signed network parameters object which matches the given hash. The hash is that of the underlying
* [NetworkParameters] object and not the `SignedData<NetworkParameters>` object that's returned. * [NetworkParameters] object and not the `SignedData<NetworkParameters>` object that's returned.
*/ */
fun getSignedNetworkParameters(hash: SecureHash): SignedData<NetworkParameters>? fun getSignedNetworkParameters(hash: SecureHash): SignedNetworkParameters?
/** /**
* Retrieve network map parameters. * Retrieve network map parameters.

View File

@ -1,17 +1,16 @@
package com.r3.corda.networkmanage.common.persistence package com.r3.corda.networkmanage.common.persistence
import com.r3.corda.networkmanage.common.persistence.entity.* 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 com.r3.corda.networkmanage.doorman.signer.LocalSigner
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.SignedData
import net.corda.core.crypto.sha256 import net.corda.core.crypto.sha256
import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.deserialize import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.nodeapi.internal.network.NetworkMap import net.corda.nodeapi.internal.network.NetworkMap
import net.corda.nodeapi.internal.network.NetworkParameters import net.corda.nodeapi.internal.network.NetworkParameters
import net.corda.nodeapi.internal.network.SignedNetworkMap
import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.CordaPersistence
/** /**
@ -40,8 +39,8 @@ class PersistentNetworkMapStorage(private val database: CordaPersistence, privat
database.transaction { database.transaction {
val networkMapEntity = NetworkMapEntity( val networkMapEntity = NetworkMapEntity(
networkMap = signedNetworkMap.raw.bytes, networkMap = signedNetworkMap.raw.bytes,
signature = signedNetworkMap.signature.signatureBytes, signature = signedNetworkMap.sig.bytes,
certificate = signedNetworkMap.signature.by.encoded certificate = signedNetworkMap.sig.by.encoded
) )
session.save(networkMapEntity) 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 // 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. // into the database.
override fun getSignedNetworkParameters(hash: SecureHash): SignedData<NetworkParameters>? { override fun getSignedNetworkParameters(hash: SecureHash): SignedNetworkParameters? {
val netParamsBytes = getNetworkParametersEntity(hash.toString())?.parametersBytes ?: return null val netParamsBytes = getNetworkParametersEntity(hash.toString())?.parametersBytes ?: return null
val sigWithCert = localSigner!!.sign(netParamsBytes) val sigWithCert = localSigner!!.signBytes(netParamsBytes)
return SignedData(SerializedBytes(netParamsBytes), DigitalSignature.WithKey(sigWithCert.by.publicKey, sigWithCert.signatureBytes)) return SignedNetworkParameters(SerializedBytes(netParamsBytes), sigWithCert)
} }
override fun getNodeInfoHashes(certificateStatus: CertificateStatus): List<SecureHash> { override fun getNodeInfoHashes(certificateStatus: CertificateStatus): List<SecureHash> {

View File

@ -1,7 +1,7 @@
package com.r3.corda.networkmanage.common.persistence.entity 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.crypto.X509CertificateFactory
import net.corda.nodeapi.internal.network.DigitalSignatureWithCert
import javax.persistence.* import javax.persistence.*
@Entity @Entity
@ -24,7 +24,7 @@ class NetworkMapEntity(
val certificate: ByteArray val certificate: ByteArray
) { ) {
/** /**
* Deserializes NetworkMapEntity.signatureBytes into the [SignatureAndCertPath] instance * Deserializes NetworkMapEntity.signatureBytes into the [DigitalSignatureWithCert] instance
*/ */
fun signatureAndCertificate(): DigitalSignatureWithCert { fun signatureAndCertificate(): DigitalSignatureWithCert {
return DigitalSignatureWithCert(X509CertificateFactory().generateCertificate(certificate.inputStream()), signature) return DigitalSignatureWithCert(X509CertificateFactory().generateCertificate(certificate.inputStream()), signature)

View File

@ -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.CertificateStatus
import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage
import net.corda.core.internal.SignedDataWithCert
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.nodeapi.internal.network.NetworkMap import net.corda.nodeapi.internal.network.NetworkMap
import net.corda.nodeapi.internal.network.SignedNetworkMap
class NetworkMapSigner(private val networkMapStorage: NetworkMapStorage, private val signer: Signer) { 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 currentSignedNetworkMap = networkMapStorage.getCurrentNetworkMap()
val nodeInfoHashes = networkMapStorage.getNodeInfoHashes(CertificateStatus.VALID) val nodeInfoHashes = networkMapStorage.getNodeInfoHashes(CertificateStatus.VALID)
val networkParameters = networkMapStorage.getLatestNetworkParameters() val networkParameters = networkMapStorage.getLatestNetworkParameters()
val networkMap = NetworkMap(nodeInfoHashes, networkParameters.serialize().hash) val serialisedNetworkMap = NetworkMap(nodeInfoHashes, networkParameters.serialize().hash).serialize()
// We wan only check if the data structure is same. if (serialisedNetworkMap != currentSignedNetworkMap?.raw) {
if (networkMap != currentSignedNetworkMap?.verified(null)) { val newSignedNetworkMap = SignedDataWithCert(serialisedNetworkMap, signer.signBytes(serialisedNetworkMap.bytes))
val digitalSignature = signer.sign(networkMap.serialize().bytes) networkMapStorage.saveNetworkMap(newSignedNetworkMap)
val signedHashedNetworkMap = SignedNetworkMap(networkMap.serialize(), digitalSignature)
networkMapStorage.saveNetworkMap(signedHashedNetworkMap)
} }
} }
} }

View File

@ -1,17 +1,24 @@
package com.r3.corda.networkmanage.common.signer 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. * An interface for arbitrary data signing functionality.
*/ */
interface Signer { interface Signer {
/** /**
* Signs given [data]. The signing key selction strategy is left to the implementing class. * Signs given bytes. 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. * @return [DigitalSignatureWithCert] that encapsulates the signature and the certificate path used in the signing process.
* @throws [AuthenticationException] if fails authentication * @throws [AuthenticationException] if fails authentication
*/ */
fun sign(data: ByteArray): DigitalSignatureWithCert fun signBytes(data: ByteArray): DigitalSignatureWithCert
fun <T : Any> signObject(obj: T): SignedDataWithCert<T> {
val serialised = obj.serialize()
return SignedDataWithCert(serialised, signBytes(serialised.bytes))
}
} }
class AuthenticationException : Exception() class AuthenticationException : Exception()

View File

@ -5,15 +5,18 @@ import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigFactory
import joptsimple.ArgumentAcceptingOptionSpec import joptsimple.ArgumentAcceptingOptionSpec
import joptsimple.OptionParser import joptsimple.OptionParser
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.sha256 import net.corda.core.crypto.sha256
import net.corda.core.internal.SignedDataWithCert
import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509CertificateFactory
import net.corda.nodeapi.internal.network.DigitalSignatureWithCert import net.corda.nodeapi.internal.network.NetworkMap
import org.bouncycastle.cert.X509CertificateHolder import net.corda.nodeapi.internal.network.NetworkParameters
import java.security.PublicKey import java.security.PublicKey
import java.security.cert.CertPath import java.security.cert.CertPath
import java.security.cert.Certificate import java.security.cert.Certificate
import java.security.cert.X509Certificate
// TODO These should be defined in node-api
typealias SignedNetworkParameters = SignedDataWithCert<NetworkParameters>
typealias SignedNetworkMap = SignedDataWithCert<NetworkMap>
// TODO: replace this with Crypto.hash when its available. // TODO: replace this with Crypto.hash when its available.
/** /**
@ -39,15 +42,10 @@ fun Array<out String>.toConfigWithOptions(registerOptions: OptionParser.() -> Un
class ShowHelpException(val parser: OptionParser, val errorMessage: String? = null) : Exception() 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(vararg certificates: Certificate): CertPath = X509CertificateFactory().delegate.generateCertPath(certificates.asList())
fun buildCertPath(certPathBytes: ByteArray): CertPath = X509CertificateFactory().delegate.generateCertPath(certPathBytes.inputStream()) 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 { private fun String.toCamelcase(): String {
return if (contains('_') || contains('-')) { return if (contains('_') || contains('-')) {
CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, this.replace("-", "_")) CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, this.replace("-", "_"))

View File

@ -53,10 +53,11 @@ data class DoormanConfig(val approveAll: Boolean = false,
val approveInterval: Long = NetworkManagementServerParameters.DEFAULT_APPROVE_INTERVAL.toMillis()) val approveInterval: Long = NetworkManagementServerParameters.DEFAULT_APPROVE_INTERVAL.toMillis())
data class NetworkMapConfig(val cacheTimeout: Long, 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()) val signInterval: Long = NetworkManagementServerParameters.DEFAULT_SIGN_INTERVAL.toMillis())
enum class Mode { enum class Mode {
// TODO CA_KEYGEN now also generates the nework map cert, so it should be renamed.
DOORMAN, CA_KEYGEN, ROOT_KEYGEN DOORMAN, CA_KEYGEN, ROOT_KEYGEN
} }

View File

@ -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.MonitoringWebService
import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService
import com.r3.corda.networkmanage.doorman.webservice.RegistrationWebService 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.Crypto
import net.corda.core.crypto.SignatureScheme
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.createDirectories import net.corda.core.internal.createDirectories
import net.corda.core.internal.div import net.corda.core.internal.div
@ -34,6 +37,7 @@ import java.time.Instant
import java.util.* import java.util.*
import java.util.concurrent.Executors import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.security.auth.x500.X500Principal
import kotlin.concurrent.thread import kotlin.concurrent.thread
import kotlin.system.exitProcess import kotlin.system.exitProcess
@ -130,17 +134,16 @@ class NetworkManagementServer : Closeable {
fun start(hostAndPort: NetworkHostAndPort, fun start(hostAndPort: NetworkHostAndPort,
database: CordaPersistence, database: CordaPersistence,
signer: LocalSigner? = null, doormanSigner: LocalSigner? = null,
updateNetworkParameters: NetworkParameters?, doormanServiceParameter: DoormanConfig?, // TODO Doorman config shouldn't be optional as the doorman is always required to run
networkMapServiceParameter: NetworkMapConfig?, startNetworkMap: NetworkMapStartParams?
doormanServiceParameter: DoormanConfig?) { ) {
val services = mutableListOf<Any>() val services = mutableListOf<Any>()
val serverStatus = NetworkManagementServerStatus() val serverStatus = NetworkManagementServerStatus()
// TODO: move signing to signing server. // TODO: move signing to signing server.
networkMapServiceParameter?.let { services += getNetworkMapService(it, database, signer, updateNetworkParameters) } startNetworkMap?.let { services += getNetworkMapService(it.config, database, it.signer, it.updateNetworkParameters) }
doormanServiceParameter?.let { services += getDoormanService(it, database, signer, serverStatus) } doormanServiceParameter?.let { services += getDoormanService(it, database, doormanSigner, serverStatus) }
require(services.isNotEmpty()) { "No service created, please provide at least one service config." } 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()) val webServer = NetworkManagementWebServer(hostAndPort, *services.toTypedArray())
webServer.start() webServer.start()
doOnClose += { webServer.close() } doOnClose += webServer::close
this.hostAndPort = webServer.hostAndPort 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) 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). */ /** 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) println(loadKeyStore(rootStoreFile, rootKeystorePassword).getCertificate(X509Utilities.CORDA_ROOT_CA).publicKey)
} }
fun generateCAKeyPair(keystoreFile: Path, rootStoreFile: Path, rootKeystorePass: String?, rootPrivateKeyPass: String?, keystorePass: String?, caPrivateKeyPass: String?) { fun generateSigningKeyPairs(keystoreFile: Path, rootStoreFile: Path, rootKeystorePass: String?, rootPrivateKeyPass: String?, keystorePass: String?, caPrivateKeyPass: String?) {
println("Generating Intermediate CA keypair and certificate using root keystore $rootStoreFile.") println("Generating intermediate and network map key pairs and certificates using root key store $rootStoreFile.")
// Get password from console if not in config. // Get password from console if not in config.
val rootKeystorePassword = rootKeystorePass ?: readPassword("Root Keystore Password: ") val rootKeystorePassword = rootKeystorePass ?: readPassword("Root key store password: ")
val rootPrivateKeyPassword = rootPrivateKeyPass ?: readPassword("Root Private Key Password: ") val rootPrivateKeyPassword = rootPrivateKeyPass ?: readPassword("Root private key password: ")
val rootKeyStore = loadKeyStore(rootStoreFile, rootKeystorePassword) 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 keyStorePassword = keystorePass ?: readPassword("Key store Password: ")
val caPrivateKeyPassword = caPrivateKeyPass ?: readPassword("CA Private Key Password: ") val privateKeyPassword = caPrivateKeyPass ?: readPassword("Private key Password: ")
// Ensure folder exists. // Ensure folder exists.
keystoreFile.parent.createDirectories() keystoreFile.parent.createDirectories()
val keyStore = loadOrCreateKeyStore(keystoreFile, keystorePassword) val keyStore = loadOrCreateKeyStore(keystoreFile, keyStorePassword)
if (keyStore.containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA)) { fun storeCertIfAbsent(alias: String, certificateType: CertificateType, subject: X500Principal, signatureScheme: SignatureScheme) {
val oldKey = loadOrCreateKeyStore(keystoreFile, rootKeystorePassword).getCertificate(X509Utilities.CORDA_INTERMEDIATE_CA).publicKey if (keyStore.containsAlias(alias)) {
println("Key ${X509Utilities.CORDA_INTERMEDIATE_CA} already exists in keystore, process will now terminate.") println("$alias already exists in keystore:")
println(oldKey) println(keyStore.getCertificate(alias))
exitProcess(1) 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) storeCertIfAbsent(
val intermediateCert = X509Utilities.createCertificate( DEFAULT_CSR_CERTIFICATE_NAME,
CertificateType.INTERMEDIATE_CA, CertificateType.INTERMEDIATE_CA,
rootKeyAndCert.certificate, X500Principal("CN=Corda Intermediate CA,OU=Corda,O=R3 Ltd,L=London,C=GB"),
rootKeyAndCert.keyPair, X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
CordaX500Name(commonName = "Corda Intermediate CA", organisation = "R3 Ltd", organisationUnit = "Corda", locality = "London", country = "GB", state = null).x500Principal,
intermediateKeyPair.public storeCertIfAbsent(
) DEFAULT_NETWORK_MAP_CERTIFICATE_NAME,
keyStore.addOrReplaceKey( CertificateType.NETWORK_MAP,
X509Utilities.CORDA_INTERMEDIATE_CA, X500Principal("CN=Corda Network Map,OU=Corda,O=R3 Ltd,L=London,C=GB"),
intermediateKeyPair.private, Crypto.EDDSA_ED25519_SHA512)
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)
} }
private fun buildLocalSigner(parameters: NetworkManagementServerParameters): LocalSigner? { private fun buildLocalSigners(parameters: NetworkManagementServerParameters): Pair<LocalSigner, LocalSigner>? {
return parameters.keystorePath?.let { if (parameters.keystorePath == null) return null
// Get password from console if not in config.
val keystorePassword = parameters.keystorePassword ?: readPassword("Keystore Password: ") // Get password from console if not in config.
val caPrivateKeyPassword = parameters.caPrivateKeyPassword ?: readPassword("CA Private Key Password: ") val keyStorePassword = parameters.keystorePassword ?: readPassword("Key store password: ")
val keystore = loadOrCreateKeyStore(parameters.keystorePath, keystorePassword) val privateKeyPassword = parameters.caPrivateKeyPassword ?: readPassword("Private key password: ")
val caKeyPair = keystore.getKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, caPrivateKeyPassword) val keyStore = loadOrCreateKeyStore(parameters.keystorePath, keyStorePassword)
val caCertPath = keystore.getCertificateChain(X509Utilities.CORDA_INTERMEDIATE_CA).map { it as X509Certificate }
LocalSigner(caKeyPair, caCertPath.toTypedArray()) 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<String>) {
rootStorePath ?: throw IllegalArgumentException("The 'rootStorePath' parameter must be specified when generating keys!"), rootStorePath ?: throw IllegalArgumentException("The 'rootStorePath' parameter must be specified when generating keys!"),
rootKeystorePassword, rootKeystorePassword,
rootPrivateKeyPassword) rootPrivateKeyPassword)
Mode.CA_KEYGEN -> generateCAKeyPair( Mode.CA_KEYGEN -> generateSigningKeyPairs(
keystorePath ?: throw IllegalArgumentException("The 'keystorePath' parameter must be specified when generating keys!"), keystorePath ?: throw IllegalArgumentException("The 'keystorePath' parameter must be specified when generating keys!"),
rootStorePath ?: throw IllegalArgumentException("The 'rootStorePath' parameter must be specified when generating keys!"), rootStorePath ?: throw IllegalArgumentException("The 'rootStorePath' parameter must be specified when generating keys!"),
rootKeystorePassword, rootKeystorePassword,
@ -289,18 +313,24 @@ fun main(args: Array<String>) {
initialiseSerialization() initialiseSerialization()
val database = configureDatabase(dataSourceProperties) val database = configureDatabase(dataSourceProperties)
// TODO: move signing to signing server. // TODO: move signing to signing server.
val signer = buildLocalSigner(this) val localSigners = buildLocalSigners(this)
if (signer != null) { if (localSigners != null) {
println("Starting network management services with local signer.") println("Starting network management services with local signing")
} }
val networkManagementServer = NetworkManagementServer() val networkManagementServer = NetworkManagementServer()
val networkParameter = updateNetworkParameters?.let { val networkParameters = updateNetworkParameters?.let {
println("Parsing network parameter from '${it.fileName}'...") // 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) 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) { Runtime.getRuntime().addShutdownHook(thread(start = false) {
networkManagementServer.close() networkManagementServer.close()

View File

@ -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.signer.Signer
import com.r3.corda.networkmanage.common.utils.buildCertPath import com.r3.corda.networkmanage.common.utils.buildCertPath
import com.r3.corda.networkmanage.common.utils.withCert import net.corda.core.crypto.Crypto
import net.corda.core.crypto.sign import net.corda.core.internal.DigitalSignatureWithCert
import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.CertificateType
import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.nodeapi.internal.network.DigitalSignatureWithCert
import org.bouncycastle.asn1.x509.GeneralName import org.bouncycastle.asn1.x509.GeneralName
import org.bouncycastle.asn1.x509.GeneralSubtree import org.bouncycastle.asn1.x509.GeneralSubtree
import org.bouncycastle.asn1.x509.NameConstraints 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. * 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. * 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<X509Certificate>) : Signer { //TODO Use a list instead of array
class LocalSigner(private val signingKeyPair: KeyPair, private val signingCertPath: Array<X509Certificate>) : Signer {
// TODO This doesn't belong in this class
fun createSignedClientCertificate(certificationRequest: PKCS10CertificationRequest): CertPath { 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, // 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. // 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()) arrayOf())
val nodeCaCert = X509Utilities.createCertificate( val nodeCaCert = X509Utilities.createCertificate(
CertificateType.NODE_CA, CertificateType.NODE_CA,
caCertPath[0], signingCertPath[0],
caKeyPair, signingKeyPair,
X500Principal(request.subject.encoded), X500Principal(request.subject.encoded),
request.publicKey, request.publicKey,
nameConstraints = nameConstraints) nameConstraints = nameConstraints)
return buildCertPath(nodeCaCert, *caCertPath) return buildCertPath(nodeCaCert, *signingCertPath)
} }
override fun sign(data: ByteArray): DigitalSignatureWithCert { override fun signBytes(data: ByteArray): DigitalSignatureWithCert {
return caKeyPair.sign(data).withCert(caCertPath.first()) return DigitalSignatureWithCert(signingCertPath[0], Crypto.doSign(signingKeyPair.private, data))
} }
} }

View File

@ -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.NetworkMapStorage
import com.r3.corda.networkmanage.common.persistence.NodeInfoStorage import com.r3.corda.networkmanage.common.persistence.NodeInfoStorage
import com.r3.corda.networkmanage.common.persistence.NodeInfoWithSigned 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.NetworkMapConfig
import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService.Companion.NETWORK_MAP_PATH import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService.Companion.NETWORK_MAP_PATH
import net.corda.core.crypto.SecureHash 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.core.utilities.contextLogger
import net.corda.nodeapi.internal.SignedNodeInfo import net.corda.nodeapi.internal.SignedNodeInfo
import net.corda.nodeapi.internal.network.NetworkParameters import net.corda.nodeapi.internal.network.NetworkParameters
import net.corda.nodeapi.internal.network.SignedNetworkMap
import java.io.InputStream import java.io.InputStream
import java.security.InvalidKeyException import java.security.InvalidKeyException
import java.security.SignatureException import java.security.SignatureException
@ -77,9 +77,9 @@ class NodeInfoWebService(private val nodeInfoStorage: NodeInfoStorage,
} }
@GET @GET
@Path("network-parameter/{netParamsHash}") // TODO Fix path to be /network-parameters @Path("network-parameters/{hash}")
fun getNetworkParameters(@PathParam("netParamsHash") netParamsHash: String): Response { fun getNetworkParameters(@PathParam("hash") hash: String): Response {
val signedNetParams = networkMapStorage.getSignedNetworkParameters(SecureHash.parse(netParamsHash)) val signedNetParams = networkMapStorage.getSignedNetworkParameters(SecureHash.parse(hash))
return createResponse(signedNetParams) return createResponse(signedNetParams)
} }

View File

@ -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_PATH: Path? = null //Paths.get("/Users/michalkit/WinDev1706Eval/Shared/TEST4.key")
val DEFAULT_KEY_FILE_PASSWORD: String? = null val DEFAULT_KEY_FILE_PASSWORD: String? = null
val DEFAULT_AUTO_USERNAME: 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) val DEFAULT_SIGN_INTERVAL = 600L // in seconds (10 minutes)
} }
} }

View File

@ -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.persistence.NetworkMapStorage
import com.r3.corda.networkmanage.common.signer.NetworkMapSigner import com.r3.corda.networkmanage.common.signer.NetworkMapSigner
import com.r3.corda.networkmanage.common.signer.Signer 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.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.getAndInitializeKeyStore
import com.r3.corda.networkmanage.hsm.utils.X509Utilities.signData
import com.r3.corda.networkmanage.hsm.utils.X509Utilities.verify 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.loggerFor
import net.corda.core.utilities.minutes import net.corda.core.utilities.minutes
import net.corda.nodeapi.internal.network.DigitalSignatureWithCert
import java.security.KeyPair
import java.security.PrivateKey import java.security.PrivateKey
import java.security.Signature
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import java.time.Duration import java.time.Duration
import java.util.concurrent.Executors import java.util.concurrent.Executors
@ -24,6 +23,7 @@ import java.util.concurrent.TimeUnit
* Encapsulates logic for periodic network map signing execution. * Encapsulates logic for periodic network map signing execution.
* It uses HSM as the signing entity with keys and certificates specified at the construction time. * 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, class HsmNetworkMapSigner(networkMapStorage: NetworkMapStorage,
private val caCertificateKeyName: String, private val caCertificateKeyName: String,
private val caPrivateKeyPass: String, private val caPrivateKeyPass: String,
@ -40,6 +40,7 @@ class HsmNetworkMapSigner(networkMapStorage: NetworkMapStorage,
private val networkMapSigner = NetworkMapSigner(networkMapStorage, this) private val networkMapSigner = NetworkMapSigner(networkMapStorage, this)
private lateinit var scheduledExecutor: ScheduledExecutorService private lateinit var scheduledExecutor: ScheduledExecutorService
// TODO This doesn't belong in this class
fun start(): HsmNetworkMapSigner { fun start(): HsmNetworkMapSigner {
val signingPeriodMillis = signingPeriod.toMillis() val signingPeriodMillis = signingPeriod.toMillis()
scheduledExecutor = Executors.newSingleThreadScheduledExecutor() scheduledExecutor = Executors.newSingleThreadScheduledExecutor()
@ -60,14 +61,18 @@ class HsmNetworkMapSigner(networkMapStorage: NetworkMapStorage,
/** /**
* Signs given data using [CryptoServerJCE.CryptoServerProvider], which connects to the underlying HSM. * 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, _ -> return authenticator.connectAndAuthenticate { provider, _ ->
val keyStore = getAndInitializeKeyStore(provider) val keyStore = getAndInitializeKeyStore(provider)
val caCertificateChain = keyStore.getCertificateChain(caCertificateKeyName) val caCertificateChain = keyStore.getCertificateChain(caCertificateKeyName)
val caKey = keyStore.getKey(caCertificateKeyName, caPrivateKeyPass.toCharArray()) as PrivateKey val caKey = keyStore.getKey(caCertificateKeyName, caPrivateKeyPass.toCharArray()) as PrivateKey
val signature = signData(data, KeyPair(caCertificateChain.first().publicKey, caKey), provider) val signature = Signature.getInstance(X509Utilities.SIGNATURE_ALGORITHM, provider).run {
verify(data, signature, caCertificateChain.first().publicKey) initSign(caKey)
signature.withCert(caCertificateChain[0] as X509Certificate) update(data)
sign()
}
verify(data, signature, caCertificateChain[0].publicKey)
DigitalSignatureWithCert(caCertificateChain[0] as X509Certificate, signature)
} }
} }
} }

View File

@ -1,7 +1,6 @@
package com.r3.corda.networkmanage.hsm.utils package com.r3.corda.networkmanage.hsm.utils
import CryptoServerJCE.CryptoServerProvider import CryptoServerJCE.CryptoServerProvider
import net.corda.core.crypto.DigitalSignature
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.x500Name import net.corda.core.internal.x500Name
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
@ -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, fun verify(data: ByteArray,
signature: DigitalSignature, signature: ByteArray,
publicKey: PublicKey, publicKey: PublicKey,
signatureAlgorithm: String = SIGNATURE_ALGORITHM) { signatureAlgorithm: String = SIGNATURE_ALGORITHM) {
val verify = Signature.getInstance(signatureAlgorithm) val verify = Signature.getInstance(signatureAlgorithm)
verify.initVerify(publicKey) verify.initVerify(publicKey)
verify.update(data) verify.update(data)
require(verify.verify(signature.bytes)) { "Signature didn't independently verify" } require(verify.verify(signature)) { "Signature didn't independently verify" }
} }
/** /**

View File

@ -1,16 +1,14 @@
package com.r3.corda.networkmanage.common.persistence package com.r3.corda.networkmanage.common.persistence
import com.r3.corda.networkmanage.TestBase import com.r3.corda.networkmanage.TestBase
import com.r3.corda.networkmanage.common.utils.withCert
import com.r3.corda.networkmanage.doorman.signer.LocalSigner import com.r3.corda.networkmanage.doorman.signer.LocalSigner
import net.corda.core.crypto.sign
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.serialization.serialize import net.corda.core.internal.signWithCert
import net.corda.nodeapi.internal.SignedNodeInfo import net.corda.nodeapi.internal.createDevNetworkMapCa
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509CertificateFactory
import net.corda.nodeapi.internal.network.NetworkMap import net.corda.nodeapi.internal.network.NetworkMap
import net.corda.nodeapi.internal.network.SignedNetworkMap import net.corda.nodeapi.internal.network.verifiedNetworkMapCert
import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.nodeapi.internal.persistence.DatabaseConfig
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
@ -31,15 +29,15 @@ class PersistentNetworkMapStorageTest : TestBase() {
private lateinit var requestStorage: PersistentCertificateRequestStorage private lateinit var requestStorage: PersistentCertificateRequestStorage
private lateinit var rootCaCert: X509Certificate private lateinit var rootCaCert: X509Certificate
private lateinit var intermediateCa: CertificateAndKeyPair private lateinit var networkMapCa: CertificateAndKeyPair
@Before @Before
fun startDb() { fun startDb() {
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath() val (rootCa) = createDevIntermediateCaCertPath()
rootCaCert = rootCa.certificate rootCaCert = rootCa.certificate
this.intermediateCa = intermediateCa networkMapCa = createDevNetworkMapCa(rootCa)
persistence = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true)) 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) nodeInfoStorage = PersistentNodeInfoStorage(persistence)
requestStorage = PersistentCertificateRequestStorage(persistence) requestStorage = PersistentCertificateRequestStorage(persistence)
} }
@ -60,9 +58,7 @@ class PersistentNetworkMapStorageTest : TestBase() {
val networkParametersHash = networkMapStorage.saveNetworkParameters(testNetworkParameters(emptyList())) val networkParametersHash = networkMapStorage.saveNetworkParameters(testNetworkParameters(emptyList()))
val networkMap = NetworkMap(listOf(nodeInfoHash), networkParametersHash) val networkMap = NetworkMap(listOf(nodeInfoHash), networkParametersHash)
val serializedNetworkMap = networkMap.serialize() val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
val signatureData = intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate)
val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, signatureData)
// when // when
networkMapStorage.saveNetworkMap(signedNetworkMap) networkMapStorage.saveNetworkMap(signedNetworkMap)
@ -70,8 +66,8 @@ class PersistentNetworkMapStorageTest : TestBase() {
// then // then
val persistedSignedNetworkMap = networkMapStorage.getCurrentNetworkMap() val persistedSignedNetworkMap = networkMapStorage.getCurrentNetworkMap()
assertEquals(signedNetworkMap.signature, persistedSignedNetworkMap?.signature) assertEquals(signedNetworkMap.sig, persistedSignedNetworkMap?.sig)
assertEquals(signedNetworkMap.verified(rootCaCert), persistedSignedNetworkMap?.verified(rootCaCert)) assertEquals(signedNetworkMap.verifiedNetworkMapCert(rootCaCert), persistedSignedNetworkMap?.verifiedNetworkMapCert(rootCaCert))
} }
@Test @Test
@ -96,9 +92,7 @@ class PersistentNetworkMapStorageTest : TestBase() {
// Sign network map making it current network map // Sign network map making it current network map
val networkMap = NetworkMap(emptyList(), networkParametersHash) val networkMap = NetworkMap(emptyList(), networkParametersHash)
val serializedNetworkMap = networkMap.serialize() val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
val signatureData = intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate)
val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, signatureData)
networkMapStorage.saveNetworkMap(signedNetworkMap) networkMapStorage.saveNetworkMap(signedNetworkMap)
// Create new network parameters // 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 // This test will probably won't be needed when we remove the explicit use of LocalSigner
@Test @Test
fun `getSignedNetworkParameters uses the local signer to return a signed object`() { fun `getSignedNetworkParameters uses the local signer to return a signed object`() {
val netParams = testNetworkParameters(emptyList()) val networkParameters = testNetworkParameters(emptyList())
val netParamsHash = networkMapStorage.saveNetworkParameters(netParams) val netParamsHash = networkMapStorage.saveNetworkParameters(networkParameters)
val signedNetParams = networkMapStorage.getSignedNetworkParameters(netParamsHash) val signedNetworkParameters = networkMapStorage.getSignedNetworkParameters(netParamsHash)
assertThat(signedNetParams?.verified()).isEqualTo(netParams) assertThat(signedNetworkParameters?.verifiedNetworkMapCert(rootCaCert)).isEqualTo(networkParameters)
assertThat(signedNetParams?.sig?.by).isEqualTo(intermediateCa.keyPair.public) assertThat(signedNetworkParameters?.sig?.by).isEqualTo(networkMapCa.certificate)
} }
@Test @Test
@ -135,9 +129,7 @@ class PersistentNetworkMapStorageTest : TestBase() {
// Create network parameters // Create network parameters
val networkParametersHash = networkMapStorage.saveNetworkParameters(testNetworkParameters(emptyList())) val networkParametersHash = networkMapStorage.saveNetworkParameters(testNetworkParameters(emptyList()))
val networkMap = NetworkMap(listOf(nodeInfoHashA), networkParametersHash) val networkMap = NetworkMap(listOf(nodeInfoHashA), networkParametersHash)
val serializedNetworkMap = networkMap.serialize() val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
val signatureData = intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate)
val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, signatureData)
// Sign network map // Sign network map
networkMapStorage.saveNetworkMap(signedNetworkMap) networkMapStorage.saveNetworkMap(signedNetworkMap)

View File

@ -3,21 +3,24 @@ package com.r3.corda.networkmanage.common.signer
import com.nhaarman.mockito_kotlin.* import com.nhaarman.mockito_kotlin.*
import com.r3.corda.networkmanage.TestBase import com.r3.corda.networkmanage.TestBase
import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage 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.SecureHash
import net.corda.core.crypto.sha256 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.core.serialization.serialize
import net.corda.nodeapi.internal.createDevNetworkMapCa
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
import net.corda.nodeapi.internal.network.NetworkMap import net.corda.nodeapi.internal.network.NetworkMap
import net.corda.nodeapi.internal.network.SignedNetworkMap import net.corda.nodeapi.internal.network.verifiedNetworkMapCert
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.testing.internal.createDevIntermediateCaCertPath
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertTrue
class NetworkMapSignerTest : TestBase() { class NetworkMapSignerTest : TestBase() {
private lateinit var signer: Signer private lateinit var signer: Signer
@ -25,13 +28,13 @@ class NetworkMapSignerTest : TestBase() {
private lateinit var networkMapSigner: NetworkMapSigner private lateinit var networkMapSigner: NetworkMapSigner
private lateinit var rootCaCert: X509Certificate private lateinit var rootCaCert: X509Certificate
private lateinit var intermediateCa: CertificateAndKeyPair private lateinit var networkMapCa: CertificateAndKeyPair
@Before @Before
fun setUp() { fun setUp() {
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath() val (rootCa) = createDevIntermediateCaCertPath()
rootCaCert = rootCa.certificate rootCaCert = rootCa.certificate
this.intermediateCa = intermediateCa networkMapCa = createDevNetworkMapCa(rootCa)
signer = mock() signer = mock()
networkMapStorage = mock() networkMapStorage = mock()
networkMapSigner = NetworkMapSigner(networkMapStorage, signer) networkMapSigner = NetworkMapSigner(networkMapStorage, signer)
@ -42,13 +45,13 @@ class NetworkMapSignerTest : TestBase() {
// given // given
val signedNodeInfoHashes = listOf(SecureHash.randomSHA256(), SecureHash.randomSHA256()) val signedNodeInfoHashes = listOf(SecureHash.randomSHA256(), SecureHash.randomSHA256())
val networkParameters = testNetworkParameters(emptyList()) val networkParameters = testNetworkParameters(emptyList())
val serializedNetworkMap = NetworkMap(signedNodeInfoHashes, SecureHash.randomSHA256()).serialize() val networkMap = NetworkMap(signedNodeInfoHashes, SecureHash.randomSHA256())
whenever(networkMapStorage.getCurrentNetworkMap()) val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
.thenReturn(SignedNetworkMap(serializedNetworkMap, intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate))) whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(signedNetworkMap)
whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(signedNodeInfoHashes) whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(signedNodeInfoHashes)
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters) whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters)
whenever(signer.sign(any())).then { whenever(signer.signBytes(any())).then {
intermediateCa.keyPair.sign(it.arguments[0] as ByteArray).withCert(intermediateCa.certificate) DigitalSignatureWithCert(networkMapCa.certificate, Crypto.doSign(networkMapCa.keyPair.private, it.arguments[0] as ByteArray))
} }
// when // when
@ -60,10 +63,10 @@ class NetworkMapSignerTest : TestBase() {
verify(networkMapStorage).getLatestNetworkParameters() verify(networkMapStorage).getLatestNetworkParameters()
argumentCaptor<SignedNetworkMap>().apply { argumentCaptor<SignedNetworkMap>().apply {
verify(networkMapStorage).saveNetworkMap(capture()) verify(networkMapStorage).saveNetworkMap(capture())
val networkMap = firstValue.verified(rootCaCert) val capturedNetworkMap = firstValue.verifiedNetworkMapCert(rootCaCert)
assertEquals(networkParameters.serialize().hash, networkMap.networkParameterHash) assertEquals(networkParameters.serialize().hash, capturedNetworkMap.networkParameterHash)
assertEquals(signedNodeInfoHashes.size, networkMap.nodeInfoHashes.size) assertEquals(signedNodeInfoHashes.size, capturedNetworkMap.nodeInfoHashes.size)
assertTrue(networkMap.nodeInfoHashes.containsAll(signedNodeInfoHashes)) assertThat(capturedNetworkMap.nodeInfoHashes).containsAll(signedNodeInfoHashes)
} }
} }
@ -73,8 +76,7 @@ class NetworkMapSignerTest : TestBase() {
val networkParameters = testNetworkParameters(emptyList()) val networkParameters = testNetworkParameters(emptyList())
val networkMapParametersHash = networkParameters.serialize().bytes.sha256() val networkMapParametersHash = networkParameters.serialize().bytes.sha256()
val networkMap = NetworkMap(emptyList(), networkMapParametersHash) val networkMap = NetworkMap(emptyList(), networkMapParametersHash)
val serializedNetworkMap = networkMap.serialize() val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate))
whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(signedNetworkMap) whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(signedNetworkMap)
whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList()) whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList())
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters) whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters)
@ -94,8 +96,8 @@ class NetworkMapSignerTest : TestBase() {
whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(null) whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(null)
whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList()) whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList())
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters) whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters)
whenever(signer.sign(any())).then { whenever(signer.signBytes(any())).then {
intermediateCa.keyPair.sign(it.arguments[0] as ByteArray).withCert(intermediateCa.certificate) DigitalSignatureWithCert(networkMapCa.certificate, Crypto.doSign(networkMapCa.keyPair.private, it.arguments[0] as ByteArray))
} }
// when // when
networkMapSigner.signNetworkMap() networkMapSigner.signNetworkMap()
@ -106,7 +108,7 @@ class NetworkMapSignerTest : TestBase() {
verify(networkMapStorage).getLatestNetworkParameters() verify(networkMapStorage).getLatestNetworkParameters()
argumentCaptor<SignedNetworkMap>().apply { argumentCaptor<SignedNetworkMap>().apply {
verify(networkMapStorage).saveNetworkMap(capture()) verify(networkMapStorage).saveNetworkMap(capture())
val networkMap = firstValue.verified(rootCaCert) val networkMap = firstValue.verifiedNetworkMapCert(rootCaCert)
assertEquals(networkParameters.serialize().hash, networkMap.networkParameterHash) assertEquals(networkParameters.serialize().hash, networkMap.networkParameterHash)
} }
} }

View File

@ -5,35 +5,33 @@ import com.nhaarman.mockito_kotlin.times
import com.nhaarman.mockito_kotlin.verify import com.nhaarman.mockito_kotlin.verify
import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage
import com.r3.corda.networkmanage.common.persistence.NodeInfoStorage import com.r3.corda.networkmanage.common.persistence.NodeInfoStorage
import com.r3.corda.networkmanage.common.utils.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 com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService
import net.corda.core.crypto.SecureHash.Companion.randomSHA256 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.identity.CordaX500Name
import net.corda.core.internal.checkOkResponse
import net.corda.core.internal.openHttpConnection 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.serialization.serialize
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.seconds import net.corda.core.utilities.seconds
import net.corda.nodeapi.internal.SignedNodeInfo import net.corda.nodeapi.internal.SignedNodeInfo
import net.corda.nodeapi.internal.createDevNetworkMapCa
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
import net.corda.nodeapi.internal.network.NetworkMap import net.corda.nodeapi.internal.network.NetworkMap
import net.corda.nodeapi.internal.network.NetworkParameters import net.corda.nodeapi.internal.network.verifiedNetworkMapCert
import net.corda.nodeapi.internal.network.SignedNetworkMap
import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.SerializationEnvironmentRule
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.testing.internal.createDevIntermediateCaCertPath
import net.corda.testing.internal.createNodeInfoAndSigned import net.corda.testing.internal.createNodeInfoAndSigned
import org.apache.commons.io.IOUtils
import org.assertj.core.api.Assertions.* import org.assertj.core.api.Assertions.*
import org.junit.Before import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import java.io.FileNotFoundException
import java.io.IOException import java.io.IOException
import java.net.URL import java.net.URL
import java.nio.charset.Charset
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import javax.ws.rs.core.MediaType import javax.ws.rs.core.MediaType
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -44,15 +42,15 @@ class NodeInfoWebServiceTest {
val testSerialization = SerializationEnvironmentRule(true) val testSerialization = SerializationEnvironmentRule(true)
private lateinit var rootCaCert: X509Certificate 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()) private val testNetworkMapConfig = NetworkMapConfig(10.seconds.toMillis(), 10.seconds.toMillis())
@Before @Before
fun init() { fun init() {
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath() val (rootCa) = createDevIntermediateCaCertPath()
rootCaCert = rootCa.certificate rootCaCert = rootCa.certificate
this.intermediateCa = intermediateCa networkMapCa = createDevNetworkMapCa(rootCa)
} }
@Test @Test
@ -106,8 +104,7 @@ class NodeInfoWebServiceTest {
@Test @Test
fun `get network map`() { fun `get network map`() {
val networkMap = NetworkMap(listOf(randomSHA256(), randomSHA256()), randomSHA256()) val networkMap = NetworkMap(listOf(randomSHA256(), randomSHA256()), randomSHA256())
val serializedNetworkMap = networkMap.serialize() val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, intermediateCa.keyPair.sign(serializedNetworkMap).withCert(intermediateCa.certificate))
val networkMapStorage: NetworkMapStorage = mock { val networkMapStorage: NetworkMapStorage = mock {
on { getCurrentNetworkMap() }.thenReturn(signedNetworkMap) on { getCurrentNetworkMap() }.thenReturn(signedNetworkMap)
@ -117,7 +114,7 @@ class NodeInfoWebServiceTest {
it.start() it.start()
val signedNetworkMapResponse = it.doGet<SignedNetworkMap>("") val signedNetworkMapResponse = it.doGet<SignedNetworkMap>("")
verify(networkMapStorage, times(1)).getCurrentNetworkMap() 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) verify(nodeInfoStorage, times(1)).getNodeInfo(nodeInfoHash)
assertEquals(nodeInfo, nodeInfoResponse.verified()) assertEquals(nodeInfo, nodeInfoResponse.verified())
assertThatExceptionOfType(FileNotFoundException::class.java).isThrownBy { assertThatExceptionOfType(IOException::class.java)
it.doGet<SignedNodeInfo>("node-info/${randomSHA256()}") .isThrownBy { it.doGet<SignedNodeInfo>("node-info/${randomSHA256()}") }
} .withMessageContaining("404")
} }
} }
@Test @Test
fun `get network parameters`() { fun `get network parameters`() {
val netParams = testNetworkParameters(emptyList()) val networkParameters = testNetworkParameters(emptyList())
val serializedNetParams = netParams.serialize() val signedNetworkParameters = networkParameters.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
val signedNetParams = SignedData(serializedNetParams, intermediateCa.keyPair.sign(serializedNetParams)) val networkParametersHash = signedNetworkParameters.raw.hash
val netParamsHash = serializedNetParams.hash
val networkMapStorage: NetworkMapStorage = mock { val networkMapStorage: NetworkMapStorage = mock {
on { getSignedNetworkParameters(netParamsHash) }.thenReturn(signedNetParams) on { getSignedNetworkParameters(networkParametersHash) }.thenReturn(signedNetworkParameters)
} }
NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), NodeInfoWebService(mock(), networkMapStorage, testNetworkMapConfig)).use { NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), NodeInfoWebService(mock(), networkMapStorage, testNetworkMapConfig)).use {
it.start() it.start()
val netParamsResponse = it.doGet<SignedData<NetworkParameters>>("network-parameter/$netParamsHash") val netParamsResponse = it.doGet<SignedNetworkParameters>("network-parameters/$networkParametersHash")
verify(networkMapStorage, times(1)).getSignedNetworkParameters(netParamsHash) verify(networkMapStorage, times(1)).getSignedNetworkParameters(networkParametersHash)
assertThat(netParamsResponse.verified()).isEqualTo(netParams) assertThat(netParamsResponse.verified()).isEqualTo(networkParameters)
assertThat(netParamsResponse.sig.by).isEqualTo(intermediateCa.keyPair.public) assertThat(netParamsResponse.sig.by).isEqualTo(networkMapCa.certificate)
assertThatExceptionOfType(FileNotFoundException::class.java).isThrownBy { assertThatExceptionOfType(IOException::class.java)
it.doGet<SignedData<NetworkParameters>>("network-parameter/${randomSHA256()}") .isThrownBy { it.doGet<SignedNetworkParameters>("network-parameters/${randomSHA256()}") }
} .withMessageContaining("404")
} }
} }
@ -173,15 +169,11 @@ class NodeInfoWebServiceTest {
requestMethod = "POST" requestMethod = "POST"
setRequestProperty("Content-Type", MediaType.APPLICATION_OCTET_STREAM) setRequestProperty("Content-Type", MediaType.APPLICATION_OCTET_STREAM)
outputStream.write(payload) outputStream.write(payload)
if (responseCode != 200) { checkOkResponse()
throw IOException("Response Code $responseCode: ${IOUtils.toString(errorStream, Charset.defaultCharset())}")
}
inputStream.close()
} }
} }
private inline fun <reified T : Any> NetworkManagementWebServer.doGet(path: String): T { private inline fun <reified T : Any> NetworkManagementWebServer.doGet(path: String): T {
val url = URL("http://$hostAndPort/network-map/$path") return URL("http://$hostAndPort/network-map/$path").openHttpConnection().responseAs()
return url.openHttpConnection().inputStream.use { it.readBytes().deserialize() }
} }
} }