From 8df18e954f093900cb4744ce0d08f2509968d97d Mon Sep 17 00:00:00 2001 From: Patrick Kuo Date: Thu, 28 Jun 2018 15:10:52 +0100 Subject: [PATCH 1/4] ENT-2014 Deletes of NodeInfo can fail to propagate leading to infinite retries (#1101) * ENT-2014 Deletes of NodeInfo can fail to propagate leading to infinite retries ENT-1880 Move identity key generation to network registration process (cherry picked from commit c3ac203) --- .../net/corda/node/internal/NodeStartup.kt | 7 ++++ .../services/network/NetworkMapUpdater.kt | 17 ++++---- .../network/PersistentNetworkMapCache.kt | 3 +- .../registration/NetworkRegistrationHelper.kt | 3 -- .../services/network/NetworkMapUpdaterTest.kt | 41 +++++++++++++++++-- .../node/internal/network/NetworkMapServer.kt | 9 ++-- 6 files changed, 62 insertions(+), 18 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt index 95a03d4c48..f9a045445b 100644 --- a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt +++ b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt @@ -311,6 +311,13 @@ open class NodeStartup(val args: Array) { println("* *") println("******************************************************************") NodeRegistrationHelper(conf, HTTPNetworkRegistrationService(compatibilityZoneURL), nodeRegistrationConfig).buildKeystore() + + // Minimal changes to make registration tool create node identity. + // TODO: Move node identity generation logic from node to registration helper. + createNode(conf, getVersionInfo()).generateAndSaveNodeInfo() + + println("Successfully registered Corda node with compatibility zone, node identity keys and certificates are stored in '${conf.certificatesDirectory}', it is advised to backup the private keys and certificates.") + println("Corda node will now terminate.") } protected open fun loadConfigFile(cmdlineOptions: CmdLineOptions): Pair> = cmdlineOptions.loadConfig() diff --git a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapUpdater.kt b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapUpdater.kt index 2541a527ba..b30e3472e8 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapUpdater.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapUpdater.kt @@ -114,6 +114,15 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal, } val currentNodeHashes = networkMapCache.allNodeHashes + + // Remove node info from network map. + (currentNodeHashes - allHashesFromNetworkMap - fileWatcher.processedNodeInfoHashes) + .mapNotNull { + if (it != ourNodeInfoHash) { + networkMapCache.getNodeByHash(it) + } else null + }.forEach(networkMapCache::removeNode) + (allHashesFromNetworkMap - currentNodeHashes).mapNotNull { // Download new node info from network map try { @@ -128,14 +137,6 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal, networkMapCache.addNode(it) } - // Remove node info from network map. - (currentNodeHashes - allHashesFromNetworkMap - fileWatcher.processedNodeInfoHashes) - .mapNotNull { - if (it != ourNodeInfoHash) { - networkMapCache.getNodeByHash(it) - } else null - }.forEach(networkMapCache::removeNode) - return cacheTimeout } diff --git a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt index bd4f0f8b4d..fb815df849 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt @@ -255,7 +255,8 @@ open class PersistentNetworkMapCache( } private fun removeInfoDB(session: Session, nodeInfo: NodeInfo) { - val info = findByIdentityKey(session, nodeInfo.legalIdentitiesAndCerts.first().owningKey).singleOrNull() + // findByIdentityKey might returns multiple node info with the same key, need to pick the right one by comparing serial. + val info = findByIdentityKey(session, nodeInfo.legalIdentitiesAndCerts.first().owningKey).singleOrNull { it.serial == nodeInfo.serial } info?.let { session.remove(it) } // invalidate cache last - this way, we might serve up the wrong info for a short time, but it will get refreshed // on the next load diff --git a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt index 6a804bfc8f..987da3d6fa 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt @@ -97,9 +97,6 @@ open class NetworkRegistrationHelper(private val config: SSLConfiguration, onSuccess(keyPair, certificates) // All done, clean up temp files. requestIdStore.deleteIfExists() - - println("Successfully registered Corda node with compatibility zone, node identity keys and certificates are stored in '${config.certificatesDirectory}', it is advised to backup the private keys and certificates.") - println("Corda node will now terminate.") } private fun validateCertificates(registeringPublicKey: PublicKey, certificates: List) { diff --git a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapUpdaterTest.kt b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapUpdaterTest.kt index fd08de8cef..e7efd37ae2 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapUpdaterTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapUpdaterTest.kt @@ -16,7 +16,10 @@ import net.corda.core.serialization.serialize import net.corda.core.utilities.millis import net.corda.node.services.api.NetworkMapCacheInternal import net.corda.nodeapi.internal.NodeInfoAndSigned -import net.corda.nodeapi.internal.network.* +import net.corda.nodeapi.internal.network.NETWORK_PARAMS_UPDATE_FILE_NAME +import net.corda.nodeapi.internal.network.NodeInfoFilesCopier +import net.corda.nodeapi.internal.network.SignedNetworkParameters +import net.corda.nodeapi.internal.network.verifiedNetworkMapCert import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.core.* import net.corda.testing.driver.PortAllocation @@ -186,7 +189,7 @@ class NetworkMapUpdaterTest { sequence( expect { update: ParametersUpdateInfo -> assertEquals(update.updateDeadline, updateDeadline) - assertEquals(update.description,"Test update") + assertEquals(update.description, "Test update") assertEquals(update.hash, newParameters.serialize().hash) assertEquals(update.parameters, newParameters) } @@ -204,7 +207,7 @@ class NetworkMapUpdaterTest { Thread.sleep(2L * cacheExpiryMs) val newHash = newParameters.serialize().hash val keyPair = Crypto.generateKeyPair() - updater.acceptNewNetworkParameters(newHash, { hash -> hash.serialize().sign(keyPair)}) + updater.acceptNewNetworkParameters(newHash, { hash -> hash.serialize().sign(keyPair) }) val updateFile = baseDir / NETWORK_PARAMS_UPDATE_FILE_NAME val signedNetworkParams = updateFile.readObject() val paramsFromFile = signedNetworkParams.verifiedNetworkMapCert(DEV_ROOT_CA.certificate) @@ -299,6 +302,38 @@ class NetworkMapUpdaterTest { assertThat(networkMapCache.allNodeHashes).containsExactlyInAnyOrder(signedMyInfo.raw.hash, signedOtherInfo.raw.hash) } + @Test + fun `network map updater removes the correct node info after node info changes`() { + setUpdater() + + val builder = TestNodeInfoBuilder() + + builder.addLegalIdentity(CordaX500Name("Test", "London", "GB")) + + val signedNodeInfo1 = builder.buildWithSigned(1).signed + val signedNodeInfo2 = builder.buildWithSigned(2).signed + + // Test adding new node. + networkMapClient.publish(signedNodeInfo1) + // Not subscribed yet. + verify(networkMapCache, times(0)).addNode(any()) + + updater.subscribeToNetworkMap() + + // TODO: Remove sleep in unit test. + Thread.sleep(2L * cacheExpiryMs) + verify(networkMapCache, times(1)).addNode(signedNodeInfo1.verified()) + assert(networkMapCache.allNodeHashes.size == 1) + networkMapClient.publish(signedNodeInfo2) + Thread.sleep(2L * cacheExpiryMs) + scheduler.advanceTimeBy(10, TimeUnit.SECONDS) + + verify(networkMapCache, times(1)).addNode(signedNodeInfo2.verified()) + verify(networkMapCache, times(1)).removeNode(signedNodeInfo1.verified()) + + assert(networkMapCache.allNodeHashes.size == 1) + } + private fun createMockNetworkMapCache(): NetworkMapCacheInternal { return mock { val data = ConcurrentHashMap() diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/network/NetworkMapServer.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/network/NetworkMapServer.kt index d74b35053c..c69c9060a4 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/network/NetworkMapServer.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/network/NetworkMapServer.kt @@ -8,8 +8,6 @@ import net.corda.core.node.NetworkParameters import net.corda.core.node.NodeInfo import net.corda.core.serialization.serialize import net.corda.core.utilities.NetworkHostAndPort -import net.corda.core.utilities.contextLogger -import net.corda.core.utilities.days import net.corda.nodeapi.internal.SignedNodeInfo import net.corda.nodeapi.internal.createDevNetworkMapCa import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair @@ -128,7 +126,12 @@ class NetworkMapServer(private val pollInterval: Duration, val hash = signedNodeInfo.raw.hash val nodeInfo = signedNodeInfo.verified() val privateNetwork = nodeNamesUUID[nodeInfo.legalIdentities[0].name] - networkMaps.computeIfAbsent(privateNetwork, { mutableSetOf() }).add(hash) + val map = networkMaps.computeIfAbsent(privateNetwork) { mutableSetOf() } + map.add(hash) + nodeInfoMap.filter { it.value.verified().legalIdentities.first().name == signedNodeInfo.verified().legalIdentities.first().name }.forEach { t, _ -> + nodeInfoMap.remove(t) + map.remove(t) + } nodeInfoMap[hash] = signedNodeInfo ok() } catch (e: Exception) { From 4267513332beb7046fd645a8ed859dbfa4457bb1 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Thu, 28 Jun 2018 16:37:51 +0100 Subject: [PATCH 2/4] CORDA-1693: Ensure that DemoBench's RPC connections terminate on shutdown. (#3468) --- .../net/corda/demobench/explorer/Explorer.kt | 3 +- .../kotlin/net/corda/demobench/pty/R3Pty.kt | 4 +- .../kotlin/net/corda/demobench/rpc/NodeRPC.kt | 49 ++++++++++++------- .../corda/demobench/views/NodeTerminalView.kt | 35 ++++++------- 4 files changed, 52 insertions(+), 39 deletions(-) diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/explorer/Explorer.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/explorer/Explorer.kt index 62cfb8223c..eb28552c88 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/explorer/Explorer.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/explorer/Explorer.kt @@ -12,6 +12,7 @@ import net.corda.demobench.readErrorLines import tornadofx.* import java.io.IOException import java.nio.file.Files +import java.nio.file.Path import java.nio.file.StandardCopyOption.REPLACE_EXISTING import java.util.concurrent.Executors @@ -123,7 +124,7 @@ class Explorer internal constructor(private val explorerController: ExplorerCont class ExplorerController : Controller() { private val jvm by inject() - private val explorerPath = jvm.applicationDir.resolve("explorer").resolve("node-explorer.jar") + private val explorerPath: Path = jvm.applicationDir.resolve("explorer").resolve("node-explorer.jar") init { log.info("Explorer JAR: $explorerPath") diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/pty/R3Pty.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/pty/R3Pty.kt index 3bd1b0c6ec..4aec5c1c79 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/pty/R3Pty.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/pty/R3Pty.kt @@ -21,7 +21,7 @@ class R3Pty(val name: CordaX500Name, settings: SettingsProvider, dimension: Dime val terminal = JediTermWidget(dimension, settings) - val isConnected: Boolean get() = terminal.ttyConnector?.isConnected == true + val isConnected: Boolean get() = terminal.ttyConnector?.isConnected ?: false override fun close() { log.info("Closing terminal '{}'", name) @@ -65,5 +65,5 @@ class R3Pty(val name: CordaX500Name, settings: SettingsProvider, dimension: Dime @Suppress("unused") @Throws(InterruptedException::class) - fun waitFor(): Int? = terminal.ttyConnector?.waitFor() + fun waitFor(): Int = terminal.ttyConnector?.waitFor() ?: -1 } diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/rpc/NodeRPC.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/rpc/NodeRPC.kt index 62cb035c16..9300fc9643 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/rpc/NodeRPC.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/rpc/NodeRPC.kt @@ -12,37 +12,46 @@ import java.util.concurrent.TimeUnit.SECONDS class NodeRPC(config: NodeConfigWrapper, start: (NodeConfigWrapper, CordaRPCOps) -> Unit, invoke: (CordaRPCOps) -> Unit) : AutoCloseable { private companion object { private val log = contextLogger() - val oneSecond = SECONDS.toMillis(1) + private val oneSecond = SECONDS.toMillis(1) } private val rpcClient = CordaRPCClient(NetworkHostAndPort("localhost", config.nodeConfig.rpcAddress.port)) + @Volatile private var rpcConnection: CordaRPCConnection? = null - private val timer = Timer() + private val timer = Timer("DemoBench NodeRPC (${config.key})", true) + @Volatile + private var timerThread: Thread? = null init { val setupTask = object : TimerTask() { override fun run() { - try { - val user = config.nodeConfig.rpcUsers[0] - val connection = rpcClient.start(user.username, user.password) - rpcConnection = connection - val ops = connection.proxy + // Grab the timer's thread so that we know which one to interrupt. + // This looks like the simplest way of getting the thread. (Ugh) + timerThread = Thread.currentThread() - // Cancel the "setup" task now that we've created the RPC client. - this.cancel() - - // Run "start-up" task, now that the RPC client is ready. - start(config, ops) - - // Schedule a new task that will refresh the display once per second. - timer.schedule(object : TimerTask() { - override fun run() { - invoke(ops) - } - }, 0, oneSecond) + val user = config.nodeConfig.rpcUsers[0] + val ops: CordaRPCOps = try { + rpcClient.start(user.username, user.password).let { connection -> + rpcConnection = connection + connection.proxy + } } catch (e: Exception) { log.warn("Node '{}' not ready yet (Error: {})", config.nodeConfig.myLegalName, e.message) + return } + + // Cancel the "setup" task now that we've created the RPC client. + cancel() + + // Run "start-up" task, now that the RPC client is ready. + start(config, ops) + + // Schedule a new task that will refresh the display once per second. + timer.schedule(object : TimerTask() { + override fun run() { + invoke(ops) + } + }, 0, oneSecond) } } @@ -53,9 +62,11 @@ class NodeRPC(config: NodeConfigWrapper, start: (NodeConfigWrapper, CordaRPCOps) override fun close() { timer.cancel() try { + // No need to notify the node because it's also shutting down. rpcConnection?.forceClose() } catch (e: Exception) { log.error("Failed to close RPC connection (Error: {})", e.message) } + timerThread?.interrupt() } } diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTerminalView.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTerminalView.kt index b91677b41a..abd626159b 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTerminalView.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTerminalView.kt @@ -42,7 +42,7 @@ class NodeTerminalView : Fragment() { override val root by fxml() private companion object { - val pageSpecification = PageSpecification(1, 1) + private val pageSpecification = PageSpecification(1, 1) } private val nodeController by inject() @@ -86,7 +86,7 @@ class NodeTerminalView : Fragment() { root.children.add(stack) root.isVisible = true - SwingUtilities.invokeLater({ + SwingUtilities.invokeLater { val r3pty = R3Pty(config.nodeConfig.myLegalName, TerminalSettingsProvider(), Dimension(160, 80), onExit) pty = r3pty @@ -112,7 +112,7 @@ class NodeTerminalView : Fragment() { rpc?.close() } } - }) + } } /* @@ -181,9 +181,9 @@ class NodeTerminalView : Fragment() { } private fun launchRPC(config: NodeConfigWrapper) = NodeRPC( - config = config, - start = this::initialise, - invoke = this::pollCashBalances + config = config, + start = ::initialise, + invoke = ::pollCashBalances ) private fun initialise(config: NodeConfigWrapper, ops: CordaRPCOps) { @@ -234,8 +234,8 @@ class NodeTerminalView : Fragment() { private fun pollCashBalances(ops: CordaRPCOps) { try { val cashBalances = ops.getCashBalances().entries.joinToString( - separator = ", ", - transform = { e -> e.value.toString() } + separator = ", ", + transform = { e -> e.value.toString() } ) Platform.runLater { @@ -252,21 +252,18 @@ class NodeTerminalView : Fragment() { header.isDisable = true subscriptions.forEach { // Don't allow any exceptions here to halt tab destruction. - try { - it.unsubscribe() - } catch (e: Exception) { - } + ignoreExceptions { it.unsubscribe() } } - webServer.close() - explorer.close() - viewer.close() - rpc?.close() + ignoreExceptions { webServer.close() } + ignoreExceptions { explorer.close() } + ignoreExceptions { viewer.close() } + ignoreExceptions { rpc?.close() } } fun destroy() { if (!isDestroyed) { shutdown() - pty?.close() + ignoreExceptions { pty?.close() } isDestroyed = true } } @@ -279,6 +276,10 @@ class NodeTerminalView : Fragment() { } } + private fun ignoreExceptions(op: () -> Unit) { + try { op() } catch (e: Exception) {} + } + class TerminalSettingsProvider : DefaultSettingsProvider() { override fun getDefaultStyle() = TextStyle(TerminalColor.WHITE, TerminalColor.rgb(50, 50, 50)) override fun emulateX11CopyPaste() = true From 33511b40dcea5b394ef6c96dc6f0a106f8d3f1c3 Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Thu, 28 Jun 2018 16:00:24 +0200 Subject: [PATCH 3/4] Minor: work around a Kotlin compiler performance regressions --- build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build.gradle b/build.gradle index 5fd6c8e84e..6935190f47 100644 --- a/build.gradle +++ b/build.gradle @@ -140,6 +140,10 @@ allprojects { apply plugin: 'org.owasp.dependencycheck' apply plugin: 'kotlin-allopen' + // This line works around a serious performance regression in the Kotlin 1.2.50 gradle plugin, it's fixed in 1.2.51 + // TODO: Remove when we upgrade past Kotlin 1.2.51 + inspectClassesForKotlinIC.enabled = false + allOpen { annotations( "javax.persistence.Entity", From 7ae0775514b8ae0e1da365d1e0761da19b29e4c6 Mon Sep 17 00:00:00 2001 From: Michal Kit Date: Fri, 29 Jun 2018 08:13:43 +0100 Subject: [PATCH 4/4] CORDA-1688 Updating CRL config docs (#3472) * CORDA-1688 Updating CRL config docs * Addressing review comments --- docs/source/corda-configuration-file.rst | 25 ++++++++++++++++++++++-- docs/source/upgrade-notes.rst | 17 ++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/docs/source/corda-configuration-file.rst b/docs/source/corda-configuration-file.rst index 2c64e7ab7f..8293a062e2 100644 --- a/docs/source/corda-configuration-file.rst +++ b/docs/source/corda-configuration-file.rst @@ -230,11 +230,13 @@ absolute path to the node's base directory. .. note:: This is temporary feature for onboarding network participants that limits their visibility for privacy reasons. :tlsCertCrlDistPoint: CRL distribution point (i.e. URL) for the TLS certificate. Default value is NULL, which indicates no CRL availability for the TLS certificate. - Note: If crlCheckSoftFail is FALSE (meaning that there is the strict CRL checking mode) this value needs to be set. + + .. note:: This needs to be set if crlCheckSoftFail is false (i.e. strict CRL checking is on). :tlsCertCrlIssuer: CRL issuer (given in the X500 name format) for the TLS certificate. Default value is NULL, which indicates that the issuer of the TLS certificate is also the issuer of the CRL. - Note: If this parameter is set then the tlsCertCrlDistPoint needs to be set as well. + + .. note:: If this parameter is set then `tlsCertCrlDistPoint` needs to be set as well. :flowMonitorPeriodMillis: ``Duration`` of the period suspended flows waiting for IO are logged. Default value is ``60 seconds``. @@ -281,3 +283,22 @@ This is an example of adding/overriding the keyStore password : .. sourcecode:: shell java -Dcorda.rpcSettings.ssl.keyStorePassword=mypassword -jar node.jar + +CRL Configuration +----------------- +The Corda Network provides an endpoint serving an empty certificate revocation list for the TLS-level certificates. +This is intended for deployments that do not provide a CRL infrastructure but still require a strict CRL mode checking. +In such a case use the following URL in `tlsCertCrlDistPoint` option configuration: + + .. sourcecode:: kotlin + + "https://crl.cordaconnect.org/cordatls.crl" + +Together with the above configuration `tlsCertCrlIssuer` option needs to be set to the following value: + + .. sourcecode:: kotlin + + "C=US, L=New York, O=R3 HoldCo LLC, OU=Corda, CN=Corda Root CA" + +This set-up ensures that the TLS-level certificates are embedded with the CRL distribution point referencing the CRL issued by R3. +In cases where a proprietary CRL infrastructure is provided those values need to be changed accordingly. diff --git a/docs/source/upgrade-notes.rst b/docs/source/upgrade-notes.rst index c7f90a8eca..fa01427ec4 100644 --- a/docs/source/upgrade-notes.rst +++ b/docs/source/upgrade-notes.rst @@ -227,6 +227,23 @@ Also, the property `rpcPort` is now deprecated, so it would be preferable to sub Equivalent changes should be performed on classes extending ``CordformDefinition``. +* Certificate Revocation List (CRL) support: + + The newly added feature of certificate revocation (see :doc:`certificate-revocation`) introduces few changes to the node configuration. + In the configuration file it is required to explicitly specify what mode of the CRL check the node should apply. For that purpose the `crlCheckSoftFail` + parameter is now expected to be set explicitly in the node's SSL configuration. + Setting the `crlCheckSoftFail` to true, relaxes the CRL checking policy. In this mode, the SSL communication + will fail only when the certificate revocation status can be checked and the certificate is revoked. Otherwise it will succeed. + If `crlCheckSoftFail` is false, then the SSL failure will occur also if the certificate revocation status cannot be checked (e.g. due to a network failure). + + Older versions of Corda do not have CRL distribution points embedded in the SSL certificates. + As such, in order to be able to reuse node and SSL certificates generated in those versions of Corda, the `crlCheckSoftFail` needs + to be set to true. This is required due to the fact that node and SSL certificates produced in the older versions of Corda miss attributes + required for the CRL check process. In this mode, if the CRL is unavailable for whatever reason, the check will still pass and the SSL connection will be allowed. + + .. note:: The support for the mitigating this issue and being able to use the `strict` mode (i.e. with `crlCheckSoftFail` = false) + of the CRL checking with the certificates generated in the previous versions of Corda is going to be added in the near future. + Testing ^^^^^^^