Use network map objects from corda instead of stubs (#146)

* * change corda dependencies to 3.0-NETWORKMAP_SNAPSHOT
* packages move fix

* fix up after rebase

* rename test

* address PR issues

* address PR issues

* fix failing test
This commit is contained in:
Patrick Kuo 2017-12-07 13:22:41 +00:00 committed by GitHub
parent 60fca0bf16
commit b3ca36132f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 191 additions and 263 deletions

4
.idea/compiler.xml generated
View File

@ -15,6 +15,8 @@
<module name="business-network-demo_integrationTest" target="1.8" /> <module name="business-network-demo_integrationTest" target="1.8" />
<module name="business-network-demo_main" target="1.8" /> <module name="business-network-demo_main" target="1.8" />
<module name="business-network-demo_test" target="1.8" /> <module name="business-network-demo_test" target="1.8" />
<module name="capsule-hsm_main" target="1.8" />
<module name="capsule-hsm_test" target="1.8" />
<module name="client_main" target="1.8" /> <module name="client_main" target="1.8" />
<module name="client_test" target="1.8" /> <module name="client_test" target="1.8" />
<module name="confidential-identities_main" target="1.8" /> <module name="confidential-identities_main" target="1.8" />
@ -172,4 +174,4 @@
<module name="webserver_test" target="1.8" /> <module name="webserver_test" target="1.8" />
</bytecodeTargetLevel> </bytecodeTargetLevel>
</component> </component>
</project> </project>

View File

@ -56,9 +56,7 @@ dependencies {
compile fileTree(dir: 'libs', include: '*.jar') compile fileTree(dir: 'libs', include: '*.jar')
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compile "net.corda:corda-core:$corda_dependency_version"
compile "net.corda:corda-node-api:$corda_dependency_version" compile "net.corda:corda-node-api:$corda_dependency_version"
testCompile "net.corda:corda-test-utils:$corda_dependency_version"
testCompile "net.corda:corda-node-driver:$corda_dependency_version" testCompile "net.corda:corda-node-driver:$corda_dependency_version"
testCompile "net.corda:corda-test-common:$corda_dependency_version" testCompile "net.corda:corda-test-common:$corda_dependency_version"

View File

@ -92,9 +92,7 @@ class DoormanIntegrationTest {
doorman.close() doorman.close()
} }
//TODO remove @Ignore once PR https://github.com/corda/corda/pull/2054 is merged
@Test @Test
@Ignore
fun `nodeInfo is published to the network map`() { fun `nodeInfo is published to the network map`() {
// Given // Given
val rootCertAndKey = createDoormanRootCertificateAndKeyPair() val rootCertAndKey = createDoormanRootCertificateAndKeyPair()
@ -111,6 +109,8 @@ class DoormanIntegrationTest {
whenever(it.compatibilityZoneURL).thenReturn(URL("http://${doormanHostAndPort.host}:${doormanHostAndPort.port}")) whenever(it.compatibilityZoneURL).thenReturn(URL("http://${doormanHostAndPort.host}:${doormanHostAndPort.port}"))
whenever(it.emailAddress).thenReturn("iTest@R3.com") whenever(it.emailAddress).thenReturn("iTest@R3.com")
} }
config.rootCaCertFile.parent.createDirectories()
X509Utilities.saveCertificateAsPEMFile(rootCertAndKey.certificate, config.rootCaCertFile)
NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.compatibilityZoneURL!!)).buildKeystore() NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.compatibilityZoneURL!!)).buildKeystore()

View File

