mirror of
https://github.com/corda/corda.git
synced 2024-12-27 08:22:35 +00:00
Removing NodeInfo signing process - this task is moved to node itself (#112)
* Removing NodeInfo signing process - this task is moved to node itself * Addressing review comments * Introducing HSM simulator polling * Addressing review comments
This commit is contained in:
parent
7786913cd8
commit
c1ae6e9647
@ -1,7 +1,7 @@
|
||||
ext {
|
||||
// We use Corda release artifact dependencies instead of project dependencies to make sure each doorman releases are
|
||||
// aligned with the corresponding Corda release.
|
||||
corda_dependency_version = '2.0-20171108.000038-27'
|
||||
corda_dependency_version = '3.0-20171115.000100-7'
|
||||
}
|
||||
|
||||
version "$corda_dependency_version"
|
||||
@ -125,7 +125,6 @@ dependencies {
|
||||
testCompile "com.nhaarman:mockito-kotlin:0.6.1"
|
||||
testRuntime "net.corda:corda-rpc:$corda_dependency_version"
|
||||
testCompile "com.spotify:docker-client:8.9.1"
|
||||
integrationTestCompile "net.corda:corda-test-common:$corda_dependency_version"
|
||||
integrationTestRuntime "net.corda:corda-rpc:$corda_dependency_version"
|
||||
|
||||
compile('com.atlassian.jira:jira-rest-java-client-core:4.0.0') {
|
||||
|
@ -1,5 +1,8 @@
|
||||
package com.r3.corda.networkmanage
|
||||
|
||||
import CryptoServerAPI.CryptoServerException
|
||||
import com.r3.corda.networkmanage.hsm.authentication.CryptoServerProviderConfig
|
||||
import com.r3.corda.networkmanage.hsm.authentication.createProvider
|
||||
import com.spotify.docker.client.DefaultDockerClient
|
||||
import com.spotify.docker.client.DockerClient
|
||||
import com.spotify.docker.client.messages.ContainerConfig
|
||||
@ -29,7 +32,9 @@ data class CryptoUserCredentials(val username: String, val password: String)
|
||||
*/
|
||||
class HsmSimulator(private val serverAddress: String = DEFAULT_SERVER_ADDRESS,
|
||||
private val imageRepoTag: String = DEFAULT_IMAGE_REPO_TAG,
|
||||
private val imageVersion: String = DEFAULT_IMAGE_VERSION) : ExternalResource() {
|
||||
private val imageVersion: String = DEFAULT_IMAGE_VERSION,
|
||||
private val registryUser: String? = REGISTRY_USERNAME,
|
||||
private val registryPass: String? = REGISTRY_PASSWORD) : ExternalResource() {
|
||||
|
||||
private companion object {
|
||||
val DEFAULT_SERVER_ADDRESS = "corda.azurecr.io"
|
||||
@ -46,6 +51,9 @@ class HsmSimulator(private val serverAddress: String = DEFAULT_SERVER_ADDRESS,
|
||||
val REGISTRY_PASSWORD = System.getenv("AZURE_CR_PASS")
|
||||
|
||||
val log = loggerFor<HsmSimulator>()
|
||||
|
||||
private val HSM_STARTUP_SLEEP_INTERVAL_MS = 500L
|
||||
private val HSM_STARTUP_POLL_MAX_COUNT = 10;
|
||||
}
|
||||
|
||||
private val localHostAndPortBinding = freeLocalHostAndPort()
|
||||
@ -53,8 +61,8 @@ class HsmSimulator(private val serverAddress: String = DEFAULT_SERVER_ADDRESS,
|
||||
private var containerId: String? = null
|
||||
|
||||
override fun before() {
|
||||
assumeFalse("Docker registry username is not set!. Skipping the test.", REGISTRY_USERNAME.isNullOrBlank())
|
||||
assumeFalse("Docker registry password is not set!. Skipping the test.", REGISTRY_PASSWORD.isNullOrBlank())
|
||||
assumeFalse("Docker registry username is not set!. Skipping the test.", registryUser.isNullOrBlank())
|
||||
assumeFalse("Docker registry password is not set!. Skipping the test.", registryPass.isNullOrBlank())
|
||||
docker = DefaultDockerClient.fromEnv().build().pullHsmSimulatorImageFromRepository()
|
||||
containerId = docker.createContainer()
|
||||
docker.startHsmSimulatorContainer()
|
||||
@ -96,17 +104,41 @@ class HsmSimulator(private val serverAddress: String = DEFAULT_SERVER_ADDRESS,
|
||||
if (containerId != null) {
|
||||
log.debug("Starting container $containerId...")
|
||||
this.startContainer(containerId)
|
||||
pollAndWaitForHsmSimulator()
|
||||
}
|
||||
}
|
||||
|
||||
private fun pollAndWaitForHsmSimulator() {
|
||||
val config = CryptoServerProviderConfig(
|
||||
Device = "${localHostAndPortBinding.port}@${localHostAndPortBinding.host}",
|
||||
KeyGroup = "*",
|
||||
KeySpecifier = -1
|
||||
)
|
||||
var pollCount = HSM_STARTUP_POLL_MAX_COUNT
|
||||
while (pollCount > 0) {
|
||||
val provider = createProvider(config)
|
||||
try {
|
||||
provider.loginPassword(CRYPTO_USER, CRYPTO_PASSWORD)
|
||||
provider.cryptoServer.authState
|
||||
return
|
||||
} catch (e: CryptoServerException) {
|
||||
pollCount--
|
||||
Thread.sleep(HSM_STARTUP_SLEEP_INTERVAL_MS)
|
||||
} finally {
|
||||
provider.logoff()
|
||||
}
|
||||
}
|
||||
throw IllegalStateException("Unable to obtain connection to initialised HSM Simulator")
|
||||
}
|
||||
|
||||
private fun getImageFullName() = "$imageRepoTag:$imageVersion"
|
||||
|
||||
private fun DockerClient.pullHsmSimulatorImageFromRepository(): DockerClient {
|
||||
this.pull(imageRepoTag,
|
||||
RegistryAuth.builder()
|
||||
.serverAddress(serverAddress)
|
||||
.username(REGISTRY_USERNAME)
|
||||
.password(REGISTRY_PASSWORD)
|
||||
.username(registryUser)
|
||||
.password(registryPass)
|
||||
.build())
|
||||
return this
|
||||
}
|
||||
|
@ -2,63 +2,73 @@ package com.r3.corda.networkmanage.doorman
|
||||
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import com.r3.corda.networkmanage.common.persistence.SchemaService
|
||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
||||
import com.r3.corda.networkmanage.common.utils.toX509Certificate
|
||||
import com.r3.corda.networkmanage.doorman.signer.LocalSigner
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.SignedData
|
||||
import net.corda.core.crypto.sign
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.node.services.network.NetworkMapClient
|
||||
import net.corda.node.utilities.*
|
||||
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
|
||||
import net.corda.node.utilities.registration.NetworkRegistrationHelper
|
||||
import net.corda.testing.ALICE
|
||||
import net.corda.testing.SerializationEnvironmentRule
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.testNodeConfiguration
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import org.junit.Ignore
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import java.net.URL
|
||||
import java.util.*
|
||||
import kotlin.test.assertEquals
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import kotlin.test.assertNotNull
|
||||
|
||||
class DoormanIntegrationTest {
|
||||
@Rule
|
||||
@JvmField
|
||||
val tempFolder = TemporaryFolder()
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
val testSerialization = SerializationEnvironmentRule(true)
|
||||
|
||||
@Test
|
||||
fun `initial registration`() {
|
||||
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Integration Test Corda Node Root CA", organisation = "R3 Ltd", locality = "London", country = "GB"), rootCAKey)
|
||||
val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey,
|
||||
CordaX500Name(commonName = "Integration Test Corda Node Intermediate CA", locality = "London", country = "GB", organisation = "R3 Ltd"), intermediateCAKey.public)
|
||||
|
||||
val database = configureDatabase(makeTestDataSourceProperties(), null, {
|
||||
// Identity service not needed doorman, corda persistence is not very generic.
|
||||
throw UnsupportedOperationException()
|
||||
}, SchemaService())
|
||||
val signer = LocalSigner(intermediateCAKey, arrayOf(intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate()))
|
||||
val rootCertAndKey = createDoormanRootCertificateAndKeyPair()
|
||||
val intermediateCertAndKey = createDoormanIntermediateCertificateAndKeyPair(rootCertAndKey)
|
||||
|
||||
//Start doorman server
|
||||
val doorman = startDoorman(NetworkHostAndPort("localhost", 0), database, true, testNetworkParameters(emptyList()), signer, 2, 10,null)
|
||||
val doorman = startDoorman(intermediateCertAndKey, rootCertAndKey.certificate)
|
||||
|
||||
// Start Corda network registration.
|
||||
val config = testNodeConfiguration(
|
||||
baseDirectory = tempFolder.root.toPath(),
|
||||
myLegalName = ALICE.name).also {
|
||||
whenever(it.certificateSigningService).thenReturn(URL("http://localhost:${doorman.hostAndPort.port}"))
|
||||
val doormanHostAndPort = doorman.hostAndPort
|
||||
whenever(it.compatibilityZoneURL).thenReturn(URL("http://${doormanHostAndPort.host}:${doormanHostAndPort.port}"))
|
||||
whenever(it.emailAddress).thenReturn("iTest@R3.com")
|
||||
}
|
||||
|
||||
NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.certificateSigningService)).buildKeystore()
|
||||
NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.compatibilityZoneURL!!)).buildKeystore()
|
||||
|
||||
// Checks the keystore are created with the right certificates and keys.
|
||||
assert(config.nodeKeystore.toFile().exists())
|
||||
assert(config.sslKeystore.toFile().exists())
|
||||
assert(config.trustStoreFile.toFile().exists())
|
||||
|
||||
val intermediateCACert = intermediateCertAndKey.certificate
|
||||
val rootCACert = rootCertAndKey.certificate
|
||||
|
||||
loadKeyStore(config.nodeKeystore, config.keyStorePassword).apply {
|
||||
assert(containsAlias(X509Utilities.CORDA_CLIENT_CA))
|
||||
assertEquals(ALICE.name.copy(commonName = X509Utilities.CORDA_CLIENT_CA_CN).x500Principal, getX509Certificate(X509Utilities.CORDA_CLIENT_CA).subjectX500Principal)
|
||||
@ -75,15 +85,91 @@ class DoormanIntegrationTest {
|
||||
assert(containsAlias(X509Utilities.CORDA_ROOT_CA))
|
||||
assertEquals(rootCACert.cert.subjectX500Principal, getX509Certificate(X509Utilities.CORDA_ROOT_CA).subjectX500Principal)
|
||||
}
|
||||
|
||||
doorman.close()
|
||||
}
|
||||
|
||||
private fun makeTestDataSourceProperties(nodeName: String = SecureHash.randomSHA256().toString()): Properties {
|
||||
val props = Properties()
|
||||
props.setProperty("dataSourceClassName", "org.h2.jdbcx.JdbcDataSource")
|
||||
props.setProperty("dataSource.url", "jdbc:h2:mem:${nodeName}_persistence;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE")
|
||||
props.setProperty("dataSource.user", "sa")
|
||||
props.setProperty("dataSource.password", "")
|
||||
return props
|
||||
//TODO remove @Ignore once PR https://github.com/corda/corda/pull/2054 is merged
|
||||
@Test
|
||||
@Ignore
|
||||
fun `nodeInfo is published to the network map`() {
|
||||
// Given
|
||||
val rootCertAndKey = createDoormanRootCertificateAndKeyPair()
|
||||
val intermediateCertAndKey = createDoormanIntermediateCertificateAndKeyPair(rootCertAndKey)
|
||||
|
||||
//Start doorman server
|
||||
val doorman = startDoorman(intermediateCertAndKey, rootCertAndKey.certificate)
|
||||
val doormanHostAndPort = doorman.hostAndPort
|
||||
|
||||
// Start Corda network registration.
|
||||
val config = testNodeConfiguration(
|
||||
baseDirectory = tempFolder.root.toPath(),
|
||||
myLegalName = ALICE.name).also {
|
||||
whenever(it.compatibilityZoneURL).thenReturn(URL("http://${doormanHostAndPort.host}:${doormanHostAndPort.port}"))
|
||||
whenever(it.emailAddress).thenReturn("iTest@R3.com")
|
||||
}
|
||||
|
||||
NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.compatibilityZoneURL!!)).buildKeystore()
|
||||
|
||||
// Publish NodeInfo
|
||||
val networkMapClient = NetworkMapClient(config.compatibilityZoneURL!!)
|
||||
val certs = loadKeyStore(config.nodeKeystore, config.keyStorePassword).getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
|
||||
val keyPair = loadKeyStore(config.nodeKeystore, config.keyStorePassword).getKeyPair(X509Utilities.CORDA_CLIENT_CA, config.keyStorePassword)
|
||||
val nodeInfo = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(buildCertPath(*certs))), 1, serial = 1L)
|
||||
val nodeInfoBytes = nodeInfo.serialize()
|
||||
|
||||
// When
|
||||
networkMapClient.publish(SignedData(nodeInfoBytes, keyPair.sign(nodeInfoBytes)))
|
||||
|
||||
// Then
|
||||
val networkMapNodeInfo = networkMapClient.getNodeInfo(nodeInfoBytes.hash)
|
||||
assertNotNull(networkMapNodeInfo)
|
||||
assertEquals(nodeInfo, networkMapNodeInfo)
|
||||
|
||||
doorman.close()
|
||||
}
|
||||
}
|
||||
|
||||
fun createDoormanIntermediateCertificateAndKeyPair(rootCertificateAndKeyPair: CertificateAndKeyPair): CertificateAndKeyPair {
|
||||
val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCertificateAndKeyPair.certificate, rootCertificateAndKeyPair.keyPair,
|
||||
CordaX500Name(commonName = "Integration Test Corda Node Intermediate CA",
|
||||
locality = "London",
|
||||
country = "GB",
|
||||
organisation = "R3 Ltd"), intermediateCAKey.public)
|
||||
return CertificateAndKeyPair(intermediateCACert, intermediateCAKey)
|
||||
}
|
||||
|
||||
fun createDoormanRootCertificateAndKeyPair(): CertificateAndKeyPair {
|
||||
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val rootCACert = X509Utilities.createSelfSignedCACertificate(
|
||||
CordaX500Name(commonName = "Integration Test Corda Node Root CA",
|
||||
organisation = "R3 Ltd", locality = "London",
|
||||
country = "GB"), rootCAKey)
|
||||
return CertificateAndKeyPair(rootCACert, rootCAKey)
|
||||
}
|
||||
|
||||
fun makeTestDataSourceProperties(nodeName: String = SecureHash.randomSHA256().toString()): Properties {
|
||||
val props = Properties()
|
||||
props.setProperty("dataSourceClassName", "org.h2.jdbcx.JdbcDataSource")
|
||||
props.setProperty("dataSource.url", "jdbc:h2:mem:${nodeName}_persistence;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE")
|
||||
props.setProperty("dataSource.user", "sa")
|
||||
props.setProperty("dataSource.password", "")
|
||||
return props
|
||||
}
|
||||
|
||||
fun startDoorman(intermediateCACertAndKey: CertificateAndKeyPair, rootCACert: X509CertificateHolder): DoormanServer {
|
||||
val signer = LocalSigner(intermediateCACertAndKey.keyPair,
|
||||
arrayOf(intermediateCACertAndKey.certificate.toX509Certificate(), rootCACert.toX509Certificate()))
|
||||
//Start doorman server
|
||||
return startDoorman(signer)
|
||||
}
|
||||
|
||||
fun startDoorman(localSigner: LocalSigner? = null): DoormanServer {
|
||||
val database = configureDatabase(makeTestDataSourceProperties(), null, {
|
||||
// Identity service not needed doorman, corda persistence is not very generic.
|
||||
throw UnsupportedOperationException()
|
||||
}, SchemaService())
|
||||
//Start doorman server
|
||||
return startDoorman(NetworkHostAndPort("localhost", 0), database, true, testNetworkParameters(emptyList()), localSigner, 2, 30,null)
|
||||
}
|
@ -11,6 +11,7 @@ import com.r3.corda.networkmanage.hsm.configuration.Parameters
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class HsmTest {
|
||||
@ -18,26 +19,30 @@ class HsmTest {
|
||||
@Rule
|
||||
@JvmField
|
||||
val hsmSimulator: HsmSimulator = HsmSimulator()
|
||||
val testParameters = Parameters(
|
||||
dataSourceProperties = mock(),
|
||||
device = "${hsmSimulator.port}@${hsmSimulator.host}",
|
||||
keySpecifier = 1,
|
||||
keyGroup = "*"
|
||||
)
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
val tempFolder = TemporaryFolder()
|
||||
|
||||
private lateinit var inputReader: InputReader
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
inputReader = mock()
|
||||
whenever(inputReader.readLine()).thenReturn(hsmSimulator.cryptoUserCredentials().username)
|
||||
whenever(inputReader.readPassword(any())).thenReturn(hsmSimulator.cryptoUserCredentials().password)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Authenticator executes the block once user is successfully authenticated`() {
|
||||
// given
|
||||
val parameters = Parameters(
|
||||
dataSourceProperties = mock(),
|
||||
device = "${hsmSimulator.port}@${hsmSimulator.host}",
|
||||
keySpecifier = 1,
|
||||
keyGroup = "*"
|
||||
)
|
||||
whenever(inputReader.readLine()).thenReturn(hsmSimulator.cryptoUserCredentials().username)
|
||||
whenever(inputReader.readPassword(any())).thenReturn(hsmSimulator.cryptoUserCredentials().password)
|
||||
val authenticator = Authenticator(parameters.createProvider(), inputReader = inputReader)
|
||||
val authenticator = Authenticator(testParameters.createProvider(), inputReader = inputReader)
|
||||
var executed = false
|
||||
|
||||
// when
|
||||
@ -48,6 +53,4 @@ class HsmTest {
|
||||
// then
|
||||
assertTrue(executed)
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -86,19 +86,20 @@ class SigningServiceIntegrationTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Signing service communicates with Doorman`() {
|
||||
fun `Signing service signs approved CSRs`() {
|
||||
//Start doorman server
|
||||
val database = configureDatabase(makeTestDataSourceProperties(), null, {
|
||||
// Identity service not needed doorman, corda persistence is not very generic.
|
||||
throw UnsupportedOperationException()
|
||||
}, SchemaService())
|
||||
val doorman = startDoorman(NetworkHostAndPort(HOST, 0), database, approveAll = true, approveInterval = 2, signInterval = 10, initialNetworkMapParameters = testNetworkParameters(emptyList()))
|
||||
val doorman = startDoorman(NetworkHostAndPort(HOST, 0), database, approveAll = true, approveInterval = 2, signInterval = 30, initialNetworkMapParameters = testNetworkParameters(emptyList()))
|
||||
|
||||
// Start Corda network registration.
|
||||
val config = testNodeConfiguration(
|
||||
baseDirectory = tempFolder.root.toPath(),
|
||||
myLegalName = ALICE.name).also {
|
||||
whenever(it.certificateSigningService).thenReturn(URL("http://$HOST:${doorman.hostAndPort.port}"))
|
||||
val doormanHostAndPort = doorman.hostAndPort
|
||||
whenever(it.compatibilityZoneURL).thenReturn(URL("http://${doormanHostAndPort.host}:${doormanHostAndPort.port}"))
|
||||
}
|
||||
|
||||
val signingServiceStorage = DBSignedCertificateRequestStorage(configureDatabase(makeTestDataSourceProperties(), makeNotInitialisingTestDatabaseProperties(), {
|
||||
@ -126,7 +127,7 @@ class SigningServiceIntegrationTest {
|
||||
// [org.hibernate.tool.schema.spi.SchemaManagementException] being thrown as the schema is missing.
|
||||
}
|
||||
}
|
||||
NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.certificateSigningService)).buildKeystore()
|
||||
NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.compatibilityZoneURL!!)).buildKeystore()
|
||||
verify(hsmSigner).sign(any())
|
||||
doorman.close()
|
||||
}
|
||||
@ -166,9 +167,9 @@ class SigningServiceIntegrationTest {
|
||||
3 -> CHARLIE.name
|
||||
else -> throw IllegalArgumentException("Unrecognised option")
|
||||
}).also {
|
||||
whenever(it.certificateSigningService).thenReturn(URL("http://$HOST:${doorman.hostAndPort.port}"))
|
||||
whenever(it.compatibilityZoneURL).thenReturn(URL("http://$HOST:${doorman.hostAndPort.port}"))
|
||||
}
|
||||
NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.certificateSigningService)).buildKeystore()
|
||||
NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.compatibilityZoneURL!!)).buildKeystore()
|
||||
}
|
||||
}.map { it.join() }
|
||||
doorman.close()
|
||||
|
@ -33,10 +33,10 @@ interface NetworkMapStorage {
|
||||
fun saveNetworkMap(signedNetworkMap: SignedNetworkMap)
|
||||
|
||||
/**
|
||||
* Retrieve all node info hashes for all signed node info with valid certificates,
|
||||
* Retrieve all node info hashes for all node info with valid certificates,
|
||||
* that are not associated with any network map yet.
|
||||
*/
|
||||
fun getDetachedSignedAndValidNodeInfoHashes(): List<SecureHash>
|
||||
fun getDetachedAndValidNodeInfoHashes(): List<SecureHash>
|
||||
|
||||
/**
|
||||
* Retrieve network parameters by their hash.
|
||||
|
@ -16,41 +16,16 @@ interface NodeInfoStorage {
|
||||
*/
|
||||
fun getCertificatePath(publicKeyHash: SecureHash): CertPath?
|
||||
|
||||
/**
|
||||
* Obtain list of registered node info hashes that haven't been signed yet and have valid certificates.
|
||||
*/
|
||||
fun getUnsignedNodeInfoHashes(): List<SecureHash>
|
||||
|
||||
/**
|
||||
* Similar to [getUnsignedNodeInfoHashes] but instead of hashes, map of node info bytes is returned.
|
||||
* @return map of node info hashes to their corresponding node info bytes
|
||||
*/
|
||||
fun getUnsignedNodeInfoBytes(): Map<SecureHash, ByteArray>
|
||||
|
||||
/**
|
||||
* Retrieve node info using nodeInfo's hash
|
||||
* @return [NodeInfo] or null if the node info is not registered.
|
||||
*/
|
||||
fun getNodeInfo(nodeInfoHash: SecureHash): NodeInfo?
|
||||
|
||||
/**
|
||||
* Retrieve node info together with its signature using nodeInfo's hash
|
||||
* @return [NodeInfo] or null if the node info is not registered.
|
||||
*/
|
||||
fun getSignedNodeInfo(nodeInfoHash: SecureHash): SignedData<NodeInfo>?
|
||||
fun getNodeInfo(nodeInfoHash: SecureHash): SignedData<NodeInfo>?
|
||||
|
||||
/**
|
||||
* The [nodeInfo] is keyed by the public key, old node info with the same public key will be replaced by the new node info.
|
||||
* @param nodeInfo node info to be stored
|
||||
* @param signature (optional) signature associated with the node info
|
||||
* @param signedNodeInfo signed node info data to be stored
|
||||
* @return hash for the newly created node info entry
|
||||
*/
|
||||
fun putNodeInfo(nodeInfo: NodeInfo, signature: DigitalSignature? = null): SecureHash
|
||||
|
||||
/**
|
||||
* Stores the signature for the given node info hash.
|
||||
* @param nodeInfoHash node info hash which signature corresponds to
|
||||
* @param signature signature for the node info
|
||||
*/
|
||||
fun signNodeInfo(nodeInfoHash: SecureHash, signature: DigitalSignature.WithKey)
|
||||
fun putNodeInfo(signedNodeInfo: SignedData<NodeInfo>): SecureHash
|
||||
}
|
@ -98,7 +98,7 @@ class PersistentNetworkMapStorage(private val database: CordaPersistence) : Netw
|
||||
session.createQuery(query).resultList.first()
|
||||
}
|
||||
|
||||
override fun getDetachedSignedAndValidNodeInfoHashes(): List<SecureHash> = database.transaction {
|
||||
override fun getDetachedAndValidNodeInfoHashes(): List<SecureHash> = database.transaction {
|
||||
val builder = session.criteriaBuilder
|
||||
// Get signed NodeInfoEntities
|
||||
val query = builder.createQuery(NodeInfoEntity::class.java).run {
|
||||
|
@ -5,16 +5,11 @@ import com.r3.corda.networkmanage.common.persistence.entity.CertificateSigningRe
|
||||
import com.r3.corda.networkmanage.common.persistence.entity.NodeInfoEntity
|
||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
||||
import com.r3.corda.networkmanage.common.utils.hashString
|
||||
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.node.NodeInfo
|
||||
import net.corda.core.serialization.SerializedBytes
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.node.utilities.CordaPersistence
|
||||
import org.hibernate.Session
|
||||
import org.hibernate.jpa.QueryHints
|
||||
import java.security.cert.CertPath
|
||||
import java.sql.Connection
|
||||
|
||||
@ -22,7 +17,8 @@ import java.sql.Connection
|
||||
* Database implementation of the [NetworkMapStorage] interface
|
||||
*/
|
||||
class PersistentNodeInfoStorage(private val database: CordaPersistence) : NodeInfoStorage {
|
||||
override fun putNodeInfo(nodeInfo: NodeInfo, signature: DigitalSignature?): SecureHash = database.transaction(Connection.TRANSACTION_SERIALIZABLE) {
|
||||
override fun putNodeInfo(signedNodeInfo: SignedData<NodeInfo>): SecureHash = database.transaction(Connection.TRANSACTION_SERIALIZABLE) {
|
||||
val nodeInfo = signedNodeInfo.verified()
|
||||
val publicKeyHash = nodeInfo.legalIdentities.first().owningKey.hashString()
|
||||
val request = singleRequestWhere(CertificateDataEntity::class.java) { builder, path ->
|
||||
val certPublicKeyHashEq = builder.equal(path.get<String>(CertificateDataEntity::publicKeyHash.name), publicKeyHash)
|
||||
@ -31,7 +27,7 @@ class PersistentNodeInfoStorage(private val database: CordaPersistence) : NodeIn
|
||||
}
|
||||
request ?: throw IllegalArgumentException("CSR data missing for provided node info: $nodeInfo")
|
||||
/*
|
||||
* Delete any previous [HashedNodeInfo] instance for this CSR
|
||||
* Delete any previous [NodeInfoEntity] instance for this CSR
|
||||
* Possibly it should be moved at the network signing process at the network signing process
|
||||
* as for a while the network map will have invalid entries (i.e. hashes for node info which have been
|
||||
* removed). Either way, there will be a period of time when the network map data will be invalid
|
||||
@ -40,18 +36,19 @@ class PersistentNodeInfoStorage(private val database: CordaPersistence) : NodeIn
|
||||
deleteRequest(NodeInfoEntity::class.java) { builder, path ->
|
||||
builder.equal(path.get<CertificateSigningRequestEntity>(NodeInfoEntity::certificateSigningRequest.name), request.certificateSigningRequest)
|
||||
}
|
||||
val serializedNodeInfo = nodeInfo.serialize().bytes
|
||||
val hash = serializedNodeInfo.sha256()
|
||||
val hash = signedNodeInfo.raw.hash
|
||||
val hashedNodeInfo = NodeInfoEntity(
|
||||
nodeInfoHash = hash.toString(),
|
||||
certificateSigningRequest = request.certificateSigningRequest,
|
||||
nodeInfoBytes = serializedNodeInfo,
|
||||
signatureBytes = signature?.bytes)
|
||||
nodeInfoBytes = signedNodeInfo.raw.bytes,
|
||||
signatureBytes = signedNodeInfo.sig.bytes,
|
||||
signaturePublicKeyBytes = signedNodeInfo.sig.by.encoded,
|
||||
signaturePublicKeyAlgorithm = signedNodeInfo.sig.by.algorithm)
|
||||
session.save(hashedNodeInfo)
|
||||
hash
|
||||
}
|
||||
|
||||
override fun getSignedNodeInfo(nodeInfoHash: SecureHash): SignedData<NodeInfo>? = database.transaction {
|
||||
override fun getNodeInfo(nodeInfoHash: SecureHash): SignedData<NodeInfo>? = database.transaction {
|
||||
val nodeInfoEntity = session.find(NodeInfoEntity::class.java, nodeInfoHash.toString())
|
||||
if (nodeInfoEntity?.signatureBytes == null) {
|
||||
null
|
||||
@ -60,18 +57,6 @@ class PersistentNodeInfoStorage(private val database: CordaPersistence) : NodeIn
|
||||
}
|
||||
}
|
||||
|
||||
override fun getNodeInfo(nodeInfoHash: SecureHash): NodeInfo? = database.transaction {
|
||||
session.find(NodeInfoEntity::class.java, nodeInfoHash.toString())?.nodeInfo()
|
||||
}
|
||||
|
||||
override fun getUnsignedNodeInfoBytes(): Map<SecureHash, ByteArray> {
|
||||
return getUnsignedNodeInfoEntities().associate { SecureHash.parse(it.nodeInfoHash) to it.nodeInfoBytes }
|
||||
}
|
||||
|
||||
override fun getUnsignedNodeInfoHashes(): List<SecureHash> {
|
||||
return getUnsignedNodeInfoEntities().map { SecureHash.parse(it.nodeInfoHash) }
|
||||
}
|
||||
|
||||
override fun getCertificatePath(publicKeyHash: SecureHash): CertPath? {
|
||||
return database.transaction {
|
||||
val builder = session.criteriaBuilder
|
||||
@ -86,44 +71,4 @@ class PersistentNodeInfoStorage(private val database: CordaPersistence) : NodeIn
|
||||
session.createQuery(query).uniqueResultOptional().orElseGet { null }?.let { buildCertPath(it) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun signNodeInfo(nodeInfoHash: SecureHash, signature: DigitalSignature.WithKey) {
|
||||
database.transaction {
|
||||
val nodeInfoEntity = session.find(NodeInfoEntity::class.java, nodeInfoHash.toString())
|
||||
if (nodeInfoEntity != null) {
|
||||
session.merge(nodeInfoEntity.copy(
|
||||
signatureBytes = signature.bytes,
|
||||
signaturePublicKeyAlgorithm = signature.by.algorithm,
|
||||
signaturePublicKeyBytes = signature.by.encoded
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getUnsignedNodeInfoEntities(): List<NodeInfoEntity> = database.transaction {
|
||||
val builder = session.criteriaBuilder
|
||||
// Retrieve all unsigned NodeInfoHash
|
||||
val query = builder.createQuery(NodeInfoEntity::class.java).run {
|
||||
from(NodeInfoEntity::class.java).run {
|
||||
where(builder.and(builder.isNull(get<ByteArray>(NodeInfoEntity::signatureBytes.name))))
|
||||
}
|
||||
}
|
||||
// Retrieve them together with their CSR
|
||||
val (hintKey, hintValue) = getNodeInfoWithCsrHint(session)
|
||||
val unsigned = session.createQuery(query).setHint(hintKey, hintValue).resultList
|
||||
// Get only those that are valid
|
||||
unsigned.filter({
|
||||
val certificateStatus = it.certificateSigningRequest?.certificateData?.certificateStatus
|
||||
certificateStatus == CertificateStatus.VALID
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates Hibernate query hint for pulling [CertificateSigningRequestEntity] when querying for [NodeInfoEntity]
|
||||
*/
|
||||
private fun getNodeInfoWithCsrHint(session: Session): Pair<String, Any> {
|
||||
val graph = session.createEntityGraph(NodeInfoEntity::class.java)
|
||||
graph.addAttributeNodes(NodeInfoEntity::certificateSigningRequest.name)
|
||||
return QueryHints.HINT_LOADGRAPH to graph
|
||||
}
|
||||
}
|
@ -31,7 +31,7 @@ class NetworkMapSigner(private val networkMapStorage: NetworkMapStorage,
|
||||
fun signNetworkMap() {
|
||||
val currentSignedNetworkMap = networkMapStorage.getCurrentNetworkMap()
|
||||
val currentNetworkMapValidNodeInfo = networkMapStorage.getCurrentNetworkMapNodeInfoHashes(listOf(CertificateStatus.VALID))
|
||||
val detachedValidNodeInfo = networkMapStorage.getDetachedSignedAndValidNodeInfoHashes()
|
||||
val detachedValidNodeInfo = networkMapStorage.getDetachedAndValidNodeInfoHashes()
|
||||
val nodeInfoHashes = currentNetworkMapValidNodeInfo + detachedValidNodeInfo
|
||||
val networkParameters = networkMapStorage.getLatestNetworkParameters()
|
||||
val networkMap = NetworkMap(nodeInfoHashes.map { it.toString() }, networkParameters.serialize().hash.toString())
|
||||
|
@ -79,7 +79,7 @@ class DoormanServer(hostAndPort: NetworkHostAndPort, private vararg val webServi
|
||||
webServices.forEach { register(it) }
|
||||
}
|
||||
val jerseyServlet = ServletHolder(ServletContainer(resourceConfig)).apply { initOrder = 0 }// Initialise at server start
|
||||
addServlet(jerseyServlet, "/api/*")
|
||||
addServlet(jerseyServlet, "/*")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -184,7 +184,7 @@ fun startDoorman(hostAndPort: NetworkHostAndPort,
|
||||
val networkMapStorage = PersistentNetworkMapStorage(database)
|
||||
val nodeInfoStorage = PersistentNodeInfoStorage(database)
|
||||
|
||||
val doorman = DoormanServer(hostAndPort, RegistrationWebService(requestProcessor, DoormanServer.serverStatus), NodeInfoWebService(nodeInfoStorage, networkMapStorage, signer))
|
||||
val doorman = DoormanServer(hostAndPort, RegistrationWebService(requestProcessor, DoormanServer.serverStatus), NodeInfoWebService(nodeInfoStorage, networkMapStorage))
|
||||
doorman.start()
|
||||
|
||||
val networkMapSigner = if (signer != null) NetworkMapSigner(networkMapStorage, signer) else null
|
||||
|
@ -3,12 +3,10 @@ package com.r3.corda.networkmanage.doorman.webservice
|
||||
import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage
|
||||
import com.r3.corda.networkmanage.common.persistence.NodeInfoStorage
|
||||
import com.r3.corda.networkmanage.common.utils.hashString
|
||||
import com.r3.corda.networkmanage.doorman.signer.LocalSigner
|
||||
import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService.Companion.networkMapPath
|
||||
import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService.Companion.NETWORK_MAP_PATH
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.SignedData
|
||||
import net.corda.core.node.NetworkParameters
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.serialize
|
||||
@ -23,42 +21,39 @@ import javax.ws.rs.core.Response
|
||||
import javax.ws.rs.core.Response.ok
|
||||
import javax.ws.rs.core.Response.status
|
||||
|
||||
@Path(networkMapPath)
|
||||
@Path(NETWORK_MAP_PATH)
|
||||
class NodeInfoWebService(private val nodeInfoStorage: NodeInfoStorage,
|
||||
private val networkMapStorage: NetworkMapStorage,
|
||||
private val signer: LocalSigner? = null) {
|
||||
private val networkMapStorage: NetworkMapStorage) {
|
||||
companion object {
|
||||
const val networkMapPath = "network-map"
|
||||
const val NETWORK_MAP_PATH = "network-map"
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("register")
|
||||
@Path("publish")
|
||||
@Consumes(MediaType.APPLICATION_OCTET_STREAM)
|
||||
fun registerNode(input: InputStream): Response {
|
||||
// TODO: Use JSON instead.
|
||||
val registrationData = input.readBytes().deserialize<SignedData<NodeInfo>>()
|
||||
|
||||
val nodeInfo = registrationData.verified()
|
||||
val digitalSignature = registrationData.sig
|
||||
|
||||
val certPath = nodeInfoStorage.getCertificatePath(SecureHash.parse(digitalSignature.by.hashString()))
|
||||
val certPath = nodeInfoStorage.getCertificatePath(SecureHash.parse(nodeInfo.legalIdentitiesAndCerts.first().certPath.certificates.first().publicKey.hashString()))
|
||||
return if (certPath != null) {
|
||||
try {
|
||||
val serializedNodeInfo = nodeInfo.serialize().bytes
|
||||
val nodeCAPubKey = certPath.certificates.first().publicKey
|
||||
// Validate node public key
|
||||
nodeInfo.legalIdentitiesAndCerts.forEach {
|
||||
require(it.certPath.certificates.any { it.publicKey == nodeCAPubKey })
|
||||
}
|
||||
require(Crypto.doVerify(nodeCAPubKey, digitalSignature.bytes, serializedNodeInfo))
|
||||
// Store the NodeInfo and notify registration listener
|
||||
nodeInfoStorage.putNodeInfo(nodeInfo, signer?.sign(serializedNodeInfo)?.signature)
|
||||
val digitalSignature = registrationData.sig
|
||||
require(Crypto.doVerify(nodeCAPubKey, digitalSignature.bytes, registrationData.raw.bytes))
|
||||
// Store the NodeInfo
|
||||
nodeInfoStorage.putNodeInfo(registrationData)
|
||||
ok()
|
||||
} catch (e: Exception) {
|
||||
// Catch exceptions thrown by signature verification.
|
||||
when (e) {
|
||||
is IllegalArgumentException, is InvalidKeyException, is SignatureException -> status(Response.Status.UNAUTHORIZED).entity(e.message)
|
||||
// Rethrow e if its not one of the expected exception, the server will return http 500 internal error.
|
||||
// Rethrow e if its not one of the expected exception, the server will return http 500 internal error.
|
||||
else -> throw e
|
||||
}
|
||||
}
|
||||
@ -76,9 +71,12 @@ class NodeInfoWebService(private val nodeInfoStorage: NodeInfoStorage,
|
||||
@GET
|
||||
@Path("{nodeInfoHash}")
|
||||
fun getNodeInfo(@PathParam("nodeInfoHash") nodeInfoHash: String): Response {
|
||||
return nodeInfoStorage.getSignedNodeInfo(SecureHash.parse(nodeInfoHash))?.let {
|
||||
ok(it.serialize().bytes).build()
|
||||
} ?: status(Response.Status.NOT_FOUND).build()
|
||||
val nodeInfo = nodeInfoStorage.getNodeInfo(SecureHash.parse(nodeInfoHash))
|
||||
return if (nodeInfo != null) {
|
||||
ok(nodeInfo.serialize().bytes).build()
|
||||
} else {
|
||||
status(Response.Status.NOT_FOUND).build()
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
|
@ -1,7 +1,6 @@
|
||||
package com.r3.corda.networkmanage.hsm
|
||||
|
||||
import com.r3.corda.networkmanage.common.persistence.PersistentNetworkMapStorage
|
||||
import com.r3.corda.networkmanage.common.persistence.PersistentNodeInfoStorage
|
||||
import com.r3.corda.networkmanage.common.persistence.SchemaService
|
||||
import com.r3.corda.networkmanage.hsm.authentication.AuthMode
|
||||
import com.r3.corda.networkmanage.hsm.authentication.Authenticator
|
||||
@ -32,10 +31,8 @@ fun run(parameters: Parameters) {
|
||||
}, SchemaService())
|
||||
val csrStorage = DBSignedCertificateRequestStorage(database)
|
||||
val networkMapStorage = PersistentNetworkMapStorage(database)
|
||||
val nodeInfoStorage = PersistentNodeInfoStorage(database)
|
||||
val hsmNetworkMapSigningThread = HsmNetworkMapSigner(
|
||||
networkMapStorage,
|
||||
nodeInfoStorage,
|
||||
networkMapCertificateName,
|
||||
networkMapPrivateKeyPass,
|
||||
keyStorePass,
|
||||
|
@ -93,7 +93,7 @@ class Authenticator(private val provider: CryptoServerProvider,
|
||||
/*
|
||||
* Configuration class for [CryptoServerProvider]
|
||||
*/
|
||||
internal data class CryptoServerProviderConfig(
|
||||
data class CryptoServerProviderConfig(
|
||||
val Device: String = "3001@127.0.0.1",
|
||||
val ConnectionTimeout: Int = 30000,
|
||||
val Timeout: Int = 60000,
|
||||
@ -113,6 +113,10 @@ fun Parameters.createProvider(): CryptoServerProvider {
|
||||
KeyGroup = keyGroup,
|
||||
KeySpecifier = keySpecifier
|
||||
)
|
||||
return createProvider(config)
|
||||
}
|
||||
|
||||
fun createProvider(config: CryptoServerProviderConfig): CryptoServerProvider {
|
||||
val cfgBuffer = ByteArrayOutputStream()
|
||||
val writer = cfgBuffer.writer(Charsets.UTF_8)
|
||||
for (property in CryptoServerProviderConfig::class.memberProperties) {
|
||||
|
@ -31,7 +31,7 @@ class KeyCertificateGenerator(private val authenticator: Authenticator,
|
||||
* @param parentPrivateKeyPassword password for the parent private key
|
||||
* @param validDays days of certificate validity
|
||||
*/
|
||||
fun generateAllCertificates(keyStorePassword: String?,
|
||||
fun generateAllCertificates(keyStorePassword: String? = null,
|
||||
intermediateCertificatesCredentials: List<CertificateNameAndPass>,
|
||||
parentCertificateName: String,
|
||||
parentPrivateKeyPassword: String,
|
||||
|
@ -1,7 +1,7 @@
|
||||
package com.r3.corda.networkmanage.hsm.signer
|
||||
|
||||
import com.google.common.util.concurrent.MoreExecutors
|
||||
import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage
|
||||
import com.r3.corda.networkmanage.common.persistence.NodeInfoStorage
|
||||
import com.r3.corda.networkmanage.common.signer.NetworkMapSigner
|
||||
import com.r3.corda.networkmanage.common.signer.SignatureAndCertPath
|
||||
import com.r3.corda.networkmanage.common.signer.Signer
|
||||
@ -15,15 +15,15 @@ import net.corda.core.utilities.minutes
|
||||
import java.security.KeyPair
|
||||
import java.security.PrivateKey
|
||||
import java.time.Duration
|
||||
import java.util.*
|
||||
import kotlin.concurrent.fixedRateTimer
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.ScheduledExecutorService
|
||||
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.
|
||||
*/
|
||||
class HsmNetworkMapSigner(networkMapStorage: NetworkMapStorage,
|
||||
private val nodeInfoStorage: NodeInfoStorage,
|
||||
private val caCertificateKeyName: String,
|
||||
private val caPrivateKeyPass: String,
|
||||
private val keyStorePassword: String?,
|
||||
@ -33,45 +33,28 @@ class HsmNetworkMapSigner(networkMapStorage: NetworkMapStorage,
|
||||
companion object {
|
||||
val log = loggerFor<HsmNetworkMapSigner>()
|
||||
val DEFAULT_SIGNING_PERIOD_MS = 10.minutes
|
||||
|
||||
private val TERMINATION_TIMEOUT_SEC = 2L
|
||||
}
|
||||
|
||||
private val networkMapSigner = NetworkMapSigner(networkMapStorage, this)
|
||||
private var fixedRateTimer: Timer? = null
|
||||
private lateinit var scheduledExecutor: ScheduledExecutorService
|
||||
|
||||
fun start(): HsmNetworkMapSigner {
|
||||
stop()
|
||||
fixedRateTimer = fixedRateTimer(
|
||||
name = "Network Map Signing Thread",
|
||||
period = signingPeriod.toMillis(),
|
||||
action = {
|
||||
try {
|
||||
signNodeInfo()
|
||||
networkMapSigner.signNetworkMap()
|
||||
} catch (exception: Exception) {
|
||||
log.warn("Exception thrown while signing network map", exception)
|
||||
}
|
||||
})
|
||||
val signingPeriodMillis = signingPeriod.toMillis()
|
||||
scheduledExecutor = Executors.newSingleThreadScheduledExecutor()
|
||||
scheduledExecutor.scheduleAtFixedRate({
|
||||
try {
|
||||
networkMapSigner.signNetworkMap()
|
||||
} catch (exception: Exception) {
|
||||
log.warn("Exception thrown while signing network map", exception)
|
||||
}
|
||||
}, signingPeriodMillis, signingPeriodMillis, TimeUnit.MILLISECONDS)
|
||||
return this
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
fixedRateTimer?.cancel()
|
||||
}
|
||||
|
||||
private fun signNodeInfo() {
|
||||
// Retrieve data
|
||||
val nodeInfoBytes = nodeInfoStorage.getUnsignedNodeInfoBytes()
|
||||
// Authenticate and sign
|
||||
authenticator.connectAndAuthenticate { provider, _ ->
|
||||
val keyStore = X509Utilities.getAndInitializeKeyStore(provider, keyStorePassword)
|
||||
val caCertificateChain = keyStore.getCertificateChain(caCertificateKeyName)
|
||||
val caKey = keyStore.getKey(caCertificateKeyName, caPrivateKeyPass.toCharArray()) as PrivateKey
|
||||
for ((nodeInfoHash, bytes) in nodeInfoBytes) {
|
||||
val signature = signData(bytes, KeyPair(caCertificateChain.first().publicKey, caKey), provider)
|
||||
verify(bytes, signature, caCertificateChain.first().publicKey)
|
||||
nodeInfoStorage.signNodeInfo(nodeInfoHash, signature)
|
||||
}
|
||||
}
|
||||
MoreExecutors.shutdownAndAwaitTermination(scheduledExecutor, TERMINATION_TIMEOUT_SEC, TimeUnit.SECONDS)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -23,28 +23,6 @@ abstract class TestBase {
|
||||
@JvmField
|
||||
val testSerialization = SerializationEnvironmentRule()
|
||||
|
||||
protected fun certificateSigningRequestEntity(
|
||||
requestId: String = SecureHash.randomSHA256().toString(),
|
||||
status: RequestStatus = RequestStatus.NEW,
|
||||
legalName: String = "TestLegalName",
|
||||
modifiedBy: List<String> = emptyList(),
|
||||
modifiedAt: Instant = Instant.now(),
|
||||
remark: String = "Test remark",
|
||||
certificateData: CertificateDataEntity? = null,
|
||||
requestBytes: ByteArray = ByteArray(0)
|
||||
): CertificateSigningRequestEntity {
|
||||
return CertificateSigningRequestEntity(
|
||||
requestId = requestId,
|
||||
status = status,
|
||||
legalName = legalName,
|
||||
modifiedBy = modifiedBy,
|
||||
modifiedAt = modifiedAt,
|
||||
remark = remark,
|
||||
certificateData = certificateData,
|
||||
requestBytes = requestBytes
|
||||
)
|
||||
}
|
||||
|
||||
protected fun certificateSigningRequest(
|
||||
requestId: String = SecureHash.randomSHA256().toString(),
|
||||
status: RequestStatus = RequestStatus.NEW,
|
||||
|
@ -7,11 +7,11 @@ import com.r3.corda.networkmanage.common.signer.SignedNetworkMap
|
||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
||||
import com.r3.corda.networkmanage.common.utils.toX509Certificate
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.SignedData
|
||||
import net.corda.core.crypto.sign
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.node.NotaryInfo
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.node.utilities.CertificateType
|
||||
@ -62,15 +62,15 @@ class DBNetworkMapStorageTest : TestBase() {
|
||||
val certPath = buildCertPath(clientCert.toX509Certificate(), intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
|
||||
requestStorage.putCertificatePath(requestId, certPath, emptyList())
|
||||
val nodeInfo = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(certPath)), 1, serial = 1L)
|
||||
val nodeInfoHash = nodeInfoStorage.putNodeInfo(nodeInfo)
|
||||
// Some random bytes
|
||||
val signature = keyPair.sign(nodeInfo.serialize())
|
||||
nodeInfoStorage.signNodeInfo(nodeInfoHash, signature)
|
||||
// Put signed node info data
|
||||
val nodeInfoBytes = nodeInfo.serialize()
|
||||
val nodeInfoHash = nodeInfoStorage.putNodeInfo(SignedData(nodeInfoBytes, keyPair.sign(nodeInfoBytes)))
|
||||
|
||||
// Create network parameters
|
||||
val networkParametersHash = networkMapStorage.putNetworkParameters(testNetworkParameters(emptyList()))
|
||||
|
||||
val signatureData = SignatureAndCertPath(signature, certPath)
|
||||
val networkMap = NetworkMap(listOf(nodeInfoHash.toString()), networkParametersHash.toString())
|
||||
val signatureData = SignatureAndCertPath(keyPair.sign(networkMap.serialize()), certPath)
|
||||
val signedNetworkMap = SignedNetworkMap(NetworkMap(listOf(nodeInfoHash.toString()), networkParametersHash.toString()), signatureData)
|
||||
|
||||
// when
|
||||
@ -121,7 +121,7 @@ class DBNetworkMapStorageTest : TestBase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getDetachedSignedAndValidNodeInfoHashes returns only valid and signed node info hashes`() {
|
||||
fun `getDetachedAndValidNodeInfoHashes returns only valid and signed node info hashes`() {
|
||||
// given
|
||||
// Create node info.
|
||||
val organisationA = "TestA"
|
||||
@ -139,11 +139,11 @@ class DBNetworkMapStorageTest : TestBase() {
|
||||
requestStorage.putCertificatePath(requestIdB, certPathB, emptyList())
|
||||
val nodeInfoA = NodeInfo(listOf(NetworkHostAndPort("my.companyA.com", 1234)), listOf(PartyAndCertificate(certPathA)), 1, serial = 1L)
|
||||
val nodeInfoB = NodeInfo(listOf(NetworkHostAndPort("my.companyB.com", 1234)), listOf(PartyAndCertificate(certPathB)), 1, serial = 1L)
|
||||
val nodeInfoHashA = nodeInfoStorage.putNodeInfo(nodeInfoA)
|
||||
val nodeInfoHashB = nodeInfoStorage.putNodeInfo(nodeInfoB)
|
||||
// Sign node info
|
||||
nodeInfoStorage.signNodeInfo(nodeInfoHashA, keyPair.sign(nodeInfoA.serialize()))
|
||||
nodeInfoStorage.signNodeInfo(nodeInfoHashB, keyPair.sign(nodeInfoB.serialize()))
|
||||
// Put signed node info data
|
||||
val nodeInfoABytes = nodeInfoA.serialize()
|
||||
val nodeInfoBBytes = nodeInfoB.serialize()
|
||||
val nodeInfoHashA = nodeInfoStorage.putNodeInfo(SignedData(nodeInfoABytes, keyPair.sign(nodeInfoABytes)))
|
||||
val nodeInfoHashB = nodeInfoStorage.putNodeInfo(SignedData(nodeInfoBBytes, keyPair.sign(nodeInfoBBytes)))
|
||||
|
||||
// Create network parameters
|
||||
val networkParametersHash = networkMapStorage.putNetworkParameters(createNetworkParameters())
|
||||
@ -155,7 +155,7 @@ class DBNetworkMapStorageTest : TestBase() {
|
||||
networkMapStorage.saveNetworkMap(signedNetworkMap)
|
||||
|
||||
// when
|
||||
val detachedHashes = networkMapStorage.getDetachedSignedAndValidNodeInfoHashes()
|
||||
val detachedHashes = networkMapStorage.getDetachedAndValidNodeInfoHashes()
|
||||
|
||||
// then
|
||||
assertEquals(1, detachedHashes.size)
|
||||
|
@ -6,12 +6,11 @@ import com.r3.corda.networkmanage.common.utils.hashString
|
||||
import com.r3.corda.networkmanage.common.utils.toX509Certificate
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.sha256
|
||||
import net.corda.core.crypto.SignedData
|
||||
import net.corda.core.crypto.sign
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.node.utilities.CertificateType
|
||||
@ -25,7 +24,6 @@ import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertNull
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class PersitenceNodeInfoStorageTest : TestBase() {
|
||||
private lateinit var requestStorage: CertificationRequestStorage
|
||||
@ -72,8 +70,8 @@ class PersitenceNodeInfoStorageTest : TestBase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test getNodeInfoHashes`() {
|
||||
// Create node info.
|
||||
fun `test getNodeInfoHash returns correct data`() {
|
||||
// given
|
||||
val organisationA = "TestA"
|
||||
val requestIdA = requestStorage.saveRequest(createRequest(organisationA).first)
|
||||
requestStorage.approveRequest(requestIdA, "TestUser")
|
||||
@ -88,22 +86,23 @@ class PersitenceNodeInfoStorageTest : TestBase() {
|
||||
val certPathB = buildCertPath(clientCertB.toX509Certificate(), intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
|
||||
requestStorage.putCertificatePath(requestIdB, certPathB, emptyList())
|
||||
val nodeInfoA = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(certPathA)), 1, serial = 1L)
|
||||
val nodeInfoSame = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(certPathA)), 1, serial = 1L)
|
||||
val nodeInfoB = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(certPathB)), 1, serial = 1L)
|
||||
|
||||
nodeInfoStorage.putNodeInfo(nodeInfoA)
|
||||
nodeInfoStorage.putNodeInfo(nodeInfoSame)
|
||||
// Put signed node info data
|
||||
val nodeInfoABytes = nodeInfoA.serialize()
|
||||
val nodeInfoBBytes = nodeInfoB.serialize()
|
||||
nodeInfoStorage.putNodeInfo(SignedData(nodeInfoABytes, keyPair.sign(nodeInfoABytes)))
|
||||
nodeInfoStorage.putNodeInfo(SignedData(nodeInfoBBytes, keyPair.sign(nodeInfoBBytes)))
|
||||
|
||||
// getNodeInfoHashes should contain 1 hash.
|
||||
assertEquals(listOf(nodeInfoA.serialize().sha256()), nodeInfoStorage.getUnsignedNodeInfoHashes())
|
||||
// when
|
||||
val persistedNodeInfoA = nodeInfoStorage.getNodeInfo(nodeInfoABytes.hash)
|
||||
val persistedNodeInfoB = nodeInfoStorage.getNodeInfo(nodeInfoBBytes.hash)
|
||||
|
||||
nodeInfoStorage.putNodeInfo(nodeInfoB)
|
||||
// getNodeInfoHashes should contain 2 hash.
|
||||
assertEquals(listOf(nodeInfoB.serialize().sha256(), nodeInfoA.serialize().sha256()).sorted(), nodeInfoStorage.getUnsignedNodeInfoHashes().sorted())
|
||||
|
||||
// Test retrieve NodeInfo.
|
||||
assertEquals(nodeInfoA, nodeInfoStorage.getNodeInfo(nodeInfoA.serialize().sha256()))
|
||||
assertEquals(nodeInfoB, nodeInfoStorage.getNodeInfo(nodeInfoB.serialize().sha256()))
|
||||
// then
|
||||
assertNotNull(persistedNodeInfoA)
|
||||
assertNotNull(persistedNodeInfoB)
|
||||
assertEquals(persistedNodeInfoA!!.verified(), nodeInfoA)
|
||||
assertEquals(persistedNodeInfoB!!.verified(), nodeInfoB)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -119,19 +118,21 @@ class PersitenceNodeInfoStorageTest : TestBase() {
|
||||
|
||||
val nodeInfo = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(certPath)), 1, serial = 1L)
|
||||
val nodeInfoSamePubKey = NodeInfo(listOf(NetworkHostAndPort("my.company2.com", 1234)), listOf(PartyAndCertificate(certPath)), 1, serial = 1L)
|
||||
val nodeInfoBytes = nodeInfo.serialize()
|
||||
val nodeInfoHash = nodeInfoStorage.putNodeInfo(SignedData(nodeInfoBytes, keyPair.sign(nodeInfoBytes)))
|
||||
assertEquals(nodeInfo, nodeInfoStorage.getNodeInfo(nodeInfoHash)?.verified())
|
||||
|
||||
nodeInfoStorage.putNodeInfo(nodeInfo)
|
||||
assertEquals(nodeInfo, nodeInfoStorage.getNodeInfo(nodeInfo.serialize().sha256()))
|
||||
|
||||
val nodeInfoSamePubKeyBytes = nodeInfoSamePubKey.serialize()
|
||||
// This should replace the node info.
|
||||
nodeInfoStorage.putNodeInfo(nodeInfoSamePubKey)
|
||||
nodeInfoStorage.putNodeInfo(SignedData(nodeInfoSamePubKeyBytes, keyPair.sign(nodeInfoSamePubKeyBytes)))
|
||||
|
||||
// Old node info should be removed.
|
||||
assertNull(nodeInfoStorage.getNodeInfo(nodeInfo.serialize().sha256()))
|
||||
assertEquals(nodeInfoSamePubKey, nodeInfoStorage.getNodeInfo(nodeInfoSamePubKey.serialize().sha256()))
|
||||
assertNull(nodeInfoStorage.getNodeInfo(nodeInfoHash))
|
||||
assertEquals(nodeInfoSamePubKey, nodeInfoStorage.getNodeInfo(nodeInfoSamePubKeyBytes.hash)?.verified())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `signNodeInfo associates signature to with node info`() {
|
||||
fun `putNodeInfo persists node info data with its signature`() {
|
||||
// given
|
||||
// Create node info.
|
||||
val organisation = "Test"
|
||||
@ -143,38 +144,16 @@ class PersitenceNodeInfoStorageTest : TestBase() {
|
||||
requestStorage.putCertificatePath(requestId, certPath, emptyList())
|
||||
|
||||
val nodeInfo = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(certPath)), 1, serial = 1L)
|
||||
val nodeInfoHash = nodeInfoStorage.putNodeInfo(nodeInfo)
|
||||
// Some random bytes
|
||||
val signature = keyPair.sign(nodeInfo.serialize())
|
||||
val nodeInfoBytes = nodeInfo.serialize()
|
||||
val signature = keyPair.sign(nodeInfoBytes)
|
||||
|
||||
// when
|
||||
nodeInfoStorage.signNodeInfo(nodeInfoHash, signature)
|
||||
val nodeInfoHash = nodeInfoStorage.putNodeInfo(SignedData(nodeInfoBytes, signature))
|
||||
|
||||
// then
|
||||
val signedNodeInfo = nodeInfoStorage.getSignedNodeInfo(nodeInfoHash)
|
||||
assertEquals(signature, signedNodeInfo?.sig)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getUnsignedNodeInfoBytes return node info bytes`() {
|
||||
// given
|
||||
// Create node info.
|
||||
val organisation = "Test"
|
||||
val requestId = requestStorage.saveRequest(createRequest(organisation).first)
|
||||
requestStorage.approveRequest(requestId, "TestUser")
|
||||
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
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())
|
||||
requestStorage.putCertificatePath(requestId, certPath, emptyList())
|
||||
|
||||
val nodeInfo = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(certPath)), 1, serial = 1L)
|
||||
val nodeInfoHash = nodeInfoStorage.putNodeInfo(nodeInfo)
|
||||
|
||||
// when
|
||||
val nodeInfoBytes = nodeInfoStorage.getUnsignedNodeInfoBytes()
|
||||
|
||||
// then
|
||||
assertTrue(nodeInfoBytes.containsKey(nodeInfoHash))
|
||||
assertEquals(nodeInfo, nodeInfoBytes[nodeInfoHash]?.deserialize()!!)
|
||||
val persistedNodeInfo = nodeInfoStorage.getNodeInfo(nodeInfoHash)
|
||||
assertNotNull(persistedNodeInfo)
|
||||
assertEquals(nodeInfo, persistedNodeInfo!!.verified())
|
||||
assertEquals(signature, persistedNodeInfo.sig)
|
||||
}
|
||||
}
|
@ -32,7 +32,7 @@ class NetworkMapSignerTest : TestBase() {
|
||||
whenever(networkMapStorage.getCurrentNetworkMap())
|
||||
.thenReturn(SignedNetworkMap(NetworkMap(signedNodeInfoHashes.map { it.toString() }, "Dummy"), mock()))
|
||||
whenever(networkMapStorage.getCurrentNetworkMapNodeInfoHashes(any())).thenReturn(signedNodeInfoHashes)
|
||||
whenever(networkMapStorage.getDetachedSignedAndValidNodeInfoHashes()).thenReturn(detachedNodeInfoHashes)
|
||||
whenever(networkMapStorage.getDetachedAndValidNodeInfoHashes()).thenReturn(detachedNodeInfoHashes)
|
||||
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkMapParameters)
|
||||
whenever(signer.sign(any())).thenReturn(mock())
|
||||
|
||||
@ -42,7 +42,7 @@ class NetworkMapSignerTest : TestBase() {
|
||||
// then
|
||||
// Verify networkMapStorage calls
|
||||
verify(networkMapStorage).getCurrentNetworkMapNodeInfoHashes(any())
|
||||
verify(networkMapStorage).getDetachedSignedAndValidNodeInfoHashes()
|
||||
verify(networkMapStorage).getDetachedAndValidNodeInfoHashes()
|
||||
verify(networkMapStorage).getLatestNetworkParameters()
|
||||
argumentCaptor<SignedNetworkMap>().apply {
|
||||
verify(networkMapStorage).saveNetworkMap(capture())
|
||||
@ -63,7 +63,7 @@ class NetworkMapSignerTest : TestBase() {
|
||||
val signedNetworkMap = SignedNetworkMap(networkMap, mock())
|
||||
whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(signedNetworkMap)
|
||||
whenever(networkMapStorage.getCurrentNetworkMapNodeInfoHashes(any())).thenReturn(emptyList())
|
||||
whenever(networkMapStorage.getDetachedSignedAndValidNodeInfoHashes()).thenReturn(emptyList())
|
||||
whenever(networkMapStorage.getDetachedAndValidNodeInfoHashes()).thenReturn(emptyList())
|
||||
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkMapParameters)
|
||||
|
||||
// when
|
||||
|
@ -4,7 +4,6 @@ import com.nhaarman.mockito_kotlin.any
|
||||
import com.nhaarman.mockito_kotlin.mock
|
||||
import com.nhaarman.mockito_kotlin.times
|
||||
import com.nhaarman.mockito_kotlin.verify
|
||||
import com.r3.corda.networkmanage.TestBase
|
||||
import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage
|
||||
import com.r3.corda.networkmanage.common.persistence.NodeInfoStorage
|
||||
import com.r3.corda.networkmanage.common.signer.NetworkMap
|
||||
@ -21,10 +20,9 @@ import net.corda.core.serialization.serialize
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.node.utilities.CertificateType
|
||||
import net.corda.node.utilities.X509Utilities
|
||||
import net.corda.nodeapi.internal.serialization.*
|
||||
import net.corda.nodeapi.internal.serialization.amqp.AMQPServerSerializationScheme
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.SerializationEnvironmentRule
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.IOException
|
||||
@ -34,7 +32,12 @@ import javax.ws.rs.core.MediaType
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
|
||||
class NodeInfoWebServiceTest : TestBase() {
|
||||
class NodeInfoWebServiceTest {
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
val testSerialization = SerializationEnvironmentRule(true)
|
||||
|
||||
private val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
private val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(locality = "London", organisation = "R3 LTD", country = "GB", commonName = "Corda Node Root CA"), rootCAKey)
|
||||
private val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
@ -57,7 +60,7 @@ class NodeInfoWebServiceTest : TestBase() {
|
||||
|
||||
DoormanServer(NetworkHostAndPort("localhost", 0), NodeInfoWebService(nodeInfoStorage, mock())).use {
|
||||
it.start()
|
||||
val registerURL = URL("http://${it.hostAndPort}/api/${NodeInfoWebService.networkMapPath}/register")
|
||||
val registerURL = URL("http://${it.hostAndPort}/${NodeInfoWebService.NETWORK_MAP_PATH}/publish")
|
||||
val nodeInfoAndSignature = SignedData(nodeInfo.serialize(), digitalSignature).serialize().bytes
|
||||
// Post node info and signature to doorman
|
||||
doPost(registerURL, nodeInfoAndSignature)
|
||||
@ -83,7 +86,7 @@ class NodeInfoWebServiceTest : TestBase() {
|
||||
|
||||
DoormanServer(NetworkHostAndPort("localhost", 0), NodeInfoWebService(nodeInfoStorage, mock())).use {
|
||||
it.start()
|
||||
val registerURL = URL("http://${it.hostAndPort}/api/${NodeInfoWebService.networkMapPath}/register")
|
||||
val registerURL = URL("http://${it.hostAndPort}/${NodeInfoWebService.NETWORK_MAP_PATH}/publish")
|
||||
val nodeInfoAndSignature = SignedData(nodeInfo.serialize(), digitalSignature).serialize().bytes
|
||||
// Post node info and signature to doorman
|
||||
assertFailsWith(IOException::class) {
|
||||
@ -101,7 +104,7 @@ class NodeInfoWebServiceTest : TestBase() {
|
||||
}
|
||||
DoormanServer(NetworkHostAndPort("localhost", 0), NodeInfoWebService(mock(), networkMapStorage)).use {
|
||||
it.start()
|
||||
val conn = URL("http://${it.hostAndPort}/api/${NodeInfoWebService.networkMapPath}").openConnection() as HttpURLConnection
|
||||
val conn = URL("http://${it.hostAndPort}/${NodeInfoWebService.NETWORK_MAP_PATH}").openConnection() as HttpURLConnection
|
||||
val signedHashedNetworkMap = conn.inputStream.readBytes().deserialize<SignedNetworkMap>()
|
||||
verify(networkMapStorage, times(1)).getCurrentNetworkMap()
|
||||
assertEquals(signedHashedNetworkMap.networkMap, hashedNetworkMap)
|
||||
@ -119,19 +122,19 @@ class NodeInfoWebServiceTest : TestBase() {
|
||||
|
||||
val nodeInfoStorage: NodeInfoStorage = mock {
|
||||
val serializedNodeInfo = nodeInfo.serialize()
|
||||
on { getSignedNodeInfo(nodeInfoHash) }.thenReturn(SignedData(serializedNodeInfo, keyPair.sign(serializedNodeInfo)))
|
||||
on { getNodeInfo(nodeInfoHash) }.thenReturn(SignedData(serializedNodeInfo, keyPair.sign(serializedNodeInfo)))
|
||||
}
|
||||
|
||||
DoormanServer(NetworkHostAndPort("localhost", 0), NodeInfoWebService(nodeInfoStorage, mock())).use {
|
||||
it.start()
|
||||
val nodeInfoURL = URL("http://${it.hostAndPort}/api/${NodeInfoWebService.networkMapPath}/$nodeInfoHash")
|
||||
val nodeInfoURL = URL("http://${it.hostAndPort}/${NodeInfoWebService.NETWORK_MAP_PATH}/$nodeInfoHash")
|
||||
val conn = nodeInfoURL.openConnection()
|
||||
val nodeInfoResponse = conn.inputStream.readBytes().deserialize<SignedData<NodeInfo>>()
|
||||
verify(nodeInfoStorage, times(1)).getSignedNodeInfo(nodeInfoHash)
|
||||
verify(nodeInfoStorage, times(1)).getNodeInfo(nodeInfoHash)
|
||||
assertEquals(nodeInfo, nodeInfoResponse.verified())
|
||||
|
||||
assertFailsWith(FileNotFoundException::class) {
|
||||
URL("http://${it.hostAndPort}/api/${NodeInfoWebService.networkMapPath}/${SecureHash.randomSHA256()}").openConnection().getInputStream()
|
||||
URL("http://${it.hostAndPort}/${NodeInfoWebService.NETWORK_MAP_PATH}/${SecureHash.randomSHA256()}").openConnection().getInputStream()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -169,7 +169,7 @@ class RegistrationWebServiceTest : TestBase() {
|
||||
}
|
||||
|
||||
private fun submitRequest(request: PKCS10CertificationRequest): String {
|
||||
val conn = URL("http://${doormanServer.hostAndPort}/api/certificate").openConnection() as HttpURLConnection
|
||||
val conn = URL("http://${doormanServer.hostAndPort}/certificate").openConnection() as HttpURLConnection
|
||||
conn.doOutput = true
|
||||
conn.requestMethod = "POST"
|
||||
conn.setRequestProperty("Content-Type", MediaType.APPLICATION_OCTET_STREAM)
|
||||
@ -178,7 +178,7 @@ class RegistrationWebServiceTest : TestBase() {
|
||||
}
|
||||
|
||||
private fun pollForResponse(id: String): PollResponse {
|
||||
val url = URL("http://${doormanServer.hostAndPort}/api/certificate/$id")
|
||||
val url = URL("http://${doormanServer.hostAndPort}/certificate/$id")
|
||||
val conn = url.openConnection() as HttpURLConnection
|
||||
conn.requestMethod = "GET"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user