diff --git a/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt b/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt index 9bf90c5a48..cf438f16f8 100644 --- a/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt @@ -20,14 +20,14 @@ class NodeKeystoreCheckTest { driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList())) { assertThatThrownBy { startNode(customOverrides = mapOf("devMode" to false)).getOrThrow() - }.hasMessageContaining("Identity certificate not found") + }.hasMessageContaining("One or more keyStores (identity or TLS) or trustStore not found.") } } @Test fun `node should throw exception if cert path doesn't chain to the trust root`() { driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList())) { - // Create keystores + // Create keystores. val keystorePassword = "password" val certificatesDirectory = baseDirectory(ALICE_NAME) / "certificates" val signingCertStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory, keystorePassword) @@ -46,7 +46,7 @@ class NodeKeystoreCheckTest { // Fiddle with node keystore. signingCertStore.get().update { - // Self signed root + // Self signed root. val badRootKeyPair = Crypto.generateKeyPair() val badRoot = X509Utilities.createSelfSignedCACertificate(X500Principal("O=Bad Root,L=Lodnon,C=GB"), badRootKeyPair) val nodeCA = getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA) diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index d0f2110f79..e1107839ad 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -69,7 +69,11 @@ import net.corda.node.utilities.NamedThreadFactory import net.corda.node.utilities.NodeBuildProperties import net.corda.nodeapi.internal.NodeInfoAndSigned import net.corda.nodeapi.internal.SignedNodeInfo +import net.corda.nodeapi.internal.config.CertificateStore import net.corda.nodeapi.internal.crypto.X509Utilities +import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_CA +import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_TLS +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.NODE_IDENTITY_ALIAS_PREFIX import net.corda.nodeapi.internal.persistence.* @@ -239,20 +243,20 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration, return proxies.fold(ops) { delegate, decorate -> decorate(delegate) } } - private fun initKeyStore(): X509Certificate { + private fun initKeyStores(): X509Certificate { if (configuration.devMode) { configuration.configureWithDevSSLCertificate() } - return validateKeyStore() + return validateKeyStores() } open fun generateAndSaveNodeInfo(): NodeInfo { check(started == null) { "Node has already been started" } log.info("Generating nodeInfo ...") - val trustRoot = initKeyStore() + val trustRoot = initKeyStores() val (identity, identityKeyPair) = obtainIdentity(notaryConfig = null) startDatabase() - val nodeCa = configuration.signingCertificateStore.get()[X509Utilities.CORDA_CLIENT_CA] + val nodeCa = configuration.signingCertificateStore.get()[CORDA_CLIENT_CA] identityService.start(trustRoot, listOf(identity.certificate, nodeCa)) return database.use { it.transaction { @@ -279,8 +283,8 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration, } log.info("Node starting up ...") - val trustRoot = initKeyStore() - val nodeCa = configuration.signingCertificateStore.get()[X509Utilities.CORDA_CLIENT_CA] + val trustRoot = initKeyStores() + val nodeCa = configuration.signingCertificateStore.get()[CORDA_CLIENT_CA] initialiseJVMAgents() schemaService.mappedSchemasWarnings().forEach { @@ -694,30 +698,50 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration, @VisibleForTesting protected open fun acceptableLiveFiberCountOnStop(): Int = 0 - private fun validateKeyStore(): X509Certificate { - val containCorrectKeys = try { - // This will throw IOException if key file not found or KeyStoreException if keystore password is incorrect. - val sslKeystore = configuration.p2pSslOptions.keyStore.get() - val identitiesKeystore = configuration.signingCertificateStore.get() - X509Utilities.CORDA_CLIENT_TLS in sslKeystore && X509Utilities.CORDA_CLIENT_CA in identitiesKeystore + private fun getCertificateStores(): AllCertificateStores? { + return try { + // The following will throw IOException if key file not found or KeyStoreException if keystore password is incorrect. + val sslKeyStore = configuration.p2pSslOptions.keyStore.get() + val identitiesKeyStore = configuration.signingCertificateStore.get() + val trustStore = configuration.p2pSslOptions.trustStore.get() + AllCertificateStores(trustStore, sslKeyStore, identitiesKeyStore) } catch (e: KeyStoreException) { - log.warn("Certificate key store found but key store password does not match configuration.") - false + log.warn("At least one of the keystores or truststore passwords does not match configuration.") + null } catch (e: IOException) { - log.error("IO exception while trying to validate keystore", e) - false + log.error("IO exception while trying to validate keystores and truststore", e) + null } - require(containCorrectKeys) { - "Identity certificate not found. " + - "Please either copy your existing identity key and certificate from another node, " + + } + + private data class AllCertificateStores(val trustStore: CertificateStore, val sslKeyStore: CertificateStore, val identitiesKeyStore: CertificateStore) + + private fun validateKeyStores(): X509Certificate { + // Step 1. Check trustStore, sslKeyStore and identitiesKeyStore exist. + val certStores = requireNotNull(getCertificateStores()) { + "One or more keyStores (identity or TLS) or trustStore not found. " + + "Please either copy your existing keys and certificates from another node, " + "or if you don't have one yet, fill out the config file and run corda.jar --initial-registration. " + "Read more at: https://docs.corda.net/permissioning.html" } + // Step 2. Check that trustStore contains the correct key-alias entry. + require(CORDA_ROOT_CA in certStores.trustStore) { + "Alias for trustRoot key not found. Please ensure you have an updated trustStore file." + } + // Step 3. Check that tls keyStore contains the correct key-alias entry. + require(CORDA_CLIENT_TLS in certStores.sslKeyStore) { + "Alias for TLS key not found. Please ensure you have an updated TLS keyStore file." + } - // Check all cert path chain to the trusted root - val sslCertChainRoot = configuration.p2pSslOptions.keyStore.get().query { getCertificateChain(X509Utilities.CORDA_CLIENT_TLS) }.last() - val nodeCaCertChainRoot = configuration.signingCertificateStore.get().query { getCertificateChain(X509Utilities.CORDA_CLIENT_CA) }.last() - val trustRoot = configuration.p2pSslOptions.trustStore.get()[X509Utilities.CORDA_ROOT_CA] + // Step 4. Check that identity keyStores contain the correct key-alias entry for Node CA. + require(CORDA_CLIENT_CA in certStores.identitiesKeyStore) { + "Alias for Node CA key not found. Please ensure you have an updated identity keyStore file." + } + + // Step 5. Check all cert paths chain to the trusted root. + val trustRoot = certStores.trustStore[CORDA_ROOT_CA] + val sslCertChainRoot = certStores.sslKeyStore.query { getCertificateChain(CORDA_CLIENT_TLS) }.last() + val nodeCaCertChainRoot = certStores.identitiesKeyStore.query { getCertificateChain(CORDA_CLIENT_CA) }.last() require(sslCertChainRoot == trustRoot) { "TLS certificate must chain to the trusted root." } require(nodeCaCertChainRoot == trustRoot) { "Client CA certificate must chain to the trusted root." }