@ -1,8 +1,8 @@
package com.r3.corda.networkmanage.common.persistence package com.r3.corda.networkmanage.common.persistence
import com.r3.corda.networkmanage.common.signer.SignedNetworkMap
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.nodeapi.internal.NetworkParameters import net.corda.nodeapi.internal.NetworkParameters
import net.corda.nodeapi.internal.SignedNetworkMap
/** /**
* Data access object interface for NetworkMap persistence layer * Data access object interface for NetworkMap persistence layer
@ -15,16 +15,13 @@ interface NetworkMapStorage {
fun getCurrentNetworkMap(): SignedNetworkMap? fun getCurrentNetworkMap(): SignedNetworkMap?
/** /**
* Retrieves current map node info hashes only. Hashes are further filtered by the [certificateStatuses] parameter * Retrieves node info hashes where the certificate status matches [certificateStatus].
* that restricts considered node info to only those which [CertificateStatus] value corresponds to one in the passed *
* collection. If null or empty list is passed then filtering has no effect and all node info hashes from the current * @param certificateStatus certificate status to be used in the node info filtering. Node info hash is returned
* network map are returned. * in the result collection if its certificate status matches [certificateStatus].
* @param certificateStatuses certificate statuses to be used in the node info filtering. Node info hash is returned * @return list of node info hashes satisfying the filtering criteria given by [certificateStatus].
* in the result collection only if it is in the current network map and its certificate status belongs to the
* [certificateStatuses] collection or if [certificateStatuses] collection is null or empty.
* @return list of current network map node info hashes satisfying the filtering criteria given by [certificateStatuses].
*/ */
fun getCurrentNetworkMapNodeInfoHashes(certificateStatuses: List<CertificateStatus>): List<SecureHash> fun getNodeInfoHashes(certificateStatus: CertificateStatus): List<SecureHash>
/** /**
* Persists a new instance of the signed network map. * Persists a new instance of the signed network map.
@ -32,17 +29,11 @@ interface NetworkMapStorage {
*/ */
fun saveNetworkMap(signedNetworkMap: SignedNetworkMap) fun saveNetworkMap(signedNetworkMap: SignedNetworkMap)
/**
* Retrieve all node info hashes for all node info with valid certificates,
* that are not associated with any network map yet.
*/
fun getDetachedAndValidNodeInfoHashes(): List<SecureHash>
/** /**
* Retrieve network parameters by their hash. * Retrieve network parameters by their hash.
* @return network parameters corresponding to the given hash or null if it does not exist * @return network parameters corresponding to the given hash or null if it does not exist
*/ */
fun getNetworkParameters(parameterHash: SecureHash): NetworkParameters fun getNetworkParameters(parameterHash: SecureHash): NetworkParameters?
/** /**
* Retrieve network map parameters. * Retrieve network map parameters.
@ -54,7 +45,7 @@ interface NetworkMapStorage {
* Persists given network parameters. * Persists given network parameters.
* @return hash corresponding to newly create network parameters entry * @return hash corresponding to newly create network parameters entry
*/ */
fun putNetworkParameters(networkParameters: NetworkParameters): SecureHash fun saveNetworkParameters(networkParameters: NetworkParameters): SecureHash
/** /**
* Retrieves the latest (i.e. most recently inserted) network parameters * Retrieves the latest (i.e. most recently inserted) network parameters

View File

@ -1,77 +1,64 @@
package com.r3.corda.networkmanage.common.persistence package com.r3.corda.networkmanage.common.persistence
import com.r3.corda.networkmanage.common.persistence.entity.NetworkMapEntity import com.r3.corda.networkmanage.common.persistence.entity.*
import com.r3.corda.networkmanage.common.persistence.entity.NetworkParametersEntity
import com.r3.corda.networkmanage.common.persistence.entity.NodeInfoEntity
import com.r3.corda.networkmanage.common.signer.NetworkMap
import com.r3.corda.networkmanage.common.signer.SignedNetworkMap
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.serialization.SerializedBytes
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.nodeapi.internal.NetworkMap
import net.corda.nodeapi.internal.NetworkParameters import net.corda.nodeapi.internal.NetworkParameters
import net.corda.nodeapi.internal.SignedNetworkMap
import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.CordaPersistence
import org.hibernate.Session
import org.hibernate.jpa.QueryHints
/** /**
* Database implementation of the [NetworkMapStorage] interface * Database implementation of the [NetworkMapStorage] interface
*/ */
class PersistentNetworkMapStorage(private val database: CordaPersistence) : NetworkMapStorage { class PersistentNetworkMapStorage(private val database: CordaPersistence) : NetworkMapStorage {
override fun getCurrentNetworkMap(): SignedNetworkMap? = database.transaction { override fun getCurrentNetworkMap(): SignedNetworkMap? = database.transaction {
val networkMapEntity = getCurrentNetworkMapEntity(getNetworkMapWithNodeInfoAndParametersHint(session)) val networkMapEntity = getCurrentNetworkMapEntity()
networkMapEntity?.let { networkMapEntity?.let {
val nodeInfoHashes = it.nodeInfoList.map { it.nodeInfoHash }
val networkParameterHash = it.parameters.parametersHash
val signatureAndCertPath = it.signatureAndCertificate() val signatureAndCertPath = it.signatureAndCertificate()
SignedNetworkMap(NetworkMap(nodeInfoHashes, networkParameterHash), signatureAndCertPath!!) SignedNetworkMap(SerializedBytes(it.networkMap), signatureAndCertPath)
} }
} }
override fun getCurrentNetworkParameters(): NetworkParameters? = database.transaction { override fun getCurrentNetworkParameters(): NetworkParameters? = database.transaction {
getCurrentNetworkMapEntity(getNetworkMapWithParametersHint(session))?.parameters?.networkParameters() getCurrentNetworkMapEntity()?.let {
val parameterHash = it.networkMap.deserialize<NetworkMap>().networkParameterHash
getNetworkParameters(parameterHash)
}
} }
override fun saveNetworkMap(signedNetworkMap: SignedNetworkMap) { override fun saveNetworkMap(signedNetworkMap: SignedNetworkMap) {
database.transaction { database.transaction {
val networkMap = signedNetworkMap.networkMap
val signatureAndCertPath = signedNetworkMap.signatureData
val signature = signatureAndCertPath.signature
val networkParametersEntity = getNetworkParametersEntity(networkMap.parametersHash)
networkParametersEntity ?: throw IllegalArgumentException("Error when retrieving network parameters entity for network map signing! - Entity does not exist")
val networkMapEntity = NetworkMapEntity( val networkMapEntity = NetworkMapEntity(
parameters = networkParametersEntity, networkMap = signedNetworkMap.raw.bytes,
signatureBytes = signature.bytes, signature = signedNetworkMap.sig.signatureBytes,
certificatePathBytes = signatureAndCertPath.certPath.serialize().bytes certificate = signedNetworkMap.sig.by.encoded
) )
session.save(networkMapEntity) session.save(networkMapEntity)
networkMap.nodeInfoHashes.forEach { }
val nodeInfoEntity = session.find(NodeInfoEntity::class.java, it) }
session.merge(nodeInfoEntity.copy(networkMap = networkMapEntity))
override fun getNetworkParameters(parameterHash: SecureHash): NetworkParameters? {
return getNetworkParametersEntity(parameterHash.toString())?.networkParameters()
}
override fun getNodeInfoHashes(certificateStatus: CertificateStatus): List<SecureHash> = database.transaction {
val builder = session.criteriaBuilder
val query = builder.createQuery(String::class.java).run {
from(NodeInfoEntity::class.java).run {
select(get<String>(NodeInfoEntity::nodeInfoHash.name))
.where(builder.equal(get<CertificateSigningRequestEntity>(NodeInfoEntity::certificateSigningRequest.name)
.get<CertificateDataEntity>(CertificateSigningRequestEntity::certificateData.name)
.get<CertificateStatus>(CertificateDataEntity::certificateStatus.name), certificateStatus))
} }
} }
session.createQuery(query).resultList.map { SecureHash.parse(it) }
} }
override fun getNetworkParameters(parameterHash: SecureHash): NetworkParameters { override fun saveNetworkParameters(networkParameters: NetworkParameters): SecureHash = database.transaction {
val entity = getNetworkParametersEntity(parameterHash.toString())
if (entity != null) {
return entity.networkParameters()
} else {
throw NoSuchElementException("Network parameters with $parameterHash do not exist")
}
}
override fun getCurrentNetworkMapNodeInfoHashes(certificateStatuses: List<CertificateStatus>): List<SecureHash> = database.transaction {
val networkMapEntity = getCurrentNetworkMapEntity(getNetworkMapWithNodeInfoAndCsrHint(session))
if (networkMapEntity != null) {
networkMapEntity.nodeInfoList.filter({
certificateStatuses.isEmpty() || certificateStatuses.contains(it.certificateSigningRequest?.certificateData?.certificateStatus)
}).map { SecureHash.parse(it.nodeInfoHash) }
} else {
emptyList()
}
}
override fun putNetworkParameters(networkParameters: NetworkParameters): SecureHash = database.transaction {
val bytes = networkParameters.serialize().bytes val bytes = networkParameters.serialize().bytes
val hash = bytes.sha256() val hash = bytes.sha256()
session.save(NetworkParametersEntity( session.save(NetworkParametersEntity(
@ -94,29 +81,16 @@ class PersistentNetworkMapStorage(private val database: CordaPersistence) : Netw
session.createQuery(query).resultList.first() session.createQuery(query).resultList.first()
} }
override fun getDetachedAndValidNodeInfoHashes(): List<SecureHash> = database.transaction { private fun getCurrentNetworkMapEntity(): NetworkMapEntity? = database.transaction {
val builder = session.criteriaBuilder
// Get signed NodeInfoEntities
val query = builder.createQuery(NodeInfoEntity::class.java).run {
from(NodeInfoEntity::class.java).run {
where(builder.and(
builder.isNull(get<ByteArray>(NodeInfoEntity::networkMap.name)),
builder.isNotNull(get<ByteArray>(NodeInfoEntity::signatureBytes.name))))
}
}
session.createQuery(query).resultList.map { SecureHash.parse(it.nodeInfoHash) }
}
private fun getCurrentNetworkMapEntity(hint: Pair<String, Any>): NetworkMapEntity? = database.transaction {
val builder = session.criteriaBuilder val builder = session.criteriaBuilder
val query = builder.createQuery(NetworkMapEntity::class.java).run { val query = builder.createQuery(NetworkMapEntity::class.java).run {
from(NetworkMapEntity::class.java).run { from(NetworkMapEntity::class.java).run {
where(builder.isNotNull(get<ByteArray?>(NetworkMapEntity::signatureBytes.name))) where(builder.isNotNull(get<ByteArray?>(NetworkMapEntity::signature.name)))
orderBy(builder.desc(get<String>(NetworkMapEntity::version.name))) orderBy(builder.desc(get<String>(NetworkMapEntity::version.name)))
} }
} }
// We just want the last signed entry // We just want the last signed entry
session.createQuery(query).setHint(hint.first, hint.second).resultList.firstOrNull() session.createQuery(query).resultList.firstOrNull()
} }
private fun getNetworkParametersEntity(parameterHash: String): NetworkParametersEntity? = database.transaction { private fun getNetworkParametersEntity(parameterHash: String): NetworkParametersEntity? = database.transaction {
@ -124,33 +98,4 @@ class PersistentNetworkMapStorage(private val database: CordaPersistence) : Netw
builder.equal(path.get<String>(NetworkParametersEntity::parametersHash.name), parameterHash) builder.equal(path.get<String>(NetworkParametersEntity::parametersHash.name), parameterHash)
} }
} }
}
/**
* Creates Hibernate query hint for pulling [NetworkParametersEntity] when querying for [NetworkMapEntity]
*/
private fun getNetworkMapWithParametersHint(session: Session): Pair<String, Any> {
val graph = session.createEntityGraph(NetworkMapEntity::class.java)
graph.addAttributeNodes(NetworkMapEntity::parameters.name)
return QueryHints.HINT_LOADGRAPH to graph
}
/**
* Creates Hibernate query hint for pulling [NodeInfoEntity] and [CertificateSigningRequestEntity] when querying for [NetworkMapEntity]
*/
private fun getNetworkMapWithNodeInfoAndCsrHint(session: Session): Pair<String, Any> {
val graph = session.createEntityGraph(NetworkMapEntity::class.java)
val subGraph = graph.addSubgraph(NetworkMapEntity::nodeInfoList.name, NodeInfoEntity::class.java)
subGraph.addAttributeNodes(NodeInfoEntity::certificateSigningRequest.name)
return QueryHints.HINT_LOADGRAPH to graph
}
/**
* Creates Hibernate query hint for pulling [NodeInfoEntity] and [NetworkParametersEntity] when querying for [NetworkMapEntity]
*/
private fun getNetworkMapWithNodeInfoAndParametersHint(session: Session): Pair<String, Any> {
val graph = session.createEntityGraph(NetworkMapEntity::class.java)
graph.addAttributeNodes(NetworkMapEntity::nodeInfoList.name)
graph.addAttributeNodes(NetworkMapEntity::parameters.name)
return QueryHints.HINT_LOADGRAPH to graph
}
}

View File

@ -1,8 +1,7 @@
package com.r3.corda.networkmanage.common.persistence.entity package com.r3.corda.networkmanage.common.persistence.entity
import com.r3.corda.networkmanage.common.signer.SignatureAndCertPath import net.corda.nodeapi.internal.DigitalSignatureWithCert
import net.corda.core.crypto.DigitalSignature import net.corda.nodeapi.internal.crypto.X509CertificateFactory
import net.corda.core.serialization.deserialize
import javax.persistence.* import javax.persistence.*
@Entity @Entity
@ -12,27 +11,23 @@ class NetworkMapEntity(
@GeneratedValue(strategy = GenerationType.SEQUENCE) @GeneratedValue(strategy = GenerationType.SEQUENCE)
val version: Long? = null, val version: Long? = null,
// Reverting relation ownership due to (potentially) unlimited number of node info items. @Lob
@OneToMany(mappedBy = "networkMap", fetch = FetchType.LAZY) @Column(name = "serialized_network_map")
val nodeInfoList: List<NodeInfoEntity> = mutableListOf(), val networkMap: ByteArray,
@OneToOne
@JoinColumn(name = "network_parameters")
val parameters: NetworkParametersEntity,
@Lob @Lob
@Column(name = "signature_bytes") @Column(name = "signature")
val signatureBytes: ByteArray, val signature: ByteArray,
@Lob @Lob
@Column(name = "certificate_path_bytes") @Column(name = "certificate")
val certificatePathBytes: ByteArray val certificate: ByteArray
) { ) {
/** /**
* Deserializes NetworkMapEntity.signatureBytes into the [SignatureAndCertPath] instance * Deserializes NetworkMapEntity.signatureBytes into the [SignatureAndCertPath] instance
*/ */
fun signatureAndCertificate(): SignatureAndCertPath? { fun signatureAndCertificate(): DigitalSignatureWithCert {
return SignatureAndCertPath(DigitalSignature(signatureBytes), certificatePathBytes.deserialize()) return DigitalSignatureWithCert(X509CertificateFactory().generateCertificate(certificate.inputStream()), signature)
} }
} }

View File

@ -18,10 +18,6 @@ class NodeInfoEntity(
@JoinColumn(name = "certificate_signing_request", nullable = true) @JoinColumn(name = "certificate_signing_request", nullable = true)
val certificateSigningRequest: CertificateSigningRequestEntity? = null, val certificateSigningRequest: CertificateSigningRequestEntity? = null,
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "network_map", nullable = true)
val networkMap: NetworkMapEntity? = null,
@Lob @Lob
@Column(name = "node_info_bytes") @Column(name = "node_info_bytes")
val nodeInfoBytes: ByteArray, val nodeInfoBytes: ByteArray,
@ -55,7 +51,6 @@ class NodeInfoEntity(
} }
fun copy(nodeInfoHash: String = this.nodeInfoHash, fun copy(nodeInfoHash: String = this.nodeInfoHash,
networkMap: NetworkMapEntity? = this.networkMap,
certificateSigningRequest: CertificateSigningRequestEntity? = this.certificateSigningRequest, certificateSigningRequest: CertificateSigningRequestEntity? = this.certificateSigningRequest,
nodeInfoBytes: ByteArray = this.nodeInfoBytes, nodeInfoBytes: ByteArray = this.nodeInfoBytes,
signatureBytes: ByteArray? = this.signatureBytes, signatureBytes: ByteArray? = this.signatureBytes,
@ -68,8 +63,7 @@ class NodeInfoEntity(
nodeInfoBytes = nodeInfoBytes, nodeInfoBytes = nodeInfoBytes,
signatureBytes = signatureBytes, signatureBytes = signatureBytes,
signaturePublicKeyBytes = signaturePublicKeyBytes, signaturePublicKeyBytes = signaturePublicKeyBytes,
signaturePublicKeyAlgorithm = signaturePublicKeyAlgorithm, signaturePublicKeyAlgorithm = signaturePublicKeyAlgorithm
networkMap = networkMap
) )
} }
} }

