mirror of
https://github.com/corda/corda.git
synced 2025-06-17 22:58:19 +00:00
CORDA-881: Signed network parameters has the network map cert attached to it instead of just the public key. (#2346)
Introduced DigitalSignatureWithCert and SignedDataWithCert as internal APIs, with the expectation that they will become public; renamed the network parameters end-point to network-parameters; updated the network-map.rst doc; and did some refactoring.
This commit is contained in:
@ -1,13 +1,9 @@
|
||||
package net.corda.node.services.network
|
||||
|
||||
import net.corda.cordform.CordformNode
|
||||
import net.corda.core.crypto.SignedData
|
||||
import net.corda.core.crypto.random63BitValue
|
||||
import net.corda.core.internal.*
|
||||
import net.corda.core.internal.concurrent.transpose
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.exists
|
||||
import net.corda.core.internal.list
|
||||
import net.corda.core.internal.readAll
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
@ -69,7 +65,7 @@ class NetworkMapTest {
|
||||
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||
val networkParameters = (alice.configuration.baseDirectory / NETWORK_PARAMS_FILE_NAME)
|
||||
.readAll()
|
||||
.deserialize<SignedData<NetworkParameters>>()
|
||||
.deserialize<SignedDataWithCert<NetworkParameters>>()
|
||||
.verified()
|
||||
// We use a random modified time above to make the network parameters unqiue so that we're sure they came
|
||||
// from the server
|
||||
|
@ -64,7 +64,11 @@ class NodeRegistrationTest {
|
||||
|
||||
@Before
|
||||
fun startServer() {
|
||||
server = NetworkMapServer(1.minutes, portAllocation.nextHostAndPort(), DEV_ROOT_CA, "localhost", registrationHandler)
|
||||
server = NetworkMapServer(
|
||||
cacheTimeout = 1.minutes,
|
||||
hostAndPort = portAllocation.nextHostAndPort(),
|
||||
myHostNameValue = "localhost",
|
||||
additionalServices = registrationHandler)
|
||||
serverHostAndPort = server.start()
|
||||
}
|
||||
|
||||
|
@ -2,10 +2,15 @@ package net.corda.services.messaging
|
||||
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.*
|
||||
import net.corda.core.internal.copyTo
|
||||
import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.exists
|
||||
import net.corda.core.internal.x500Name
|
||||
import net.corda.nodeapi.RPCApi
|
||||
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_USER
|
||||
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEER_USER
|
||||
import net.corda.nodeapi.RPCApi
|
||||
import net.corda.nodeapi.internal.DEV_INTERMEDIATE_CA
|
||||
import net.corda.nodeapi.internal.DEV_ROOT_CA
|
||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
||||
import net.corda.nodeapi.internal.crypto.*
|
||||
import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration
|
||||
@ -90,20 +95,13 @@ class MQSecurityAsNodeTest : MQSecurityTest() {
|
||||
javaClass.classLoader.getResourceAsStream("certificates/cordatruststore.jks").copyTo(trustStoreFile)
|
||||
}
|
||||
|
||||
val caKeyStore = loadKeyStore(
|
||||
javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"),
|
||||
"cordacadevpass")
|
||||
|
||||
val rootCACert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA)
|
||||
val intermediateCA = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
|
||||
|
||||
val clientKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
// Set name constrain to the legal name.
|
||||
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.x500Name))), arrayOf())
|
||||
val clientCACert = X509Utilities.createCertificate(
|
||||
CertificateType.INTERMEDIATE_CA,
|
||||
intermediateCA.certificate,
|
||||
intermediateCA.keyPair,
|
||||
DEV_INTERMEDIATE_CA.certificate,
|
||||
DEV_INTERMEDIATE_CA.keyPair,
|
||||
legalName.x500Principal,
|
||||
clientKeyPair.public,
|
||||
nameConstraints = nameConstraints)
|
||||
@ -123,7 +121,7 @@ class MQSecurityAsNodeTest : MQSecurityTest() {
|
||||
X509Utilities.CORDA_CLIENT_CA,
|
||||
clientKeyPair.private,
|
||||
keyPass,
|
||||
arrayOf(clientCACert, intermediateCA.certificate, rootCACert))
|
||||
arrayOf(clientCACert, DEV_INTERMEDIATE_CA.certificate, DEV_ROOT_CA.certificate))
|
||||
clientCAKeystore.save(nodeKeystore, keyStorePassword)
|
||||
|
||||
val tlsKeystore = loadOrCreateKeyStore(sslKeystore, keyStorePassword)
|
||||
@ -131,7 +129,7 @@ class MQSecurityAsNodeTest : MQSecurityTest() {
|
||||
X509Utilities.CORDA_CLIENT_TLS,
|
||||
tlsKeyPair.private,
|
||||
keyPass,
|
||||
arrayOf(clientTLSCert, clientCACert, intermediateCA.certificate, rootCACert))
|
||||
arrayOf(clientTLSCert, clientCACert, DEV_INTERMEDIATE_CA.certificate, DEV_ROOT_CA.certificate))
|
||||
tlsKeystore.save(sslKeystore, keyStorePassword)
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.context.InvocationContext
|
||||
import net.corda.core.crypto.CompositeKey
|
||||
import net.corda.core.crypto.DigitalSignature
|
||||
import net.corda.core.crypto.SignedData
|
||||
import net.corda.core.crypto.sign
|
||||
import net.corda.core.flows.*
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
@ -67,6 +66,7 @@ import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
import net.corda.nodeapi.internal.crypto.loadKeyStore
|
||||
import net.corda.nodeapi.internal.network.NETWORK_PARAMS_FILE_NAME
|
||||
import net.corda.nodeapi.internal.network.NetworkParameters
|
||||
import net.corda.nodeapi.internal.network.verifiedNetworkMapCert
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import net.corda.nodeapi.internal.persistence.HibernateConfiguration
|
||||
@ -208,7 +208,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
||||
val (identity, identityKeyPair) = obtainIdentity(notaryConfig = null)
|
||||
val identityService = makeIdentityService(identity.certificate)
|
||||
networkMapClient = configuration.compatibilityZoneURL?.let { NetworkMapClient(it, identityService.trustRoot) }
|
||||
retrieveNetworkParameters()
|
||||
retrieveNetworkParameters(identityService.trustRoot)
|
||||
// Do all of this in a database transaction so anything that might need a connection has one.
|
||||
val (startedImpl, schedulerService) = initialiseDatabasePersistence(schemaService, identityService) { database ->
|
||||
val networkMapCache = NetworkMapCacheImpl(PersistentNetworkMapCache(database, networkParameters.notaries), identityService)
|
||||
@ -643,23 +643,19 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
||||
return PersistentKeyManagementService(identityService, keyPairs)
|
||||
}
|
||||
|
||||
private fun retrieveNetworkParameters() {
|
||||
val networkParamsFile = configuration.baseDirectory.list { paths ->
|
||||
paths.filter { it.fileName.toString() == NETWORK_PARAMS_FILE_NAME }.findFirst().orElse(null)
|
||||
}
|
||||
private fun retrieveNetworkParameters(trustRoot: X509Certificate) {
|
||||
val networkParamsFile = configuration.baseDirectory / NETWORK_PARAMS_FILE_NAME
|
||||
|
||||
networkParameters = if (networkParamsFile != null) {
|
||||
networkParamsFile.readAll().deserialize<SignedData<NetworkParameters>>().verified()
|
||||
networkParameters = if (networkParamsFile.exists()) {
|
||||
networkParamsFile.readAll().deserialize<SignedDataWithCert<NetworkParameters>>().verifiedNetworkMapCert(trustRoot)
|
||||
} else {
|
||||
log.info("No network-parameters file found. Expecting network parameters to be available from the network map.")
|
||||
val networkMapClient = checkNotNull(networkMapClient) {
|
||||
"Node hasn't been configured to connect to a network map from which to get the network parameters"
|
||||
}
|
||||
val (networkMap, _) = networkMapClient.getNetworkMap()
|
||||
val signedParams = checkNotNull(networkMapClient.getNetworkParameter(networkMap.networkParameterHash)) {
|
||||
"Failed loading network parameters from network map server"
|
||||
}
|
||||
val verifiedParams = signedParams.verified()
|
||||
val signedParams = networkMapClient.getNetworkParameters(networkMap.networkParameterHash)
|
||||
val verifiedParams = signedParams.verifiedNetworkMapCert(trustRoot)
|
||||
signedParams.serialize().open().copyTo(configuration.baseDirectory / NETWORK_PARAMS_FILE_NAME)
|
||||
verifiedParams
|
||||
}
|
||||
|
@ -52,10 +52,7 @@ fun SSLConfiguration.configureDevKeyAndTrustStores(myLegalName: CordaX500Name) {
|
||||
loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordatruststore.jks"), "trustpass").save(trustStoreFile, trustStorePassword)
|
||||
}
|
||||
if (!sslKeystore.exists() || !nodeKeystore.exists()) {
|
||||
val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
|
||||
val rootCert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA)
|
||||
val intermediateCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
|
||||
createDevKeyStores(rootCert, intermediateCa, myLegalName)
|
||||
createDevKeyStores(myLegalName)
|
||||
|
||||
// Move distributed service composite key (generated by IdentityGenerator.generateToDisk) to keystore if exists.
|
||||
val distributedServiceKeystore = certificatesDirectory / "distributedService.jks"
|
||||
|
@ -2,28 +2,26 @@ package net.corda.node.services.network
|
||||
|
||||
import com.google.common.util.concurrent.MoreExecutors
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.SignedData
|
||||
import net.corda.core.internal.SignedDataWithCert
|
||||
import net.corda.core.internal.checkOkResponse
|
||||
import net.corda.core.internal.openHttpConnection
|
||||
import net.corda.core.internal.responseAs
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.core.utilities.minutes
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.node.services.api.NetworkMapCacheInternal
|
||||
import net.corda.node.utilities.NamedThreadFactory
|
||||
import net.corda.nodeapi.internal.SignedNodeInfo
|
||||
import net.corda.nodeapi.internal.network.NetworkMap
|
||||
import net.corda.nodeapi.internal.network.NetworkParameters
|
||||
import net.corda.nodeapi.internal.network.SignedNetworkMap
|
||||
import net.corda.nodeapi.internal.SignedNodeInfo
|
||||
import net.corda.nodeapi.internal.network.verifiedNetworkMapCert
|
||||
import okhttp3.CacheControl
|
||||
import okhttp3.Headers
|
||||
import org.apache.commons.io.IOUtils
|
||||
import rx.Subscription
|
||||
import java.io.BufferedReader
|
||||
import java.io.Closeable
|
||||
import java.io.IOException
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import java.security.cert.X509Certificate
|
||||
import java.time.Duration
|
||||
@ -40,42 +38,29 @@ class NetworkMapClient(compatibilityZoneURL: URL, private val trustedRoot: X509C
|
||||
requestMethod = "POST"
|
||||
setRequestProperty("Content-Type", "application/octet-stream")
|
||||
outputStream.use { signedNodeInfo.serialize().open().copyTo(it) }
|
||||
if (responseCode != 200) {
|
||||
throw IOException("Response Code $responseCode: ${IOUtils.toString(errorStream)}")
|
||||
}
|
||||
checkOkResponse()
|
||||
}
|
||||
}
|
||||
|
||||
fun getNetworkMap(): NetworkMapResponse {
|
||||
val conn = networkMapUrl.openHttpConnection()
|
||||
val signedNetworkMap = conn.inputStream.use { it.readBytes() }.deserialize<SignedNetworkMap>()
|
||||
val networkMap = signedNetworkMap.verified(trustedRoot)
|
||||
val timeout = CacheControl.parse(Headers.of(conn.headerFields.filterKeys { it != null }.mapValues { it.value.first() })).maxAgeSeconds().seconds
|
||||
val connection = networkMapUrl.openHttpConnection()
|
||||
val signedNetworkMap = connection.responseAs<SignedDataWithCert<NetworkMap>>()
|
||||
val networkMap = signedNetworkMap.verifiedNetworkMapCert(trustedRoot)
|
||||
val timeout = CacheControl.parse(Headers.of(connection.headerFields.filterKeys { it != null }.mapValues { it.value[0] })).maxAgeSeconds().seconds
|
||||
return NetworkMapResponse(networkMap, timeout)
|
||||
}
|
||||
|
||||
fun getNodeInfo(nodeInfoHash: SecureHash): NodeInfo? {
|
||||
val conn = URL("$networkMapUrl/node-info/$nodeInfoHash").openHttpConnection()
|
||||
return if (conn.responseCode == HttpURLConnection.HTTP_NOT_FOUND) {
|
||||
null
|
||||
} else {
|
||||
val signedNodeInfo = conn.inputStream.use { it.readBytes() }.deserialize<SignedNodeInfo>()
|
||||
signedNodeInfo.verified()
|
||||
}
|
||||
fun getNodeInfo(nodeInfoHash: SecureHash): NodeInfo {
|
||||
return URL("$networkMapUrl/node-info/$nodeInfoHash").openHttpConnection().responseAs<SignedNodeInfo>().verified()
|
||||
}
|
||||
|
||||
fun getNetworkParameter(networkParameterHash: SecureHash): SignedData<NetworkParameters>? {
|
||||
val conn = URL("$networkMapUrl/network-parameter/$networkParameterHash").openHttpConnection()
|
||||
return if (conn.responseCode == HttpURLConnection.HTTP_NOT_FOUND) {
|
||||
null
|
||||
} else {
|
||||
conn.inputStream.use { it.readBytes() }.deserialize()
|
||||
}
|
||||
fun getNetworkParameters(networkParameterHash: SecureHash): SignedDataWithCert<NetworkParameters> {
|
||||
return URL("$networkMapUrl/network-parameters/$networkParameterHash").openHttpConnection().responseAs()
|
||||
}
|
||||
|
||||
fun myPublicHostname(): String {
|
||||
val conn = URL("$networkMapUrl/my-hostname").openHttpConnection()
|
||||
return conn.inputStream.bufferedReader().use(BufferedReader::readLine)
|
||||
val connection = URL("$networkMapUrl/my-hostname").openHttpConnection()
|
||||
return connection.inputStream.bufferedReader().use(BufferedReader::readLine)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,9 +24,7 @@ class HTTPNetworkRegistrationService(compatibilityZoneURL: URL) : NetworkRegistr
|
||||
@Throws(CertificateRequestException::class)
|
||||
override fun retrieveCertificates(requestId: String): Array<Certificate>? {
|
||||
// Poll server to download the signed certificate once request has been approved.
|
||||
val url = URL("$registrationURL/$requestId")
|
||||
|
||||
val conn = url.openConnection() as HttpURLConnection
|
||||
val conn = URL("$registrationURL/$requestId").openHttpConnection()
|
||||
conn.requestMethod = "GET"
|
||||
|
||||
return when (conn.responseCode) {
|
||||
|
@ -1,6 +1,5 @@
|
||||
package net.corda.node.services.network
|
||||
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.sha256
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.utilities.seconds
|
||||
@ -22,7 +21,6 @@ import org.junit.Test
|
||||
import java.io.IOException
|
||||
import java.net.URL
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
|
||||
class NetworkMapClientTest {
|
||||
@Rule
|
||||
@ -83,8 +81,8 @@ class NetworkMapClientTest {
|
||||
@Test
|
||||
fun `download NetworkParameter correctly`() {
|
||||
// The test server returns same network parameter for any hash.
|
||||
val networkParameter = networkMapClient.getNetworkParameter(SecureHash.randomSHA256())?.verified()
|
||||
assertNotNull(networkParameter)
|
||||
val parametersHash = server.networkParameters.serialize().hash
|
||||
val networkParameter = networkMapClient.getNetworkParameters(parametersHash).verified()
|
||||
assertEquals(server.networkParameters, networkParameter)
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user