mirror of
https://github.com/corda/corda.git
synced 2025-01-27 06:39:38 +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:
parent
0a56c75543
commit
bbfbb08c43
1
.gitignore
vendored
1
.gitignore
vendored
@ -37,6 +37,7 @@ lib/quasar.jar
|
|||||||
.idea/markdown-navigator
|
.idea/markdown-navigator
|
||||||
.idea/runConfigurations
|
.idea/runConfigurations
|
||||||
.idea/dictionaries
|
.idea/dictionaries
|
||||||
|
.idea/codeStyles/
|
||||||
/gradle-plugins/.idea/
|
/gradle-plugins/.idea/
|
||||||
|
|
||||||
# Include the -parameters compiler option by default in IntelliJ required for serialization.
|
# Include the -parameters compiler option by default in IntelliJ required for serialization.
|
||||||
|
@ -407,7 +407,7 @@ object Crypto {
|
|||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@Throws(InvalidKeyException::class, SignatureException::class)
|
@Throws(InvalidKeyException::class, SignatureException::class)
|
||||||
fun doSign(privateKey: PrivateKey, clearData: ByteArray) = doSign(findSignatureScheme(privateKey), privateKey, clearData)
|
fun doSign(privateKey: PrivateKey, clearData: ByteArray): ByteArray = doSign(findSignatureScheme(privateKey), privateKey, clearData)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic way to sign [ByteArray] data with a [PrivateKey] and a known schemeCodeName [String].
|
* Generic way to sign [ByteArray] data with a [PrivateKey] and a known schemeCodeName [String].
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
package net.corda.core.internal
|
||||||
|
|
||||||
|
import net.corda.core.crypto.DigitalSignature
|
||||||
|
import net.corda.core.crypto.SignedData
|
||||||
|
import net.corda.core.crypto.verify
|
||||||
|
import net.corda.core.serialization.CordaSerializable
|
||||||
|
import net.corda.core.serialization.SerializedBytes
|
||||||
|
import net.corda.core.serialization.deserialize
|
||||||
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
|
|
||||||
|
// TODO: Rename this to DigitalSignature.WithCert once we're happy for it to be public API. The methods will need documentation
|
||||||
|
// and the correct exceptions will be need to be annotated
|
||||||
|
/** A digital signature with attached certificate of the public key. */
|
||||||
|
class DigitalSignatureWithCert(val by: X509Certificate, bytes: ByteArray) : DigitalSignature(bytes) {
|
||||||
|
fun verify(content: ByteArray): Boolean = by.publicKey.verify(content, this)
|
||||||
|
fun verify(content: OpaqueBytes): Boolean = verify(content.bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Similar to [SignedData] but instead of just attaching the public key, the certificate for the key is attached instead. */
|
||||||
|
@CordaSerializable
|
||||||
|
class SignedDataWithCert<T : Any>(val raw: SerializedBytes<T>, val sig: DigitalSignatureWithCert) {
|
||||||
|
fun verified(): T {
|
||||||
|
sig.verify(raw)
|
||||||
|
return uncheckedCast(raw.deserialize<Any>())
|
||||||
|
}
|
||||||
|
}
|
@ -3,11 +3,14 @@
|
|||||||
package net.corda.core.internal
|
package net.corda.core.internal
|
||||||
|
|
||||||
import net.corda.core.cordapp.CordappProvider
|
import net.corda.core.cordapp.CordappProvider
|
||||||
|
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.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.node.ServicesForResolution
|
import net.corda.core.node.ServicesForResolution
|
||||||
import net.corda.core.serialization.SerializationContext
|
import net.corda.core.serialization.SerializationContext
|
||||||
|
import net.corda.core.serialization.deserialize
|
||||||
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.transactions.WireTransaction
|
import net.corda.core.transactions.WireTransaction
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
@ -27,6 +30,8 @@ import java.nio.charset.Charset
|
|||||||
import java.nio.charset.StandardCharsets.UTF_8
|
import java.nio.charset.StandardCharsets.UTF_8
|
||||||
import java.nio.file.*
|
import java.nio.file.*
|
||||||
import java.nio.file.attribute.FileAttribute
|
import java.nio.file.attribute.FileAttribute
|
||||||
|
import java.security.PrivateKey
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.temporal.Temporal
|
import java.time.temporal.Temporal
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -308,6 +313,19 @@ fun TransactionBuilder.toLedgerTransaction(services: ServicesForResolution, seri
|
|||||||
val KClass<*>.packageName: String get() = java.`package`.name
|
val KClass<*>.packageName: String get() = java.`package`.name
|
||||||
|
|
||||||
fun URL.openHttpConnection(): HttpURLConnection = openConnection() as HttpURLConnection
|
fun URL.openHttpConnection(): HttpURLConnection = openConnection() as HttpURLConnection
|
||||||
|
|
||||||
|
fun HttpURLConnection.checkOkResponse() {
|
||||||
|
if (responseCode != 200) {
|
||||||
|
val message = errorStream.use { it.reader().readText() }
|
||||||
|
throw IOException("Response Code $responseCode: $message")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : Any> HttpURLConnection.responseAs(): T {
|
||||||
|
checkOkResponse()
|
||||||
|
return inputStream.use { it.readBytes() }.deserialize()
|
||||||
|
}
|
||||||
|
|
||||||
/** Analogous to [Thread.join]. */
|
/** Analogous to [Thread.join]. */
|
||||||
fun ExecutorService.join() {
|
fun ExecutorService.join() {
|
||||||
shutdown() // Do not change to shutdownNow, tests use this method to assert the executor has no more tasks.
|
shutdown() // Do not change to shutdownNow, tests use this method to assert the executor has no more tasks.
|
||||||
@ -336,3 +354,9 @@ val CordaX500Name.x500Name: X500Name
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
val CordaX500Name.Companion.unspecifiedCountry
|
val CordaX500Name.Companion.unspecifiedCountry
|
||||||
get() = "ZZ"
|
get() = "ZZ"
|
||||||
|
|
||||||
|
fun <T : Any> T.signWithCert(privateKey: PrivateKey, certificate: X509Certificate): SignedDataWithCert<T> {
|
||||||
|
val serialised = serialize()
|
||||||
|
val signature = Crypto.doSign(privateKey, serialised.bytes)
|
||||||
|
return SignedDataWithCert(serialised, DigitalSignatureWithCert(certificate, signature))
|
||||||
|
}
|
||||||
|
@ -1,84 +1,78 @@
|
|||||||
Network Map
|
Network Map
|
||||||
===========
|
===========
|
||||||
|
|
||||||
The network map stores a collection of ``NodeInfo`` objects, each representing another node with which the node can interact.
|
The network map is a collection of signed ``NodeInfo`` objects (signed by the node it represents and thus tamper-proof)
|
||||||
There are two sources from which a Corda node can retrieve ``NodeInfo`` objects:
|
forming the set of reachable nodes in a compatbility zone. A node can receive these objects from two sources:
|
||||||
|
|
||||||
1. the REST protocol with the network map service, which also provides a publishing API,
|
1. The HTTP network map service if the ``compatibilityZoneURL`` config key is specified.
|
||||||
|
2. The ``additional-node-infos`` directory within the node's directory.
|
||||||
|
|
||||||
2. the ``additional-node-infos`` directory.
|
HTTP network map service
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
If the node is configured with the ``compatibilityZoneURL`` config then it first uploads its own signed ``NodeInfo``
|
||||||
|
to the server (and each time it changes on startup) and then proceeds to download the entire network map. The network map
|
||||||
|
consists of a list of ``NodeInfo`` hashes. The node periodically polls for the network map (based on the HTTP cache expiry
|
||||||
|
header) and any new hash entries are downloaded and cached. Entries which no longer exist are deleted from the node's cache.
|
||||||
|
|
||||||
Protocol Design
|
The set of REST end-points for the network map service are as follows.
|
||||||
---------------
|
|
||||||
The node info publishing protocol:
|
|
||||||
|
|
||||||
* Create a ``NodeInfo`` object, and sign it to create a ``SignedNodeInfo`` object.
|
+----------------+-----------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
| Request method | Path | Description |
|
||||||
* Serialise the signed data and POST the data to the network map server.
|
+================+=========================================+==============================================================================================================================================+
|
||||||
|
| POST | /network-map/publish | For the node to upload its signed ``NodeInfo`` object to the network map. |
|
||||||
* The network map server validates the signature and acknowledges the registration with a HTTP 200 response, it will return HTTP 400 "Bad Request" if the data failed validation or if the public key wasn't registered with the network.
|
+----------------+-----------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
| GET | /network-map | Retrieve the current signed network map object. The entire object is signed with the network map certificate which is also attached. |
|
||||||
* The network map server will sign and distribute the new network map periodically.
|
+----------------+-----------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
| GET | /network-map/node-info/{hash} | Retrieve a signed ``NodeInfo`` as specified in the network map object. |
|
||||||
Node side network map update protocol:
|
+----------------+-----------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
| GET | /network-map/network-parameters/{hash} | Retrieve the signed network parameters (see below). The entire object is signed with the network map certificate which is also attached. |
|
||||||
* The Corda node will query the network map service periodically according to the ``Expires`` attribute in the HTTP header.
|
+----------------+-----------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
|
||||||
* The network map service returns a signed ``NetworkMap`` object which looks as follows:
|
|
||||||
|
|
||||||
.. container:: codeset
|
|
||||||
|
|
||||||
.. sourcecode:: kotlin
|
|
||||||
|
|
||||||
data class NetworkMap {
|
|
||||||
val nodeInfoHashes: List<SecureHash>,
|
|
||||||
val networkParametersHash: SecureHash
|
|
||||||
}
|
|
||||||
|
|
||||||
The object contains list of node info hashes and hash of the network parameters data structure (without the signatures).
|
|
||||||
|
|
||||||
* The node updates its local copy of ``NodeInfos`` if it is different from the newly downloaded ``NetworkMap``.
|
|
||||||
|
|
||||||
Network Map service REST API:
|
|
||||||
|
|
||||||
+----------------+-----------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+
|
|
||||||
| Request method | Path | Description |
|
|
||||||
+================+===================================+========================================================================================================================================================+
|
|
||||||
| POST | /network-map/publish | Publish new ``NodeInfo`` to the network map service, the legal identity in ``NodeInfo`` must match with the identity registered with the doorman. |
|
|
||||||
+----------------+-----------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+
|
|
||||||
| GET | /network-map | Retrieve ``NetworkMap`` from the server, the ``NetworkMap`` object contains list of node info hashes and ``NetworkParameters`` hash. |
|
|
||||||
+----------------+-----------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+
|
|
||||||
| GET | /network-map/node-info/{hash} | Retrieve ``NodeInfo`` object with the same hash. |
|
|
||||||
+----------------+-----------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+
|
|
||||||
| GET | /network-map/parameters/{hash} | Retrieve ``NetworkParameters`` object with the same hash. |
|
|
||||||
+----------------+-----------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+
|
|
||||||
|
|
||||||
TODO: Access control of the network map will be added in the future.
|
|
||||||
|
|
||||||
|
|
||||||
The ``additional-node-infos`` directory
|
The ``additional-node-infos`` directory
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
Each Corda node reads, and continuously polls, the files contained in a directory named ``additional-node-infos`` inside the node base directory.
|
|
||||||
|
|
||||||
Nodes expect to find a serialized ``SignedNodeInfo`` object, the same object which is sent to network map server.
|
Alongside the HTTP network map service, or as a replacement if the node isn't connected to one, the node polls the
|
||||||
|
contents of the ``additional-node-infos`` directory located in its base directory. Each file is expected to be the same
|
||||||
Whenever a node starts it writes on disk a file containing its own ``NodeInfo``, this file is called ``nodeInfo-XXX`` where ``XXX`` is a long string.
|
signed ``NodeInfo`` object that the network map service vends. These are automtically added to the node's cache and can
|
||||||
|
be used to supplement or replace the HTTP network map. If the same node is advertised through both mechanisms then the
|
||||||
Hence if an operator wants node A to see node B they can pick B's ``NodeInfo`` file from B base directory and drop it into A's ``additional-node-infos`` directory.
|
latest one is taken.
|
||||||
|
|
||||||
|
On startup the node generates its own signed node info file, filename of the format ``nodeInfo-${hash}``. To create a simple
|
||||||
|
network without the HTTP network map service then simply place this file in the ``additional-node-infos`` directory
|
||||||
|
of every node that's part of this network.
|
||||||
|
|
||||||
Network parameters
|
Network parameters
|
||||||
------------------
|
------------------
|
||||||
Network parameters are constants that every node participating in the network needs to agree on and use for interop purposes.
|
|
||||||
The structure is distributed as a file containing serialized ``SignedData<NetworkParameters>`` with a signature from
|
|
||||||
a sub-key of the compatibility zone root cert. Network map advertises the hash of currently used network parameters.
|
|
||||||
The ``NetworkParameters`` structure contains:
|
|
||||||
* ``minimumPlatformVersion`` - minimum version of Corda platform that is required for nodes in the network.
|
|
||||||
* ``notaries`` - list of well known and trusted notary identities with information on validation type.
|
|
||||||
* ``maxMessageSize`` - maximum P2P message size sent over the wire in bytes.
|
|
||||||
* ``maxTransactionSize`` - maximum permitted transaction size in bytes.
|
|
||||||
* ``modifiedTime`` - the time the network parameters were created by the CZ operator.
|
|
||||||
* ``epoch`` - version number of the network parameters. Starting from 1, this will always increment on each new set of parameters.
|
|
||||||
|
|
||||||
The set of parameters is still under development and we may find the need to add additional fields.
|
Network parameters are a set of values that every node participating in the network needs to agree on and use to
|
||||||
|
correctly interoperate with each other. If the node is using the HTTP network map service then on first startup it will
|
||||||
|
download the signed network parameters, cache it in a ``network-parameters`` file and apply them on the node.
|
||||||
|
|
||||||
|
.. warning:: If the ``network-parameters`` file is changed and no longer matches what the network map service is advertising
|
||||||
|
then the node will automatically shutdown. Resolution to this is to delete the incorrect file and restart the node so
|
||||||
|
that the parameters can be downloaded again.
|
||||||
|
|
||||||
|
.. note:: A future release will support the notion of network parameters changes.
|
||||||
|
|
||||||
|
If the node isn't using a HTTP network map service then it's expected the signed file is provided by some other means.
|
||||||
|
For such a scenario there is the network bootstrapper tool which in addition to generating the network parameters file
|
||||||
|
also distributes the node info files to the node directories. More information can be found in :doc:`setting-up-a-corda-network`.
|
||||||
|
|
||||||
|
The current set of network parameters:
|
||||||
|
|
||||||
|
:minimumPlatformVersion: The minimum platform version that the nodes must be running. Any node which is below this will
|
||||||
|
not start.
|
||||||
|
:notaries: List of identity and validation type (either validating or non-validating) of the notaries which are permitted
|
||||||
|
in the compatibility zone.
|
||||||
|
:maxMessageSize: Maximum allowed P2P message size sent over the wire in bytes. Any message larger than this will be
|
||||||
|
split up.
|
||||||
|
:maxTransactionSize: Maximum permitted transaction size in bytes.
|
||||||
|
:modifiedTime: The time when the network parameters were last modified by the compatibility zone operator.
|
||||||
|
:epoch: Version number of the network parameters. Starting from 1, this will always increment whenever any of the
|
||||||
|
parameters change.
|
||||||
|
|
||||||
|
.. note:: ``maxTransactionSize`` is currently not enforced in the node, but will be in a later release.
|
||||||
|
|
||||||
|
More parameters may be added in future releases.
|
||||||
|
@ -34,13 +34,8 @@ object DevIdentityGenerator {
|
|||||||
override val trustStorePassword get() = throw NotImplementedError("Not expected to be called")
|
override val trustStorePassword get() = throw NotImplementedError("Not expected to be called")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO The passwords for the dev key stores are spread everywhere and should be constants in a single location
|
|
||||||
val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
|
|
||||||
val intermediateCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
|
|
||||||
val rootCert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA)
|
|
||||||
|
|
||||||
nodeSslConfig.certificatesDirectory.createDirectories()
|
nodeSslConfig.certificatesDirectory.createDirectories()
|
||||||
nodeSslConfig.createDevKeyStores(rootCert, intermediateCa, legalName)
|
nodeSslConfig.createDevKeyStores(legalName)
|
||||||
|
|
||||||
val keyStoreWrapper = KeyStoreWrapper(nodeSslConfig.nodeKeystore, nodeSslConfig.keyStorePassword)
|
val keyStoreWrapper = KeyStoreWrapper(nodeSslConfig.nodeKeystore, nodeSslConfig.keyStorePassword)
|
||||||
val identity = keyStoreWrapper.storeLegalIdentity(legalName, "$NODE_IDENTITY_ALIAS_PREFIX-private-key", Crypto.generateKeyPair())
|
val identity = keyStoreWrapper.storeLegalIdentity(legalName, "$NODE_IDENTITY_ALIAS_PREFIX-private-key", Crypto.generateKeyPair())
|
||||||
@ -54,16 +49,12 @@ object DevIdentityGenerator {
|
|||||||
val keyPairs = (1..dirs.size).map { generateKeyPair() }
|
val keyPairs = (1..dirs.size).map { generateKeyPair() }
|
||||||
val compositeKey = CompositeKey.Builder().addKeys(keyPairs.map { it.public }).build(threshold)
|
val compositeKey = CompositeKey.Builder().addKeys(keyPairs.map { it.public }).build(threshold)
|
||||||
|
|
||||||
val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
|
|
||||||
val intermediateCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
|
|
||||||
val rootCert = caKeyStore.getCertificate(X509Utilities.CORDA_ROOT_CA)
|
|
||||||
|
|
||||||
keyPairs.zip(dirs) { keyPair, nodeDir ->
|
keyPairs.zip(dirs) { keyPair, nodeDir ->
|
||||||
val (serviceKeyCert, compositeKeyCert) = listOf(keyPair.public, compositeKey).map { publicKey ->
|
val (serviceKeyCert, compositeKeyCert) = listOf(keyPair.public, compositeKey).map { publicKey ->
|
||||||
X509Utilities.createCertificate(
|
X509Utilities.createCertificate(
|
||||||
CertificateType.SERVICE_IDENTITY,
|
CertificateType.SERVICE_IDENTITY,
|
||||||
intermediateCa.certificate,
|
DEV_INTERMEDIATE_CA.certificate,
|
||||||
intermediateCa.keyPair,
|
DEV_INTERMEDIATE_CA.keyPair,
|
||||||
notaryName.x500Principal,
|
notaryName.x500Principal,
|
||||||
publicKey)
|
publicKey)
|
||||||
}
|
}
|
||||||
@ -74,7 +65,7 @@ object DevIdentityGenerator {
|
|||||||
"$DISTRIBUTED_NOTARY_ALIAS_PREFIX-private-key",
|
"$DISTRIBUTED_NOTARY_ALIAS_PREFIX-private-key",
|
||||||
keyPair.private,
|
keyPair.private,
|
||||||
"cordacadevkeypass".toCharArray(),
|
"cordacadevkeypass".toCharArray(),
|
||||||
arrayOf(serviceKeyCert, intermediateCa.certificate, rootCert))
|
arrayOf(serviceKeyCert, DEV_INTERMEDIATE_CA.certificate, DEV_ROOT_CA.certificate))
|
||||||
keystore.save(distServKeyStoreFile, "cordacadevpass")
|
keystore.save(distServKeyStoreFile, "cordacadevpass")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,12 +9,17 @@ import org.bouncycastle.asn1.x509.GeneralName
|
|||||||
import org.bouncycastle.asn1.x509.GeneralSubtree
|
import org.bouncycastle.asn1.x509.GeneralSubtree
|
||||||
import org.bouncycastle.asn1.x509.NameConstraints
|
import org.bouncycastle.asn1.x509.NameConstraints
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
|
|
||||||
|
// TODO Merge this file and DevIdentityGenerator
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the node and SSL key stores needed by a node. The node key store will be populated with a node CA cert (using
|
* Create the node and SSL key stores needed by a node. The node key store will be populated with a node CA cert (using
|
||||||
* the given legal name), and the SSL key store will store the TLS cert which is a sub-cert of the node CA.
|
* the given legal name), and the SSL key store will store the TLS cert which is a sub-cert of the node CA.
|
||||||
*/
|
*/
|
||||||
fun SSLConfiguration.createDevKeyStores(rootCert: X509Certificate, intermediateCa: CertificateAndKeyPair, legalName: CordaX500Name) {
|
fun SSLConfiguration.createDevKeyStores(legalName: CordaX500Name,
|
||||||
|
rootCert: X509Certificate = DEV_ROOT_CA.certificate,
|
||||||
|
intermediateCa: CertificateAndKeyPair = DEV_INTERMEDIATE_CA) {
|
||||||
val (nodeCaCert, nodeCaKeyPair) = createDevNodeCa(intermediateCa, legalName)
|
val (nodeCaCert, nodeCaKeyPair) = createDevNodeCa(intermediateCa, legalName)
|
||||||
|
|
||||||
loadOrCreateKeyStore(nodeKeystore, keyStorePassword).apply {
|
loadOrCreateKeyStore(nodeKeystore, keyStorePassword).apply {
|
||||||
@ -39,6 +44,17 @@ fun SSLConfiguration.createDevKeyStores(rootCert: X509Certificate, intermediateC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun createDevNetworkMapCa(rootCa: CertificateAndKeyPair = DEV_ROOT_CA): CertificateAndKeyPair {
|
||||||
|
val keyPair = Crypto.generateKeyPair()
|
||||||
|
val cert = X509Utilities.createCertificate(
|
||||||
|
CertificateType.NETWORK_MAP,
|
||||||
|
rootCa.certificate,
|
||||||
|
rootCa.keyPair,
|
||||||
|
X500Principal("CN=Network Map,O=R3 Ltd,L=London,C=GB"),
|
||||||
|
keyPair.public)
|
||||||
|
return CertificateAndKeyPair(cert, keyPair)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a dev node CA cert, as a sub-cert of the given [intermediateCa], and matching key pair using the given
|
* Create a dev node CA cert, as a sub-cert of the given [intermediateCa], and matching key pair using the given
|
||||||
* [CordaX500Name] as the cert subject.
|
* [CordaX500Name] as the cert subject.
|
||||||
@ -55,3 +71,16 @@ fun createDevNodeCa(intermediateCa: CertificateAndKeyPair, legalName: CordaX500N
|
|||||||
nameConstraints = nameConstraints)
|
nameConstraints = nameConstraints)
|
||||||
return CertificateAndKeyPair(cert, keyPair)
|
return CertificateAndKeyPair(cert, keyPair)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val DEV_INTERMEDIATE_CA: CertificateAndKeyPair get() = DevCaHelper.loadDevCa(X509Utilities.CORDA_INTERMEDIATE_CA)
|
||||||
|
|
||||||
|
val DEV_ROOT_CA: CertificateAndKeyPair get() = DevCaHelper.loadDevCa(X509Utilities.CORDA_ROOT_CA)
|
||||||
|
|
||||||
|
// We need a class so that we can get hold of the class loader
|
||||||
|
internal object DevCaHelper {
|
||||||
|
fun loadDevCa(alias: String): CertificateAndKeyPair {
|
||||||
|
// TODO: Should be identity scheme
|
||||||
|
val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
|
||||||
|
return caKeyStore.getCertificateAndKeyPair(alias, "cordacadevkeypass")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -13,12 +13,14 @@ import java.security.SignatureException
|
|||||||
* A signed [NodeInfo] object containing a signature for each identity. The list of signatures is expected
|
* A signed [NodeInfo] object containing a signature for each identity. The list of signatures is expected
|
||||||
* to be in the same order as the identities.
|
* to be in the same order as the identities.
|
||||||
*/
|
*/
|
||||||
|
// TODO Move this to net.corda.nodeapi.internal.network
|
||||||
// TODO Add signatures for composite keys. The current thinking is to make sure there is a signature for each leaf key
|
// TODO Add signatures for composite keys. The current thinking is to make sure there is a signature for each leaf key
|
||||||
// that the node owns. This check can only be done by the network map server as it can check with the doorman if a node
|
// that the node owns. This check can only be done by the network map server as it can check with the doorman if a node
|
||||||
// is part of a composite identity. This of course further requires the doorman being able to issue CSRs for composite
|
// is part of a composite identity. This of course further requires the doorman being able to issue CSRs for composite
|
||||||
// public keys.
|
// public keys.
|
||||||
@CordaSerializable
|
@CordaSerializable
|
||||||
class SignedNodeInfo(val raw: SerializedBytes<NodeInfo>, val signatures: List<DigitalSignature>) {
|
class SignedNodeInfo(val raw: SerializedBytes<NodeInfo>, val signatures: List<DigitalSignature>) {
|
||||||
|
// TODO Add root cert param (or TrustAnchor) to make sure all the identities belong to the same root
|
||||||
fun verified(): NodeInfo {
|
fun verified(): NodeInfo {
|
||||||
val nodeInfo = raw.deserialize()
|
val nodeInfo = raw.deserialize()
|
||||||
val identities = nodeInfo.legalIdentities.filterNot { it.owningKey is CompositeKey }
|
val identities = nodeInfo.legalIdentities.filterNot { it.owningKey is CompositeKey }
|
||||||
|
@ -1,16 +1,12 @@
|
|||||||
package net.corda.nodeapi.internal.network
|
package net.corda.nodeapi.internal.network
|
||||||
|
|
||||||
import net.corda.core.crypto.DigitalSignature
|
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.verify
|
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
|
import net.corda.core.internal.CertRole
|
||||||
|
import net.corda.core.internal.SignedDataWithCert
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import net.corda.core.serialization.SerializedBytes
|
|
||||||
import net.corda.core.serialization.deserialize
|
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
import java.security.SignatureException
|
|
||||||
import java.security.cert.CertPathValidatorException
|
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
@ -55,28 +51,8 @@ data class NetworkParameters(
|
|||||||
@CordaSerializable
|
@CordaSerializable
|
||||||
data class NotaryInfo(val identity: Party, val validating: Boolean)
|
data class NotaryInfo(val identity: Party, val validating: Boolean)
|
||||||
|
|
||||||
/**
|
fun <T : Any> SignedDataWithCert<T>.verifiedNetworkMapCert(rootCert: X509Certificate): T {
|
||||||
* A serialized [NetworkMap] and its signature and certificate. Enforces signature validity in order to deserialize the data
|
require(CertRole.extract(sig.by) == CertRole.NETWORK_MAP) { "Incorrect cert role: ${CertRole.extract(sig.by)}" }
|
||||||
* contained within.
|
X509Utilities.validateCertificateChain(rootCert, sig.by, rootCert)
|
||||||
*/
|
return verified()
|
||||||
@CordaSerializable
|
}
|
||||||
class SignedNetworkMap(val raw: SerializedBytes<NetworkMap>, val signature: DigitalSignatureWithCert) {
|
|
||||||
/**
|
|
||||||
* Return the deserialized NetworkMap if the signature and certificate can be verified.
|
|
||||||
*
|
|
||||||
* @throws CertPathValidatorException if the certificate path is invalid.
|
|
||||||
* @throws SignatureException if the signature is invalid.
|
|
||||||
*/
|
|
||||||
@Throws(SignatureException::class, CertPathValidatorException::class)
|
|
||||||
fun verified(trustedRoot: X509Certificate): NetworkMap {
|
|
||||||
signature.by.publicKey.verify(raw.bytes, signature)
|
|
||||||
// Assume network map cert is under the default trust root.
|
|
||||||
X509Utilities.validateCertificateChain(trustedRoot, signature.by, trustedRoot)
|
|
||||||
return raw.deserialize()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: This class should reside in the [DigitalSignature] class.
|
|
||||||
// TODO: Removing the val from signatureBytes causes serialisation issues
|
|
||||||
/** A digital signature that identifies who the public key is owned by, and the certificate which provides prove of the identity */
|
|
||||||
class DigitalSignatureWithCert(val by: X509Certificate, val signatureBytes: ByteArray) : DigitalSignature(signatureBytes)
|
|
@ -1,35 +1,32 @@
|
|||||||
package net.corda.nodeapi.internal.network
|
package net.corda.nodeapi.internal.network
|
||||||
|
|
||||||
import net.corda.core.crypto.Crypto
|
|
||||||
import net.corda.core.crypto.SignedData
|
|
||||||
import net.corda.core.crypto.sign
|
|
||||||
import net.corda.core.internal.copyTo
|
import net.corda.core.internal.copyTo
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
|
import net.corda.core.internal.signWithCert
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
import net.corda.nodeapi.internal.createDevNetworkMapCa
|
||||||
|
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||||
import java.nio.file.FileAlreadyExistsException
|
import java.nio.file.FileAlreadyExistsException
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.StandardCopyOption
|
import java.nio.file.StandardCopyOption
|
||||||
import java.security.KeyPair
|
|
||||||
|
|
||||||
class NetworkParametersCopier(
|
class NetworkParametersCopier(
|
||||||
networkParameters: NetworkParameters,
|
networkParameters: NetworkParameters,
|
||||||
signingKeyPair: KeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME),
|
networkMapCa: CertificateAndKeyPair = createDevNetworkMapCa(),
|
||||||
overwriteFile: Boolean = false
|
overwriteFile: Boolean = false
|
||||||
) {
|
) {
|
||||||
private val copyOptions = if (overwriteFile) arrayOf(StandardCopyOption.REPLACE_EXISTING) else emptyArray()
|
private val copyOptions = if (overwriteFile) arrayOf(StandardCopyOption.REPLACE_EXISTING) else emptyArray()
|
||||||
private val serializedNetworkParameters = networkParameters.let {
|
private val serialisedSignedNetParams = networkParameters.signWithCert(
|
||||||
val serialize = it.serialize()
|
networkMapCa.keyPair.private,
|
||||||
val signature = signingKeyPair.sign(serialize)
|
networkMapCa.certificate
|
||||||
SignedData(serialize, signature).serialize()
|
).serialize()
|
||||||
}
|
|
||||||
|
|
||||||
fun install(nodeDir: Path) {
|
fun install(nodeDir: Path) {
|
||||||
try {
|
try {
|
||||||
serializedNetworkParameters.open().copyTo(nodeDir / NETWORK_PARAMS_FILE_NAME, *copyOptions)
|
serialisedSignedNetParams.open().copyTo(nodeDir / NETWORK_PARAMS_FILE_NAME, *copyOptions)
|
||||||
} catch (e: FileAlreadyExistsException) {
|
} catch (e: FileAlreadyExistsException) {
|
||||||
// This is only thrown if the file already exists and we didn't specify to overwrite it. In that case we
|
// This is only thrown if the file already exists and we didn't specify to overwrite it. In that case we
|
||||||
// ignore this exception as we're happy with the existing file.
|
// ignore this exception as we're happy with the existing file.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -168,7 +168,7 @@ class X509UtilitiesTest {
|
|||||||
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
|
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
|
||||||
|
|
||||||
// Generate server cert and private key and populate another keystore suitable for SSL
|
// Generate server cert and private key and populate another keystore suitable for SSL
|
||||||
sslConfig.createDevKeyStores(rootCa.certificate, intermediateCa, MEGA_CORP.name)
|
sslConfig.createDevKeyStores(MEGA_CORP.name, rootCa.certificate, intermediateCa)
|
||||||
|
|
||||||
// Load back server certificate
|
// Load back server certificate
|
||||||
val serverKeyStore = loadKeyStore(sslConfig.nodeKeystore, sslConfig.keyStorePassword)
|
val serverKeyStore = loadKeyStore(sslConfig.nodeKeystore, sslConfig.keyStorePassword)
|
||||||
@ -203,7 +203,7 @@ class X509UtilitiesTest {
|
|||||||
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
|
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
|
||||||
|
|
||||||
// Generate server cert and private key and populate another keystore suitable for SSL
|
// Generate server cert and private key and populate another keystore suitable for SSL
|
||||||
sslConfig.createDevKeyStores(rootCa.certificate, intermediateCa, MEGA_CORP.name)
|
sslConfig.createDevKeyStores(MEGA_CORP.name, rootCa.certificate, intermediateCa)
|
||||||
sslConfig.createTrustStore(rootCa.certificate)
|
sslConfig.createTrustStore(rootCa.certificate)
|
||||||
|
|
||||||
val keyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword)
|
val keyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword)
|
||||||
|
@ -1,13 +1,9 @@
|
|||||||
package net.corda.node.services.network
|
package net.corda.node.services.network
|
||||||
|
|
||||||
import net.corda.cordform.CordformNode
|
import net.corda.cordform.CordformNode
|
||||||
import net.corda.core.crypto.SignedData
|
|
||||||
import net.corda.core.crypto.random63BitValue
|
import net.corda.core.crypto.random63BitValue
|
||||||
|
import net.corda.core.internal.*
|
||||||
import net.corda.core.internal.concurrent.transpose
|
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.node.NodeInfo
|
||||||
import net.corda.core.serialization.deserialize
|
import net.corda.core.serialization.deserialize
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
@ -69,7 +65,7 @@ class NetworkMapTest {
|
|||||||
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||||
val networkParameters = (alice.configuration.baseDirectory / NETWORK_PARAMS_FILE_NAME)
|
val networkParameters = (alice.configuration.baseDirectory / NETWORK_PARAMS_FILE_NAME)
|
||||||
.readAll()
|
.readAll()
|
||||||
.deserialize<SignedData<NetworkParameters>>()
|
.deserialize<SignedDataWithCert<NetworkParameters>>()
|
||||||
.verified()
|
.verified()
|
||||||
// We use a random modified time above to make the network parameters unqiue so that we're sure they came
|
// We use a random modified time above to make the network parameters unqiue so that we're sure they came
|
||||||
// from the server
|
// from the server
|
||||||
|
@ -64,7 +64,11 @@ class NodeRegistrationTest {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun startServer() {
|
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()
|
serverHostAndPort = server.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,10 +2,15 @@ package net.corda.services.messaging
|
|||||||
|
|
||||||
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.*
|
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.NODE_USER
|
||||||
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEER_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.config.SSLConfiguration
|
||||||
import net.corda.nodeapi.internal.crypto.*
|
import net.corda.nodeapi.internal.crypto.*
|
||||||
import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration
|
import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration
|
||||||
@ -90,20 +95,13 @@ class MQSecurityAsNodeTest : MQSecurityTest() {
|
|||||||
javaClass.classLoader.getResourceAsStream("certificates/cordatruststore.jks").copyTo(trustStoreFile)
|
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)
|
val clientKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
// Set name constrain to the legal name.
|
// Set name constrain to the legal name.
|
||||||
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.x500Name))), arrayOf())
|
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.x500Name))), arrayOf())
|
||||||
val clientCACert = X509Utilities.createCertificate(
|
val clientCACert = X509Utilities.createCertificate(
|
||||||
CertificateType.INTERMEDIATE_CA,
|
CertificateType.INTERMEDIATE_CA,
|
||||||
intermediateCA.certificate,
|
DEV_INTERMEDIATE_CA.certificate,
|
||||||
intermediateCA.keyPair,
|
DEV_INTERMEDIATE_CA.keyPair,
|
||||||
legalName.x500Principal,
|
legalName.x500Principal,
|
||||||
clientKeyPair.public,
|
clientKeyPair.public,
|
||||||
nameConstraints = nameConstraints)
|
nameConstraints = nameConstraints)
|
||||||
@ -123,7 +121,7 @@ class MQSecurityAsNodeTest : MQSecurityTest() {
|
|||||||
X509Utilities.CORDA_CLIENT_CA,
|
X509Utilities.CORDA_CLIENT_CA,
|
||||||
clientKeyPair.private,
|
clientKeyPair.private,
|
||||||
keyPass,
|
keyPass,
|
||||||
arrayOf(clientCACert, intermediateCA.certificate, rootCACert))
|
arrayOf(clientCACert, DEV_INTERMEDIATE_CA.certificate, DEV_ROOT_CA.certificate))
|
||||||
clientCAKeystore.save(nodeKeystore, keyStorePassword)
|
clientCAKeystore.save(nodeKeystore, keyStorePassword)
|
||||||
|
|
||||||
val tlsKeystore = loadOrCreateKeyStore(sslKeystore, keyStorePassword)
|
val tlsKeystore = loadOrCreateKeyStore(sslKeystore, keyStorePassword)
|
||||||
@ -131,7 +129,7 @@ class MQSecurityAsNodeTest : MQSecurityTest() {
|
|||||||
X509Utilities.CORDA_CLIENT_TLS,
|
X509Utilities.CORDA_CLIENT_TLS,
|
||||||
tlsKeyPair.private,
|
tlsKeyPair.private,
|
||||||
keyPass,
|
keyPass,
|
||||||
arrayOf(clientTLSCert, clientCACert, intermediateCA.certificate, rootCACert))
|
arrayOf(clientTLSCert, clientCACert, DEV_INTERMEDIATE_CA.certificate, DEV_ROOT_CA.certificate))
|
||||||
tlsKeystore.save(sslKeystore, keyStorePassword)
|
tlsKeystore.save(sslKeystore, keyStorePassword)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,6 @@ import net.corda.core.concurrent.CordaFuture
|
|||||||
import net.corda.core.context.InvocationContext
|
import net.corda.core.context.InvocationContext
|
||||||
import net.corda.core.crypto.CompositeKey
|
import net.corda.core.crypto.CompositeKey
|
||||||
import net.corda.core.crypto.DigitalSignature
|
import net.corda.core.crypto.DigitalSignature
|
||||||
import net.corda.core.crypto.SignedData
|
|
||||||
import net.corda.core.crypto.sign
|
import net.corda.core.crypto.sign
|
||||||
import net.corda.core.flows.*
|
import net.corda.core.flows.*
|
||||||
import net.corda.core.identity.CordaX500Name
|
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.crypto.loadKeyStore
|
||||||
import net.corda.nodeapi.internal.network.NETWORK_PARAMS_FILE_NAME
|
import net.corda.nodeapi.internal.network.NETWORK_PARAMS_FILE_NAME
|
||||||
import net.corda.nodeapi.internal.network.NetworkParameters
|
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.CordaPersistence
|
||||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||||
import net.corda.nodeapi.internal.persistence.HibernateConfiguration
|
import net.corda.nodeapi.internal.persistence.HibernateConfiguration
|
||||||
@ -208,7 +208,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
|||||||
val (identity, identityKeyPair) = obtainIdentity(notaryConfig = null)
|
val (identity, identityKeyPair) = obtainIdentity(notaryConfig = null)
|
||||||
val identityService = makeIdentityService(identity.certificate)
|
val identityService = makeIdentityService(identity.certificate)
|
||||||
networkMapClient = configuration.compatibilityZoneURL?.let { NetworkMapClient(it, identityService.trustRoot) }
|
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.
|
// 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 (startedImpl, schedulerService) = initialiseDatabasePersistence(schemaService, identityService) { database ->
|
||||||
val networkMapCache = NetworkMapCacheImpl(PersistentNetworkMapCache(database, networkParameters.notaries), identityService)
|
val networkMapCache = NetworkMapCacheImpl(PersistentNetworkMapCache(database, networkParameters.notaries), identityService)
|
||||||
@ -643,23 +643,19 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
|||||||
return PersistentKeyManagementService(identityService, keyPairs)
|
return PersistentKeyManagementService(identityService, keyPairs)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun retrieveNetworkParameters() {
|
private fun retrieveNetworkParameters(trustRoot: X509Certificate) {
|
||||||
val networkParamsFile = configuration.baseDirectory.list { paths ->
|
val networkParamsFile = configuration.baseDirectory / NETWORK_PARAMS_FILE_NAME
|
||||||
paths.filter { it.fileName.toString() == NETWORK_PARAMS_FILE_NAME }.findFirst().orElse(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
networkParameters = if (networkParamsFile != null) {
|
networkParameters = if (networkParamsFile.exists()) {
|
||||||
networkParamsFile.readAll().deserialize<SignedData<NetworkParameters>>().verified()
|
networkParamsFile.readAll().deserialize<SignedDataWithCert<NetworkParameters>>().verifiedNetworkMapCert(trustRoot)
|
||||||
} else {
|
} else {
|
||||||
log.info("No network-parameters file found. Expecting network parameters to be available from the network map.")
|
log.info("No network-parameters file found. Expecting network parameters to be available from the network map.")
|
||||||
val networkMapClient = checkNotNull(networkMapClient) {
|
val networkMapClient = checkNotNull(networkMapClient) {
|
||||||
"Node hasn't been configured to connect to a network map from which to get the network parameters"
|
"Node hasn't been configured to connect to a network map from which to get the network parameters"
|
||||||
}
|
}
|
||||||
val (networkMap, _) = networkMapClient.getNetworkMap()
|
val (networkMap, _) = networkMapClient.getNetworkMap()
|
||||||
val signedParams = checkNotNull(networkMapClient.getNetworkParameter(networkMap.networkParameterHash)) {
|
val signedParams = networkMapClient.getNetworkParameters(networkMap.networkParameterHash)
|
||||||
"Failed loading network parameters from network map server"
|
val verifiedParams = signedParams.verifiedNetworkMapCert(trustRoot)
|
||||||
}
|
|
||||||
val verifiedParams = signedParams.verified()
|
|
||||||
signedParams.serialize().open().copyTo(configuration.baseDirectory / NETWORK_PARAMS_FILE_NAME)
|
signedParams.serialize().open().copyTo(configuration.baseDirectory / NETWORK_PARAMS_FILE_NAME)
|
||||||
verifiedParams
|
verifiedParams
|
||||||
}
|
}
|
||||||
|
@ -52,10 +52,7 @@ fun SSLConfiguration.configureDevKeyAndTrustStores(myLegalName: CordaX500Name) {
|
|||||||
loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordatruststore.jks"), "trustpass").save(trustStoreFile, trustStorePassword)
|
loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordatruststore.jks"), "trustpass").save(trustStoreFile, trustStorePassword)
|
||||||
}
|
}
|
||||||
if (!sslKeystore.exists() || !nodeKeystore.exists()) {
|
if (!sslKeystore.exists() || !nodeKeystore.exists()) {
|
||||||
val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
|
createDevKeyStores(myLegalName)
|
||||||
val rootCert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA)
|
|
||||||
val intermediateCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
|
|
||||||
createDevKeyStores(rootCert, intermediateCa, myLegalName)
|
|
||||||
|
|
||||||
// Move distributed service composite key (generated by IdentityGenerator.generateToDisk) to keystore if exists.
|
// Move distributed service composite key (generated by IdentityGenerator.generateToDisk) to keystore if exists.
|
||||||
val distributedServiceKeystore = certificatesDirectory / "distributedService.jks"
|
val distributedServiceKeystore = certificatesDirectory / "distributedService.jks"
|
||||||
|
@ -2,28 +2,26 @@ package net.corda.node.services.network
|
|||||||
|
|
||||||
import com.google.common.util.concurrent.MoreExecutors
|
import com.google.common.util.concurrent.MoreExecutors
|
||||||
import net.corda.core.crypto.SecureHash
|
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.openHttpConnection
|
||||||
|
import net.corda.core.internal.responseAs
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.serialization.deserialize
|
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.utilities.contextLogger
|
import net.corda.core.utilities.contextLogger
|
||||||
import net.corda.core.utilities.minutes
|
import net.corda.core.utilities.minutes
|
||||||
import net.corda.core.utilities.seconds
|
import net.corda.core.utilities.seconds
|
||||||
import net.corda.node.services.api.NetworkMapCacheInternal
|
import net.corda.node.services.api.NetworkMapCacheInternal
|
||||||
import net.corda.node.utilities.NamedThreadFactory
|
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.NetworkMap
|
||||||
import net.corda.nodeapi.internal.network.NetworkParameters
|
import net.corda.nodeapi.internal.network.NetworkParameters
|
||||||
import net.corda.nodeapi.internal.network.SignedNetworkMap
|
import net.corda.nodeapi.internal.network.verifiedNetworkMapCert
|
||||||
import net.corda.nodeapi.internal.SignedNodeInfo
|
|
||||||
import okhttp3.CacheControl
|
import okhttp3.CacheControl
|
||||||
import okhttp3.Headers
|
import okhttp3.Headers
|
||||||
import org.apache.commons.io.IOUtils
|
|
||||||
import rx.Subscription
|
import rx.Subscription
|
||||||
import java.io.BufferedReader
|
import java.io.BufferedReader
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
import java.io.IOException
|
|
||||||
import java.net.HttpURLConnection
|
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
@ -40,42 +38,29 @@ class NetworkMapClient(compatibilityZoneURL: URL, private val trustedRoot: X509C
|
|||||||
requestMethod = "POST"
|
requestMethod = "POST"
|
||||||
setRequestProperty("Content-Type", "application/octet-stream")
|
setRequestProperty("Content-Type", "application/octet-stream")
|
||||||
outputStream.use { signedNodeInfo.serialize().open().copyTo(it) }
|
outputStream.use { signedNodeInfo.serialize().open().copyTo(it) }
|
||||||
if (responseCode != 200) {
|
checkOkResponse()
|
||||||
throw IOException("Response Code $responseCode: ${IOUtils.toString(errorStream)}")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getNetworkMap(): NetworkMapResponse {
|
fun getNetworkMap(): NetworkMapResponse {
|
||||||
val conn = networkMapUrl.openHttpConnection()
|
val connection = networkMapUrl.openHttpConnection()
|
||||||
val signedNetworkMap = conn.inputStream.use { it.readBytes() }.deserialize<SignedNetworkMap>()
|
val signedNetworkMap = connection.responseAs<SignedDataWithCert<NetworkMap>>()
|
||||||
val networkMap = signedNetworkMap.verified(trustedRoot)
|
val networkMap = signedNetworkMap.verifiedNetworkMapCert(trustedRoot)
|
||||||
val timeout = CacheControl.parse(Headers.of(conn.headerFields.filterKeys { it != null }.mapValues { it.value.first() })).maxAgeSeconds().seconds
|
val timeout = CacheControl.parse(Headers.of(connection.headerFields.filterKeys { it != null }.mapValues { it.value[0] })).maxAgeSeconds().seconds
|
||||||
return NetworkMapResponse(networkMap, timeout)
|
return NetworkMapResponse(networkMap, timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getNodeInfo(nodeInfoHash: SecureHash): NodeInfo? {
|
fun getNodeInfo(nodeInfoHash: SecureHash): NodeInfo {
|
||||||
val conn = URL("$networkMapUrl/node-info/$nodeInfoHash").openHttpConnection()
|
return URL("$networkMapUrl/node-info/$nodeInfoHash").openHttpConnection().responseAs<SignedNodeInfo>().verified()
|
||||||
return if (conn.responseCode == HttpURLConnection.HTTP_NOT_FOUND) {
|
|
||||||
null
|
|
||||||
} else {
|
|
||||||
val signedNodeInfo = conn.inputStream.use { it.readBytes() }.deserialize<SignedNodeInfo>()
|
|
||||||
signedNodeInfo.verified()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getNetworkParameter(networkParameterHash: SecureHash): SignedData<NetworkParameters>? {
|
fun getNetworkParameters(networkParameterHash: SecureHash): SignedDataWithCert<NetworkParameters> {
|
||||||
val conn = URL("$networkMapUrl/network-parameter/$networkParameterHash").openHttpConnection()
|
return URL("$networkMapUrl/network-parameters/$networkParameterHash").openHttpConnection().responseAs()
|
||||||
return if (conn.responseCode == HttpURLConnection.HTTP_NOT_FOUND) {
|
|
||||||
null
|
|
||||||
} else {
|
|
||||||
conn.inputStream.use { it.readBytes() }.deserialize()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun myPublicHostname(): String {
|
fun myPublicHostname(): String {
|
||||||
val conn = URL("$networkMapUrl/my-hostname").openHttpConnection()
|
val connection = URL("$networkMapUrl/my-hostname").openHttpConnection()
|
||||||
return conn.inputStream.bufferedReader().use(BufferedReader::readLine)
|
return connection.inputStream.bufferedReader().use(BufferedReader::readLine)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,9 +24,7 @@ class HTTPNetworkRegistrationService(compatibilityZoneURL: URL) : NetworkRegistr
|
|||||||
@Throws(CertificateRequestException::class)
|
@Throws(CertificateRequestException::class)
|
||||||
override fun retrieveCertificates(requestId: String): Array<Certificate>? {
|
override fun retrieveCertificates(requestId: String): Array<Certificate>? {
|
||||||
// Poll server to download the signed certificate once request has been approved.
|
// Poll server to download the signed certificate once request has been approved.
|
||||||
val url = URL("$registrationURL/$requestId")
|
val conn = URL("$registrationURL/$requestId").openHttpConnection()
|
||||||
|
|
||||||
val conn = url.openConnection() as HttpURLConnection
|
|
||||||
conn.requestMethod = "GET"
|
conn.requestMethod = "GET"
|
||||||
|
|
||||||
return when (conn.responseCode) {
|
return when (conn.responseCode) {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package net.corda.node.services.network
|
package net.corda.node.services.network
|
||||||
|
|
||||||
import net.corda.core.crypto.SecureHash
|
|
||||||
import net.corda.core.crypto.sha256
|
import net.corda.core.crypto.sha256
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.utilities.seconds
|
import net.corda.core.utilities.seconds
|
||||||
@ -22,7 +21,6 @@ import org.junit.Test
|
|||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertNotNull
|
|
||||||
|
|
||||||
class NetworkMapClientTest {
|
class NetworkMapClientTest {
|
||||||
@Rule
|
@Rule
|
||||||
@ -83,8 +81,8 @@ class NetworkMapClientTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `download NetworkParameter correctly`() {
|
fun `download NetworkParameter correctly`() {
|
||||||
// The test server returns same network parameter for any hash.
|
// The test server returns same network parameter for any hash.
|
||||||
val networkParameter = networkMapClient.getNetworkParameter(SecureHash.randomSHA256())?.verified()
|
val parametersHash = server.networkParameters.serialize().hash
|
||||||
assertNotNull(networkParameter)
|
val networkParameter = networkMapClient.getNetworkParameters(parametersHash).verified()
|
||||||
assertEquals(server.networkParameters, networkParameter)
|
assertEquals(server.networkParameters, networkParameter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,20 +1,16 @@
|
|||||||
package net.corda.testing.node.internal.network
|
package net.corda.testing.node.internal.network
|
||||||
|
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.internal.signWithCert
|
||||||
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.SignedNodeInfo
|
import net.corda.nodeapi.internal.SignedNodeInfo
|
||||||
|
import net.corda.nodeapi.internal.createDevNetworkMapCa
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
|
||||||
import net.corda.nodeapi.internal.network.DigitalSignatureWithCert
|
|
||||||
import net.corda.nodeapi.internal.network.NetworkMap
|
import net.corda.nodeapi.internal.network.NetworkMap
|
||||||
import net.corda.nodeapi.internal.network.NetworkParameters
|
import net.corda.nodeapi.internal.network.NetworkParameters
|
||||||
import net.corda.nodeapi.internal.network.SignedNetworkMap
|
|
||||||
import net.corda.testing.DEV_ROOT_CA
|
|
||||||
import org.eclipse.jetty.server.Server
|
import org.eclipse.jetty.server.Server
|
||||||
import org.eclipse.jetty.server.ServerConnector
|
import org.eclipse.jetty.server.ServerConnector
|
||||||
import org.eclipse.jetty.server.handler.HandlerCollection
|
import org.eclipse.jetty.server.handler.HandlerCollection
|
||||||
@ -28,34 +24,19 @@ import java.net.InetSocketAddress
|
|||||||
import java.security.SignatureException
|
import java.security.SignatureException
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import javax.security.auth.x500.X500Principal
|
|
||||||
import javax.ws.rs.*
|
import javax.ws.rs.*
|
||||||
import javax.ws.rs.core.MediaType
|
import javax.ws.rs.core.MediaType
|
||||||
import javax.ws.rs.core.Response
|
import javax.ws.rs.core.Response
|
||||||
import javax.ws.rs.core.Response.ok
|
import javax.ws.rs.core.Response.ok
|
||||||
import javax.ws.rs.core.Response.status
|
import javax.ws.rs.core.Response.status
|
||||||
|
|
||||||
class NetworkMapServer(cacheTimeout: Duration,
|
class NetworkMapServer(private val cacheTimeout: Duration,
|
||||||
hostAndPort: NetworkHostAndPort,
|
hostAndPort: NetworkHostAndPort,
|
||||||
rootCa: CertificateAndKeyPair = DEV_ROOT_CA,
|
private val networkMapCa: CertificateAndKeyPair = createDevNetworkMapCa(),
|
||||||
private val myHostNameValue: String = "test.host.name",
|
private val myHostNameValue: String = "test.host.name",
|
||||||
vararg additionalServices: Any) : Closeable {
|
vararg additionalServices: Any) : Closeable {
|
||||||
companion object {
|
companion object {
|
||||||
private val stubNetworkParameters = NetworkParameters(1, emptyList(), 10485760, 40000, Instant.now(), 10)
|
private val stubNetworkParameters = NetworkParameters(1, emptyList(), 10485760, 40000, Instant.now(), 10)
|
||||||
|
|
||||||
private fun networkMapKeyAndCert(rootCAKeyAndCert: CertificateAndKeyPair): CertificateAndKeyPair {
|
|
||||||
val networkMapKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
|
||||||
val networkMapCert = X509Utilities.createCertificate(
|
|
||||||
CertificateType.NETWORK_MAP,
|
|
||||||
rootCAKeyAndCert.certificate,
|
|
||||||
rootCAKeyAndCert.keyPair,
|
|
||||||
X500Principal("CN=Corda Network Map,O=R3 Ltd,L=London,C=GB"),
|
|
||||||
networkMapKey.public)
|
|
||||||
// Check that the certificate validates. Nodes will perform this check upon receiving a network map,
|
|
||||||
// it's better to fail here than there.
|
|
||||||
X509Utilities.validateCertificateChain(rootCAKeyAndCert.certificate, networkMapCert)
|
|
||||||
return CertificateAndKeyPair(networkMapCert, networkMapKey)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val server: Server
|
private val server: Server
|
||||||
@ -64,9 +45,7 @@ class NetworkMapServer(cacheTimeout: Duration,
|
|||||||
check(field == stubNetworkParameters) { "Network parameters can be set only once" }
|
check(field == stubNetworkParameters) { "Network parameters can be set only once" }
|
||||||
field = networkParameters
|
field = networkParameters
|
||||||
}
|
}
|
||||||
private val serializedParameters get() = networkParameters.serialize()
|
private val service = InMemoryNetworkMapService()
|
||||||
private val service = InMemoryNetworkMapService(cacheTimeout, networkMapKeyAndCert(rootCa))
|
|
||||||
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
server = Server(InetSocketAddress(hostAndPort.host, hostAndPort.port)).apply {
|
server = Server(InetSocketAddress(hostAndPort.host, hostAndPort.port)).apply {
|
||||||
@ -106,14 +85,10 @@ class NetworkMapServer(cacheTimeout: Duration,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Path("network-map")
|
@Path("network-map")
|
||||||
inner class InMemoryNetworkMapService(private val cacheTimeout: Duration,
|
inner class InMemoryNetworkMapService {
|
||||||
private val networkMapKeyAndCert: CertificateAndKeyPair) {
|
|
||||||
private val nodeInfoMap = mutableMapOf<SecureHash, SignedNodeInfo>()
|
private val nodeInfoMap = mutableMapOf<SecureHash, SignedNodeInfo>()
|
||||||
private val parametersHash by lazy { serializedParameters.hash }
|
private val signedNetParams by lazy {
|
||||||
private val signedParameters by lazy {
|
networkParameters.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
|
||||||
SignedData(
|
|
||||||
serializedParameters,
|
|
||||||
DigitalSignature.WithKey(networkMapKeyAndCert.keyPair.public, Crypto.doSign(networkMapKeyAndCert.keyPair.private, serializedParameters.bytes)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
@ -121,10 +96,9 @@ class NetworkMapServer(cacheTimeout: Duration,
|
|||||||
@Consumes(MediaType.APPLICATION_OCTET_STREAM)
|
@Consumes(MediaType.APPLICATION_OCTET_STREAM)
|
||||||
fun publishNodeInfo(input: InputStream): Response {
|
fun publishNodeInfo(input: InputStream): Response {
|
||||||
return try {
|
return try {
|
||||||
val registrationData = input.readBytes().deserialize<SignedNodeInfo>()
|
val signedNodeInfo = input.readBytes().deserialize<SignedNodeInfo>()
|
||||||
val nodeInfo = registrationData.verified()
|
signedNodeInfo.verified()
|
||||||
val nodeInfoHash = nodeInfo.serialize().sha256()
|
nodeInfoMap[signedNodeInfo.raw.hash] = signedNodeInfo
|
||||||
nodeInfoMap.put(nodeInfoHash, registrationData)
|
|
||||||
ok()
|
ok()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
when (e) {
|
when (e) {
|
||||||
@ -137,10 +111,8 @@ class NetworkMapServer(cacheTimeout: Duration,
|
|||||||
@GET
|
@GET
|
||||||
@Produces(MediaType.APPLICATION_OCTET_STREAM)
|
@Produces(MediaType.APPLICATION_OCTET_STREAM)
|
||||||
fun getNetworkMap(): Response {
|
fun getNetworkMap(): Response {
|
||||||
val networkMap = NetworkMap(nodeInfoMap.keys.toList(), parametersHash)
|
val networkMap = NetworkMap(nodeInfoMap.keys.toList(), signedNetParams.raw.hash)
|
||||||
val serializedNetworkMap = networkMap.serialize()
|
val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
|
||||||
val signature = Crypto.doSign(networkMapKeyAndCert.keyPair.private, serializedNetworkMap.bytes)
|
|
||||||
val signedNetworkMap = SignedNetworkMap(networkMap.serialize(), DigitalSignatureWithCert(networkMapKeyAndCert.certificate, signature))
|
|
||||||
return Response.ok(signedNetworkMap.serialize().bytes).header("Cache-Control", "max-age=${cacheTimeout.seconds}").build()
|
return Response.ok(signedNetworkMap.serialize().bytes).header("Cache-Control", "max-age=${cacheTimeout.seconds}").build()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,16 +134,15 @@ class NetworkMapServer(cacheTimeout: Duration,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("network-parameter/{var}")
|
@Path("network-parameters/{var}")
|
||||||
@Produces(MediaType.APPLICATION_OCTET_STREAM)
|
@Produces(MediaType.APPLICATION_OCTET_STREAM)
|
||||||
fun getNetworkParameter(@PathParam("var") networkParameterHash: String): Response {
|
fun getNetworkParameter(@PathParam("var") hash: String): Response {
|
||||||
return Response.ok(signedParameters.serialize().bytes).build()
|
require(signedNetParams.raw.hash == SecureHash.parse(hash))
|
||||||
|
return Response.ok(signedNetParams.serialize().bytes).build()
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("my-hostname")
|
@Path("my-hostname")
|
||||||
fun getHostName(): Response {
|
fun getHostName(): Response = Response.ok(myHostNameValue).build()
|
||||||
return Response.ok(myHostNameValue).build()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,6 @@ import net.corda.core.contracts.TypeOnlyCommandData
|
|||||||
import net.corda.core.crypto.generateKeyPair
|
import net.corda.core.crypto.generateKeyPair
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
|
||||||
import net.corda.nodeapi.internal.crypto.getCertificateAndKeyPair
|
|
||||||
import net.corda.nodeapi.internal.crypto.loadKeyStore
|
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
@ -32,17 +29,10 @@ val ALICE_NAME = CordaX500Name("Alice Corp", "Madrid", "ES")
|
|||||||
val BOB_NAME = CordaX500Name("Bob Plc", "Rome", "IT")
|
val BOB_NAME = CordaX500Name("Bob Plc", "Rome", "IT")
|
||||||
@JvmField
|
@JvmField
|
||||||
val CHARLIE_NAME = CordaX500Name("Charlie Ltd", "Athens", "GR")
|
val CHARLIE_NAME = CordaX500Name("Charlie Ltd", "Athens", "GR")
|
||||||
val DEV_INTERMEDIATE_CA: CertificateAndKeyPair by lazy {
|
|
||||||
// TODO: Should be identity scheme
|
|
||||||
val caKeyStore = loadKeyStore(ClassLoader.getSystemResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
|
|
||||||
caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
|
|
||||||
}
|
|
||||||
|
|
||||||
val DEV_ROOT_CA: CertificateAndKeyPair by lazy {
|
val DEV_INTERMEDIATE_CA: CertificateAndKeyPair by lazy { net.corda.nodeapi.internal.DEV_INTERMEDIATE_CA }
|
||||||
// TODO: Should be identity scheme
|
|
||||||
val caKeyStore = loadKeyStore(ClassLoader.getSystemResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
|
val DEV_ROOT_CA: CertificateAndKeyPair by lazy { net.corda.nodeapi.internal.DEV_ROOT_CA }
|
||||||
caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_ROOT_CA, "cordacadevkeypass")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun dummyCommand(vararg signers: PublicKey = arrayOf(generateKeyPair().public)) = Command<TypeOnlyCommandData>(DummyCommandData, signers.toList())
|
fun dummyCommand(vararg signers: PublicKey = arrayOf(generateKeyPair().public)) = Command<TypeOnlyCommandData>(DummyCommandData, signers.toList())
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user