View File

@ -2,43 +2,22 @@ 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.serialization.CordaSerializable
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.nodeapi.internal.NetworkMap
import net.corda.nodeapi.internal.SignedNetworkMap
/** class NetworkMapSigner(private val networkMapStorage: NetworkMapStorage, private val signer: Signer) {
* Encapsulates the network map signing procedure.
* To sign a network map following steps need to be done:
* 1) Collect all node info data that has been signed and has valid certificates
* 2) Retrieve most up-to-date network parameters
* 3) Sign hashed version of the network map
* 4) Persist network map data together with its signature
* Once the network map is signed it is considered to be the current network map.
*
* This class resides in the common package as it is intended to be used in both local and distributed deployments.
* This means that it can be executed by a remote (e.g. HSM) signing service or locally by Doorman.
*/
@CordaSerializable
data class NetworkMap(val nodeInfoHashes: List<String>, val parametersHash: String)
@CordaSerializable
data class SignedNetworkMap(val networkMap: NetworkMap, val signatureData: SignatureAndCertPath)
class NetworkMapSigner(private val networkMapStorage: NetworkMapStorage,
private val signer: Signer) {
/** /**
* Signs the network map. * Signs the network map.
*/ */
fun signNetworkMap() { fun signNetworkMap() {
val currentSignedNetworkMap = networkMapStorage.getCurrentNetworkMap() val currentSignedNetworkMap = networkMapStorage.getCurrentNetworkMap()
val currentNetworkMapValidNodeInfo = networkMapStorage.getCurrentNetworkMapNodeInfoHashes(listOf(CertificateStatus.VALID)) val nodeInfoHashes = networkMapStorage.getNodeInfoHashes(CertificateStatus.VALID)
val detachedValidNodeInfo = networkMapStorage.getDetachedAndValidNodeInfoHashes()
val nodeInfoHashes = currentNetworkMapValidNodeInfo + detachedValidNodeInfo
val networkParameters = networkMapStorage.getLatestNetworkParameters() val networkParameters = networkMapStorage.getLatestNetworkParameters()
val networkMap = NetworkMap(nodeInfoHashes.map { it.toString() }, networkParameters.serialize().hash.toString()) val networkMap = NetworkMap(nodeInfoHashes, networkParameters.serialize().hash)
if (networkMap != currentSignedNetworkMap?.networkMap) { if (networkMap != currentSignedNetworkMap?.verified()) {
val digitalSignature = signer.sign(networkMap.serialize().bytes) val digitalSignature = signer.sign(networkMap.serialize().bytes)
require(digitalSignature != null) { "Error while signing network map." } val signedHashedNetworkMap = SignedNetworkMap(networkMap.serialize(), digitalSignature)
val signedHashedNetworkMap = SignedNetworkMap(networkMap, digitalSignature!!)
networkMapStorage.saveNetworkMap(signedHashedNetworkMap) networkMapStorage.saveNetworkMap(signedHashedNetworkMap)
} }
} }

View File

@ -2,19 +2,19 @@ package com.r3.corda.networkmanage.common.signer
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.DigitalSignature
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.nodeapi.internal.DigitalSignatureWithCert
import java.security.cert.CertPath import java.security.cert.CertPath
@CordaSerializable
data class SignatureAndCertPath(val signature: DigitalSignature, val certPath: CertPath)
/** /**
* 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 [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. * @return [SignatureAndCertPath] that encapsulates the signature and the certificate path used in the signing process.
* @throws [AuthenticationException] if fails authentication
*/ */
fun sign(data: ByteArray): SignatureAndCertPath? fun sign(data: ByteArray): DigitalSignatureWithCert
} }
class AuthenticationException : Exception()

