CORDA-1968 Ensure we check for the trustroot existence and alias validity. (#3917)

* Ensure we check for the trustroot existence and alias validity.

* fix missing keystore message on integration test after changes.

* adding more requirements to check for missing aliases to validateKeyStores

* import X509Utilities const values (CORDA_ROOT_CA, CORDA_CLIENT_CA, CORDA_CLIENT_TLS) to AbstractNode.

* address review comment: avoid using !! (not null)
This commit is contained in:
Konstantinos Chalkias
2018-09-11 12:37:21 +01:00
committed by PokeyBot
parent 37c7fff8b1
commit 3ff7fc2585
2 changed files with 50 additions and 26 deletions

View File

@ -20,14 +20,14 @@ class NodeKeystoreCheckTest {
driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList())) { driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList())) {
assertThatThrownBy { assertThatThrownBy {
startNode(customOverrides = mapOf("devMode" to false)).getOrThrow() 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 @Test
fun `node should throw exception if cert path doesn't chain to the trust root`() { fun `node should throw exception if cert path doesn't chain to the trust root`() {
driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList())) { driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList())) {
// Create keystores // Create keystores.
val keystorePassword = "password" val keystorePassword = "password"
val certificatesDirectory = baseDirectory(ALICE_NAME) / "certificates" val certificatesDirectory = baseDirectory(ALICE_NAME) / "certificates"
val signingCertStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory, keystorePassword) val signingCertStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory, keystorePassword)
@ -46,7 +46,7 @@ class NodeKeystoreCheckTest {
// Fiddle with node keystore. // Fiddle with node keystore.
signingCertStore.get().update { signingCertStore.get().update {
// Self signed root // Self signed root.
val badRootKeyPair = Crypto.generateKeyPair() val badRootKeyPair = Crypto.generateKeyPair()
val badRoot = X509Utilities.createSelfSignedCACertificate(X500Principal("O=Bad Root,L=Lodnon,C=GB"), badRootKeyPair) val badRoot = X509Utilities.createSelfSignedCACertificate(X500Principal("O=Bad Root,L=Lodnon,C=GB"), badRootKeyPair)
val nodeCA = getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA) val nodeCA = getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)

View File

@ -69,7 +69,11 @@ import net.corda.node.utilities.NamedThreadFactory
import net.corda.node.utilities.NodeBuildProperties import net.corda.node.utilities.NodeBuildProperties
import net.corda.nodeapi.internal.NodeInfoAndSigned import net.corda.nodeapi.internal.NodeInfoAndSigned
import net.corda.nodeapi.internal.SignedNodeInfo 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
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.DISTRIBUTED_NOTARY_ALIAS_PREFIX
import net.corda.nodeapi.internal.crypto.X509Utilities.NODE_IDENTITY_ALIAS_PREFIX import net.corda.nodeapi.internal.crypto.X509Utilities.NODE_IDENTITY_ALIAS_PREFIX
import net.corda.nodeapi.internal.persistence.* 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) } return proxies.fold(ops) { delegate, decorate -> decorate(delegate) }
} }
private fun initKeyStore(): X509Certificate { private fun initKeyStores(): X509Certificate {
if (configuration.devMode) { if (configuration.devMode) {
configuration.configureWithDevSSLCertificate() configuration.configureWithDevSSLCertificate()
} }
return validateKeyStore() return validateKeyStores()
} }
open fun generateAndSaveNodeInfo(): NodeInfo { open fun generateAndSaveNodeInfo(): NodeInfo {
check(started == null) { "Node has already been started" } check(started == null) { "Node has already been started" }
log.info("Generating nodeInfo ...") log.info("Generating nodeInfo ...")
val trustRoot = initKeyStore() val trustRoot = initKeyStores()
val (identity, identityKeyPair) = obtainIdentity(notaryConfig = null) val (identity, identityKeyPair) = obtainIdentity(notaryConfig = null)
startDatabase() 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)) identityService.start(trustRoot, listOf(identity.certificate, nodeCa))
return database.use { return database.use {
it.transaction { it.transaction {
@ -279,8 +283,8 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
} }
log.info("Node starting up ...") log.info("Node starting up ...")
val trustRoot = initKeyStore() val trustRoot = initKeyStores()
val nodeCa = configuration.signingCertificateStore.get()[X509Utilities.CORDA_CLIENT_CA] val nodeCa = configuration.signingCertificateStore.get()[CORDA_CLIENT_CA]
initialiseJVMAgents() initialiseJVMAgents()
schemaService.mappedSchemasWarnings().forEach { schemaService.mappedSchemasWarnings().forEach {
@ -694,30 +698,50 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
@VisibleForTesting @VisibleForTesting
protected open fun acceptableLiveFiberCountOnStop(): Int = 0 protected open fun acceptableLiveFiberCountOnStop(): Int = 0
private fun validateKeyStore(): X509Certificate { private fun getCertificateStores(): AllCertificateStores? {
val containCorrectKeys = try { return try {
// This will throw IOException if key file not found or KeyStoreException if keystore password is incorrect. // The following will throw IOException if key file not found or KeyStoreException if keystore password is incorrect.
val sslKeystore = configuration.p2pSslOptions.keyStore.get() val sslKeyStore = configuration.p2pSslOptions.keyStore.get()
val identitiesKeystore = configuration.signingCertificateStore.get() val identitiesKeyStore = configuration.signingCertificateStore.get()
X509Utilities.CORDA_CLIENT_TLS in sslKeystore && X509Utilities.CORDA_CLIENT_CA in identitiesKeystore val trustStore = configuration.p2pSslOptions.trustStore.get()
AllCertificateStores(trustStore, sslKeyStore, identitiesKeyStore)
} catch (e: KeyStoreException) { } catch (e: KeyStoreException) {
log.warn("Certificate key store found but key store password does not match configuration.") log.warn("At least one of the keystores or truststore passwords does not match configuration.")
false null
} catch (e: IOException) { } catch (e: IOException) {
log.error("IO exception while trying to validate keystore", e) log.error("IO exception while trying to validate keystores and truststore", e)
false 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. " + "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" "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 // Step 4. Check that identity keyStores contain the correct key-alias entry for Node CA.
val sslCertChainRoot = configuration.p2pSslOptions.keyStore.get().query { getCertificateChain(X509Utilities.CORDA_CLIENT_TLS) }.last() require(CORDA_CLIENT_CA in certStores.identitiesKeyStore) {
val nodeCaCertChainRoot = configuration.signingCertificateStore.get().query { getCertificateChain(X509Utilities.CORDA_CLIENT_CA) }.last() "Alias for Node CA key not found. Please ensure you have an updated identity keyStore file."
val trustRoot = configuration.p2pSslOptions.trustStore.get()[X509Utilities.CORDA_ROOT_CA] }
// 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(sslCertChainRoot == trustRoot) { "TLS certificate must chain to the trusted root." }
require(nodeCaCertChainRoot == trustRoot) { "Client CA certificate must chain to the trusted root." } require(nodeCaCertChainRoot == trustRoot) { "Client CA certificate must chain to the trusted root." }