CORDA-1476 Adding TLS certificate CRL extension point configs (#3140)

* Adding TLS certificate CRL extension point configs

* Addressing review comments

* Addressing review comments - round 3
This commit is contained in:
Michal Kit 2018-05-17 08:21:24 +01:00 committed by GitHub
parent 27803cdc9e
commit 0ee116a1d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 70 additions and 5 deletions

View File

@ -205,6 +205,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.
: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.
Examples
--------
@ -294,4 +301,4 @@ path to the node's base directory.
:permissions: A list of permissions for starting flows via RPC. To give the user the permission to start the flow
``foo.bar.FlowClass``, add the string ``StartFlow.foo.bar.FlowClass`` to the list. If the list
contains the string ``ALL``, the user can start any flow via RPC. This value is intended for administrator
users and for development.
users and for development.

View File

@ -12,6 +12,7 @@ import net.corda.node.services.config.rpc.NodeRpcOptions
import net.corda.nodeapi.internal.config.*
import net.corda.nodeapi.internal.persistence.DatabaseConfig
import net.corda.tools.shell.SSHDConfiguration
import org.bouncycastle.asn1.x500.X500Name
import org.slf4j.Logger
import java.net.URL
import java.nio.file.Path
@ -52,6 +53,8 @@ interface NodeConfiguration : NodeSSLConfiguration {
// do not change this value without syncing it with ScheduledFlowsDrainingModeTest
val drainingModePollPeriod: Duration get() = Duration.ofSeconds(5)
val extraNetworkMapKeys: List<UUID>
val tlsCertCrlDistPoint: URL?
val tlsCertCrlIssuer: String?
fun validate(): List<String>
@ -133,6 +136,8 @@ data class NodeConfigurationImpl(
override val crlCheckSoftFail: Boolean,
override val dataSourceProperties: Properties,
override val compatibilityZoneURL: URL? = null,
override val tlsCertCrlDistPoint: URL? = null,
override val tlsCertCrlIssuer: String? = null,
override val rpcUsers: List<User>,
override val security: SecurityConfiguration? = null,
override val verifierType: VerifierType,
@ -179,10 +184,29 @@ data class NodeConfigurationImpl(
}.asOptions(fallbackSslOptions)
}
private fun validateTlsCertCrlConfig(): List<String> {
val errors = mutableListOf<String>()
if (tlsCertCrlIssuer != null) {
if (tlsCertCrlDistPoint == null) {
errors += "tlsCertCrlDistPoint needs to be specified when tlsCertCrlIssuer is not NULL"
}
try {
X500Name(tlsCertCrlIssuer)
} catch (e: Exception) {
errors += "Error when parsing tlsCertCrlIssuer: ${e.message}"
}
}
if (!crlCheckSoftFail && tlsCertCrlDistPoint == null) {
errors += "tlsCertCrlDistPoint needs to be specified when crlCheckSoftFail is FALSE"
}
return errors
}
override fun validate(): List<String> {
val errors = mutableListOf<String>()
errors += validateDevModeOptions()
errors += validateRpcOptions(rpcOptions)
errors += validateTlsCertCrlConfig()
return errors
}

View File

@ -3,6 +3,7 @@ package net.corda.node.utilities.registration
import net.corda.core.crypto.Crypto
import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.*
import net.corda.core.utilities.contextLogger
import net.corda.node.NodeRegistrationOption
import net.corda.node.services.config.NodeConfiguration
import net.corda.nodeapi.internal.config.SSLConfiguration
@ -12,6 +13,7 @@ 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 org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.openssl.jcajce.JcaPEMWriter
import org.bouncycastle.util.io.pem.PemObject
import java.io.StringWriter
@ -216,6 +218,10 @@ class NodeRegistrationHelper(private val config: NodeConfiguration, certService:
CORDA_CLIENT_CA,
CertRole.NODE_CA) {
companion object {
val logger = contextLogger()
}
override fun onSuccess(nodeCAKeyPair: KeyPair, certificates: List<X509Certificate>) {
createSSLKeystore(nodeCAKeyPair, certificates)
createTruststore(certificates.last())
@ -230,7 +236,10 @@ class NodeRegistrationHelper(private val config: NodeConfiguration, certService:
certificates.first(),
nodeCAKeyPair,
config.myLegalName.x500Principal,
sslKeyPair.public)
sslKeyPair.public,
crlDistPoint = config.tlsCertCrlDistPoint?.toString(),
crlIssuer = if (config.tlsCertCrlIssuer != null) X500Name(config.tlsCertCrlIssuer) else null)
logger.info("Generated TLS certificate: $sslCert")
setPrivateKey(CORDA_CLIENT_TLS, sslKeyPair.private, listOf(sslCert) + certificates)
}
println("SSL private key and certificate stored in ${config.sslKeystore}.")

View File

@ -13,6 +13,7 @@ import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Test
import java.net.URI
import java.net.URL
import java.nio.file.Paths
import java.util.*
import kotlin.test.assertFalse
@ -28,6 +29,27 @@ class NodeConfigurationImplTest {
configDebugOptions(false, null)
}
@Test
fun `can't have tlsCertCrlDistPoint null when tlsCertCrlIssuer is given`() {
val configValidationResult = configTlsCertCrlOptions(null, "C=US, L=New York, OU=Corda, O=R3 HoldCo LLC, CN=Corda Root CA").validate()
assertTrue { configValidationResult.isNotEmpty() }
assertThat(configValidationResult.first()).contains("tlsCertCrlDistPoint needs to be specified when tlsCertCrlIssuer is not NULL")
}
@Test
fun `tlsCertCrlIssuer validation fails when misconfigured`() {
val configValidationResult = configTlsCertCrlOptions(URL("http://test.com/crl"), "Corda Root CA").validate()
assertTrue { configValidationResult.isNotEmpty() }
assertThat(configValidationResult.first()).contains("Error when parsing tlsCertCrlIssuer:")
}
@Test
fun `can't have tlsCertCrlDistPoint null when crlCheckSoftFail is false`() {
val configValidationResult = configTlsCertCrlOptions(null, null, false).validate()
assertTrue { configValidationResult.isNotEmpty() }
assertThat(configValidationResult.first()).contains("tlsCertCrlDistPoint needs to be specified when crlCheckSoftFail is FALSE")
}
@Test
fun `check devModeOptions flag helper`() {
assertTrue { configDebugOptions(true, null).shouldCheckCheckpoints() }
@ -114,8 +136,8 @@ class NodeConfigurationImplTest {
return testConfiguration.copy(devMode = devMode, devModeOptions = devModeOptions)
}
private fun testConfiguration(dataSourceProperties: Properties): NodeConfigurationImpl {
return testConfiguration.copy(dataSourceProperties = dataSourceProperties)
private fun configTlsCertCrlOptions(tlsCertCrlDistPoint: URL?, tlsCertCrlIssuer: String?, crlCheckSoftFail: Boolean = true): NodeConfiguration {
return testConfiguration.copy(tlsCertCrlDistPoint = tlsCertCrlDistPoint, tlsCertCrlIssuer = tlsCertCrlIssuer, crlCheckSoftFail = crlCheckSoftFail)
}
private val testConfiguration = testNodeConfiguration()
@ -147,7 +169,8 @@ class NodeConfigurationImplTest {
devMode = true,
noLocalShell = false,
rpcSettings = rpcSettings,
crlCheckSoftFail = true
crlCheckSoftFail = true,
tlsCertCrlDistPoint = null
)
}
}

View File

@ -58,6 +58,8 @@ class NetworkRegistrationHelperTest {
doReturn("cordacadevpass").whenever(it).keyStorePassword
doReturn(nodeLegalName).whenever(it).myLegalName
doReturn("").whenever(it).emailAddress
doReturn(null).whenever(it).tlsCertCrlDistPoint
doReturn(null).whenever(it).tlsCertCrlIssuer
}
}