View File

@ -4,7 +4,9 @@ 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.nodeapi.internal.DigitalSignatureWithCert
import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509CertificateFactory
import org.bouncycastle.cert.X509CertificateHolder import org.bouncycastle.cert.X509CertificateHolder
import java.security.PublicKey import java.security.PublicKey
@ -41,3 +43,5 @@ fun X509CertificateHolder.toX509Certificate(): X509Certificate = X509Certificate
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)

View File

@ -11,7 +11,6 @@ import com.r3.corda.networkmanage.doorman.signer.JiraCsrHandler
import com.r3.corda.networkmanage.doorman.signer.LocalSigner import com.r3.corda.networkmanage.doorman.signer.LocalSigner
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.typesafe.config.ConfigFactory
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.createDirectories import net.corda.core.internal.createDirectories
@ -32,6 +31,7 @@ import java.io.Closeable
import java.net.InetSocketAddress import java.net.InetSocketAddress
import java.net.URI import java.net.URI
import java.nio.file.Path import java.nio.file.Path
import java.security.cert.X509Certificate
import java.time.Instant import java.time.Instant
import java.util.concurrent.Executors import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@ -190,7 +190,7 @@ fun startDoorman(hostAndPort: NetworkHostAndPort,
// Persisting new network parameters // Persisting new network parameters
val currentNetworkParameters = networkMapStorage.getCurrentNetworkParameters() val currentNetworkParameters = networkMapStorage.getCurrentNetworkParameters()
if (currentNetworkParameters == null) { if (currentNetworkParameters == null) {
networkMapStorage.putNetworkParameters(networkMapParameters) networkMapStorage.saveNetworkParameters(networkMapParameters)
} else { } else {
throw UnsupportedOperationException("Network parameters already exist. Updating them via the file config is not supported yet.") throw UnsupportedOperationException("Network parameters already exist. Updating them via the file config is not supported yet.")
} }
@ -242,8 +242,8 @@ private fun buildLocalSigner(parameters: DoormanParameters): LocalSigner? {
val caPrivateKeyPassword = parameters.caPrivateKeyPassword ?: readPassword("CA Private Key Password: ") val caPrivateKeyPassword = parameters.caPrivateKeyPassword ?: readPassword("CA Private Key Password: ")
val keystore = loadOrCreateKeyStore(parameters.keystorePath, keystorePassword) val keystore = loadOrCreateKeyStore(parameters.keystorePath, keystorePassword)
val caKeyPair = keystore.getKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, caPrivateKeyPassword) val caKeyPair = keystore.getKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, caPrivateKeyPassword)
val caCertPath = keystore.getCertificateChain(X509Utilities.CORDA_INTERMEDIATE_CA) val caCertPath = keystore.getCertificateChain(X509Utilities.CORDA_INTERMEDIATE_CA).map { it as X509Certificate }
LocalSigner(caKeyPair, caCertPath) LocalSigner(caKeyPair, caCertPath.toTypedArray())
} }
} }

View File

@ -71,7 +71,7 @@ class JiraCsrHandler(private val jiraClient: JiraClient, private val storage: Ce
jiraClient.getApprovedRequests().forEach { (id, approvedBy) -> storage.approveRequest(id, approvedBy) } jiraClient.getApprovedRequests().forEach { (id, approvedBy) -> storage.approveRequest(id, approvedBy) }
delegate.processApprovedRequests() delegate.processApprovedRequests()
val signedRequests = storage.getRequests(RequestStatus.SIGNED).mapNotNull { val signedRequests = storage.getRequests(RequestStatus.SIGNED).mapNotNull {
it.certData?.certPath.let { certs -> it.requestId to certs!! } it.certData?.certPath?.let { certs -> it.requestId to certs }
}.toMap() }.toMap()
jiraClient.updateSignedRequests(signedRequests) jiraClient.updateSignedRequests(signedRequests)
} }

View File

