mirror of
https://github.com/corda/corda.git
synced 2025-02-01 00:45:59 +00:00
ENT-1443 Add cert role to CSR and doorman issue cert according to the cert role (#2620)
* ENT-1443 Add cert role to CSR and doorman issue cert according to the cert role (#431) * Doorman and HSM create certificate base on requested cert role specified in the certificate signing request. (cherry picked from commit 94f7392) * remove R3 corda code
This commit is contained in:
parent
c8672d373f
commit
1552e992e7
@ -2,10 +2,7 @@
|
||||
|
||||
package net.corda.nodeapi.internal.config
|
||||
|
||||
import com.typesafe.config.Config
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import com.typesafe.config.ConfigUtil
|
||||
import com.typesafe.config.ConfigValueFactory
|
||||
import com.typesafe.config.*
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.noneOrSingle
|
||||
import net.corda.core.internal.uncheckedCast
|
||||
@ -78,7 +75,12 @@ private fun Config.getSingleValue(path: String, type: KType): Any? {
|
||||
NetworkHostAndPort::class -> NetworkHostAndPort.parse(getString(path))
|
||||
Path::class -> Paths.get(getString(path))
|
||||
URL::class -> URL(getString(path))
|
||||
CordaX500Name::class -> CordaX500Name.parse(getString(path))
|
||||
CordaX500Name::class -> {
|
||||
when (getValue(path).valueType()) {
|
||||
ConfigValueType.OBJECT -> getConfig(path).parseAs()
|
||||
else -> CordaX500Name.parse(getString(path))
|
||||
}
|
||||
}
|
||||
Properties::class -> getConfig(path).toProperties()
|
||||
Config::class -> getConfig(path)
|
||||
else -> if (typeClass.java.isEnum) {
|
||||
|
@ -259,13 +259,17 @@ object X509Utilities {
|
||||
private fun createCertificateSigningRequest(subject: X500Principal,
|
||||
email: String,
|
||||
keyPair: KeyPair,
|
||||
signatureScheme: SignatureScheme): PKCS10CertificationRequest {
|
||||
signatureScheme: SignatureScheme,
|
||||
certRole: CertRole): PKCS10CertificationRequest {
|
||||
val signer = ContentSignerBuilder.build(signatureScheme, keyPair.private, Crypto.findProvider(signatureScheme.providerName))
|
||||
return JcaPKCS10CertificationRequestBuilder(subject, keyPair.public).addAttribute(BCStyle.E, DERUTF8String(email)).build(signer)
|
||||
return JcaPKCS10CertificationRequestBuilder(subject, keyPair.public)
|
||||
.addAttribute(BCStyle.E, DERUTF8String(email))
|
||||
.addAttribute(ASN1ObjectIdentifier(CordaOID.X509_EXTENSION_CORDA_ROLE), certRole)
|
||||
.build(signer)
|
||||
}
|
||||
|
||||
fun createCertificateSigningRequest(subject: X500Principal, email: String, keyPair: KeyPair): PKCS10CertificationRequest {
|
||||
return createCertificateSigningRequest(subject, email, keyPair, DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
fun createCertificateSigningRequest(subject: X500Principal, email: String, keyPair: KeyPair, certRole: CertRole = CertRole.NODE_CA): PKCS10CertificationRequest {
|
||||
return createCertificateSigningRequest(subject, email, keyPair, DEFAULT_TLS_SIGNATURE_SCHEME, certRole)
|
||||
}
|
||||
|
||||
fun buildCertPath(first: X509Certificate, remaining: List<X509Certificate>): CertPath {
|
||||
@ -284,19 +288,24 @@ object X509Utilities {
|
||||
}
|
||||
}
|
||||
|
||||
// Assuming cert type to role is 1:1
|
||||
val CertRole.certificateType: CertificateType get() = CertificateType.values().first { it.role == this }
|
||||
|
||||
/**
|
||||
* Convert a [X509Certificate] into Bouncycastle's [X509CertificateHolder].
|
||||
*
|
||||
* NOTE: To avoid unnecessary copying use [X509Certificate] where possible.
|
||||
*/
|
||||
fun X509Certificate.toBc() = X509CertificateHolder(encoded)
|
||||
|
||||
fun X509CertificateHolder.toJca(): X509Certificate = X509CertificateFactory().generateCertificate(encoded.inputStream())
|
||||
|
||||
val CertPath.x509Certificates: List<X509Certificate> get() {
|
||||
require(type == "X.509") { "Not an X.509 cert path: $this" }
|
||||
// We're not mapping the list to avoid creating a new one.
|
||||
return uncheckedCast(certificates)
|
||||
}
|
||||
val CertPath.x509Certificates: List<X509Certificate>
|
||||
get() {
|
||||
require(type == "X.509") { "Not an X.509 cert path: $this" }
|
||||
// We're not mapping the list to avoid creating a new one.
|
||||
return uncheckedCast(certificates)
|
||||
}
|
||||
|
||||
val Certificate.x509: X509Certificate get() = requireNotNull(this as? X509Certificate) { "Not an X.509 certificate: $this" }
|
||||
|
||||
|
@ -87,10 +87,15 @@ class ConfigParsingTest {
|
||||
|
||||
@Test
|
||||
fun CordaX500Name() {
|
||||
val name1 = CordaX500Name(organisation = "Mock Party", locality = "London", country = "GB")
|
||||
testPropertyType<CordaX500NameData, CordaX500NameListData, CordaX500Name>(
|
||||
CordaX500Name(organisation = "Mock Party", locality = "London", country = "GB"),
|
||||
name1,
|
||||
CordaX500Name(organisation = "Mock Party 2", locality = "London", country = "GB"),
|
||||
valuesToString = true)
|
||||
|
||||
// Test with config object.
|
||||
val config = config("value" to mapOf("organisation" to "Mock Party", "locality" to "London", "country" to "GB"))
|
||||
assertThat(config.parseAs<CordaX500NameData>().value).isEqualTo(name1)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -273,6 +278,7 @@ class ConfigParsingTest {
|
||||
data class OldData(
|
||||
@OldConfig("oldValue")
|
||||
val newValue: String)
|
||||
|
||||
data class DataWithCompanion(val value: Int) {
|
||||
companion object {
|
||||
@Suppress("unused")
|
||||
|
@ -3,7 +3,10 @@ package net.corda.node
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import joptsimple.OptionParser
|
||||
import joptsimple.util.EnumConverter
|
||||
import joptsimple.util.PathConverter
|
||||
import net.corda.core.internal.CertRole
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.exists
|
||||
import net.corda.node.services.config.ConfigHelper
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.node.services.config.parseAsNodeConfiguration
|
||||
@ -34,9 +37,11 @@ class ArgsParser {
|
||||
private val sshdServerArg = optionParser.accepts("sshd", "Enables SSHD server for node administration.")
|
||||
private val noLocalShellArg = optionParser.accepts("no-local-shell", "Do not start the embedded shell locally.")
|
||||
private val isRegistrationArg = optionParser.accepts("initial-registration", "Start initial node registration with Corda network to obtain certificate from the permissioning server.")
|
||||
private val networkRootTruststorePathArg = optionParser.accepts("network-root-truststore", "Network root trust store obtained from network operator.")
|
||||
private val networkRootTrustStorePathArg = optionParser.accepts("network-root-truststore", "Network root trust store obtained from network operator.")
|
||||
.withRequiredArg()
|
||||
private val networkRootTruststorePasswordArg = optionParser.accepts("network-root-truststore-password", "Network root trust store password obtained from network operator.")
|
||||
.withValuesConvertedBy(PathConverter())
|
||||
.defaultsTo((Paths.get("certificates") / "network-root-truststore.jks"))
|
||||
private val networkRootTrustStorePasswordArg = optionParser.accepts("network-root-truststore-password", "Network root trust store password obtained from network operator.")
|
||||
.withRequiredArg()
|
||||
private val isVersionArg = optionParser.accepts("version", "Print the version and exit")
|
||||
private val justGenerateNodeInfoArg = optionParser.accepts("just-generate-node-info",
|
||||
@ -60,16 +65,23 @@ class ArgsParser {
|
||||
val sshdServer = optionSet.has(sshdServerArg)
|
||||
val justGenerateNodeInfo = optionSet.has(justGenerateNodeInfoArg)
|
||||
val bootstrapRaftCluster = optionSet.has(bootstrapRaftClusterArg)
|
||||
val networkRootTruststorePath = optionSet.valueOf(networkRootTruststorePathArg)?.let { Paths.get(it).normalize().toAbsolutePath() }
|
||||
val networkRootTruststorePassword = optionSet.valueOf(networkRootTruststorePasswordArg)
|
||||
val networkRootTrustStorePath = optionSet.valueOf(networkRootTrustStorePathArg)
|
||||
val networkRootTrustStorePassword = optionSet.valueOf(networkRootTrustStorePasswordArg)
|
||||
|
||||
val registrationConfig = if (isRegistration) {
|
||||
requireNotNull(networkRootTrustStorePassword) { "Network root trust store password must be provided in registration mode." }
|
||||
require(networkRootTrustStorePath.exists()) { "Network root trust store path: '$networkRootTrustStorePath' doesn't exist" }
|
||||
NodeRegistrationOption(networkRootTrustStorePath, networkRootTrustStorePassword)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
return CmdLineOptions(baseDirectory,
|
||||
configFile,
|
||||
help,
|
||||
loggingLevel,
|
||||
logToConsole,
|
||||
isRegistration,
|
||||
networkRootTruststorePath,
|
||||
networkRootTruststorePassword,
|
||||
registrationConfig,
|
||||
isVersion,
|
||||
noLocalShell,
|
||||
sshdServer,
|
||||
@ -80,14 +92,14 @@ class ArgsParser {
|
||||
fun printHelp(sink: PrintStream) = optionParser.printHelpOn(sink)
|
||||
}
|
||||
|
||||
data class NodeRegistrationOption(val networkRootTrustStorePath: Path, val networkRootTrustStorePassword: String)
|
||||
|
||||
data class CmdLineOptions(val baseDirectory: Path,
|
||||
val configFile: Path,
|
||||
val help: Boolean,
|
||||
val loggingLevel: Level,
|
||||
val logToConsole: Boolean,
|
||||
val isRegistration: Boolean,
|
||||
val networkRootTruststorePath: Path?,
|
||||
val networkRootTruststorePassword: String?,
|
||||
val nodeRegistrationConfig: NodeRegistrationOption?,
|
||||
val isVersion: Boolean,
|
||||
val noLocalShell: Boolean,
|
||||
val sshdServer: Boolean,
|
||||
@ -97,10 +109,8 @@ data class CmdLineOptions(val baseDirectory: Path,
|
||||
val config = ConfigHelper.loadConfig(baseDirectory, configFile, configOverrides = ConfigFactory.parseMap(
|
||||
mapOf("noLocalShell" to this.noLocalShell)
|
||||
)).parseAsNodeConfiguration()
|
||||
if (isRegistration) {
|
||||
if (nodeRegistrationConfig != null) {
|
||||
requireNotNull(config.compatibilityZoneURL) { "Compatibility Zone Url must be provided in registration mode." }
|
||||
requireNotNull(networkRootTruststorePath) { "Network root trust store path must be provided in registration mode." }
|
||||
requireNotNull(networkRootTruststorePassword) { "Network root trust store password must be provided in registration mode." }
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
@ -99,9 +99,9 @@ open class NodeStartup(val args: Array<String>) {
|
||||
try {
|
||||
banJavaSerialisation(conf)
|
||||
preNetworkRegistration(conf)
|
||||
if (shouldRegisterWithNetwork(cmdlineOptions, conf)) {
|
||||
if (cmdlineOptions.nodeRegistrationConfig != null) {
|
||||
// Null checks for [compatibilityZoneURL], [rootTruststorePath] and [rootTruststorePassword] has been done in [CmdLineOptions.loadConfig]
|
||||
registerWithNetwork(conf, cmdlineOptions.networkRootTruststorePath!!, cmdlineOptions.networkRootTruststorePassword!!)
|
||||
registerWithNetwork(conf, cmdlineOptions.nodeRegistrationConfig)
|
||||
return true
|
||||
}
|
||||
logStartupInfo(versionInfo, cmdlineOptions, conf)
|
||||
@ -184,12 +184,7 @@ open class NodeStartup(val args: Array<String>) {
|
||||
logger.info("Starting as node on ${conf.p2pAddress}")
|
||||
}
|
||||
|
||||
private fun shouldRegisterWithNetwork(cmdlineOptions: CmdLineOptions, conf: NodeConfiguration): Boolean {
|
||||
val compatibilityZoneURL = conf.compatibilityZoneURL
|
||||
return !(!cmdlineOptions.isRegistration || compatibilityZoneURL == null)
|
||||
}
|
||||
|
||||
open protected fun registerWithNetwork(conf: NodeConfiguration, networkRootTruststorePath: Path, networkRootTruststorePassword: String) {
|
||||
open protected fun registerWithNetwork(conf: NodeConfiguration, nodeRegistrationConfig: NodeRegistrationOption) {
|
||||
val compatibilityZoneURL = conf.compatibilityZoneURL!!
|
||||
println()
|
||||
println("******************************************************************")
|
||||
@ -197,7 +192,7 @@ open class NodeStartup(val args: Array<String>) {
|
||||
println("* Registering as a new participant with Corda network *")
|
||||
println("* *")
|
||||
println("******************************************************************")
|
||||
NetworkRegistrationHelper(conf, HTTPNetworkRegistrationService(compatibilityZoneURL), networkRootTruststorePath, networkRootTruststorePassword).buildKeystore()
|
||||
NetworkRegistrationHelper(conf, HTTPNetworkRegistrationService(compatibilityZoneURL), nodeRegistrationConfig).buildKeystore()
|
||||
}
|
||||
|
||||
open protected fun loadConfigFile(cmdlineOptions: CmdLineOptions): NodeConfiguration = cmdlineOptions.loadConfig()
|
||||
|
@ -3,7 +3,10 @@ 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.node.NodeRegistrationOption
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.nodeapi.internal.DevIdentityGenerator
|
||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||
import net.corda.nodeapi.internal.crypto.X509KeyStore
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
@ -22,10 +25,18 @@ import java.security.cert.X509Certificate
|
||||
* Helper for managing the node registration process, which checks for any existing certificates and requests them if
|
||||
* needed.
|
||||
*/
|
||||
class NetworkRegistrationHelper(private val config: NodeConfiguration,
|
||||
class NetworkRegistrationHelper(private val config: SSLConfiguration,
|
||||
private val myLegalName: CordaX500Name,
|
||||
private val emailAddress: String,
|
||||
private val certService: NetworkRegistrationService,
|
||||
networkRootTrustStorePath: Path,
|
||||
networkRootTruststorePassword: String) {
|
||||
private val networkRootTrustStorePath: Path,
|
||||
networkRootTrustStorePassword: String,
|
||||
private val certRole: CertRole) {
|
||||
|
||||
// Constructor for corda node, cert role is restricted to [CertRole.NODE_CA].
|
||||
constructor(config: NodeConfiguration, certService: NetworkRegistrationService, regConfig: NodeRegistrationOption) :
|
||||
this(config, config.myLegalName, config.emailAddress, certService, regConfig.networkRootTrustStorePath, regConfig.networkRootTrustStorePassword, CertRole.NODE_CA)
|
||||
|
||||
private companion object {
|
||||
const val SELF_SIGNED_PRIVATE_KEY = "Self Signed Private Key"
|
||||
}
|
||||
@ -41,7 +52,7 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration,
|
||||
"$networkRootTrustStorePath does not exist. This file must contain the root CA cert of your compatibility zone. " +
|
||||
"Please contact your CZ operator."
|
||||
}
|
||||
rootTrustStore = X509KeyStore.fromFile(networkRootTrustStorePath, networkRootTruststorePassword)
|
||||
rootTrustStore = X509KeyStore.fromFile(networkRootTrustStorePath, networkRootTrustStorePassword)
|
||||
rootCert = rootTrustStore.getCertificate(CORDA_ROOT_CA)
|
||||
}
|
||||
|
||||
@ -68,7 +79,7 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration,
|
||||
// We use the self sign certificate to store the key temporarily in the keystore while waiting for the request approval.
|
||||
if (SELF_SIGNED_PRIVATE_KEY !in nodeKeyStore) {
|
||||
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val selfSignCert = X509Utilities.createSelfSignedCACertificate(config.myLegalName.x500Principal, keyPair)
|
||||
val selfSignCert = X509Utilities.createSelfSignedCACertificate(myLegalName.x500Principal, keyPair)
|
||||
// Save to the key store.
|
||||
nodeKeyStore.setPrivateKey(SELF_SIGNED_PRIVATE_KEY, keyPair.private, listOf(selfSignCert), keyPassword = privateKeyPassword)
|
||||
nodeKeyStore.save()
|
||||
@ -87,36 +98,59 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration,
|
||||
throw certificateRequestException
|
||||
}
|
||||
|
||||
val nodeCaCert = certificates[0]
|
||||
val certificate = certificates.first()
|
||||
|
||||
val nodeCaSubject = try {
|
||||
CordaX500Name.build(nodeCaCert.subjectX500Principal)
|
||||
CordaX500Name.build(certificate.subjectX500Principal)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
throw CertificateRequestException("Received node CA cert has invalid subject name: ${e.message}")
|
||||
}
|
||||
if (nodeCaSubject != config.myLegalName) {
|
||||
if (nodeCaSubject != myLegalName) {
|
||||
throw CertificateRequestException("Subject of received node CA cert doesn't match with node legal name: $nodeCaSubject")
|
||||
}
|
||||
|
||||
val nodeCaCertRole = try {
|
||||
CertRole.extract(nodeCaCert)
|
||||
CertRole.extract(certificate)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
throw CertificateRequestException("Unable to extract cert role from received node CA cert: ${e.message}")
|
||||
}
|
||||
if (nodeCaCertRole != CertRole.NODE_CA) {
|
||||
throw CertificateRequestException("Received node CA cert has invalid role: $nodeCaCertRole")
|
||||
}
|
||||
|
||||
// Validate certificate chain returned from the doorman with the root cert obtained via out-of-band process, to prevent MITM attack on doorman server.
|
||||
X509Utilities.validateCertificateChain(rootCert, certificates)
|
||||
|
||||
println("Certificate signing request approved, storing private key with the certificate chain.")
|
||||
// Save private key and certificate chain to the key store.
|
||||
nodeKeyStore.setPrivateKey(CORDA_CLIENT_CA, keyPair.private, certificates, keyPassword = privateKeyPassword)
|
||||
nodeKeyStore.internal.deleteEntry(SELF_SIGNED_PRIVATE_KEY)
|
||||
nodeKeyStore.save()
|
||||
println("Node private key and certificate stored in ${config.nodeKeystore}.")
|
||||
|
||||
when (nodeCaCertRole) {
|
||||
CertRole.NODE_CA -> {
|
||||
// Save private key and certificate chain to the key store.
|
||||
nodeKeyStore.setPrivateKey(CORDA_CLIENT_CA, keyPair.private, certificates, keyPassword = privateKeyPassword)
|
||||
nodeKeyStore.internal.deleteEntry(SELF_SIGNED_PRIVATE_KEY)
|
||||
nodeKeyStore.save()
|
||||
println("Node private key and certificate stored in ${config.nodeKeystore}.")
|
||||
|
||||
config.loadSslKeyStore(createNew = true).update {
|
||||
println("Generating SSL certificate for node messaging service.")
|
||||
val sslKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val sslCert = X509Utilities.createCertificate(
|
||||
CertificateType.TLS,
|
||||
certificate,
|
||||
keyPair,
|
||||
myLegalName.x500Principal,
|
||||
sslKeyPair.public)
|
||||
setPrivateKey(CORDA_CLIENT_TLS, sslKeyPair.private, listOf(sslCert) + certificates)
|
||||
}
|
||||
println("SSL private key and certificate stored in ${config.sslKeystore}.")
|
||||
}
|
||||
// TODO: Fix this, this is not needed in corda node.
|
||||
CertRole.SERVICE_IDENTITY -> {
|
||||
// Only create keystore containing notary's key for service identity role.
|
||||
nodeKeyStore.setPrivateKey("${DevIdentityGenerator.DISTRIBUTED_NOTARY_ALIAS_PREFIX}-private-key", keyPair.private, certificates, keyPassword = privateKeyPassword)
|
||||
nodeKeyStore.internal.deleteEntry(SELF_SIGNED_PRIVATE_KEY)
|
||||
nodeKeyStore.save()
|
||||
println("Service identity private key and certificate stored in ${config.nodeKeystore}.")
|
||||
}
|
||||
else -> throw CertificateRequestException("Received node CA cert has invalid role: $nodeCaCertRole")
|
||||
}
|
||||
// Save root certificates to trust store.
|
||||
config.loadTrustStore(createNew = true).update {
|
||||
println("Generating trust store for corda node.")
|
||||
@ -124,20 +158,6 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration,
|
||||
setCertificate(CORDA_ROOT_CA, certificates.last())
|
||||
}
|
||||
println("Node trust store stored in ${config.trustStoreFile}.")
|
||||
|
||||
config.loadSslKeyStore(createNew = true).update {
|
||||
println("Generating SSL certificate for node messaging service.")
|
||||
val sslKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val sslCert = X509Utilities.createCertificate(
|
||||
CertificateType.TLS,
|
||||
nodeCaCert,
|
||||
keyPair,
|
||||
config.myLegalName.x500Principal,
|
||||
sslKeyPair.public)
|
||||
setPrivateKey(CORDA_CLIENT_TLS, sslKeyPair.private, listOf(sslCert) + certificates)
|
||||
}
|
||||
println("SSL private key and certificate stored in ${config.sslKeystore}.")
|
||||
|
||||
// All done, clean up temp files.
|
||||
requestIdStore.deleteIfExists()
|
||||
}
|
||||
@ -169,15 +189,15 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration,
|
||||
private fun submitOrResumeCertificateSigningRequest(keyPair: KeyPair): String {
|
||||
// Retrieve request id from file if exists, else post a request to server.
|
||||
return if (!requestIdStore.exists()) {
|
||||
val request = X509Utilities.createCertificateSigningRequest(config.myLegalName.x500Principal, config.emailAddress, keyPair)
|
||||
val request = X509Utilities.createCertificateSigningRequest(myLegalName.x500Principal, emailAddress, keyPair, certRole)
|
||||
val writer = StringWriter()
|
||||
JcaPEMWriter(writer).use {
|
||||
it.writeObject(PemObject("CERTIFICATE REQUEST", request.encoded))
|
||||
}
|
||||
println("Certificate signing request with the following information will be submitted to the Corda certificate signing server.")
|
||||
println()
|
||||
println("Legal Name: ${config.myLegalName}")
|
||||
println("Email: ${config.emailAddress}")
|
||||
println("Legal Name: $myLegalName")
|
||||
println("Email: $emailAddress")
|
||||
println()
|
||||
println("Public Key: ${keyPair.public}")
|
||||
println()
|
||||
|
@ -2,12 +2,14 @@ package net.corda.node
|
||||
|
||||
import joptsimple.OptionException
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.nodeapi.internal.crypto.X509KeyStore
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
||||
import org.junit.Test
|
||||
import org.slf4j.event.Level
|
||||
import java.nio.file.Paths
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
|
||||
class ArgsParserTest {
|
||||
private val parser = ArgsParser()
|
||||
@ -21,14 +23,12 @@ class ArgsParserTest {
|
||||
help = false,
|
||||
logToConsole = false,
|
||||
loggingLevel = Level.INFO,
|
||||
isRegistration = false,
|
||||
nodeRegistrationConfig = null,
|
||||
isVersion = false,
|
||||
noLocalShell = false,
|
||||
sshdServer = false,
|
||||
justGenerateNodeInfo = false,
|
||||
bootstrapRaftCluster = false,
|
||||
networkRootTruststorePassword = null,
|
||||
networkRootTruststorePath = null))
|
||||
bootstrapRaftCluster = false))
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -113,11 +113,17 @@ class ArgsParserTest {
|
||||
|
||||
@Test
|
||||
fun `initial-registration`() {
|
||||
val truststorePath = Paths.get("truststore") / "file.jks"
|
||||
val truststorePath = workingDirectory / "truststore" / "file.jks"
|
||||
assertThatExceptionOfType(IllegalArgumentException::class.java).isThrownBy {
|
||||
parser.parse("--initial-registration", "--network-root-truststore", "$truststorePath", "--network-root-truststore-password", "password-test")
|
||||
}.withMessageContaining("Network root trust store path").withMessageContaining("doesn't exist")
|
||||
|
||||
X509KeyStore.fromFile(truststorePath, "dummy_password", createNew = true)
|
||||
|
||||
val cmdLineOptions = parser.parse("--initial-registration", "--network-root-truststore", "$truststorePath", "--network-root-truststore-password", "password-test")
|
||||
assertThat(cmdLineOptions.isRegistration).isTrue()
|
||||
assertEquals(truststorePath.toAbsolutePath(), cmdLineOptions.networkRootTruststorePath)
|
||||
assertEquals("password-test", cmdLineOptions.networkRootTruststorePassword)
|
||||
assertNotNull(cmdLineOptions.nodeRegistrationConfig)
|
||||
assertEquals(truststorePath.toAbsolutePath(), cmdLineOptions.nodeRegistrationConfig?.networkRootTrustStorePath)
|
||||
assertEquals("password-test", cmdLineOptions.nodeRegistrationConfig?.networkRootTrustStorePassword)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -13,7 +13,9 @@ import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.x500Name
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.node.NodeRegistrationOption
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.nodeapi.internal.DevIdentityGenerator
|
||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||
import net.corda.nodeapi.internal.crypto.X509KeyStore
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
@ -141,6 +143,38 @@ class NetworkRegistrationHelperTest {
|
||||
}.isInstanceOf(CertPathValidatorException::class.java)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `create service identity cert`() {
|
||||
assertThat(config.nodeKeystore).doesNotExist()
|
||||
assertThat(config.sslKeystore).doesNotExist()
|
||||
assertThat(config.trustStoreFile).doesNotExist()
|
||||
|
||||
val serviceIdentityCertPath = createServiceIdentityCertPath()
|
||||
|
||||
saveNetworkTrustStore(serviceIdentityCertPath.last())
|
||||
createRegistrationHelper(serviceIdentityCertPath).buildKeystore()
|
||||
|
||||
val nodeKeystore = config.loadNodeKeyStore()
|
||||
val trustStore = config.loadTrustStore()
|
||||
assertThat(config.sslKeystore).doesNotExist()
|
||||
|
||||
val serviceIdentityAlias = "${DevIdentityGenerator.DISTRIBUTED_NOTARY_ALIAS_PREFIX}-private-key"
|
||||
|
||||
nodeKeystore.run {
|
||||
assertFalse(contains(X509Utilities.CORDA_INTERMEDIATE_CA))
|
||||
assertFalse(contains(X509Utilities.CORDA_ROOT_CA))
|
||||
assertFalse(contains(X509Utilities.CORDA_CLIENT_TLS))
|
||||
assertFalse(contains(X509Utilities.CORDA_CLIENT_CA))
|
||||
assertThat(getCertificateChain(serviceIdentityAlias)).containsExactlyElementsOf(serviceIdentityCertPath)
|
||||
}
|
||||
|
||||
trustStore.run {
|
||||
assertFalse(contains(X509Utilities.CORDA_CLIENT_CA))
|
||||
assertFalse(contains(X509Utilities.CORDA_INTERMEDIATE_CA))
|
||||
assertThat(getCertificate(X509Utilities.CORDA_ROOT_CA)).isEqualTo(serviceIdentityCertPath.last())
|
||||
}
|
||||
}
|
||||
|
||||
private fun createNodeCaCertPath(type: CertificateType = CertificateType.NODE_CA,
|
||||
legalName: CordaX500Name = nodeLegalName): List<X509Certificate> {
|
||||
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
|
||||
@ -156,12 +190,25 @@ class NetworkRegistrationHelperTest {
|
||||
return listOf(nodeCaCert, intermediateCa.certificate, rootCa.certificate)
|
||||
}
|
||||
|
||||
private fun createServiceIdentityCertPath(type: CertificateType = CertificateType.SERVICE_IDENTITY,
|
||||
legalName: CordaX500Name = nodeLegalName): List<X509Certificate> {
|
||||
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
|
||||
val keyPair = Crypto.generateKeyPair()
|
||||
val serviceIdentityCert = X509Utilities.createCertificate(
|
||||
type,
|
||||
intermediateCa.certificate,
|
||||
intermediateCa.keyPair,
|
||||
legalName.x500Principal,
|
||||
keyPair.public)
|
||||
return listOf(serviceIdentityCert, intermediateCa.certificate, rootCa.certificate)
|
||||
}
|
||||
|
||||
private fun createRegistrationHelper(response: List<X509Certificate>): NetworkRegistrationHelper {
|
||||
val certService = rigorousMock<NetworkRegistrationService>().also {
|
||||
doReturn(requestId).whenever(it).submitRequest(any())
|
||||
doReturn(CertificateResponse(5.seconds, response)).whenever(it).retrieveCertificates(eq(requestId))
|
||||
}
|
||||
return NetworkRegistrationHelper(config, certService, config.certificatesDirectory / networkRootTrustStoreFileName, networkRootTrustStorePassword)
|
||||
return NetworkRegistrationHelper(config, certService, NodeRegistrationOption(config.certificatesDirectory / networkRootTrustStoreFileName, networkRootTrustStorePassword))
|
||||
}
|
||||
|
||||
private fun saveNetworkTrustStore(rootCert: X509Certificate) {
|
||||
|
@ -23,6 +23,7 @@ import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.millis
|
||||
import net.corda.node.NodeRegistrationOption
|
||||
import net.corda.node.internal.Node
|
||||
import net.corda.node.internal.NodeStartup
|
||||
import net.corda.node.internal.StartedNode
|
||||
@ -259,7 +260,7 @@ class DriverDSLImpl(
|
||||
|
||||
return if (startNodesInProcess) {
|
||||
executorService.fork {
|
||||
NetworkRegistrationHelper(config.corda, HTTPNetworkRegistrationService(compatibilityZoneURL), rootTruststorePath, rootTruststorePassword).buildKeystore()
|
||||
NetworkRegistrationHelper(config.corda, HTTPNetworkRegistrationService(compatibilityZoneURL), NodeRegistrationOption(rootTruststorePath, rootTruststorePassword)).buildKeystore()
|
||||
config
|
||||
}
|
||||
} else {
|
||||
|
Loading…
x
Reference in New Issue
Block a user