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
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-<version>.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-<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.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)
)
}
)
}
}

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.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<NodeConfiguration>().also {
doReturn(tempFolder.root.toPath()).whenever(it).baseDirectory
@ -198,5 +151,3 @@ class SigningServiceIntegrationTest {
return props
}
}
internal fun makeNotInitialisingTestDatabaseProperties() = DatabaseConfig(runMigration = false)

View File

@ -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<NetworkParameters>` object that's returned.
*/
fun getSignedNetworkParameters(hash: SecureHash): SignedData<NetworkParameters>?
fun getSignedNetworkParameters(hash: SecureHash): SignedNetworkParameters?
/**
* Retrieve network map parameters.

View File

@ -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<NetworkParameters>? {
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<SecureHash> {

View File

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

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

View File

@ -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 <T : Any> signObject(obj: T): SignedDataWithCert<T> {
val serialised = obj.serialize()
return SignedDataWithCert(serialised, signBytes(serialised.bytes))
}
}
class AuthenticationException : Exception()

View File

@ -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<NetworkParameters>
typealias SignedNetworkMap = SignedDataWithCert<NetworkMap>
// 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()
// 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("-", "_"))

View File

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

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.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<Any>()
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<LocalSigner, LocalSigner>? {
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<String>) {
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<String>) {
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()

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.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<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 {
// 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))
}
}

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

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

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

View File

@ -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" }
}
/**

View File

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

View File

@ -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<SignedNetworkMap>().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<SignedNetworkMap>().apply {
verify(networkMapStorage).saveNetworkMap(capture())
val networkMap = firstValue.verified(rootCaCert)
val networkMap = firstValue.verifiedNetworkMapCert(rootCaCert)
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.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<SignedNetworkMap>("")
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<SignedNodeInfo>("node-info/${randomSHA256()}")
}
assertThatExceptionOfType(IOException::class.java)
.isThrownBy { it.doGet<SignedNodeInfo>("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<SignedData<NetworkParameters>>("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<SignedNetworkParameters>("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<SignedData<NetworkParameters>>("network-parameter/${randomSHA256()}")
}
assertThatExceptionOfType(IOException::class.java)
.isThrownBy { it.doGet<SignedNetworkParameters>("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 <reified T : Any> 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()
}
}