@ -1,12 +1,13 @@
package com.r3.corda.networkmanage.doorman.signer package com.r3.corda.networkmanage.doorman.signer
import com.r3.corda.networkmanage.common.signer.SignatureAndCertPath
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.toX509Certificate import com.r3.corda.networkmanage.common.utils.toX509Certificate
import com.r3.corda.networkmanage.common.utils.withCert
import net.corda.core.crypto.sign import net.corda.core.crypto.sign
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.toX509CertHolder import net.corda.core.internal.toX509CertHolder
import net.corda.nodeapi.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 org.bouncycastle.asn1.x509.GeneralName import org.bouncycastle.asn1.x509.GeneralName
@ -16,13 +17,13 @@ import org.bouncycastle.pkcs.PKCS10CertificationRequest
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
import java.security.KeyPair import java.security.KeyPair
import java.security.cert.CertPath import java.security.cert.CertPath
import java.security.cert.Certificate import java.security.cert.X509Certificate
/** /**
* 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<Certificate>) : Signer { class LocalSigner(private val caKeyPair: KeyPair, private val caCertPath: Array<X509Certificate>) : Signer {
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.
@ -41,7 +42,7 @@ class LocalSigner(private val caKeyPair: KeyPair, private val caCertPath: Array<
return buildCertPath(clientCertificate, *caCertPath) return buildCertPath(clientCertificate, *caCertPath)
} }
override fun sign(data: ByteArray): SignatureAndCertPath { override fun sign(data: ByteArray): DigitalSignatureWithCert {
return SignatureAndCertPath(caKeyPair.sign(data), buildCertPath(*caCertPath)) return caKeyPair.sign(data).withCert(caCertPath.first())
} }
} }

View File

@ -74,7 +74,7 @@ class NodeInfoWebService(private val nodeInfoStorage: NodeInfoStorage,
} }
@GET @GET
@Path("{nodeInfoHash}") @Path("node-info/{nodeInfoHash}")
fun getNodeInfo(@PathParam("nodeInfoHash") nodeInfoHash: String): Response { fun getNodeInfo(@PathParam("nodeInfoHash") nodeInfoHash: String): Response {
val nodeInfo = nodeInfoStorage.getNodeInfo(SecureHash.parse(nodeInfoHash)) val nodeInfo = nodeInfoStorage.getNodeInfo(SecureHash.parse(nodeInfoHash))
return if (nodeInfo != null) { return if (nodeInfo != null) {

View File

@ -1,6 +1,7 @@
package com.r3.corda.networkmanage.hsm.authentication package com.r3.corda.networkmanage.hsm.authentication
import CryptoServerJCE.CryptoServerProvider import CryptoServerJCE.CryptoServerProvider
import com.r3.corda.networkmanage.common.signer.AuthenticationException
import com.r3.corda.networkmanage.hsm.configuration.Parameters import com.r3.corda.networkmanage.hsm.configuration.Parameters
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
@ -25,8 +26,8 @@ class Authenticator(private val provider: CryptoServerProvider,
* 1) [CryptoServerProvider] instance * 1) [CryptoServerProvider] instance
* 2) List of strings that corresponds to user names authenticated against the HSM. * 2) List of strings that corresponds to user names authenticated against the HSM.
*/ */
fun connectAndAuthenticate(block: (CryptoServerProvider, List<String>) -> Unit) { fun <T : Any> connectAndAuthenticate(block: (CryptoServerProvider, List<String>) -> T): T {
try { return try {
val authenticated = mutableListOf<String>() val authenticated = mutableListOf<String>()
loop@ while (true) { loop@ while (true) {
val user = if (autoUsername.isNullOrEmpty()) { val user = if (autoUsername.isNullOrEmpty()) {
@ -78,6 +79,8 @@ class Authenticator(private val provider: CryptoServerProvider,
} }
if (!authenticated.isEmpty()) { if (!authenticated.isEmpty()) {
block(provider, authenticated) block(provider, authenticated)
} else {
throw AuthenticationException()
} }
} finally { } finally {
try { try {
@ -89,8 +92,8 @@ class Authenticator(private val provider: CryptoServerProvider,
} }
} }
} }
/* /*
* Configuration class for [CryptoServerProvider] * Configuration class for [CryptoServerProvider]
*/ */
data class CryptoServerProviderConfig( data class CryptoServerProviderConfig(

View File

@ -3,15 +3,17 @@ package com.r3.corda.networkmanage.hsm.signer
import com.google.common.util.concurrent.MoreExecutors 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.SignatureAndCertPath
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.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.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.signData
import com.r3.corda.networkmanage.hsm.utils.X509Utilities.verify import com.r3.corda.networkmanage.hsm.utils.X509Utilities.verify
import net.corda.core.internal.cert
import net.corda.core.internal.toX509CertHolder
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.core.utilities.minutes import net.corda.core.utilities.minutes
import net.corda.nodeapi.internal.DigitalSignatureWithCert
import java.security.KeyPair import java.security.KeyPair
import java.security.PrivateKey import java.security.PrivateKey
import java.time.Duration import java.time.Duration
@ -59,16 +61,14 @@ 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): SignatureAndCertPath? { override fun sign(data: ByteArray): DigitalSignatureWithCert {
var result: SignatureAndCertPath? = null return authenticator.connectAndAuthenticate { provider, _ ->
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 = signData(data, KeyPair(caCertificateChain.first().publicKey, caKey), provider)
verify(data, signature, caCertificateChain.first().publicKey) verify(data, signature, caCertificateChain.first().publicKey)
result = SignatureAndCertPath(signature, buildCertPath(*caCertificateChain)) signature.withCert(caCertificateChain.first().toX509CertHolder().cert)
} }
return result
} }
} }

View File

@ -11,7 +11,6 @@ import net.corda.core.identity.CordaX500Name
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.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.DatabaseConfig
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.pkcs.PKCS10CertificationRequest import org.bouncycastle.pkcs.PKCS10CertificationRequest

View File

@ -1,30 +1,30 @@
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.signer.NetworkMap
import com.r3.corda.networkmanage.common.signer.SignatureAndCertPath
import com.r3.corda.networkmanage.common.signer.SignedNetworkMap
import com.r3.corda.networkmanage.common.utils.buildCertPath import com.r3.corda.networkmanage.common.utils.buildCertPath
import com.r3.corda.networkmanage.common.utils.toX509Certificate import com.r3.corda.networkmanage.common.utils.toX509Certificate
import com.r3.corda.networkmanage.common.utils.withCert
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SignedData import net.corda.core.crypto.SignedData
import net.corda.core.crypto.sign import net.corda.core.crypto.sign
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.PartyAndCertificate import net.corda.core.identity.PartyAndCertificate
import net.corda.core.internal.cert
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
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.nodeapi.internal.NetworkMap
import net.corda.nodeapi.internal.SignedNetworkMap
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.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.DatabaseConfig
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
import org.assertj.core.api.Assertions.assertThat
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertTrue
class DBNetworkMapStorageTest : TestBase() { class DBNetworkMapStorageTest : TestBase() {
private lateinit var networkMapStorage: NetworkMapStorage private lateinit var networkMapStorage: NetworkMapStorage
@ -57,7 +57,7 @@ class DBNetworkMapStorageTest : TestBase() {
val requestId = requestStorage.saveRequest(createRequest(organisation).first) val requestId = requestStorage.saveRequest(createRequest(organisation).first)
requestStorage.markRequestTicketCreated(requestId) requestStorage.markRequestTicketCreated(requestId)
requestStorage.approveRequest(requestId, "TestUser") requestStorage.approveRequest(requestId, "TestUser")
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val keyPair = Crypto.generateKeyPair()
val clientCert = X509Utilities.createCertificate(CertificateType.CLIENT_CA, intermediateCACert, intermediateCAKey, CordaX500Name(organisation = organisation, locality = "London", country = "GB"), keyPair.public) val clientCert = X509Utilities.createCertificate(CertificateType.CLIENT_CA, intermediateCACert, intermediateCAKey, CordaX500Name(organisation = organisation, locality = "London", country = "GB"), keyPair.public)
val certPath = buildCertPath(clientCert.toX509Certificate(), intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate()) val certPath = buildCertPath(clientCert.toX509Certificate(), intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
requestStorage.putCertificatePath(requestId, certPath, emptyList()) requestStorage.putCertificatePath(requestId, certPath, emptyList())
@ -67,25 +67,28 @@ class DBNetworkMapStorageTest : TestBase() {
val nodeInfoHash = nodeInfoStorage.putNodeInfo(SignedData(nodeInfoBytes, keyPair.sign(nodeInfoBytes))) val nodeInfoHash = nodeInfoStorage.putNodeInfo(SignedData(nodeInfoBytes, keyPair.sign(nodeInfoBytes)))
// Create network parameters // Create network parameters
val networkParametersHash = networkMapStorage.putNetworkParameters(testNetworkParameters(emptyList())) val networkParametersHash = networkMapStorage.saveNetworkParameters(testNetworkParameters(emptyList()))
val networkMap = NetworkMap(listOf(nodeInfoHash.toString()), networkParametersHash.toString()) val networkMap = NetworkMap(listOf(nodeInfoHash), networkParametersHash)
val signatureData = SignatureAndCertPath(keyPair.sign(networkMap.serialize()), certPath) val serializedNetworkMap = networkMap.serialize()
val signedNetworkMap = SignedNetworkMap(NetworkMap(listOf(nodeInfoHash.toString()), networkParametersHash.toString()), signatureData) val signatureData = intermediateCAKey.sign(serializedNetworkMap).withCert(intermediateCACert.cert)
val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, signatureData)
// when // when
networkMapStorage.saveNetworkMap(signedNetworkMap) networkMapStorage.saveNetworkMap(signedNetworkMap)
// then // then
val persistedSignedNetworkMap = networkMapStorage.getCurrentNetworkMap() val persistedSignedNetworkMap = networkMapStorage.getCurrentNetworkMap()
assertEquals(signedNetworkMap, persistedSignedNetworkMap)
assertEquals(signedNetworkMap.sig, persistedSignedNetworkMap?.sig)
assertEquals(signedNetworkMap.verified(), persistedSignedNetworkMap?.verified())
} }
@Test @Test
fun `getLatestNetworkParameters returns last inserted`() { fun `getLatestNetworkParameters returns last inserted`() {
// given // given
networkMapStorage.putNetworkParameters(createNetworkParameters(minimumPlatformVersion = 1)) networkMapStorage.saveNetworkParameters(createNetworkParameters(minimumPlatformVersion = 1))
networkMapStorage.putNetworkParameters(createNetworkParameters(minimumPlatformVersion = 2)) networkMapStorage.saveNetworkParameters(createNetworkParameters(minimumPlatformVersion = 2))
// when // when
val latest = networkMapStorage.getLatestNetworkParameters() val latest = networkMapStorage.getLatestNetworkParameters()
@ -98,20 +101,20 @@ class DBNetworkMapStorageTest : TestBase() {
fun `getCurrentNetworkParameters returns current network map parameters`() { fun `getCurrentNetworkParameters returns current network map parameters`() {
// given // given
// Create network parameters // Create network parameters
val networkMapParametersHash = networkMapStorage.putNetworkParameters(createNetworkParameters(1)) val networkMapParametersHash = networkMapStorage.saveNetworkParameters(createNetworkParameters(1))
// Create empty network map // Create empty network map
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val intermediateCert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, intermediateCACert, intermediateCAKey, CordaX500Name(organisation = "Corda", locality = "London", country = "GB"), keyPair.public) val intermediateCert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, intermediateCACert, intermediateCAKey, CordaX500Name(organisation = "Corda", locality = "London", country = "GB"), keyPair.public)
val certPath = buildCertPath(intermediateCert.toX509Certificate(), intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
// Sign network map making it current network map // Sign network map making it current network map
val hashedNetworkMap = NetworkMap(emptyList(), networkMapParametersHash.toString()) val networkMap = NetworkMap(emptyList(), networkMapParametersHash)
val signatureData = SignatureAndCertPath(keyPair.sign(hashedNetworkMap.serialize()), certPath) val serializedNetworkMap = networkMap.serialize()
val signedNetworkMap = SignedNetworkMap(hashedNetworkMap, signatureData) val signatureData = keyPair.sign(serializedNetworkMap).withCert(intermediateCert.cert)
val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, signatureData)
networkMapStorage.saveNetworkMap(signedNetworkMap) networkMapStorage.saveNetworkMap(signedNetworkMap)
// Create new network parameters // Create new network parameters
networkMapStorage.putNetworkParameters(createNetworkParameters(2)) networkMapStorage.saveNetworkParameters(createNetworkParameters(2))
// when // when
val result = networkMapStorage.getCurrentNetworkParameters() val result = networkMapStorage.getCurrentNetworkParameters()
@ -121,7 +124,7 @@ class DBNetworkMapStorageTest : TestBase() {
} }
@Test @Test
fun `getDetachedAndValidNodeInfoHashes returns only valid and signed node info hashes`() { fun `getValidNodeInfoHashes returns only valid and signed node info hashes`() {
// given // given
// Create node info. // Create node info.
val organisationA = "TestA" val organisationA = "TestA"
@ -148,19 +151,19 @@ class DBNetworkMapStorageTest : TestBase() {
val nodeInfoHashB = nodeInfoStorage.putNodeInfo(SignedData(nodeInfoBBytes, keyPair.sign(nodeInfoBBytes))) val nodeInfoHashB = nodeInfoStorage.putNodeInfo(SignedData(nodeInfoBBytes, keyPair.sign(nodeInfoBBytes)))
// Create network parameters // Create network parameters
val networkParametersHash = networkMapStorage.putNetworkParameters(createNetworkParameters()) val networkParametersHash = networkMapStorage.saveNetworkParameters(createNetworkParameters())
val networkMap = NetworkMap(listOf(nodeInfoHashA.toString()), networkParametersHash.toString()) val networkMap = NetworkMap(listOf(nodeInfoHashA), networkParametersHash)
val signatureData = SignatureAndCertPath(keyPair.sign(networkMap.serialize()), certPathA) val serializedNetworkMap = networkMap.serialize()
val signedNetworkMap = SignedNetworkMap(networkMap, signatureData) val signatureData = keyPair.sign(serializedNetworkMap).withCert(clientCertA.cert)
val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, signatureData)
// Sign network map // Sign network map
networkMapStorage.saveNetworkMap(signedNetworkMap) networkMapStorage.saveNetworkMap(signedNetworkMap)
// when // when
val detachedHashes = networkMapStorage.getDetachedAndValidNodeInfoHashes() val validNodeInfoHash = networkMapStorage.getNodeInfoHashes(CertificateStatus.VALID)
// then // then
assertEquals(1, detachedHashes.size) assertThat(validNodeInfoHash).containsOnly(nodeInfoHashA, nodeInfoHashB)
assertTrue(detachedHashes.contains(nodeInfoHashB))
} }
} }

View File

@ -3,9 +3,18 @@ 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 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.identity.CordaX500Name
import net.corda.core.internal.cert
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.nodeapi.internal.NetworkMap
import net.corda.nodeapi.internal.SignedNetworkMap
import net.corda.nodeapi.internal.crypto.CertificateType
import net.corda.nodeapi.internal.crypto.X509Utilities
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -15,7 +24,10 @@ class NetworkMapSignerTest : TestBase() {
private lateinit var signer: Signer private lateinit var signer: Signer
private lateinit var networkMapStorage: NetworkMapStorage private lateinit var networkMapStorage: NetworkMapStorage
private lateinit var networkMapSigner: NetworkMapSigner private lateinit var networkMapSigner: NetworkMapSigner
private val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
private val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", locality = "London", organisation = "R3 LTD", country = "GB"), rootCAKey)
private val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
private val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, CordaX500Name(commonName = "Corda Node Intermediate CA", locality = "London", organisation = "R3 LTD", country = "GB"), intermediateCAKey.public)
@Before @Before
fun setUp() { fun setUp() {
signer = mock() signer = mock()
@ -27,30 +39,29 @@ class NetworkMapSignerTest : TestBase() {
fun `signNetworkMap builds and signs network map`() { fun `signNetworkMap builds and signs network map`() {
// given // given
val signedNodeInfoHashes = listOf(SecureHash.randomSHA256(), SecureHash.randomSHA256()) val signedNodeInfoHashes = listOf(SecureHash.randomSHA256(), SecureHash.randomSHA256())
val detachedNodeInfoHashes = listOf(SecureHash.randomSHA256())
val networkMapParameters = createNetworkParameters() val networkMapParameters = createNetworkParameters()
val serializedNetworkMap = NetworkMap(signedNodeInfoHashes, SecureHash.randomSHA256()).serialize()
whenever(networkMapStorage.getCurrentNetworkMap()) whenever(networkMapStorage.getCurrentNetworkMap())
.thenReturn(SignedNetworkMap(NetworkMap(signedNodeInfoHashes.map { it.toString() }, "Dummy"), mock())) .thenReturn(SignedNetworkMap(serializedNetworkMap, intermediateCAKey.sign(serializedNetworkMap).withCert(intermediateCACert.cert)))
whenever(networkMapStorage.getCurrentNetworkMapNodeInfoHashes(any())).thenReturn(signedNodeInfoHashes) whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(signedNodeInfoHashes)
whenever(networkMapStorage.getDetachedAndValidNodeInfoHashes()).thenReturn(detachedNodeInfoHashes)
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkMapParameters) whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkMapParameters)
whenever(signer.sign(any())).thenReturn(mock()) whenever(signer.sign(any())).then {
intermediateCAKey.sign(it.arguments.first() as ByteArray).withCert(intermediateCACert.cert)
}
// when // when
networkMapSigner.signNetworkMap() networkMapSigner.signNetworkMap()
// then // then
// Verify networkMapStorage calls // Verify networkMapStorage calls
verify(networkMapStorage).getCurrentNetworkMapNodeInfoHashes(any()) verify(networkMapStorage).getNodeInfoHashes(any())
verify(networkMapStorage).getDetachedAndValidNodeInfoHashes()
verify(networkMapStorage).getLatestNetworkParameters() verify(networkMapStorage).getLatestNetworkParameters()
argumentCaptor<SignedNetworkMap>().apply { argumentCaptor<SignedNetworkMap>().apply {
verify(networkMapStorage).saveNetworkMap(capture()) verify(networkMapStorage).saveNetworkMap(capture())
val networkMap = firstValue.networkMap val networkMap = firstValue.verified()
assertEquals(networkMapParameters.serialize().hash.toString(), networkMap.parametersHash) assertEquals(networkMapParameters.serialize().hash, networkMap.networkParameterHash)
assertEquals(signedNodeInfoHashes.size + detachedNodeInfoHashes.size, networkMap.nodeInfoHashes.size) assertEquals(signedNodeInfoHashes.size, networkMap.nodeInfoHashes.size)
assertTrue(networkMap.nodeInfoHashes.containsAll(signedNodeInfoHashes.map { it.toString() })) assertTrue(networkMap.nodeInfoHashes.containsAll(signedNodeInfoHashes))
assertTrue(networkMap.nodeInfoHashes.containsAll(detachedNodeInfoHashes.map { it.toString() }))
} }
} }
@ -59,11 +70,11 @@ class NetworkMapSignerTest : TestBase() {
// given // given
val networkMapParameters = createNetworkParameters() val networkMapParameters = createNetworkParameters()
val networkMapParametersHash = networkMapParameters.serialize().bytes.sha256() val networkMapParametersHash = networkMapParameters.serialize().bytes.sha256()
val networkMap = NetworkMap(emptyList(), networkMapParametersHash.toString()) val networkMap = NetworkMap(emptyList(), networkMapParametersHash)
val signedNetworkMap = SignedNetworkMap(networkMap, mock()) val serializedNetworkMap = networkMap.serialize()
val signedNetworkMap = SignedNetworkMap(serializedNetworkMap, intermediateCAKey.sign(serializedNetworkMap).withCert(intermediateCACert.cert))
whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(signedNetworkMap) whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(signedNetworkMap)
whenever(networkMapStorage.getCurrentNetworkMapNodeInfoHashes(any())).thenReturn(emptyList()) whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList())
whenever(networkMapStorage.getDetachedAndValidNodeInfoHashes()).thenReturn(emptyList())
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkMapParameters) whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkMapParameters)
// when // when
@ -79,23 +90,22 @@ class NetworkMapSignerTest : TestBase() {
// given // given
val networkMapParameters = createNetworkParameters() val networkMapParameters = createNetworkParameters()
whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(null) whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(null)
whenever(networkMapStorage.getCurrentNetworkMapNodeInfoHashes(any())).thenReturn(emptyList()) whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList())
whenever(networkMapStorage.getDetachedAndValidNodeInfoHashes()).thenReturn(emptyList())
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkMapParameters) whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkMapParameters)
whenever(signer.sign(any())).thenReturn(mock()) whenever(signer.sign(any())).then {
intermediateCAKey.sign(it.arguments.first() as ByteArray).withCert(intermediateCACert.cert)
}
// when // when
networkMapSigner.signNetworkMap() networkMapSigner.signNetworkMap()
// then // then
// Verify networkMapStorage calls // Verify networkMapStorage calls
verify(networkMapStorage).getCurrentNetworkMapNodeInfoHashes(any()) verify(networkMapStorage).getNodeInfoHashes(any())
verify(networkMapStorage).getDetachedAndValidNodeInfoHashes()
verify(networkMapStorage).getLatestNetworkParameters() verify(networkMapStorage).getLatestNetworkParameters()
argumentCaptor<SignedNetworkMap>().apply { argumentCaptor<SignedNetworkMap>().apply {
verify(networkMapStorage).saveNetworkMap(capture()) verify(networkMapStorage).saveNetworkMap(capture())
val networkMap = firstValue.networkMap val networkMap = firstValue.verified()
assertEquals(networkMapParameters.serialize().hash.toString(), networkMap.parametersHash) assertEquals(networkMapParameters.serialize().hash, networkMap.networkParameterHash)
} }
} }
} }

