mirror of
https://github.com/corda/corda.git
synced 2025-01-30 08:04:16 +00:00
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:
parent
27803cdc9e
commit
0ee116a1d9
@ -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.
|
.. 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
|
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
|
: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
|
``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
|
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.
|
@ -12,6 +12,7 @@ import net.corda.node.services.config.rpc.NodeRpcOptions
|
|||||||
import net.corda.nodeapi.internal.config.*
|
import net.corda.nodeapi.internal.config.*
|
||||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||||
import net.corda.tools.shell.SSHDConfiguration
|
import net.corda.tools.shell.SSHDConfiguration
|
||||||
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
@ -52,6 +53,8 @@ interface NodeConfiguration : NodeSSLConfiguration {
|
|||||||
// do not change this value without syncing it with ScheduledFlowsDrainingModeTest
|
// do not change this value without syncing it with ScheduledFlowsDrainingModeTest
|
||||||
val drainingModePollPeriod: Duration get() = Duration.ofSeconds(5)
|
val drainingModePollPeriod: Duration get() = Duration.ofSeconds(5)
|
||||||
val extraNetworkMapKeys: List<UUID>
|
val extraNetworkMapKeys: List<UUID>
|
||||||
|
val tlsCertCrlDistPoint: URL?
|
||||||
|
val tlsCertCrlIssuer: String?
|
||||||
|
|
||||||
fun validate(): List<String>
|
fun validate(): List<String>
|
||||||
|
|
||||||
@ -133,6 +136,8 @@ data class NodeConfigurationImpl(
|
|||||||
override val crlCheckSoftFail: Boolean,
|
override val crlCheckSoftFail: Boolean,
|
||||||
override val dataSourceProperties: Properties,
|
override val dataSourceProperties: Properties,
|
||||||
override val compatibilityZoneURL: URL? = null,
|
override val compatibilityZoneURL: URL? = null,
|
||||||
|
override val tlsCertCrlDistPoint: URL? = null,
|
||||||
|
override val tlsCertCrlIssuer: String? = null,
|
||||||
override val rpcUsers: List<User>,
|
override val rpcUsers: List<User>,
|
||||||
override val security: SecurityConfiguration? = null,
|
override val security: SecurityConfiguration? = null,
|
||||||
override val verifierType: VerifierType,
|
override val verifierType: VerifierType,
|
||||||
@ -179,10 +184,29 @@ data class NodeConfigurationImpl(
|
|||||||
}.asOptions(fallbackSslOptions)
|
}.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> {
|
override fun validate(): List<String> {
|
||||||
val errors = mutableListOf<String>()
|
val errors = mutableListOf<String>()
|
||||||
errors += validateDevModeOptions()
|
errors += validateDevModeOptions()
|
||||||
errors += validateRpcOptions(rpcOptions)
|
errors += validateRpcOptions(rpcOptions)
|
||||||
|
errors += validateTlsCertCrlConfig()
|
||||||
return errors
|
return errors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package net.corda.node.utilities.registration
|
|||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.*
|
import net.corda.core.internal.*
|
||||||
|
import net.corda.core.utilities.contextLogger
|
||||||
import net.corda.node.NodeRegistrationOption
|
import net.corda.node.NodeRegistrationOption
|
||||||
import net.corda.node.services.config.NodeConfiguration
|
import net.corda.node.services.config.NodeConfiguration
|
||||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
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_CA
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_TLS
|
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.CORDA_ROOT_CA
|
||||||
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
import org.bouncycastle.openssl.jcajce.JcaPEMWriter
|
import org.bouncycastle.openssl.jcajce.JcaPEMWriter
|
||||||
import org.bouncycastle.util.io.pem.PemObject
|
import org.bouncycastle.util.io.pem.PemObject
|
||||||
import java.io.StringWriter
|
import java.io.StringWriter
|
||||||
@ -216,6 +218,10 @@ class NodeRegistrationHelper(private val config: NodeConfiguration, certService:
|
|||||||
CORDA_CLIENT_CA,
|
CORDA_CLIENT_CA,
|
||||||
CertRole.NODE_CA) {
|
CertRole.NODE_CA) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val logger = contextLogger()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onSuccess(nodeCAKeyPair: KeyPair, certificates: List<X509Certificate>) {
|
override fun onSuccess(nodeCAKeyPair: KeyPair, certificates: List<X509Certificate>) {
|
||||||
createSSLKeystore(nodeCAKeyPair, certificates)
|
createSSLKeystore(nodeCAKeyPair, certificates)
|
||||||
createTruststore(certificates.last())
|
createTruststore(certificates.last())
|
||||||
@ -230,7 +236,10 @@ class NodeRegistrationHelper(private val config: NodeConfiguration, certService:
|
|||||||
certificates.first(),
|
certificates.first(),
|
||||||
nodeCAKeyPair,
|
nodeCAKeyPair,
|
||||||
config.myLegalName.x500Principal,
|
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)
|
setPrivateKey(CORDA_CLIENT_TLS, sslKeyPair.private, listOf(sslCert) + certificates)
|
||||||
}
|
}
|
||||||
println("SSL private key and certificate stored in ${config.sslKeystore}.")
|
println("SSL private key and certificate stored in ${config.sslKeystore}.")
|
||||||
|
@ -13,6 +13,7 @@ import org.assertj.core.api.Assertions.assertThat
|
|||||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
|
import java.net.URL
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.test.assertFalse
|
import kotlin.test.assertFalse
|
||||||
@ -28,6 +29,27 @@ class NodeConfigurationImplTest {
|
|||||||
configDebugOptions(false, null)
|
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
|
@Test
|
||||||
fun `check devModeOptions flag helper`() {
|
fun `check devModeOptions flag helper`() {
|
||||||
assertTrue { configDebugOptions(true, null).shouldCheckCheckpoints() }
|
assertTrue { configDebugOptions(true, null).shouldCheckCheckpoints() }
|
||||||
@ -114,8 +136,8 @@ class NodeConfigurationImplTest {
|
|||||||
return testConfiguration.copy(devMode = devMode, devModeOptions = devModeOptions)
|
return testConfiguration.copy(devMode = devMode, devModeOptions = devModeOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun testConfiguration(dataSourceProperties: Properties): NodeConfigurationImpl {
|
private fun configTlsCertCrlOptions(tlsCertCrlDistPoint: URL?, tlsCertCrlIssuer: String?, crlCheckSoftFail: Boolean = true): NodeConfiguration {
|
||||||
return testConfiguration.copy(dataSourceProperties = dataSourceProperties)
|
return testConfiguration.copy(tlsCertCrlDistPoint = tlsCertCrlDistPoint, tlsCertCrlIssuer = tlsCertCrlIssuer, crlCheckSoftFail = crlCheckSoftFail)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val testConfiguration = testNodeConfiguration()
|
private val testConfiguration = testNodeConfiguration()
|
||||||
@ -147,7 +169,8 @@ class NodeConfigurationImplTest {
|
|||||||
devMode = true,
|
devMode = true,
|
||||||
noLocalShell = false,
|
noLocalShell = false,
|
||||||
rpcSettings = rpcSettings,
|
rpcSettings = rpcSettings,
|
||||||
crlCheckSoftFail = true
|
crlCheckSoftFail = true,
|
||||||
|
tlsCertCrlDistPoint = null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,6 +58,8 @@ class NetworkRegistrationHelperTest {
|
|||||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||||
doReturn(nodeLegalName).whenever(it).myLegalName
|
doReturn(nodeLegalName).whenever(it).myLegalName
|
||||||
doReturn("").whenever(it).emailAddress
|
doReturn("").whenever(it).emailAddress
|
||||||
|
doReturn(null).whenever(it).tlsCertCrlDistPoint
|
||||||
|
doReturn(null).whenever(it).tlsCertCrlIssuer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user