mirror of
https://github.com/corda/corda.git
synced 2024-12-23 23:02:29 +00:00
ENT-992: Introducing the CRR submission tool (#633)
* Introducing the CRR submission tool * Addressing review comments * Addressing review comments - round 2 - Redesign of the tool. * Fixing messages
This commit is contained in:
parent
9c07e67100
commit
625d0447aa
5
docs/source/crr-submission-tool.rst
Normal file
5
docs/source/crr-submission-tool.rst
Normal file
@ -0,0 +1,5 @@
|
||||
Certificate Revocation Request Submission Tool
|
||||
==============================================
|
||||
|
||||
The purpose of the Certificate Revocation Request (CRR) Submission Tool is to facilitate the process of creating a CRR.
|
||||
The tool is designed with the support line in mind, and assumes it is for internal (i.e. within the doorman service managing company) usage.
|
12
docs/source/running-crr-submission-tool.rst
Normal file
12
docs/source/running-crr-submission-tool.rst
Normal file
@ -0,0 +1,12 @@
|
||||
Running the Certificate Revocation Request Submission Tool
|
||||
==========================================================
|
||||
|
||||
The purpose of this tool is to facilitate the certificate revocation request submission process.
|
||||
See :doc:`crr-submission-tool` for more details.
|
||||
|
||||
See the Readme under ``network-management`` for detailed building instructions.
|
||||
|
||||
Command line argument
|
||||
----------------------
|
||||
At startup, the Certificate Revocation Request Submission Tool takes only one command line argument: ``--submission-url``,
|
||||
that should be followed by the url to the certificate revocation request submission endpoint.
|
@ -15,9 +15,6 @@ At startup, the HSM Certificate Generation Tool reads a configuration file, pass
|
||||
This is an example of what a tool configuration file might look like:
|
||||
.. literalinclude:: ../../network-management/generator.conf
|
||||
|
||||
Invoke doorman with ``-?`` for a full list of supported command-line arguments.
|
||||
|
||||
|
||||
General configuration parameters
|
||||
--------------------------------
|
||||
Allowed parameters are:
|
||||
|
@ -48,6 +48,19 @@ The built file will appear in
|
||||
network-management/capsule-hsm-cert-generator/build/libs/hsm-cert-generator-<VERSION>.jar
|
||||
```
|
||||
|
||||
## Certificate Revocation Request Submission Tool
|
||||
|
||||
To build a fat jar containing all the CRR submission tool code you can simply invoke
|
||||
```
|
||||
./gradlew network-management:capsule-crr-submission:buildCrrSubmissionJAR
|
||||
```
|
||||
|
||||
The built file will appear in
|
||||
```
|
||||
network-management/capsule-crr-submission/build/libs/crr-submission-<VERSION>.jar
|
||||
```
|
||||
|
||||
|
||||
# Logs
|
||||
In order to set the desired logging level the system properties need to be used.
|
||||
Appropriate system properties can be set at the execution time.
|
||||
|
49
network-management/capsule-crr-submission/build.gradle
Normal file
49
network-management/capsule-crr-submission/build.gradle
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* R3 Proprietary and Confidential
|
||||
*
|
||||
* Copyright (c) 2018 R3 Limited. All rights reserved.
|
||||
*
|
||||
* The intellectual and technical concepts contained herein are proprietary to R3 and its suppliers and are protected by trade secret law.
|
||||
*
|
||||
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
|
||||
*/
|
||||
|
||||
apply plugin: 'net.corda.plugins.publish-utils'
|
||||
apply plugin: 'us.kirchmeier.capsule'
|
||||
|
||||
description 'HSM Certificate Generator'
|
||||
|
||||
version project(':network-management').version
|
||||
|
||||
configurations {
|
||||
runtimeArtifacts.extendsFrom runtime
|
||||
}
|
||||
|
||||
task buildCrrSubmissionJAR(type: FatCapsule, dependsOn: 'jar') {
|
||||
applicationClass 'com.r3.corda.networkmanage.tools.crr.submission.MainKt'
|
||||
archiveName "crr-submission-${version}.jar"
|
||||
capsuleManifest {
|
||||
applicationVersion = corda_release_version
|
||||
systemProperties['visualvm.display.name'] = 'CRR Submission Tool'
|
||||
minJavaVersion = '1.8.0'
|
||||
jvmArgs = ['-XX:+UseG1GC']
|
||||
}
|
||||
applicationSource = files(
|
||||
project(':network-management').configurations.runtime,
|
||||
project(':network-management').jar
|
||||
)
|
||||
}
|
||||
|
||||
artifacts {
|
||||
runtimeArtifacts buildCrrSubmissionJAR
|
||||
publish buildCrrSubmissionJAR
|
||||
}
|
||||
|
||||
jar {
|
||||
classifier "ignore"
|
||||
}
|
||||
|
||||
publish {
|
||||
name 'crr-submission'
|
||||
disableDefaultJar = true
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.r3.corda.networkmanage.common.configuration
|
||||
|
||||
import com.r3.corda.networkmanage.common.utils.ShowHelpException
|
||||
import joptsimple.OptionParser
|
||||
import joptsimple.util.PathConverter
|
||||
import joptsimple.util.PathProperties
|
||||
import java.nio.file.Path
|
||||
|
||||
/**
|
||||
* Parses key generator command line options.
|
||||
*/
|
||||
fun parseCommandLine(vararg args: String): Path {
|
||||
val optionParser = OptionParser()
|
||||
val configFileArg = optionParser
|
||||
.accepts("config-file", "The path to the config file")
|
||||
.withRequiredArg()
|
||||
.required()
|
||||
.describedAs("filepath")
|
||||
.withValuesConvertedBy(PathConverter(PathProperties.FILE_EXISTING))
|
||||
val helpOption = optionParser.acceptsAll(listOf("h", "help"), "show help").forHelp()
|
||||
|
||||
val optionSet = optionParser.parse(*args)
|
||||
// Print help and exit on help option or if there are missing options.
|
||||
if (optionSet.has(helpOption) || !optionSet.has(configFileArg)) {
|
||||
throw ShowHelpException(optionParser)
|
||||
}
|
||||
return optionSet.valueOf(configFileArg).toAbsolutePath()
|
||||
}
|
@ -10,16 +10,12 @@
|
||||
|
||||
package com.r3.corda.networkmanage.dev
|
||||
|
||||
import com.r3.corda.networkmanage.common.utils.ShowHelpException
|
||||
import com.r3.corda.networkmanage.hsm.generator.CommandLineOptions
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import com.typesafe.config.ConfigParseOptions
|
||||
import joptsimple.OptionParser
|
||||
import net.corda.nodeapi.internal.*
|
||||
import net.corda.nodeapi.internal.config.parseAs
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
|
||||
/**
|
||||
* Holds configuration necessary for generating DEV key store and trust store.
|
||||
@ -35,29 +31,6 @@ data class GeneratorConfiguration(val privateKeyPass: String = DEV_CA_PRIVATE_KE
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses dev generator command line options.
|
||||
*/
|
||||
fun parseCommandLine(vararg args: String): CommandLineOptions? {
|
||||
val optionParser = OptionParser()
|
||||
val configFileArg = optionParser
|
||||
.accepts("config-file", "The path to the config file")
|
||||
.withRequiredArg()
|
||||
.describedAs("filepath")
|
||||
val helpOption = optionParser.acceptsAll(listOf("h", "?", "help"), "show help").forHelp()
|
||||
|
||||
val optionSet = optionParser.parse(*args)
|
||||
// Print help and exit on help option.
|
||||
if (optionSet.has(helpOption)) {
|
||||
throw ShowHelpException(optionParser)
|
||||
}
|
||||
return if (optionSet.has(configFileArg)) {
|
||||
CommandLineOptions(Paths.get(optionSet.valueOf(configFileArg)).toAbsolutePath())
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a configuration file, which contains all the configuration - i.e. for the key store generator.
|
||||
*/
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
package com.r3.corda.networkmanage.dev
|
||||
|
||||
import com.r3.corda.networkmanage.common.configuration.parseCommandLine
|
||||
import com.r3.corda.networkmanage.doorman.CORDA_X500_BASE
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.internal.createDirectories
|
||||
@ -33,7 +34,7 @@ private val logger = LogManager.getLogger("com.r3.corda.networkmanage.dev.Main")
|
||||
* Look for the 'certificates' directory.
|
||||
*/
|
||||
fun main(args: Array<String>) {
|
||||
run(parseParameters(parseCommandLine(*args)?.configFile))
|
||||
run(parseParameters(parseCommandLine(*args)))
|
||||
}
|
||||
|
||||
fun run(configuration: GeneratorConfiguration) {
|
||||
|
@ -10,15 +10,11 @@
|
||||
|
||||
package com.r3.corda.networkmanage.hsm.generator
|
||||
|
||||
import com.r3.corda.networkmanage.common.utils.ShowHelpException
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import com.typesafe.config.ConfigParseOptions
|
||||
import joptsimple.OptionParser
|
||||
import net.corda.core.internal.isRegularFile
|
||||
import net.corda.nodeapi.internal.config.parseAs
|
||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
|
||||
/**
|
||||
* Holds configuration necessary for user's authentication against HSM.
|
||||
@ -62,37 +58,6 @@ data class CertificateConfiguration(val keyGroup: String,
|
||||
val keyCurve: String, // we use "NIST-P256", check Utimaco docs for other options
|
||||
val keyGenMechanism: Int) // MECH_KEYGEN_UNCOMP = 4 or MECH_RND_REAL = 0
|
||||
|
||||
/**
|
||||
* Holds arguments for command line options.
|
||||
*/
|
||||
data class CommandLineOptions(val configFile: Path) {
|
||||
init {
|
||||
check(configFile.isRegularFile()) { "Config file $configFile does not exist" }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses key generator command line options.
|
||||
*/
|
||||
fun parseCommandLine(vararg args: String): CommandLineOptions {
|
||||
val optionParser = OptionParser()
|
||||
val configFileArg = optionParser
|
||||
.accepts("config-file", "The path to the config file")
|
||||
.withRequiredArg()
|
||||
.describedAs("filepath")
|
||||
val helpOption = optionParser.acceptsAll(listOf("h", "?", "help"), "show help").forHelp()
|
||||
|
||||
val optionSet = optionParser.parse(*args)
|
||||
// Print help and exit on help option or if there are missing options.
|
||||
if (optionSet.has(helpOption) || !optionSet.has(configFileArg)) {
|
||||
throw ShowHelpException(optionParser)
|
||||
}
|
||||
|
||||
val configFile = Paths.get(optionSet.valueOf(configFileArg)).toAbsolutePath()
|
||||
|
||||
return CommandLineOptions(configFile)
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a configuration file, which contains all the configuration - i.e. for user and certificate parameters.
|
||||
*/
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
package com.r3.corda.networkmanage.hsm.generator
|
||||
|
||||
import com.r3.corda.networkmanage.common.configuration.parseCommandLine
|
||||
import com.r3.corda.networkmanage.hsm.authentication.CryptoServerProviderConfig
|
||||
import com.r3.corda.networkmanage.hsm.utils.mapCryptoServerException
|
||||
import net.corda.nodeapi.internal.crypto.CertificateType.ROOT_CA
|
||||
@ -18,7 +19,7 @@ import org.apache.logging.log4j.LogManager
|
||||
private val logger = LogManager.getLogger("com.r3.corda.networkmanage.hsm.generator.Main")
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
run(parseParameters(parseCommandLine(*args)?.configFile))
|
||||
run(parseParameters(parseCommandLine(*args)))
|
||||
}
|
||||
|
||||
fun run(parameters: GeneratorParameters) {
|
||||
|
@ -0,0 +1,21 @@
|
||||
package com.r3.corda.networkmanage.tools.crr.submission
|
||||
|
||||
import com.r3.corda.networkmanage.common.utils.ShowHelpException
|
||||
import joptsimple.OptionParser
|
||||
import java.net.URL
|
||||
|
||||
fun parseSubmissionUrl(vararg args: String): URL {
|
||||
val optionParser = OptionParser()
|
||||
val submissionUrlArg = optionParser
|
||||
.accepts("submission-url", "CRR submission endpoint.")
|
||||
.withRequiredArg()
|
||||
.required()
|
||||
val helpOption = optionParser.acceptsAll(listOf("h", "help"), "show help").forHelp()
|
||||
|
||||
val optionSet = optionParser.parse(*args)
|
||||
// Print help and exit on help option or if there are missing options.
|
||||
if (optionSet.has(helpOption) || !optionSet.has(submissionUrlArg)) {
|
||||
throw ShowHelpException(optionParser)
|
||||
}
|
||||
return URL(optionSet.valueOf(submissionUrlArg))
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
package com.r3.corda.networkmanage.tools.crr.submission
|
||||
|
||||
import com.r3.corda.networkmanage.common.utils.initialiseSerialization
|
||||
import com.r3.corda.networkmanage.hsm.authentication.ConsoleInputReader
|
||||
import com.r3.corda.networkmanage.hsm.authentication.InputReader
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.post
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.nodeapi.internal.network.CertificateRevocationRequest
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import java.math.BigInteger
|
||||
import java.net.URL
|
||||
import java.security.cert.CRLReason
|
||||
|
||||
private val logger = LogManager.getLogger("com.r3.corda.networkmanage.common.tools.crr.Main")
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
initialiseSerialization()
|
||||
try {
|
||||
submit(parseSubmissionUrl(*args))
|
||||
} catch (e: Exception) {
|
||||
logger.error("Error when submitting a certificate revocation request.", e)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
fun submit(url: URL, inputReader: InputReader = ConsoleInputReader()) {
|
||||
val certificateSerialNumber = inputReader.getOptionalInput("certificate serial number")?.let { BigInteger(it) }
|
||||
val csrRequestId = inputReader.getOptionalInput("certificate signing request ID")
|
||||
val legalName = inputReader.getOptionalInput("node X.500 legal name")?.let { CordaX500Name.parse(it) }
|
||||
CertificateRevocationRequest.validateOptional(certificateSerialNumber, csrRequestId, legalName)
|
||||
val reason = inputReader.getRequiredInput("revocation reason").let { CRLReason.valueOf(it) }
|
||||
val reporter = inputReader.getRequiredInput("reporter of the revocation request")
|
||||
val request = CertificateRevocationRequest(certificateSerialNumber, csrRequestId, legalName, reason, reporter)
|
||||
logger.debug("POST to $url request: $request")
|
||||
val requestId = String(url.post(request.serialize()))
|
||||
logger.debug("Certificate revocation request successfully submitted. Request ID: $requestId")
|
||||
println("Successfully submitted certificate revocation request. Generated request ID: $requestId")
|
||||
}
|
||||
|
||||
private fun InputReader.getOptionalInput(attributeName: String): String? {
|
||||
print("Type in $attributeName (press enter if not available):")
|
||||
return this.readLine()?.let {
|
||||
if (it.isBlank()) null else it
|
||||
}
|
||||
}
|
||||
|
||||
private fun InputReader.getRequiredInput(attributeName: String): String {
|
||||
print("Type in $attributeName:")
|
||||
val line = this.readLine()
|
||||
return if (line == null || line.isNullOrBlank()) {
|
||||
throw IllegalArgumentException("The $attributeName needs to be specified.")
|
||||
} else {
|
||||
line
|
||||
}
|
||||
}
|
@ -10,7 +10,7 @@
|
||||
|
||||
package com.r3.corda.networkmanage.dev
|
||||
|
||||
import com.r3.corda.networkmanage.hsm.generator.parseCommandLine
|
||||
import com.r3.corda.networkmanage.common.configuration.parseCommandLine
|
||||
import net.corda.nodeapi.internal.DEV_CA_KEY_STORE_FILE
|
||||
import net.corda.nodeapi.internal.DEV_CA_KEY_STORE_PASS
|
||||
import net.corda.nodeapi.internal.DEV_CA_TRUST_STORE_FILE
|
||||
@ -31,7 +31,7 @@ class GeneratorConfigurationTest {
|
||||
|
||||
@Test
|
||||
fun `config file is parsed correctly`() {
|
||||
val config = parseParameters(parseCommandLine("--config-file", configPath).configFile)
|
||||
val config = parseParameters(parseCommandLine("--config-file", configPath))
|
||||
assertEquals(GeneratorConfiguration.DEFAULT_DIRECTORY, config.directory)
|
||||
assertEquals(DEV_CA_KEY_STORE_FILE, config.keyStoreFileName)
|
||||
assertEquals(DEV_CA_KEY_STORE_PASS, config.keyStorePass)
|
||||
|
@ -10,8 +10,10 @@
|
||||
|
||||
package com.r3.corda.networkmanage.hsm.generator
|
||||
|
||||
import com.r3.corda.networkmanage.common.configuration.parseCommandLine
|
||||
import com.r3.corda.networkmanage.common.utils.ShowHelpException
|
||||
import com.typesafe.config.ConfigException
|
||||
import joptsimple.OptionException
|
||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.junit.Test
|
||||
@ -28,23 +30,23 @@ class GeneratorParametersTest {
|
||||
|
||||
@Test
|
||||
fun `should fail when config file is missing`() {
|
||||
val message = assertFailsWith<IllegalStateException> {
|
||||
val message = assertFailsWith<OptionException> {
|
||||
parseCommandLine("--config-file", "not-existing-file")
|
||||
}.message
|
||||
Assertions.assertThat(message).contains("Config file ")
|
||||
Assertions.assertThat(message).contains("not-existing-file")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should throw ShowHelpException when help option is passed on the command line`() {
|
||||
assertFailsWith<ShowHelpException> {
|
||||
parseCommandLine("-?")
|
||||
parseCommandLine("-h")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should fail when config is invalid`() {
|
||||
assertFailsWith<ConfigException.Missing> {
|
||||
parseParameters(parseCommandLine("--config-file", invalidConfigPath).configFile)
|
||||
parseParameters(parseCommandLine("--config-file", invalidConfigPath))
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,6 +71,6 @@ class GeneratorParametersTest {
|
||||
}
|
||||
|
||||
private fun parseCommandLineAndGetParameters(): GeneratorParameters {
|
||||
return parseParameters(parseCommandLine(*validArgs).configFile)
|
||||
return parseParameters(parseCommandLine(*validArgs))
|
||||
}
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* R3 Proprietary and Confidential
|
||||
*
|
||||
* Copyright (c) 2018 R3 Limited. All rights reserved.
|
||||
*
|
||||
* The intellectual and technical concepts contained herein are proprietary to R3 and its suppliers and are protected by trade secret law.
|
||||
*
|
||||
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
|
||||
*/
|
||||
|
||||
package com.r3.corda.networkmanage.tools.crr.submission
|
||||
|
||||
import com.nhaarman.mockito_kotlin.eq
|
||||
import com.nhaarman.mockito_kotlin.mock
|
||||
import com.nhaarman.mockito_kotlin.verify
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import com.r3.corda.networkmanage.doorman.NetworkManagementWebServer
|
||||
import com.r3.corda.networkmanage.doorman.signer.CrrHandler
|
||||
import com.r3.corda.networkmanage.doorman.webservice.CertificateRevocationRequestWebService
|
||||
import com.r3.corda.networkmanage.hsm.authentication.InputReader
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.nodeapi.internal.network.CertificateRevocationRequest
|
||||
import net.corda.testing.core.SerializationEnvironmentRule
|
||||
import net.corda.testing.core.freeLocalHostAndPort
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import java.math.BigInteger
|
||||
import java.net.URL
|
||||
import java.security.cert.CRLReason
|
||||
|
||||
class CertificateRevocationRequestSubmissionToolTest {
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
val testSerialization = SerializationEnvironmentRule(true)
|
||||
|
||||
private val hostAndPort = freeLocalHostAndPort()
|
||||
private lateinit var webServer: NetworkManagementWebServer
|
||||
private lateinit var inputReader: InputReader
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
inputReader = mock()
|
||||
}
|
||||
|
||||
@After
|
||||
fun close() {
|
||||
webServer.close()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `submit request succeeds`() {
|
||||
// given
|
||||
val request = CertificateRevocationRequest(
|
||||
certificateSerialNumber = BigInteger.TEN,
|
||||
csrRequestId = "TestCSRId",
|
||||
legalName = CordaX500Name.parse("O=TestOrg, C=GB, L=London"),
|
||||
reason = CRLReason.KEY_COMPROMISE,
|
||||
reporter = "TestReporter"
|
||||
)
|
||||
|
||||
givenUserConsoleSequentialInputOnReadLine(request.certificateSerialNumber.toString(),
|
||||
request.csrRequestId!!,
|
||||
request.legalName.toString(),
|
||||
request.reason.name,
|
||||
request.reporter)
|
||||
|
||||
val requestId = SecureHash.randomSHA256().toString()
|
||||
val requestProcessor = mock<CrrHandler> {
|
||||
on { saveRevocationRequest(eq(request)) }.then { requestId }
|
||||
}
|
||||
startSigningServer(requestProcessor)
|
||||
|
||||
// when
|
||||
submit(URL("http://$hostAndPort/certificate-revocation-request"), inputReader)
|
||||
|
||||
// then
|
||||
verify(requestProcessor).saveRevocationRequest(eq(request))
|
||||
}
|
||||
|
||||
private fun givenUserConsoleSequentialInputOnReadLine(vararg inputs: String) {
|
||||
var sequence = whenever(inputReader.readLine()).thenReturn(inputs.first())
|
||||
inputs.drop(1).forEach {
|
||||
sequence = sequence.thenReturn(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun startSigningServer(handler: CrrHandler) {
|
||||
webServer = NetworkManagementWebServer(hostAndPort, CertificateRevocationRequestWebService(handler))
|
||||
webServer.start()
|
||||
}
|
||||
}
|
@ -15,9 +15,15 @@ data class CertificateRevocationRequest(val certificateSerialNumber: BigInteger?
|
||||
val legalName: CordaX500Name? = null,
|
||||
val reason: CRLReason,
|
||||
val reporter: String) {
|
||||
init {
|
||||
require(certificateSerialNumber != null || csrRequestId != null || legalName != null) {
|
||||
"At least one of the following needs to be specified: certificateSerialNumber, csrRequestId, legalName."
|
||||
companion object {
|
||||
fun validateOptional(certificateSerialNumber: BigInteger?, csrRequestId: String?, legalName: CordaX500Name?) {
|
||||
require(certificateSerialNumber != null || csrRequestId != null || legalName != null) {
|
||||
"At least one of the following needs to be specified: certificateSerialNumber, csrRequestId, legalName."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
validateOptional(certificateSerialNumber, csrRequestId, legalName)
|
||||
}
|
||||
}
|
@ -47,6 +47,7 @@ include 'network-management'
|
||||
include 'network-management:capsule'
|
||||
include 'network-management:capsule-hsm'
|
||||
include 'network-management:capsule-hsm-cert-generator'
|
||||
include 'network-management:capsule-crr-submission'
|
||||
include 'network-management:registration-tool'
|
||||
include 'tools:jmeter'
|
||||
include 'tools:explorer'
|
||||
|
Loading…
Reference in New Issue
Block a user