View File

@ -6,18 +6,20 @@ 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.signer.NetworkMap
import com.r3.corda.networkmanage.common.signer.SignedNetworkMap
import com.r3.corda.networkmanage.common.utils.buildCertPath import com.r3.corda.networkmanage.common.utils.buildCertPath
import com.r3.corda.networkmanage.common.utils.toX509Certificate import com.r3.corda.networkmanage.common.utils.toX509Certificate
import com.r3.corda.networkmanage.common.utils.withCert
import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService
import net.corda.core.crypto.* import net.corda.core.crypto.*
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.PartyAndCertificate import net.corda.core.identity.PartyAndCertificate
import net.corda.core.internal.cert
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
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.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.nodeapi.internal.NetworkMap
import net.corda.nodeapi.internal.SignedNetworkMap
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.testing.SerializationEnvironmentRule import net.corda.testing.SerializationEnvironmentRule
@ -97,16 +99,17 @@ class NodeInfoWebServiceTest {
@Test @Test
fun `get network map`() { fun `get network map`() {
val hashedNetworkMap = NetworkMap(listOf(SecureHash.randomSHA256().toString(), SecureHash.randomSHA256().toString()), SecureHash.randomSHA256().toString()) val networkMap = NetworkMap(listOf(SecureHash.randomSHA256(), SecureHash.randomSHA256()), SecureHash.randomSHA256())
val serializedNetworkMap = networkMap.serialize()
val networkMapStorage: NetworkMapStorage = mock { val networkMapStorage: NetworkMapStorage = mock {
on { getCurrentNetworkMap() }.thenReturn(SignedNetworkMap(hashedNetworkMap, mock())) on { getCurrentNetworkMap() }.thenReturn(SignedNetworkMap(serializedNetworkMap, intermediateCAKey.sign(serializedNetworkMap).withCert(intermediateCACert.cert)))
} }
DoormanServer(NetworkHostAndPort("localhost", 0), NodeInfoWebService(mock(), networkMapStorage)).use { DoormanServer(NetworkHostAndPort("localhost", 0), NodeInfoWebService(mock(), networkMapStorage)).use {
it.start() it.start()
val conn = URL("http://${it.hostAndPort}/${NodeInfoWebService.NETWORK_MAP_PATH}").openConnection() as HttpURLConnection val conn = URL("http://${it.hostAndPort}/${NodeInfoWebService.NETWORK_MAP_PATH}").openConnection() as HttpURLConnection
val signedHashedNetworkMap = conn.inputStream.readBytes().deserialize<SignedNetworkMap>() val signedNetworkMap = conn.inputStream.readBytes().deserialize<SignedNetworkMap>()
verify(networkMapStorage, times(1)).getCurrentNetworkMap() verify(networkMapStorage, times(1)).getCurrentNetworkMap()
assertEquals(signedHashedNetworkMap.networkMap, hashedNetworkMap) assertEquals(signedNetworkMap.verified(), networkMap)
} }
} }
@ -126,7 +129,7 @@ class NodeInfoWebServiceTest {
DoormanServer(NetworkHostAndPort("localhost", 0), NodeInfoWebService(nodeInfoStorage, mock())).use { DoormanServer(NetworkHostAndPort("localhost", 0), NodeInfoWebService(nodeInfoStorage, mock())).use {
it.start() it.start()
val nodeInfoURL = URL("http://${it.hostAndPort}/${NodeInfoWebService.NETWORK_MAP_PATH}/$nodeInfoHash") val nodeInfoURL = URL("http://${it.hostAndPort}/${NodeInfoWebService.NETWORK_MAP_PATH}/node-info/$nodeInfoHash")
val conn = nodeInfoURL.openConnection() val conn = nodeInfoURL.openConnection()
val nodeInfoResponse = conn.inputStream.readBytes().deserialize<SignedData<NodeInfo>>() val nodeInfoResponse = conn.inputStream.readBytes().deserialize<SignedData<NodeInfo>>()
verify(nodeInfoStorage, times(1)).getNodeInfo(nodeInfoHash) verify(nodeInfoStorage, times(1)).getNodeInfo(nodeInfoHash)

View File

@ -3,9 +3,10 @@ package com.r3.corda.networkmanage.hsm.authentication
import CryptoServerJCE.CryptoServerProvider import CryptoServerJCE.CryptoServerProvider
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.signer.AuthenticationException
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import kotlin.test.assertFalse import kotlin.test.assertFailsWith
import kotlin.test.assertTrue import kotlin.test.assertTrue
class AuthenticatorTest : TestBase() { class AuthenticatorTest : TestBase() {
@ -24,15 +25,15 @@ class AuthenticatorTest : TestBase() {
fun `connectAndAuthenticate aborts when user inputs Q`() { fun `connectAndAuthenticate aborts when user inputs Q`() {
// given // given
givenUserConsoleInputOnReadLine("Q") givenUserConsoleInputOnReadLine("Q")
var executed = false
// when // when
Authenticator(provider = provider, inputReader = inputReader).connectAndAuthenticate { _, _ -> executed = true } assertFailsWith<AuthenticationException> {
Authenticator(provider = provider, inputReader = inputReader).connectAndAuthenticate { _, _ -> }
}
// then //then
assertFalse(executed) verify(provider, never()).loginPassword(any(), any<String>())
verify(provider, never()).loginPassword(any<String>(), any<String>()) verify(provider, never()).loginSign(any(), any(), any())
verify(provider, never()).loginSign(any<String>(), any<String>(), any<String>())
} }
@Test @Test
@ -50,7 +51,7 @@ class AuthenticatorTest : TestBase() {
// then // then
verify(provider).loginPassword(username, password) verify(provider).loginPassword(username, password)
verify(provider, never()).loginSign(any<String>(), any<String>(), any<String>()) verify(provider, never()).loginSign(any(), any(), any())
assertTrue(executed) assertTrue(executed)
} }
@ -67,7 +68,7 @@ class AuthenticatorTest : TestBase() {
// then // then
verify(provider).loginSign(username, ":cs2:cyb:USB0", null) verify(provider).loginSign(username, ":cs2:cyb:USB0", null)
verify(provider, never()).loginPassword(any<String>(), any<String>()) verify(provider, never()).loginPassword(any(), any<String>())
assertTrue(executed) assertTrue(executed)
} }
@ -86,7 +87,7 @@ class AuthenticatorTest : TestBase() {
// then // then
verify(provider, times(3)).loginPassword(username, password) verify(provider, times(3)).loginPassword(username, password)
verify(provider, never()).loginSign(any<String>(), any<String>(), any<String>()) verify(provider, never()).loginSign(any(), any(), any())
assertTrue(executed) assertTrue(executed)
} }
@ -98,7 +99,7 @@ class AuthenticatorTest : TestBase() {
} }
private fun givenUserConsoleInputOnReadPassword(input: String) { private fun givenUserConsoleInputOnReadPassword(input: String) {
whenever(inputReader.readPassword(any<String>())).thenReturn(input) whenever(inputReader.readPassword(any())).thenReturn(input)
} }
private fun givenUserConsoleInputOnReadLine(input: String) { private fun givenUserConsoleInputOnReadLine(input: String) {