mirror of
https://github.com/corda/corda.git
synced 2025-04-20 17:11:26 +00:00
Merge branch 'master' into anthony/features/ENT-2256
This commit is contained in:
commit
da97f7bd82
9
.ci/ci-gradle-build-cache-init.sh
Executable file
9
.ci/ci-gradle-build-cache-init.sh
Executable file
@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
export GRADLE_BUILD_CACHE_URL="${GRADLE_BUILD_CACHE_URL:-http://localhost:5071/cache/}"
|
||||
export USE_GRADLE_DAEMON="${USE_GRADLE_DAEMON:-false}"
|
||||
export GRADLE_CACHE_DEBUG="${GRADLE_CACHE_DEBUG:-false}"
|
||||
export PERFORM_GRADLE_SCAN="${PERFORM_GRADLE_SCAN:---scan}"
|
||||
|
||||
# cd %teamcity.build.checkoutDir%
|
||||
echo "Using Gradle Build Cache: $GRADLE_BUILD_CACHE_URL"
|
@ -84,6 +84,7 @@ see changes to this list.
|
||||
* Giulio Katis (Westpac)
|
||||
* Giuseppe Cardone (Intesa Sanpaolo)
|
||||
* Guy Hochstetler (R3)
|
||||
* Hristo Gatsinski (Industria)
|
||||
* Ian Cusden (UBS)
|
||||
* Ian Grigg (R3)
|
||||
* Igor Nitto (R3)
|
||||
|
12
build.gradle
12
build.gradle
@ -125,6 +125,7 @@ plugins {
|
||||
|
||||
// Add the shadow plugin to the plugins classpath for the entire project.
|
||||
id 'com.github.johnrengelman.shadow' version '2.0.4' apply false
|
||||
id "com.gradle.build-scan" version "1.16"
|
||||
}
|
||||
|
||||
ext {
|
||||
@ -229,6 +230,12 @@ allprojects {
|
||||
if (project.path.startsWith(':experimental') && System.getProperty("experimental.test.enable") == null) {
|
||||
enabled = false
|
||||
}
|
||||
|
||||
// Required to use Gradle build cache (until Gradle 5.0 is released with default value of "append" set to false)
|
||||
// See https://github.com/gradle/gradle/issues/5269 and https://github.com/gradle/gradle/pull/6419
|
||||
extensions.configure(TypeOf.typeOf(JacocoTaskExtension)) { ex ->
|
||||
ex.append = false
|
||||
}
|
||||
}
|
||||
|
||||
group 'com.r3.corda'
|
||||
@ -474,3 +481,8 @@ wrapper {
|
||||
gradleVersion = "4.10.1"
|
||||
distributionType = Wrapper.DistributionType.ALL
|
||||
}
|
||||
|
||||
buildScan {
|
||||
termsOfServiceUrl = 'https://gradle.com/terms-of-service'
|
||||
termsOfServiceAgree = 'yes'
|
||||
}
|
15
buildCacheSettings.gradle
Normal file
15
buildCacheSettings.gradle
Normal file
@ -0,0 +1,15 @@
|
||||
// Gradle Build Cache configuration recommendation: https://docs.gradle.org/current/userguide/build_cache.html
|
||||
ext {
|
||||
isCiServer = System.getenv().containsKey("CORDA_CI")
|
||||
gradleBuildCacheURL = System.getenv().containsKey("GRADLE_BUILD_CACHE_URL") ? System.getenv().get("GRADLE_BUILD_CACHE_URL") : 'http://localhost:5071/cache/'
|
||||
}
|
||||
|
||||
buildCache {
|
||||
local {
|
||||
enabled = !isCiServer
|
||||
}
|
||||
remote(HttpBuildCache) {
|
||||
url = gradleBuildCacheURL
|
||||
push = isCiServer
|
||||
}
|
||||
}
|
@ -1,2 +1,4 @@
|
||||
rootProject.name = 'buildSrc'
|
||||
include 'canonicalizer'
|
||||
|
||||
apply from: '../buildCacheSettings.gradle'
|
@ -4,6 +4,7 @@ Networks
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
joining-a-network
|
||||
setting-up-a-corda-network
|
||||
running-a-notary
|
||||
permissioning
|
||||
|
@ -47,9 +47,11 @@ in the `Kotlin CorDapp Template <https://github.com/corda/cordapp-template-kotli
|
||||
sshdPort 2223
|
||||
// Includes the corda-finance CorDapp on our node.
|
||||
cordapps = ["$corda_release_distribution:corda-finance:$corda_release_version"]
|
||||
// Specify a JVM argument to be used when running the node (in this case, extra heap size).
|
||||
extraConfig = [
|
||||
jvmArgs : [ "-Xmx1g"]
|
||||
// Setting the JMX reporter type.
|
||||
jmxReporterType: 'JOLOKIA',
|
||||
// Setting the H2 address.
|
||||
h2Settings: [ address: 'localhost:10030' ]
|
||||
]
|
||||
}
|
||||
node {
|
||||
|
@ -9,7 +9,7 @@ Corda uses industry-standard tools:
|
||||
* **IntelliJ IDEA** - supported versions **2017.x** and **2018.x** (with Kotlin plugin version |kotlin_version|)
|
||||
* **Git**
|
||||
|
||||
We also use Gradle and Kotlin, but you do not need to install them. A standalone Gradle wrapper is provided, and it
|
||||
We also use Gradle and Kotlin, but you do not need to install them. A standalone Gradle wrapper is provided, and it
|
||||
will download the correct version of Kotlin.
|
||||
|
||||
Please note:
|
||||
@ -49,12 +49,14 @@ The set-up instructions are available for the following platforms:
|
||||
|
||||
* :ref:`deb-ubuntu-label`
|
||||
|
||||
* :ref:`fedora-label`
|
||||
|
||||
.. _windows-label:
|
||||
|
||||
Windows
|
||||
-------
|
||||
|
||||
.. warning:: If you are using a Mac or a Debian/Ubuntu machine, please follow the :ref:`mac-label` or :ref:`deb-ubuntu-label` instructions instead.
|
||||
.. warning:: If you are using a Mac, Debian/Ubuntu or Fedora machine, please follow the :ref:`mac-label`, :ref:`deb-ubuntu-label` or :ref:`fedora-label` instructions instead.
|
||||
|
||||
Java
|
||||
^^^^
|
||||
@ -142,7 +144,7 @@ Run from IntelliJ
|
||||
Mac
|
||||
---
|
||||
|
||||
.. warning:: If you are using a Windows or a Debian/Ubuntu machine, please follow the :ref:`windows-label` or :ref:`deb-ubuntu-label` instructions instead.
|
||||
.. warning:: If you are using a Windows, Debian/Ubuntu or Fedora machine, please follow the :ref:`windows-label`, :ref:`deb-ubuntu-label` or :ref:`fedora-label` instructions instead.
|
||||
|
||||
Java
|
||||
^^^^
|
||||
@ -249,7 +251,7 @@ You can clone these repos to your local machine by running the command ``git clo
|
||||
Debian/Ubuntu
|
||||
-------------
|
||||
|
||||
.. warning:: If you are using a Mac or a Windows machine, please follow the :ref:`mac-label` or :ref:`windows-label` instructions instead.
|
||||
.. warning:: If you are using a Mac, Windows or Fedora machine, please follow the :ref:`mac-label`, :ref:`windows-label` or :ref:`fedora-label` instructions instead.
|
||||
|
||||
These instructions were tested on Ubuntu Desktop 18.04 LTS.
|
||||
|
||||
@ -273,6 +275,35 @@ Jetbrains offers a pre-built snap package that allows for easy, one-step install
|
||||
2. Click ``Install``, then ``View in Desktop Store``. Choose ``Ubuntu Software`` in the Launch Application window.
|
||||
3. Ensure the Kotlin plugin in Intellij is updated to version |kotlin_version|
|
||||
|
||||
.. _fedora-label:
|
||||
|
||||
Fedora
|
||||
-------------
|
||||
|
||||
.. warning:: If you are using a Mac, Windows or Debian/Ubuntu machine, please follow the :ref:`mac-label`, :ref:`windows-label` or :ref:`deb-ubuntu-label` instructions instead.
|
||||
|
||||
These instructions were tested on Fedora 28.
|
||||
|
||||
Java
|
||||
^^^^
|
||||
1. Download the RPM installation file of Oracle JDK from https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html.
|
||||
2. Install the package with ``rpm -ivh jdk-<version>-linux-<architecture>.rpm`` or use the default software manager.
|
||||
3. Choose java version by using the following command ``alternatives --config java``
|
||||
4. Verify that the JDK was installed correctly by running ``java -version``
|
||||
|
||||
Git
|
||||
^^^^
|
||||
1. From the terminal, Git can be installed using dnf with the command ``sudo dnf install git``
|
||||
2. Verify that git was installed correctly by typing ``git --version``
|
||||
|
||||
IntelliJ
|
||||
^^^^^^^^
|
||||
1. Visit https://www.jetbrains.com/idea/download/download-thanks.html?platform=linux&code=IIC
|
||||
2. Unpack the ``tar.gz`` file using the following command ``tar xfz ideaIC-<version>.tar.gz -C /opt``
|
||||
3. Run IntelliJ with ``/opt/ideaIC-<version>/bin/idea.sh``
|
||||
4. Ensure the Kotlin plugin in IntelliJ is updated to version |kotlin_version|
|
||||
|
||||
|
||||
Next steps
|
||||
----------
|
||||
The best way to check that everything is working fine is by taking a deeper look at the
|
||||
|
54
docs/source/joining-a-network.rst
Normal file
54
docs/source/joining-a-network.rst
Normal file
@ -0,0 +1,54 @@
|
||||
.. highlight:: kotlin
|
||||
.. raw:: html
|
||||
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/codesets.js"></script>
|
||||
|
||||
Connecting to a compatibility zone
|
||||
==================================
|
||||
|
||||
Every Corda node is part of a network (also called a zone) that is *permissioned*. Production deployments require a
|
||||
secure certificate authority. Most users will join an existing network such as the main Corda network or the Corda
|
||||
TestNet.
|
||||
|
||||
To connect to a compatibility zone you need to register with its certificate signing authority (doorman) by submitting
|
||||
a certificate signing request (CSR) to obtain a valid identity for the zone. You could do this out of band, for instance
|
||||
via email or a web form, but there's also a simple request/response protocol built into Corda.
|
||||
|
||||
Before you can register, you must first have received the trust store file containing the root certificate from the zone
|
||||
operator. For high security zones this might be delivered physically. Then run the following command:
|
||||
|
||||
``java -jar corda.jar --initial-registration --network-root-truststore-password <trust store password>``
|
||||
|
||||
By default it will expect the trust store file to be in the location ``certificates/network-root-truststore.jks``.
|
||||
This can be overridden with the additional ``--network-root-truststore`` flag.
|
||||
|
||||
The certificate signing request will be created based on node information obtained from the node configuration.
|
||||
The following information from the node configuration file is needed to generate the request.
|
||||
|
||||
* **myLegalName** Your company's legal name as an X.500 string. X.500 allows differentiation between entities with the same
|
||||
name, as the legal name needs to be unique on the network. If another node has already been permissioned with this
|
||||
name then the permissioning server will automatically reject the request. The request will also be rejected if it
|
||||
violates legal name rules, see :ref:`node_naming` for more information. You can use the X.500 schema to disambiguate
|
||||
entities that have the same or similar brand names.
|
||||
|
||||
* **emailAddress** e.g. "admin@company.com"
|
||||
|
||||
* **devMode** must be set to false
|
||||
|
||||
* **networkServices** or **compatibilityZoneURL** The Corda compatibility zone services must be configured. This must be either:
|
||||
|
||||
* **compatibilityZoneURL** The Corda compatibility zone network management service root URL.
|
||||
* **networkServices** Replaces the ``compatibilityZoneURL`` when the doorman and network map services
|
||||
are configured to operate on different URL endpoints. The ``doorman`` entry is used for registration.
|
||||
|
||||
A new pair of private and public keys generated by the Corda node will be used to create the request.
|
||||
|
||||
The utility will submit the request to the doorman server and poll for a result periodically to retrieve the
|
||||
certificates. Once the request has been approved and the certificates downloaded from the server, the node will create
|
||||
the keystore and trust store using the certificates and the generated private key.
|
||||
|
||||
.. note:: You can exit the utility at any time if the approval process is taking longer than expected. The request
|
||||
process will resume on restart as long as the ``--initial-registration`` flag is specified.
|
||||
|
||||
This process only is needed when the node connects to the network for the first time, or when the certificate expires.
|
@ -22,13 +22,23 @@ A Corda node has the following structure:
|
||||
├── persistence.mv.db // The node's database (present when the node uses the embedded H2 database instance)
|
||||
└── shell-commands // Custom shell commands defined by the node owner
|
||||
|
||||
The node is configured by editing its ``node.conf`` file (see :doc:`corda-configuration-file`). You install CorDapps on
|
||||
the node by dropping CorDapp JARs into the ``cordapps`` folder.
|
||||
You install CorDapps on the node by placing CorDapp JARs in the ``cordapps`` folder.
|
||||
|
||||
In development mode (i.e. when ``devMode = true``, see :doc:`corda-configuration-file`), the ``certificates``
|
||||
directory is filled with pre-configured keystores if the required keystores do not exist. This ensures that developers
|
||||
can get the nodes working as quickly as possible. However, these pre-configured keystores are not secure, to learn more
|
||||
see :doc:`permissioning`.
|
||||
In development mode (i.e. when ``devMode = true``), the ``certificates`` directory is filled with pre-configured
|
||||
keystores if they do not already exist to ensure that developers can get the nodes working as quickly as
|
||||
possible.
|
||||
|
||||
.. warning:: These pre-configured keystores are not secure and must not used in a production environments.
|
||||
|
||||
The keystores store the key pairs and certificates under the following aliases:
|
||||
|
||||
* ``nodekeystore.jks`` uses the aliases ``cordaclientca`` and ``identity-private-key``
|
||||
* ``sslkeystore.jks`` uses the alias ``cordaclienttls``
|
||||
|
||||
All the keystores use the password provided in the node's configuration file using the ``keyStorePassword`` attribute.
|
||||
If no password is configured, it defaults to ``cordacadevpass``.
|
||||
|
||||
To learn more, see :doc:`permissioning`.
|
||||
|
||||
.. _node_naming:
|
||||
|
||||
|
@ -9,43 +9,28 @@ Network permissioning
|
||||
|
||||
.. contents::
|
||||
|
||||
Every Corda node is a part of a network (also called a zone), and networks are *permissioned*. To connect to a
|
||||
zone, a node needs a signed X.509 certificate from the network operator. Production deployments require a secure certificate authority.
|
||||
The issued certificates take the form of three keystores in a node's ``<workspace>/certificates/`` folder:
|
||||
|
||||
* ``network-root-truststore.jks``, the network/zone operator's public keys and certificates as provided by them with a standard password. Can be deleted after initial registration
|
||||
* ``truststore.jks``, the network/zone operator's public keys and certificates in keystore with a locally configurable password as protection against certain attacks
|
||||
* ``nodekeystore.jks``, which stores the node’s identity keypairs and certificates
|
||||
* ``sslkeystore.jks``, which stores the node’s TLS keypairs and certificates
|
||||
|
||||
Most users will join an existing network such as the main Corda network or the Corda TestNet. You can also build your
|
||||
own networks. During development, no network is required because you can use the included tools to pre-create
|
||||
and pre-distribute the certificates and map files that would normally be provided dynamically by the network. Effectively
|
||||
the :doc:`bootstrapper tool <network-bootstrapper>` creates a private semi-static network for you.
|
||||
|
||||
Certificate hierarchy
|
||||
---------------------
|
||||
|
||||
A Corda network has three types of certificate authorities (CAs):
|
||||
|
||||
* The **root network CA**, that defines the extent of a compatibility zone.
|
||||
* The **doorman CA**. The doorman CA is used instead of the root network CA for day-to-day key signing to reduce the
|
||||
risk of the root network CA's private key being compromised. This is equivalent to an intermediate certificate
|
||||
in the web PKI.
|
||||
* Each node also serves as its own CA in issuing the child certificates that it uses to sign its identity keys and TLS
|
||||
certificates.
|
||||
* The **root network CA** that defines the extent of a compatibility zone
|
||||
* The **doorman CA** that is used instead of the root network CA for day-to-day key signing to reduce the risk of the root
|
||||
network CA's private key being compromised. This is equivalent to an intermediate certificate in the web PKI
|
||||
* Each node also serves as its own CA, issuing the child certificates that it uses to sign its identity keys and TLS
|
||||
certificates
|
||||
|
||||
Each certificate has an X.509 extension in it that defines the certificate/key's role in the system (see below for details).
|
||||
They also use X.509 name constraints to ensure that the X.500 names that encode a human meaningful identity are propagated
|
||||
Each certificate contains an X.509 extension that defines the certificate/key's role in the system (see below for details).
|
||||
It also uses X.509 name constraints to ensure that the X.500 names that encode human meaningful identities are propagated
|
||||
to all the child certificates properly. The following constraints are imposed:
|
||||
|
||||
* Doorman certificates are issued by a network root. Network root certs do not contain a role extension.
|
||||
* Node certificates are signed by a doorman certificate (as defined by the extension).
|
||||
* Legal identity/TLS certificates are issued by a certificate marked as node CA.
|
||||
* Confidential identity certificates are issued by a certificate marked as well known legal identity.
|
||||
* Party certificates are marked as either a well known identity or a confidential identity.
|
||||
* Doorman certificates are issued by a network root. Network root certs do not contain a role extension
|
||||
* Node certificates are signed by a doorman certificate (as defined by the extension)
|
||||
* Legal identity/TLS certificates are issued by a certificate marked as node CA
|
||||
* Confidential identity certificates are issued by a certificate marked as well known legal identity
|
||||
* Party certificates are marked as either a well known identity or a confidential identity
|
||||
|
||||
The structure of certificates above Doorman/Network map is intentionally left untouched, as they are not relevant to
|
||||
The structure of certificates above the doorman/network map is intentionally left untouched, as they are not relevant to
|
||||
the identity service and therefore there is no advantage in enforcing a specific structure on those certificates. The
|
||||
certificate hierarchy consistency checks are required because nodes can issue their own certificates and can set
|
||||
their own role flags on certificates, and it's important to verify that these are set consistently with the
|
||||
@ -58,23 +43,29 @@ We can visualise the permissioning structure as follows:
|
||||
:scale: 55%
|
||||
:align: center
|
||||
|
||||
Keypair and certificate formats
|
||||
-------------------------------
|
||||
Key pair and certificate formats
|
||||
--------------------------------
|
||||
|
||||
You can use any standard key tools to create the required public/private keypairs and certificates. The keypairs and
|
||||
certificates must obey the following restrictions:
|
||||
The required key pairs and certificates take the form of the following Java-style keystores (this may change in future to
|
||||
support PKCS#12 keystores) in the node's ``<workspace>/certificates/`` folder:
|
||||
|
||||
* ``network-root-truststore.jks``, the network/zone operator's root certificate as provided by them with a standard password. Can be deleted after initial registration
|
||||
* ``truststore.jks``, the network/zone operator's root certificate in keystore with a locally configurable password as protection against certain attacks
|
||||
* ``nodekeystore.jks``, which stores the node’s identity key pairs and certificates
|
||||
* ``sslkeystore.jks``, which stores the node’s TLS key pair and certificate
|
||||
|
||||
The key pairs and certificates must obey the following restrictions:
|
||||
|
||||
1. The certificates must follow the `X.509v3 standard <https://tools.ietf.org/html/rfc5280>`__
|
||||
2. The TLS certificates must follow the `TLS v1.2 standard <https://tools.ietf.org/html/rfc5246>`__
|
||||
3. The root network CA, doorman CA, and node CA keys, as well as the node TLS keys, must follow one of the following schemes:
|
||||
|
||||
* ECDSA using the NIST P-256 curve (secp256r1)
|
||||
* ECDSA using the Koblitz k1 curve (secp256k1)
|
||||
* RSA with 3072-bit key size or higher.
|
||||
* RSA with 3072-bit key size or higher
|
||||
|
||||
The required identity and TLS keys/certificates will be automatically generated for you by the node on first run.
|
||||
However, you can also generate them manually for more control. The ``X509Utilities`` class shows how to generate the
|
||||
required public/private keypairs and certificates using Bouncy Castle. You can find it in the `Corda repository
|
||||
<https://github.com/corda/corda/blob/master/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt>`__.
|
||||
4. The node CA certificates must have the basic constraints extension set to true
|
||||
5. The TLS certificates must have the basic constraints extension set to false
|
||||
|
||||
Certificate role extension
|
||||
--------------------------
|
||||
@ -97,235 +88,3 @@ it is important to recognise these extensions and the constraints noted above.
|
||||
|
||||
Certificate path validation is extended so that a certificate must contain the extension if the extension was present
|
||||
in the certificate of the issuer.
|
||||
|
||||
|
||||
Manually creating the node keys
|
||||
-------------------------------
|
||||
|
||||
The node expects a Java-style key store (this may change in future to support PKCS#12 keystores) called ``nodekeystore.jks``,
|
||||
with the private key and certificate having an alias of "cordaclientca". This certificate should be signed by the
|
||||
doorman CA for your network. The basic constraints extension must be set to true.
|
||||
|
||||
For the TLS keys, the basic constraints extension must be set to false. The keystore name is ``sslkeystore.jks`` and
|
||||
the key alias must be ``cordaclienttls``.
|
||||
|
||||
These two files should be in the node's certificate directory (``<workspace>/certificates/``), along with the network's
|
||||
own root certificates in a ``network-root-truststore.jks`` file.
|
||||
|
||||
Connecting to a compatibility zone
|
||||
----------------------------------
|
||||
|
||||
To connect to a compatibility zone you need to register with their certificate signing authority (doorman) by submitting
|
||||
a certificate signing request (CSR) to obtain a valid identity for the zone. You could do this out of band, for instance
|
||||
via email or a web form, but there's also a simple request/response protocol built into Corda.
|
||||
|
||||
Before you can register, you must first have received the trust store file containing the root certificate from the zone
|
||||
operator. For high security zones this might be delivered physically. Then run the following command:
|
||||
|
||||
``java -jar corda.jar --initial-registration --network-root-truststore-password <trust store password>``
|
||||
|
||||
By default it will expect the trust store file to be in the location ``certificates/network-root-truststore.jks``.
|
||||
This can be overridden with the additional ``--network-root-truststore`` flag.
|
||||
|
||||
The certificate signing request will be created based on node information obtained from the node configuration.
|
||||
The following information from the node configuration file is needed to generate the request.
|
||||
|
||||
* **myLegalName** Your company's legal name as an X.500 string. X.500 allows differentiation between entities with the same
|
||||
name, as the legal name needs to be unique on the network. If another node has already been permissioned with this
|
||||
name then the permissioning server will automatically reject the request. The request will also be rejected if it
|
||||
violates legal name rules, see :ref:`node_naming` for more information. You can use the X.500 schema to disambiguate
|
||||
entities that have the same or similar brand names.
|
||||
|
||||
* **emailAddress** e.g. "admin@company.com"
|
||||
|
||||
* **devMode** must be set to false
|
||||
|
||||
* **networkServices** or **compatibilityZoneURL** The Corda compatibility zone services must be configured. This must be either:
|
||||
|
||||
* **compatibilityZoneURL** The Corda compatibility zone network management service root URL.
|
||||
* **networkServices** Replaces the ``compatibilityZoneURL`` when the doorman and network map services
|
||||
are configured to operate on different URL endpoints. The ``doorman`` entry is used for registration.
|
||||
|
||||
A new pair of private and public keys generated by the Corda node will be used to create the request.
|
||||
|
||||
The utility will submit the request to the doorman server and poll for a result periodically to retrieve the
|
||||
certificates. Once the request has been approved and the certificates downloaded from the server, the node will create
|
||||
the keystore and trust store using the certificates and the generated private key.
|
||||
|
||||
.. note:: You can exit the utility at any time if the approval process is taking longer than expected. The request
|
||||
process will resume on restart as long as the ``--initial-registration`` flag is specified.
|
||||
|
||||
This process only is needed when the node connects to the network for the first time, or when the certificate expires.
|
||||
|
||||
Creating your own compatibility zone
|
||||
------------------------------------
|
||||
|
||||
This section documents how to implement your own doorman and network map servers, which is the basic process required to
|
||||
create a dedicated zone. At this time we do not provide tooling to do this, because the needs of each zone are different
|
||||
and no generic, configurable doorman codebase has been written.
|
||||
|
||||
Do you need a zone?
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Think twice before going down this route:
|
||||
|
||||
1. It isn't necessary for testing.
|
||||
2. It isn't necessary for adding another layer of permissioning or 'know your customer' requirements onto your app.
|
||||
|
||||
**Testing.** Creating a production-ready zone isn't necessary for testing as you can use the :doc:`network bootstrapper <network-bootstrapper>`
|
||||
tool to create all the certificates, keys, and distribute the needed map files to run many nodes. The bootstrapper can
|
||||
create a network locally on your desktop/laptop but it also knows how to automate cloud providers via their APIs and
|
||||
using Docker. In this way you can bring up a simulation of a real Corda network with different nodes on different
|
||||
machines in the cloud for your own testing. Testing this way has several advantages, most obviously that you avoid
|
||||
race conditions in your tests caused by nodes/tests starting before all map data has propagated to all nodes.
|
||||
You can read more about the reasons for the creation of the bootstrapper tool
|
||||
`in a blog post on the design thinking behind Corda's network map infrastructure <https://medium.com/corda/cordas-new-network-map-infrastructure-8c4c248fd7f3>`__.
|
||||
|
||||
**Permissioning.** And creating a zone is also unnecessary for imposing permissioning requirements beyond that of the
|
||||
base Corda network. You can control who can use your app by creating a *business network*. A business network is what we
|
||||
call a coalition of nodes that have chosen to run a particular app within a given commercial context. Business networks
|
||||
aren't represented in the Corda API at this time, partly because the technical side is so simple. You can create one
|
||||
via a simple three step process:
|
||||
|
||||
1. Distribute a list of X.500 names that are members of your business network, e.g. a simple way to do this is by
|
||||
hosting a text file with one name per line on your website at a fixed HTTPS URL. You could also write a simple
|
||||
request/response flow that serves the list over the Corda protocol itself, although this requires the business
|
||||
network to have a node for itself.
|
||||
2. Write a bit of code that downloads and caches the contents of this file on disk, and which loads it into memory in
|
||||
the node. A good place to do this is in a class annotated with ``@CordaService``, because this class can expose
|
||||
a ``Set<Party>`` field representing the membership of your service.
|
||||
3. In your flows use ``serviceHub.findService`` to get a reference to your ``@CordaService`` class, read the list of
|
||||
members and at the start of each flow, throw a FlowException if the counterparty isn't in the membership list.
|
||||
|
||||
In this way you can impose a centrally controlled ACL that all members will collectively enforce.
|
||||
|
||||
.. note:: A production-ready Corda network and a new iteration of the testnet will be available soon.
|
||||
|
||||
Why create your own zone?
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The primary reason to create a zone and provide the associated infrastructure is control over *network parameters*. These
|
||||
are settings that control Corda's operation, and on which all users in a network must agree. Failure to agree would create
|
||||
the Corda equivalent of a blockchain "hard fork". Parameters control things like the root of identity,
|
||||
how quickly users should upgrade, how long nodes can be offline before they are evicted from the system and so on.
|
||||
|
||||
Creating a zone involves the following steps:
|
||||
|
||||
1. Create the zone private keys and certificates. This procedure is conventional and no special knowledge is required:
|
||||
any self-signed set of certificates can be used. A professional quality zone will probably keep the keys inside a
|
||||
hardware security module (as the main Corda network and test networks do).
|
||||
2. Write a network map server.
|
||||
3. Optionally, create a doorman server.
|
||||
4. Finally, you would select and generate your network parameter file.
|
||||
|
||||
Writing a network map server
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This server implements a simple HTTP based protocol described in the ":doc:`network-map`" page.
|
||||
The map server is responsible for gathering NodeInfo files from nodes, storing them, and distributing them back to the
|
||||
nodes in the zone. By doing this it is also responsible for choosing who is in and who is out: having a signed
|
||||
identity certificate is not enough to be a part of a Corda zone, you also need to be listed in the network map.
|
||||
It can be thought of as a DNS equivalent. If you want to de-list a user, you would do it here.
|
||||
|
||||
It is very likely that your map server won't be entirely standalone, but rather, integrated with whatever your master
|
||||
user database is.
|
||||
|
||||
The network map server also distributes signed network parameter files and controls the roll-out schedule for when they
|
||||
become available for download and opt-in, and when they become enforced. This is again a policy decision you will
|
||||
probably choose to place some simple UI or workflow tooling around, in particular to enforce restrictions on who can
|
||||
edit the map or the parameters.
|
||||
|
||||
Writing a doorman server
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This step is optional because your users can obtain a signed certificate in many different ways. The doorman protocol
|
||||
is again a very simple HTTP based approach in which a node creates keys and requests a certificate, polling until it
|
||||
gets back what it expects. However, you could also integrate this process with the rest of your signup process. For example,
|
||||
by building a tool that's integrated with your payment flow (if payment is required to take part in your zone at all).
|
||||
Alternatively you may wish to distribute USB smartcard tokens that generate the private key on first use, as is typically
|
||||
seen in national PKIs. There are many options.
|
||||
|
||||
If you do choose to make a doorman server, the bulk of the code you write will be workflow related. For instance,
|
||||
related to keeping track of an applicant as they proceed through approval. You should also impose any naming policies
|
||||
you have in the doorman process. If names are meant to match identities registered in government databases then that
|
||||
should be enforced here, alternatively, if names can be self-selected or anonymous, you would only bother with a
|
||||
deduplication check. Again it will likely be integrated with a master user database.
|
||||
|
||||
Corda does not currently provide a doorman or network map service out of the box, partly because when stripped of the
|
||||
zone specific policy there isn't much to them: just a basic HTTP server that most programmers will have favourite
|
||||
frameworks for anyway.
|
||||
|
||||
The protocol is:
|
||||
|
||||
* If $URL = ``https://some.server.com/some/path``
|
||||
* Node submits a PKCS#10 certificate signing request using HTTP POST to ``$URL/certificate``. It will have a MIME
|
||||
type of ``application/octet-stream``. The ``Platform-Version`` header is set to be "1.0" and the ``Client-Version`` header to reflect the node software version.
|
||||
* The server returns an opaque string that references this request (let's call it ``$requestid``, or an HTTP error if something went wrong.
|
||||
* The returned request ID should be persisted to disk, to handle zones where approval may take a long time due to manual
|
||||
intervention being required.
|
||||
* The node starts polling ``$URL/$requestid`` using HTTP GET. The poll interval can be controlled by the server returning
|
||||
a response with a ``Cache-Control`` header.
|
||||
* If the request is answered with a ``200 OK`` response, the body is expected to be a zip file. Each file is expected to
|
||||
be a binary X.509 certificate, and the certs are expected to be in order.
|
||||
* If the request is answered with a ``204 No Content`` response, the node will try again later.
|
||||
* If the request is answered with a ``403 Not Authorized`` response, the node will treat that as request rejection and give up.
|
||||
* Other response codes will cause the node to abort with an exception.
|
||||
|
||||
Setting zone parameters
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Zone parameters are stored in a file containing a Corda AMQP serialised ``SignedDataWithCert<NetworkParameters>``
|
||||
object. It is easy to create such a file with a small Java or Kotlin program. The ``NetworkParameters`` object is a
|
||||
simple data holder that could be read from e.g. a config file, or settings from a database. Signing and saving the
|
||||
resulting file is just a few lines of code. A full example can be found in `NetworkParametersCopier.kt
|
||||
<https://github.com/corda/corda/blob/master/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkParametersCopier.kt>`__,
|
||||
but a flavour of it looks like this:
|
||||
|
||||
.. container:: codeset
|
||||
|
||||
.. sourcecode:: java
|
||||
|
||||
NetworkParameters networkParameters = new NetworkParameters(
|
||||
4, // minPlatformVersion
|
||||
Collections.emptyList(), // notaries
|
||||
1024 * 1024 * 20, // maxMessageSize
|
||||
1024 * 1024 * 15, // maxTransactionSize
|
||||
Instant.now(), // modifiedTime
|
||||
2, // epoch
|
||||
Collections.emptyMap() // whitelist
|
||||
);
|
||||
CertificateAndKeyPair signingCertAndKeyPair = loadNetworkMapCA();
|
||||
SerializedBytes<SignedDataWithCert<NetworkParameters>> bytes = SerializedBytes.from(netMapCA.sign(networkParameters));
|
||||
Files.copy(bytes.open(), Paths.get("params-file"));
|
||||
|
||||
.. sourcecode:: kotlin
|
||||
|
||||
val networkParameters = NetworkParameters(
|
||||
minimumPlatformVersion = 4,
|
||||
notaries = listOf(...),
|
||||
maxMessageSize = 1024 * 1024 * 20 // 20mb, for example.
|
||||
maxTransactionSize = 1024 * 1024 * 15,
|
||||
modifiedTime = Instant.now(),
|
||||
epoch = 2,
|
||||
... etc ...
|
||||
)
|
||||
val signingCertAndKeyPair: CertificateAndKeyPair = loadNetworkMapCA()
|
||||
val signedParams: SerializedBytes<SignedNetworkParameters> = signingCertAndKeyPair.sign(networkParameters).serialize()
|
||||
signedParams.open().copyTo(Paths.get("/some/path"))
|
||||
|
||||
Each individual parameter is documented in `the JavaDocs/KDocs for the NetworkParameters class
|
||||
<https://docs.corda.net/api/kotlin/corda/net.corda.core.node/-network-parameters/index.html>`__. The network map
|
||||
certificate is usually chained off the root certificate, and can be created according to the instructions above. Each
|
||||
time the zone parameters are changed, the epoch should be incremented. Epochs are essentially version numbers for the
|
||||
parameters, and they therefore cannot go backwards. Once saved, the new parameters can be served by the network map server.
|
||||
|
||||
Selecting parameter values
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
How to choose the parameters? This is the most complex question facing you as a new zone operator. Some settings may seem
|
||||
straightforward and others may involve cost/benefit trade-offs specific to your business. For example, you could choose
|
||||
to run a validating notary yourself, in which case you would (in the absence of SGX) see all the users' data. Or you could
|
||||
run a non-validating notary, with BFT fault tolerance, which implies recruiting others to take part in the cluster.
|
||||
|
||||
New network parameters will be added over time as Corda evolves. You will need to ensure that when your users upgrade,
|
||||
all the new network parameters are being served. You can ask for advice on the `corda-dev mailing list <https://groups.io/g/corda-dev>`__.
|
@ -2,11 +2,10 @@ Serialization
|
||||
=============
|
||||
|
||||
.. toctree::
|
||||
:caption: Other docs
|
||||
:maxdepth: 1
|
||||
|
||||
serialization.rst
|
||||
cordapp-custom-serializers
|
||||
serialization-default-evolution.rst
|
||||
serialization-enum-evolution.rst
|
||||
blob-inspector
|
||||
blob-inspector
|
||||
|
@ -5,73 +5,186 @@ Setting up a Corda network
|
||||
|
||||
.. contents::
|
||||
|
||||
A Corda network consists of a number of machines running nodes. These nodes communicate using persistent protocols in
|
||||
order to create and validate transactions.
|
||||
Bootstrapping a development network
|
||||
-----------------------------------
|
||||
|
||||
There are three broader categories of functionality one such node may have. These pieces of functionality are provided
|
||||
as services, and one node may run several of them.
|
||||
When testing CorDapps during development, you should use the :doc:`bootstrapper tool <network-bootstrapper>` to create
|
||||
a local test network.
|
||||
|
||||
* Notary: Nodes running a notary service witness state spends and have the final say in whether a transaction is a
|
||||
double-spend or not
|
||||
* Oracle: Network services that link the ledger to the outside world by providing facts that affect the validity of
|
||||
transactions
|
||||
* Regular node: All nodes have a vault and may start protocols communicating with other nodes, notaries and oracles and
|
||||
evolve their private ledger
|
||||
Creating your own compatibility zone
|
||||
------------------------------------
|
||||
|
||||
Setting up your own network
|
||||
---------------------------
|
||||
This section documents how to implement your own doorman and network map servers, which is the basic process required to
|
||||
create a dedicated zone. At this time we do not provide tooling to do this, because the needs of each zone are different
|
||||
and no generic, configurable doorman codebase has been written.
|
||||
|
||||
Certificates
|
||||
~~~~~~~~~~~~
|
||||
Do you need a zone?
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Every node in a given Corda network must have an identity certificate signed by the network's root CA. See
|
||||
:doc:`permissioning` for more information.
|
||||
Think twice before going down this route:
|
||||
|
||||
Configuration
|
||||
~~~~~~~~~~~~~
|
||||
1. It isn't necessary for testing.
|
||||
2. It isn't necessary for adding another layer of permissioning or 'know your customer' requirements onto your app.
|
||||
|
||||
A node can be configured by adding/editing ``node.conf`` in the node's directory. For details see :doc:`corda-configuration-file`.
|
||||
**Testing.** Creating a production-ready zone isn't necessary for testing as you can use the :doc:`network bootstrapper <network-bootstrapper>`
|
||||
tool to create all the certificates, keys, and distribute the needed map files to run many nodes. The bootstrapper can
|
||||
create a network locally on your desktop/laptop but it also knows how to automate cloud providers via their APIs and
|
||||
using Docker. In this way you can bring up a simulation of a real Corda network with different nodes on different
|
||||
machines in the cloud for your own testing. Testing this way has several advantages, most obviously that you avoid
|
||||
race conditions in your tests caused by nodes/tests starting before all map data has propagated to all nodes.
|
||||
You can read more about the reasons for the creation of the bootstrapper tool
|
||||
`in a blog post on the design thinking behind Corda's network map infrastructure <https://medium.com/corda/cordas-new-network-map-infrastructure-8c4c248fd7f3>`__.
|
||||
|
||||
An example configuration:
|
||||
**Permissioning.** And creating a zone is also unnecessary for imposing permissioning requirements beyond that of the
|
||||
base Corda network. You can control who can use your app by creating a *business network*. A business network is what we
|
||||
call a coalition of nodes that have chosen to run a particular app within a given commercial context. Business networks
|
||||
aren't represented in the Corda API at this time, partly because the technical side is so simple. You can create one
|
||||
via a simple three step process:
|
||||
|
||||
.. literalinclude:: example-code/src/main/resources/example-node.conf
|
||||
:language: cfg
|
||||
1. Distribute a list of X.500 names that are members of your business network, e.g. a simple way to do this is by
|
||||
hosting a text file with one name per line on your website at a fixed HTTPS URL. You could also write a simple
|
||||
request/response flow that serves the list over the Corda protocol itself, although this requires the business
|
||||
network to have a node for itself.
|
||||
2. Write a bit of code that downloads and caches the contents of this file on disk, and which loads it into memory in
|
||||
the node. A good place to do this is in a class annotated with ``@CordaService``, because this class can expose
|
||||
a ``Set<Party>`` field representing the membership of your service.
|
||||
3. In your flows use ``serviceHub.findService`` to get a reference to your ``@CordaService`` class, read the list of
|
||||
members and at the start of each flow, throw a FlowException if the counterparty isn't in the membership list.
|
||||
|
||||
The most important fields regarding network configuration are:
|
||||
In this way you can impose a centrally controlled ACL that all members will collectively enforce.
|
||||
|
||||
* ``p2pAddress``: This specifies a host and port to which Artemis will bind for messaging with other nodes. Note that the
|
||||
address bound will **NOT** be ``my-corda-node``, but rather ``::`` (all addresses on all network interfaces). The hostname specified
|
||||
is the hostname *that must be externally resolvable by other nodes in the network*. In the above configuration this is the
|
||||
resolvable name of a machine in a VPN.
|
||||
* ``rpcAddress``: The address to which Artemis will bind for RPC calls.
|
||||
* ``notary.serviceLegalName``: The name of the notary service, required to setup distributed notaries with the network-bootstrapper.
|
||||
.. note:: A production-ready Corda network and a new iteration of the testnet will be available soon.
|
||||
|
||||
Starting the nodes
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
Why create your own zone?
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
You will first need to create the local network by bootstrapping it with the bootstrapper. Details of how to do that can
|
||||
be found in :doc:`network-bootstrapper`.
|
||||
The primary reason to create a zone and provide the associated infrastructure is control over *network parameters*. These
|
||||
are settings that control Corda's operation, and on which all users in a network must agree. Failure to agree would create
|
||||
the Corda equivalent of a blockchain "hard fork". Parameters control things like the root of identity,
|
||||
how quickly users should upgrade, how long nodes can be offline before they are evicted from the system and so on.
|
||||
|
||||
Once that's done you may now start the nodes in any order. You should see a banner, some log lines and eventually
|
||||
``Node started up and registered``, indicating that the node is fully started.
|
||||
Creating a zone involves the following steps:
|
||||
|
||||
In terms of process management there is no prescribed method. You may start the jars by hand or perhaps use systemd and friends.
|
||||
1. Create the zone private keys and certificates. This procedure is conventional and no special knowledge is required:
|
||||
any self-signed set of certificates can be used. A professional quality zone will probably keep the keys inside a
|
||||
hardware security module (as the main Corda network and test networks do).
|
||||
2. Write a network map server.
|
||||
3. Optionally, create a doorman server.
|
||||
4. Finally, you would select and generate your network parameter file.
|
||||
|
||||
Logging
|
||||
~~~~~~~
|
||||
Writing a network map server
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Only a handful of important lines are printed to the console. For
|
||||
details/diagnosing problems check the logs.
|
||||
This server implements a simple HTTP based protocol described in the ":doc:`network-map`" page.
|
||||
The map server is responsible for gathering NodeInfo files from nodes, storing them, and distributing them back to the
|
||||
nodes in the zone. By doing this it is also responsible for choosing who is in and who is out: having a signed
|
||||
identity certificate is not enough to be a part of a Corda zone, you also need to be listed in the network map.
|
||||
It can be thought of as a DNS equivalent. If you want to de-list a user, you would do it here.
|
||||
|
||||
Logging is standard log4j2_ and may be configured accordingly. Logs
|
||||
are by default redirected to files in ``NODE_DIRECTORY/logs/``.
|
||||
It is very likely that your map server won't be entirely standalone, but rather, integrated with whatever your master
|
||||
user database is.
|
||||
|
||||
Connecting to the nodes
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
The network map server also distributes signed network parameter files and controls the roll-out schedule for when they
|
||||
become available for download and opt-in, and when they become enforced. This is again a policy decision you will
|
||||
probably choose to place some simple UI or workflow tooling around, in particular to enforce restrictions on who can
|
||||
edit the map or the parameters.
|
||||
|
||||
Once a node has started up successfully you may connect to it as a client to initiate protocols/query state etc.
|
||||
Depending on your network setup you may need to tunnel to do this remotely.
|
||||
Writing a doorman server
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
See the :doc:`tutorial-clientrpc-api` on how to establish an RPC link.
|
||||
This step is optional because your users can obtain a signed certificate in many different ways. The doorman protocol
|
||||
is again a very simple HTTP based approach in which a node creates keys and requests a certificate, polling until it
|
||||
gets back what it expects. However, you could also integrate this process with the rest of your signup process. For example,
|
||||
by building a tool that's integrated with your payment flow (if payment is required to take part in your zone at all).
|
||||
Alternatively you may wish to distribute USB smartcard tokens that generate the private key on first use, as is typically
|
||||
seen in national PKIs. There are many options.
|
||||
|
||||
Sidenote: A client is always associated with a single node with a single identity, which only sees their part of the ledger.
|
||||
If you do choose to make a doorman server, the bulk of the code you write will be workflow related. For instance,
|
||||
related to keeping track of an applicant as they proceed through approval. You should also impose any naming policies
|
||||
you have in the doorman process. If names are meant to match identities registered in government databases then that
|
||||
should be enforced here, alternatively, if names can be self-selected or anonymous, you would only bother with a
|
||||
deduplication check. Again it will likely be integrated with a master user database.
|
||||
|
||||
Corda does not currently provide a doorman or network map service out of the box, partly because when stripped of the
|
||||
zone specific policy there isn't much to them: just a basic HTTP server that most programmers will have favourite
|
||||
frameworks for anyway.
|
||||
|
||||
The protocol is:
|
||||
|
||||
* If $URL = ``https://some.server.com/some/path``
|
||||
* Node submits a PKCS#10 certificate signing request using HTTP POST to ``$URL/certificate``. It will have a MIME
|
||||
type of ``application/octet-stream``. The ``Platform-Version`` header is set to be "1.0" and the ``Client-Version`` header to reflect the node software version
|
||||
* The server returns an opaque string that references this request (let's call it ``$requestid``, or an HTTP error if something went wrong
|
||||
* The returned request ID should be persisted to disk, to handle zones where approval may take a long time due to manual
|
||||
intervention being required
|
||||
* The node starts polling ``$URL/$requestid`` using HTTP GET. The poll interval can be controlled by the server returning
|
||||
a response with a ``Cache-Control`` header
|
||||
* If the request is answered with a ``200 OK`` response, the body is expected to be a zip file. Each file is expected to
|
||||
be a binary X.509 certificate, and the certs are expected to be in order
|
||||
* If the request is answered with a ``204 No Content`` response, the node will try again later
|
||||
* If the request is answered with a ``403 Not Authorized`` response, the node will treat that as request rejection and give up
|
||||
* Other response codes will cause the node to abort with an exception
|
||||
|
||||
You can use any standard key tools to create the required key pairs and certificates. The ``X509Utilities`` class in the
|
||||
`Corda repository
|
||||
<https://github.com/corda/corda/blob/master/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt>`__
|
||||
shows how to generate the required key pairs and certificates using Bouncy Castle.
|
||||
|
||||
Setting zone parameters
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Zone parameters are stored in a file containing a Corda AMQP serialised ``SignedDataWithCert<NetworkParameters>``
|
||||
object. It is easy to create such a file with a small Java or Kotlin program. The ``NetworkParameters`` object is a
|
||||
simple data holder that could be read from e.g. a config file, or settings from a database. Signing and saving the
|
||||
resulting file is just a few lines of code. A full example can be found in `NetworkParametersCopier.kt
|
||||
<https://github.com/corda/corda/blob/master/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkParametersCopier.kt>`__,
|
||||
but a flavour of it looks like this:
|
||||
|
||||
.. container:: codeset
|
||||
|
||||
.. sourcecode:: java
|
||||
|
||||
NetworkParameters networkParameters = new NetworkParameters(
|
||||
4, // minPlatformVersion
|
||||
Collections.emptyList(), // notaries
|
||||
1024 * 1024 * 20, // maxMessageSize
|
||||
1024 * 1024 * 15, // maxTransactionSize
|
||||
Instant.now(), // modifiedTime
|
||||
2, // epoch
|
||||
Collections.emptyMap() // whitelist
|
||||
);
|
||||
CertificateAndKeyPair signingCertAndKeyPair = loadNetworkMapCA();
|
||||
SerializedBytes<SignedDataWithCert<NetworkParameters>> bytes = SerializedBytes.from(netMapCA.sign(networkParameters));
|
||||
Files.copy(bytes.open(), Paths.get("params-file"));
|
||||
|
||||
.. sourcecode:: kotlin
|
||||
|
||||
val networkParameters = NetworkParameters(
|
||||
minimumPlatformVersion = 4,
|
||||
notaries = listOf(...),
|
||||
maxMessageSize = 1024 * 1024 * 20 // 20mb, for example.
|
||||
maxTransactionSize = 1024 * 1024 * 15,
|
||||
modifiedTime = Instant.now(),
|
||||
epoch = 2,
|
||||
... etc ...
|
||||
)
|
||||
val signingCertAndKeyPair: CertificateAndKeyPair = loadNetworkMapCA()
|
||||
val signedParams: SerializedBytes<SignedNetworkParameters> = signingCertAndKeyPair.sign(networkParameters).serialize()
|
||||
signedParams.open().copyTo(Paths.get("/some/path"))
|
||||
|
||||
Each individual parameter is documented in `the JavaDocs/KDocs for the NetworkParameters class
|
||||
<https://docs.corda.net/api/kotlin/corda/net.corda.core.node/-network-parameters/index.html>`__. The network map
|
||||
certificate is usually chained off the root certificate, and can be created according to the instructions above. Each
|
||||
time the zone parameters are changed, the epoch should be incremented. Epochs are essentially version numbers for the
|
||||
parameters, and they therefore cannot go backwards. Once saved, the new parameters can be served by the network map server.
|
||||
|
||||
Selecting parameter values
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
How to choose the parameters? This is the most complex question facing you as a new zone operator. Some settings may seem
|
||||
straightforward and others may involve cost/benefit trade-offs specific to your business. For example, you could choose
|
||||
to run a validating notary yourself, in which case you would (in the absence of SGX) see all the users' data. Or you could
|
||||
run a non-validating notary, with BFT fault tolerance, which implies recruiting others to take part in the cluster.
|
||||
|
||||
New network parameters will be added over time as Corda evolves. You will need to ensure that when your users upgrade,
|
||||
all the new network parameters are being served. You can ask for advice on the `corda-dev mailing list <https://groups.io/g/corda-dev>`__.
|
@ -396,7 +396,7 @@ please follow the instructions in :doc:`node-database`.
|
||||
|
||||
Using the example RPC client
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
``/src/main/kotlin-source/com/example/client/ExampleClientRPC.kt`` defines a simple RPC client that connects to a node,
|
||||
``/src/main/kotlin/com/example/client/ExampleClientRPC.kt`` defines a simple RPC client that connects to a node,
|
||||
logs any existing IOUs and listens for any future IOUs. If you haven't created
|
||||
any IOUs when you first connect to one of the nodes, the client will simply log any future IOUs that are agreed.
|
||||
|
||||
|
@ -1,2 +1,3 @@
|
||||
kotlin.incremental=true
|
||||
org.gradle.jvmargs=-XX:+UseG1GC -Xmx1g
|
||||
org.gradle.jvmargs=-XX:+UseG1GC -Xmx1g -Dfile.encoding=UTF-8
|
||||
org.gradle.caching=true
|
||||
|
@ -18,8 +18,7 @@ class ArtemisTcpTransport {
|
||||
companion object {
|
||||
val CIPHER_SUITES = listOf(
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256"
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
|
||||
)
|
||||
|
||||
val TLS_VERSIONS = listOf("TLSv1.2")
|
||||
|
@ -63,8 +63,7 @@ class X509UtilitiesTest {
|
||||
val MEGA_CORP = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).party
|
||||
val CIPHER_SUITES = arrayOf(
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256"
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
|
||||
)
|
||||
// We ensure that all of the algorithms are both used (at least once) as first and second in the following [Pair]s.
|
||||
// We also add [DEFAULT_TLS_SIGNATURE_SCHEME] and [DEFAULT_IDENTITY_SIGNATURE_SCHEME] combinations for consistency.
|
||||
|
12
node/dist/build.gradle
vendored
12
node/dist/build.gradle
vendored
@ -1,4 +1,5 @@
|
||||
import org.gradle.internal.jvm.Jvm
|
||||
import org.apache.tools.ant.taskdefs.condition.Os
|
||||
|
||||
apply plugin: 'net.corda.plugins.publish-utils'
|
||||
|
||||
@ -59,20 +60,15 @@ task copyLauncherLibs(type: Copy, dependsOn: [project(':launcher').jar]) {
|
||||
into "$buildDir/tmp/launcher-lib"
|
||||
}
|
||||
|
||||
def isLinux = System.properties['os.name'].toLowerCase().contains('linux')
|
||||
def isMac = System.properties['os.name'].toLowerCase().contains('mac')
|
||||
if (isLinux || isMac) {
|
||||
if (Os.isFamily(Os.FAMILY_UNIX)) {
|
||||
println("Detected *nix system, enabling distribution creation")
|
||||
|
||||
|
||||
task buildLauncher(type: Exec, dependsOn: [copyLauncherLibs]) {
|
||||
description 'Build Launcher executable'
|
||||
|
||||
def relativeDir
|
||||
if (isLinux)
|
||||
relativeDir = "launcher"
|
||||
else
|
||||
relativeDir = "launcher.app/Contents"
|
||||
def relativeDir = "launcher"
|
||||
|
||||
|
||||
def extraArgs = [
|
||||
"-BjvmOptions=-javaagent:../../lib/quasar-core-${quasar_version}-jdk8.jar=${project(':node:capsule').quasarExcludeExpression}",
|
||||
|
@ -51,7 +51,7 @@ open class NetworkRegistrationHelper(private val certificatesDirectory: Path,
|
||||
}
|
||||
|
||||
private val requestIdStore = certificatesDirectory / "certificate-request-id.txt"
|
||||
private val rootTrustStore: X509KeyStore
|
||||
protected val rootTrustStore: X509KeyStore
|
||||
protected val rootCert: X509Certificate
|
||||
|
||||
init {
|
||||
@ -294,9 +294,18 @@ class NodeRegistrationHelper(private val config: NodeConfiguration, certService:
|
||||
private fun createTruststore(rootCertificate: X509Certificate) {
|
||||
// Save root certificates to trust store.
|
||||
config.p2pSslOptions.trustStore.get(createNew = true).update {
|
||||
if (this.aliases().hasNext()) {
|
||||
logger.warn("The node's trust store already exists. The following certificates will be overridden: ${this.aliases().asSequence()}")
|
||||
}
|
||||
println("Generating trust store for corda node.")
|
||||
// Assumes certificate chain always starts with client certificate and end with root certificate.
|
||||
setCertificate(CORDA_ROOT_CA, rootCertificate)
|
||||
// Copy remaining certificates from the network-trust-store
|
||||
rootTrustStore.aliases().asSequence().filter { it != CORDA_ROOT_CA }.forEach {
|
||||
val certificate = rootTrustStore.getCertificate(it)
|
||||
logger.info("Copying trusted certificate to the node's trust store: Alias: $it, Certificate: $certificate")
|
||||
setCertificate(it, certificate)
|
||||
}
|
||||
}
|
||||
println("Node trust store stored in ${config.p2pSslOptions.trustStore.path}.")
|
||||
}
|
||||
|
@ -64,8 +64,7 @@ class TLSAuthenticationTests {
|
||||
// Default supported TLS schemes for Corda nodes.
|
||||
private val CORDA_TLS_CIPHER_SUITES = arrayOf(
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256"
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
|
||||
)
|
||||
|
||||
@Test
|
||||
@ -161,27 +160,6 @@ class TLSAuthenticationTests {
|
||||
testConnect(serverSocket, clientSocket, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `All RSA - avoid ECC for DH`() {
|
||||
val (serverSocketFactory, clientSocketFactory) = buildTLSFactories(
|
||||
rootCAScheme = Crypto.RSA_SHA256,
|
||||
intermediateCAScheme = Crypto.RSA_SHA256,
|
||||
client1CAScheme = Crypto.RSA_SHA256,
|
||||
client1TLSScheme = Crypto.RSA_SHA256,
|
||||
client2CAScheme = Crypto.RSA_SHA256,
|
||||
client2TLSScheme = Crypto.RSA_SHA256
|
||||
)
|
||||
|
||||
val (serverSocket, clientSocket) = buildTLSSockets(
|
||||
serverSocketFactory,
|
||||
clientSocketFactory,
|
||||
0,
|
||||
0,
|
||||
CORDA_TLS_CIPHER_SUITES,
|
||||
arrayOf("TLS_DHE_RSA_WITH_AES_128_GCM_SHA256")) // Second client accepts DHE only.
|
||||
testConnect(serverSocket, clientSocket, "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256")
|
||||
}
|
||||
|
||||
// According to RFC 5246 (TLS 1.2), section 7.4.1.2 ClientHello cipher_suites:
|
||||
// This is a list of the cryptographic options supported by the client, with the client's first preference first.
|
||||
//
|
||||
|
@ -20,11 +20,13 @@ import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||
import net.corda.nodeapi.internal.crypto.X509KeyStore
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities.DISTRIBUTED_NOTARY_ALIAS_PREFIX
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities.createSelfSignedCACertificate
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.internal.stubs.CertificateStoreStubs
|
||||
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
import net.corda.testing.internal.stubs.CertificateStoreStubs
|
||||
import org.assertj.core.api.Assertions.*
|
||||
import org.bouncycastle.asn1.x509.GeneralName
|
||||
import org.bouncycastle.asn1.x509.GeneralSubtree
|
||||
@ -38,7 +40,9 @@ import java.security.PublicKey
|
||||
import java.security.cert.CertPathValidatorException
|
||||
import java.security.cert.X509Certificate
|
||||
import javax.security.auth.x500.X500Principal
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class NetworkRegistrationHelperTest {
|
||||
private val fs = Jimfs.newFileSystem(unix())
|
||||
@ -53,6 +57,7 @@ class NetworkRegistrationHelperTest {
|
||||
val baseDirectory = fs.getPath("/baseDir").createDirectories()
|
||||
|
||||
abstract class AbstractNodeConfiguration : NodeConfiguration
|
||||
|
||||
val certificatesDirectory = baseDirectory / "certificates"
|
||||
config = rigorousMock<AbstractNodeConfiguration>().also {
|
||||
doReturn(baseDirectory).whenever(it).baseDirectory
|
||||
@ -78,7 +83,7 @@ class NetworkRegistrationHelperTest {
|
||||
assertThat(config.p2pSslOptions.keyStore.getOptional()).isNull()
|
||||
assertThat(config.p2pSslOptions.trustStore.getOptional()).isNull()
|
||||
|
||||
val rootAndIntermediateCA = createDevIntermediateCaCertPath().also { saveNetworkTrustStore(it.first.certificate) }
|
||||
val rootAndIntermediateCA = createDevIntermediateCaCertPath().also { saveNetworkTrustStore(CORDA_ROOT_CA to it.first.certificate) }
|
||||
|
||||
createRegistrationHelper(rootAndIntermediateCA = rootAndIntermediateCA).buildKeystore()
|
||||
|
||||
@ -122,7 +127,7 @@ class NetworkRegistrationHelperTest {
|
||||
@Test
|
||||
fun `node CA with incorrect cert role`() {
|
||||
val nodeCaCertPath = createNodeCaCertPath(type = CertificateType.TLS)
|
||||
saveNetworkTrustStore(nodeCaCertPath.last())
|
||||
saveNetworkTrustStore(CORDA_ROOT_CA to nodeCaCertPath.last())
|
||||
val registrationHelper = createFixedResponseRegistrationHelper(nodeCaCertPath)
|
||||
assertThatExceptionOfType(CertificateRequestException::class.java)
|
||||
.isThrownBy { registrationHelper.buildKeystore() }
|
||||
@ -133,19 +138,39 @@ class NetworkRegistrationHelperTest {
|
||||
fun `node CA with incorrect subject`() {
|
||||
val invalidName = CordaX500Name("Foo", "MU", "GB")
|
||||
val nodeCaCertPath = createNodeCaCertPath(legalName = invalidName)
|
||||
saveNetworkTrustStore(nodeCaCertPath.last())
|
||||
saveNetworkTrustStore(CORDA_ROOT_CA to nodeCaCertPath.last())
|
||||
val registrationHelper = createFixedResponseRegistrationHelper(nodeCaCertPath)
|
||||
assertThatExceptionOfType(CertificateRequestException::class.java)
|
||||
.isThrownBy { registrationHelper.buildKeystore() }
|
||||
.withMessageContaining(invalidName.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `multiple certificates are copied to the node's trust store`() {
|
||||
val extraTrustedCertAlias = "trusted_test"
|
||||
val extraTrustedCert = createSelfSignedCACertificate(
|
||||
X500Principal("O=Test Trusted CA,L=MU,C=GB"),
|
||||
Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
|
||||
val rootAndIntermediateCA = createDevIntermediateCaCertPath().also {
|
||||
saveNetworkTrustStore(CORDA_ROOT_CA to it.first.certificate, extraTrustedCertAlias to extraTrustedCert)
|
||||
}
|
||||
|
||||
val registrationHelper = createRegistrationHelper(rootAndIntermediateCA = rootAndIntermediateCA)
|
||||
registrationHelper.buildKeystore()
|
||||
val trustStore = config.p2pSslOptions.trustStore.get()
|
||||
trustStore.run {
|
||||
assertTrue(contains(extraTrustedCertAlias))
|
||||
assertTrue(contains(CORDA_ROOT_CA))
|
||||
assertEquals(extraTrustedCert, get(extraTrustedCertAlias))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `wrong root cert in truststore`() {
|
||||
val wrongRootCert = X509Utilities.createSelfSignedCACertificate(
|
||||
val wrongRootCert = createSelfSignedCACertificate(
|
||||
X500Principal("O=Foo,L=MU,C=GB"),
|
||||
Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
|
||||
saveNetworkTrustStore(wrongRootCert)
|
||||
saveNetworkTrustStore(CORDA_ROOT_CA to wrongRootCert)
|
||||
|
||||
val registrationHelper = createRegistrationHelper()
|
||||
assertThatThrownBy {
|
||||
@ -159,7 +184,7 @@ class NetworkRegistrationHelperTest {
|
||||
assertThat(config.p2pSslOptions.keyStore.getOptional()).isNull()
|
||||
assertThat(config.p2pSslOptions.trustStore.getOptional()).isNull()
|
||||
|
||||
val rootAndIntermediateCA = createDevIntermediateCaCertPath().also { saveNetworkTrustStore(it.first.certificate) }
|
||||
val rootAndIntermediateCA = createDevIntermediateCaCertPath().also { saveNetworkTrustStore(CORDA_ROOT_CA to it.first.certificate) }
|
||||
|
||||
createRegistrationHelper(CertRole.SERVICE_IDENTITY, rootAndIntermediateCA).buildKeystore()
|
||||
|
||||
@ -239,11 +264,19 @@ class NetworkRegistrationHelperTest {
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveNetworkTrustStore(rootCert: X509Certificate) {
|
||||
/**
|
||||
* Saves given certificates into the truststore.
|
||||
*
|
||||
* @param trustedCertificates pairs containing the alias under which the given certificate needs to be stored and
|
||||
* the certificate itself.
|
||||
*/
|
||||
private fun saveNetworkTrustStore(vararg trustedCertificates: Pair<String, X509Certificate>) {
|
||||
config.certificatesDirectory.createDirectories()
|
||||
val rootTruststorePath = config.certificatesDirectory / networkRootTrustStoreFileName
|
||||
X509KeyStore.fromFile(rootTruststorePath, networkRootTrustStorePassword, createNew = true).update {
|
||||
setCertificate(X509Utilities.CORDA_ROOT_CA, rootCert)
|
||||
trustedCertificates.forEach {
|
||||
setCertificate(it.first, it.second)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -80,6 +80,8 @@ include 'tools:notary-healthcheck:contract'
|
||||
include 'tools:notary-healthcheck:cordapp'
|
||||
include 'tools:notary-healthcheck:client'
|
||||
|
||||
apply from: 'buildCacheSettings.gradle'
|
||||
|
||||
if (JavaVersion.current() == JavaVersion.VERSION_1_8) {
|
||||
include 'core-deterministic'
|
||||
include 'core-deterministic:testing'
|
||||
|
Loading…
x
Reference in New Issue
Block a user