mirror of
https://github.com/corda/corda.git
synced 2024-12-28 08:48:57 +00:00
Mkit cp fix network map serialization issue (#430)
* Fixing serialization issue for the network map signer * Addressing review comments * Adding more assertions to the network map test * Changing assertTrue to assertThat
This commit is contained in:
parent
9f8536d010
commit
88a6e59906
@ -1,21 +1,23 @@
|
|||||||
package com.r3.corda.networkmanage.hsm
|
package com.r3.corda.networkmanage.common
|
||||||
|
|
||||||
import com.nhaarman.mockito_kotlin.any
|
import com.nhaarman.mockito_kotlin.any
|
||||||
import com.nhaarman.mockito_kotlin.mock
|
import com.nhaarman.mockito_kotlin.mock
|
||||||
import com.nhaarman.mockito_kotlin.whenever
|
import com.nhaarman.mockito_kotlin.whenever
|
||||||
import com.r3.corda.networkmanage.HsmSimulator
|
import com.r3.corda.networkmanage.HsmSimulator
|
||||||
import com.r3.corda.networkmanage.hsm.authentication.CryptoServerProviderConfig
|
|
||||||
import com.r3.corda.networkmanage.hsm.authentication.InputReader
|
import com.r3.corda.networkmanage.hsm.authentication.InputReader
|
||||||
import com.r3.corda.networkmanage.hsm.configuration.Parameters
|
import com.r3.corda.networkmanage.hsm.configuration.Parameters
|
||||||
import com.r3.corda.networkmanage.hsm.generator.AuthMode
|
import com.r3.corda.networkmanage.hsm.generator.AuthMode
|
||||||
import com.r3.corda.networkmanage.hsm.generator.CertificateConfiguration
|
import com.r3.corda.networkmanage.hsm.generator.CertificateConfiguration
|
||||||
import com.r3.corda.networkmanage.hsm.generator.GeneratorParameters
|
import com.r3.corda.networkmanage.hsm.generator.GeneratorParameters
|
||||||
import com.r3.corda.networkmanage.hsm.generator.UserAuthenticationParameters
|
import com.r3.corda.networkmanage.hsm.generator.UserAuthenticationParameters
|
||||||
|
import net.corda.core.crypto.random63BitValue
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
|
import org.junit.Before
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.rules.TemporaryFolder
|
import org.junit.rules.TemporaryFolder
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
abstract class HsmCertificateTest {
|
abstract class HsmBaseTest {
|
||||||
companion object {
|
companion object {
|
||||||
val ROOT_CERT_KEY_GROUP = "DEV.CORDACONNECT.ROOT"
|
val ROOT_CERT_KEY_GROUP = "DEV.CORDACONNECT.ROOT"
|
||||||
val NETWORK_MAP_CERT_KEY_GROUP = "DEV.CORDACONNECT.OPS.NETMAP"
|
val NETWORK_MAP_CERT_KEY_GROUP = "DEV.CORDACONNECT.OPS.NETMAP"
|
||||||
@ -23,6 +25,11 @@ abstract class HsmCertificateTest {
|
|||||||
val ROOT_CERT_SUBJECT = "CN=Corda Root CA, O=R3 HoldCo LLC, OU=Corda, L=New York, C=US"
|
val ROOT_CERT_SUBJECT = "CN=Corda Root CA, O=R3 HoldCo LLC, OU=Corda, L=New York, C=US"
|
||||||
val NETWORK_MAP_CERT_SUBJECT = "CN=Corda Network Map, O=R3 HoldCo LLC, OU=Corda, L=New York, C=US"
|
val NETWORK_MAP_CERT_SUBJECT = "CN=Corda Network Map, O=R3 HoldCo LLC, OU=Corda, L=New York, C=US"
|
||||||
val DOORMAN_CERT_SUBJECT = "CN=Corda Doorman CA, O=R3 HoldCo LLC, OU=Corda, L=New York, C=US"
|
val DOORMAN_CERT_SUBJECT = "CN=Corda Doorman CA, O=R3 HoldCo LLC, OU=Corda, L=New York, C=US"
|
||||||
|
val HSM_USER_CONFIGS = listOf(UserAuthenticationParameters(
|
||||||
|
username = "INTEGRATION_TEST",
|
||||||
|
authMode = AuthMode.PASSWORD,
|
||||||
|
authToken = "INTEGRATION_TEST",
|
||||||
|
keyFilePassword = null))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
@ -33,42 +40,43 @@ abstract class HsmCertificateTest {
|
|||||||
@JvmField
|
@JvmField
|
||||||
val hsmSimulator: HsmSimulator = HsmSimulator()
|
val hsmSimulator: HsmSimulator = HsmSimulator()
|
||||||
|
|
||||||
protected val rootCertParameters: GeneratorParameters by lazy {
|
private fun createGeneratorParameters(certConfig: CertificateConfiguration): GeneratorParameters {
|
||||||
GeneratorParameters(
|
return GeneratorParameters(
|
||||||
hsmHost = hsmSimulator.host,
|
hsmHost = hsmSimulator.host,
|
||||||
hsmPort = hsmSimulator.port,
|
hsmPort = hsmSimulator.port,
|
||||||
trustStoreDirectory = tempFolder.root.toPath(),
|
trustStoreDirectory = tempFolder.root.toPath(),
|
||||||
trustStorePassword = "",
|
trustStorePassword = "",
|
||||||
userConfigs = listOf(UserAuthenticationParameters(
|
userConfigs = HSM_USER_CONFIGS,
|
||||||
username = "INTEGRATION_TEST",
|
certConfig = certConfig
|
||||||
authMode = AuthMode.PASSWORD,
|
|
||||||
authToken = "INTEGRATION_TEST",
|
|
||||||
keyFilePassword = null
|
|
||||||
)),
|
|
||||||
certConfig = CertificateConfiguration(
|
|
||||||
keySpecifier = 1,
|
|
||||||
keyGroup = ROOT_CERT_KEY_GROUP,
|
|
||||||
storeKeysExternal = false,
|
|
||||||
rootKeyGroup = null,
|
|
||||||
subject = ROOT_CERT_SUBJECT,
|
|
||||||
validDays = 3650,
|
|
||||||
keyCurve = "NIST-P256",
|
|
||||||
certificateType = CertificateType.ROOT_CA,
|
|
||||||
keyExport = 0,
|
|
||||||
keyGenMechanism = 4,
|
|
||||||
keyOverride = 0,
|
|
||||||
crlIssuer = null,
|
|
||||||
crlDistributionUrl = null
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected val providerConfig: CryptoServerProviderConfig by lazy {
|
protected lateinit var dbName: String
|
||||||
CryptoServerProviderConfig(
|
|
||||||
Device = "${rootCertParameters.hsmPort}@${rootCertParameters.hsmHost}",
|
@Before
|
||||||
KeySpecifier = rootCertParameters.certConfig.keySpecifier,
|
fun generateDbName() {
|
||||||
KeyGroup = rootCertParameters.certConfig.keyGroup,
|
dbName = random63BitValue().toString()
|
||||||
StoreKeysExternal = rootCertParameters.certConfig.storeKeysExternal)
|
}
|
||||||
|
|
||||||
|
protected fun createGeneratorParameters(keyGroup: String,
|
||||||
|
rootKeyGroup: String?,
|
||||||
|
certificateType: CertificateType,
|
||||||
|
subject: String): GeneratorParameters {
|
||||||
|
return createGeneratorParameters(CertificateConfiguration(
|
||||||
|
keySpecifier = 1,
|
||||||
|
keyGroup = keyGroup,
|
||||||
|
storeKeysExternal = false,
|
||||||
|
rootKeyGroup = rootKeyGroup,
|
||||||
|
subject = subject,
|
||||||
|
validDays = 3650,
|
||||||
|
keyCurve = "NIST-P256",
|
||||||
|
certificateType = certificateType,
|
||||||
|
keyExport = 0,
|
||||||
|
keyGenMechanism = 4,
|
||||||
|
keyOverride = 0,
|
||||||
|
crlIssuer = null,
|
||||||
|
crlDistributionUrl = null
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
protected val hsmSigningServiceConfig = Parameters(
|
protected val hsmSigningServiceConfig = Parameters(
|
||||||
@ -88,4 +96,8 @@ abstract class HsmCertificateTest {
|
|||||||
whenever(inputReader.readPassword(any())).thenReturn(hsmSimulator.cryptoUserCredentials().password)
|
whenever(inputReader.readPassword(any())).thenReturn(hsmSimulator.cryptoUserCredentials().password)
|
||||||
return inputReader
|
return inputReader
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected fun makeTestDataSourceProperties(): Properties {
|
||||||
|
return makeTestDataSourceProperties(dbName)
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package com.r3.corda.networkmanage.common
|
||||||
|
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
const val HOST = "localhost"
|
||||||
|
|
||||||
|
fun makeTestDataSourceProperties(dbName: String): Properties {
|
||||||
|
val props = Properties()
|
||||||
|
props.setProperty("dataSourceClassName", "org.h2.jdbcx.JdbcDataSource")
|
||||||
|
props.setProperty("dataSource.url", "jdbc:h2:mem:$dbName;DB_CLOSE_DELAY=-1")
|
||||||
|
props.setProperty("dataSource.user", "sa")
|
||||||
|
props.setProperty("dataSource.password", "")
|
||||||
|
return props
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package com.r3.corda.networkmanage.doorman
|
package com.r3.corda.networkmanage.doorman
|
||||||
|
|
||||||
|
import com.r3.corda.networkmanage.common.makeTestDataSourceProperties
|
||||||
import com.r3.corda.networkmanage.common.persistence.configureDatabase
|
import com.r3.corda.networkmanage.common.persistence.configureDatabase
|
||||||
import com.r3.corda.networkmanage.common.utils.CertPathAndKey
|
import com.r3.corda.networkmanage.common.utils.CertPathAndKey
|
||||||
import com.r3.corda.networkmanage.doorman.signer.LocalSigner
|
import com.r3.corda.networkmanage.doorman.signer.LocalSigner
|
||||||
@ -35,7 +36,6 @@ import org.assertj.core.api.Assertions.assertThat
|
|||||||
import org.junit.*
|
import org.junit.*
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
import java.util.*
|
|
||||||
import kotlin.streams.toList
|
import kotlin.streams.toList
|
||||||
|
|
||||||
// This is the same test as the one in net.corda.node.utilities.registration but using the real doorman and with some
|
// This is the same test as the one in net.corda.node.utilities.registration but using the real doorman and with some
|
||||||
@ -46,7 +46,8 @@ class NodeRegistrationTest : IntegrationTest() {
|
|||||||
private val aliceName = CordaX500Name("Alice", "London", "GB")
|
private val aliceName = CordaX500Name("Alice", "London", "GB")
|
||||||
private val genevieveName = CordaX500Name("Genevieve", "London", "GB")
|
private val genevieveName = CordaX500Name("Genevieve", "London", "GB")
|
||||||
|
|
||||||
@ClassRule @JvmField
|
@ClassRule
|
||||||
|
@JvmField
|
||||||
val databaseSchemas = IntegrationTestSchemas(notaryName.organisation, aliceName.organisation, genevieveName.organisation)
|
val databaseSchemas = IntegrationTestSchemas(notaryName.organisation, aliceName.organisation, genevieveName.organisation)
|
||||||
|
|
||||||
private val timeoutMillis = 5.seconds.toMillis()
|
private val timeoutMillis = 5.seconds.toMillis()
|
||||||
@ -58,16 +59,17 @@ class NodeRegistrationTest : IntegrationTest() {
|
|||||||
|
|
||||||
private val portAllocation = PortAllocation.Incremental(10000)
|
private val portAllocation = PortAllocation.Incremental(10000)
|
||||||
private val serverAddress = portAllocation.nextHostAndPort()
|
private val serverAddress = portAllocation.nextHostAndPort()
|
||||||
private val dbId = random63BitValue().toString()
|
|
||||||
|
|
||||||
private lateinit var rootCaCert: X509Certificate
|
private lateinit var rootCaCert: X509Certificate
|
||||||
private lateinit var csrCa: CertificateAndKeyPair
|
private lateinit var csrCa: CertificateAndKeyPair
|
||||||
private lateinit var networkMapCa: CertificateAndKeyPair
|
private lateinit var networkMapCa: CertificateAndKeyPair
|
||||||
|
|
||||||
private var server: NetworkManagementServer? = null
|
private var server: NetworkManagementServer? = null
|
||||||
|
private lateinit var dbName: String
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun init() {
|
fun init() {
|
||||||
|
dbName = random63BitValue().toString()
|
||||||
val (rootCa, doormanCa) = createDevIntermediateCaCertPath()
|
val (rootCa, doormanCa) = createDevIntermediateCaCertPath()
|
||||||
rootCaCert = rootCa.certificate
|
rootCaCert = rootCa.certificate
|
||||||
this.csrCa = doormanCa
|
this.csrCa = doormanCa
|
||||||
@ -140,7 +142,7 @@ class NodeRegistrationTest : IntegrationTest() {
|
|||||||
return NetworkManagementServer().apply {
|
return NetworkManagementServer().apply {
|
||||||
start(
|
start(
|
||||||
serverAddress,
|
serverAddress,
|
||||||
configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true)),
|
configureDatabase(makeTestDataSourceProperties(dbName), DatabaseConfig(runMigration = true)),
|
||||||
CertPathAndKey(listOf(csrCa.certificate, rootCaCert), csrCa.keyPair.private),
|
CertPathAndKey(listOf(csrCa.certificate, rootCaCert), csrCa.keyPair.private),
|
||||||
DoormanConfig(approveAll = true, jiraConfig = null, approveInterval = timeoutMillis),
|
DoormanConfig(approveAll = true, jiraConfig = null, approveInterval = timeoutMillis),
|
||||||
networkParameters?.let {
|
networkParameters?.let {
|
||||||
@ -162,14 +164,4 @@ class NodeRegistrationTest : IntegrationTest() {
|
|||||||
}
|
}
|
||||||
assertThat(rpc.networkMapSnapshot()).containsOnlyElementsOf(nodes.map { it.nodeInfo })
|
assertThat(rpc.networkMapSnapshot()).containsOnlyElementsOf(nodes.map { it.nodeInfo })
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Use the other dbs in the integration tests
|
|
||||||
private fun makeTestDataSourceProperties(): Properties {
|
|
||||||
val props = Properties()
|
|
||||||
props.setProperty("dataSourceClassName", "org.h2.jdbcx.JdbcDataSource")
|
|
||||||
props.setProperty("dataSource.url", "jdbc:h2:mem:${dbId}_persistence;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE")
|
|
||||||
props.setProperty("dataSource.user", "sa")
|
|
||||||
props.setProperty("dataSource.password", "")
|
|
||||||
return props
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
package com.r3.corda.networkmanage.hsm
|
package com.r3.corda.networkmanage.hsm
|
||||||
|
|
||||||
|
import com.r3.corda.networkmanage.common.HsmBaseTest
|
||||||
import com.r3.corda.networkmanage.hsm.authentication.Authenticator
|
import com.r3.corda.networkmanage.hsm.authentication.Authenticator
|
||||||
import com.r3.corda.networkmanage.hsm.authentication.createProvider
|
import com.r3.corda.networkmanage.hsm.authentication.createProvider
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
class HsmAuthenticatorTest : HsmCertificateTest() {
|
class HsmAuthenticatorTest : HsmBaseTest() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Authenticator executes the block once user is successfully authenticated`() {
|
fun `Authenticator executes the block once user is successfully authenticated`() {
|
||||||
|
@ -3,7 +3,9 @@ package com.r3.corda.networkmanage.hsm
|
|||||||
import com.nhaarman.mockito_kotlin.any
|
import com.nhaarman.mockito_kotlin.any
|
||||||
import com.nhaarman.mockito_kotlin.mock
|
import com.nhaarman.mockito_kotlin.mock
|
||||||
import com.nhaarman.mockito_kotlin.whenever
|
import com.nhaarman.mockito_kotlin.whenever
|
||||||
|
import com.r3.corda.networkmanage.common.HsmBaseTest
|
||||||
import com.r3.corda.networkmanage.common.utils.CORDA_NETWORK_MAP
|
import com.r3.corda.networkmanage.common.utils.CORDA_NETWORK_MAP
|
||||||
|
import com.r3.corda.networkmanage.hsm.authentication.CryptoServerProviderConfig
|
||||||
import com.r3.corda.networkmanage.hsm.authentication.InputReader
|
import com.r3.corda.networkmanage.hsm.authentication.InputReader
|
||||||
import com.r3.corda.networkmanage.hsm.generator.AutoAuthenticator
|
import com.r3.corda.networkmanage.hsm.generator.AutoAuthenticator
|
||||||
import com.r3.corda.networkmanage.hsm.generator.run
|
import com.r3.corda.networkmanage.hsm.generator.run
|
||||||
@ -18,7 +20,7 @@ import java.security.cert.X509Certificate
|
|||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertNotNull
|
import kotlin.test.assertNotNull
|
||||||
|
|
||||||
class HsmKeyGenerationTest : HsmCertificateTest() {
|
class HsmKeyGenerationTest : HsmBaseTest() {
|
||||||
|
|
||||||
private lateinit var inputReader: InputReader
|
private lateinit var inputReader: InputReader
|
||||||
|
|
||||||
@ -32,28 +34,29 @@ class HsmKeyGenerationTest : HsmCertificateTest() {
|
|||||||
@Test
|
@Test
|
||||||
fun `Root and network map certificates have different namespace`() {
|
fun `Root and network map certificates have different namespace`() {
|
||||||
// when root cert is created
|
// when root cert is created
|
||||||
run(rootCertParameters)
|
run(createGeneratorParameters(
|
||||||
|
keyGroup = ROOT_CERT_KEY_GROUP,
|
||||||
|
rootKeyGroup = null,
|
||||||
|
certificateType = CertificateType.ROOT_CA,
|
||||||
|
subject = ROOT_CERT_SUBJECT
|
||||||
|
))
|
||||||
// when network map cert is created
|
// when network map cert is created
|
||||||
run(rootCertParameters.copy(
|
run(createGeneratorParameters(
|
||||||
certConfig = rootCertParameters.certConfig.copy(
|
keyGroup = NETWORK_MAP_CERT_KEY_GROUP,
|
||||||
keyGroup = NETWORK_MAP_CERT_KEY_GROUP,
|
rootKeyGroup = ROOT_CERT_KEY_GROUP,
|
||||||
rootKeyGroup = ROOT_CERT_KEY_GROUP,
|
certificateType = CertificateType.NETWORK_MAP,
|
||||||
certificateType = CertificateType.NETWORK_MAP,
|
subject = NETWORK_MAP_CERT_SUBJECT
|
||||||
subject = NETWORK_MAP_CERT_SUBJECT
|
|
||||||
)
|
|
||||||
))
|
))
|
||||||
// when doorman cert is created
|
// when doorman cert is created
|
||||||
run(rootCertParameters.copy(
|
run(createGeneratorParameters(
|
||||||
certConfig = rootCertParameters.certConfig.copy(
|
keyGroup = DOORMAN_CERT_KEY_GROUP,
|
||||||
keyGroup = DOORMAN_CERT_KEY_GROUP,
|
rootKeyGroup = ROOT_CERT_KEY_GROUP,
|
||||||
rootKeyGroup = ROOT_CERT_KEY_GROUP,
|
certificateType = CertificateType.INTERMEDIATE_CA,
|
||||||
certificateType = CertificateType.INTERMEDIATE_CA,
|
subject = DOORMAN_CERT_SUBJECT
|
||||||
subject = DOORMAN_CERT_SUBJECT
|
|
||||||
)
|
|
||||||
))
|
))
|
||||||
|
|
||||||
// then root cert is persisted in the HSM
|
// then root cert is persisted in the HSM
|
||||||
AutoAuthenticator(providerConfig, rootCertParameters.userConfigs).connectAndAuthenticate { provider ->
|
AutoAuthenticator(createProviderConfig(ROOT_CERT_KEY_GROUP), HSM_USER_CONFIGS).connectAndAuthenticate { provider ->
|
||||||
val keyStore = HsmX509Utilities.getAndInitializeKeyStore(provider)
|
val keyStore = HsmX509Utilities.getAndInitializeKeyStore(provider)
|
||||||
val rootCert = keyStore.getCertificate(CORDA_ROOT_CA) as X509Certificate
|
val rootCert = keyStore.getCertificate(CORDA_ROOT_CA) as X509Certificate
|
||||||
assertEquals(rootCert.issuerX500Principal, rootCert.subjectX500Principal)
|
assertEquals(rootCert.issuerX500Principal, rootCert.subjectX500Principal)
|
||||||
@ -61,7 +64,7 @@ class HsmKeyGenerationTest : HsmCertificateTest() {
|
|||||||
|
|
||||||
// then network map cert is persisted in the HSM
|
// then network map cert is persisted in the HSM
|
||||||
|
|
||||||
AutoAuthenticator(providerConfig.copy(KeyGroup = NETWORK_MAP_CERT_KEY_GROUP), rootCertParameters.userConfigs)
|
AutoAuthenticator(createProviderConfig(NETWORK_MAP_CERT_KEY_GROUP), HSM_USER_CONFIGS)
|
||||||
.connectAndAuthenticate { provider ->
|
.connectAndAuthenticate { provider ->
|
||||||
val keyStore = HsmX509Utilities.getAndInitializeKeyStore(provider)
|
val keyStore = HsmX509Utilities.getAndInitializeKeyStore(provider)
|
||||||
val networkMapCert = keyStore.getCertificate(CORDA_NETWORK_MAP) as X509Certificate
|
val networkMapCert = keyStore.getCertificate(CORDA_NETWORK_MAP) as X509Certificate
|
||||||
@ -71,7 +74,7 @@ class HsmKeyGenerationTest : HsmCertificateTest() {
|
|||||||
|
|
||||||
// then doorman cert is persisted in the HSM
|
// then doorman cert is persisted in the HSM
|
||||||
|
|
||||||
AutoAuthenticator(providerConfig.copy(KeyGroup = DOORMAN_CERT_KEY_GROUP), rootCertParameters.userConfigs)
|
AutoAuthenticator(createProviderConfig(DOORMAN_CERT_KEY_GROUP), HSM_USER_CONFIGS)
|
||||||
.connectAndAuthenticate { provider ->
|
.connectAndAuthenticate { provider ->
|
||||||
val keyStore = HsmX509Utilities.getAndInitializeKeyStore(provider)
|
val keyStore = HsmX509Utilities.getAndInitializeKeyStore(provider)
|
||||||
val networkMapCert = keyStore.getCertificate(CORDA_INTERMEDIATE_CA) as X509Certificate
|
val networkMapCert = keyStore.getCertificate(CORDA_INTERMEDIATE_CA) as X509Certificate
|
||||||
@ -79,4 +82,12 @@ class HsmKeyGenerationTest : HsmCertificateTest() {
|
|||||||
assertEquals(CordaX500Name.parse(ROOT_CERT_SUBJECT).x500Principal, networkMapCert.issuerX500Principal)
|
assertEquals(CordaX500Name.parse(ROOT_CERT_SUBJECT).x500Principal, networkMapCert.issuerX500Principal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun createProviderConfig(keyGroup: String): CryptoServerProviderConfig {
|
||||||
|
return CryptoServerProviderConfig(
|
||||||
|
Device = "${hsmSimulator.port}@${hsmSimulator.host}",
|
||||||
|
KeySpecifier = 1,
|
||||||
|
KeyGroup = keyGroup,
|
||||||
|
StoreKeysExternal = false)
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,6 +1,11 @@
|
|||||||
package com.r3.corda.networkmanage.hsm
|
package com.r3.corda.networkmanage.hsm
|
||||||
|
|
||||||
import com.nhaarman.mockito_kotlin.mock
|
import com.nhaarman.mockito_kotlin.mock
|
||||||
|
import com.r3.corda.networkmanage.common.HsmBaseTest
|
||||||
|
import com.r3.corda.networkmanage.common.persistence.PersistentNetworkMapStorage
|
||||||
|
import com.r3.corda.networkmanage.common.persistence.configureDatabase
|
||||||
|
import com.r3.corda.networkmanage.common.signer.NetworkMapSigner
|
||||||
|
import com.r3.corda.networkmanage.common.utils.initialiseSerialization
|
||||||
import com.r3.corda.networkmanage.hsm.authentication.Authenticator
|
import com.r3.corda.networkmanage.hsm.authentication.Authenticator
|
||||||
import com.r3.corda.networkmanage.hsm.authentication.createProvider
|
import com.r3.corda.networkmanage.hsm.authentication.createProvider
|
||||||
import com.r3.corda.networkmanage.hsm.generator.run
|
import com.r3.corda.networkmanage.hsm.generator.run
|
||||||
@ -8,82 +13,45 @@ import com.r3.corda.networkmanage.hsm.persistence.ApprovedCertificateRequestData
|
|||||||
import com.r3.corda.networkmanage.hsm.signer.HsmCsrSigner
|
import com.r3.corda.networkmanage.hsm.signer.HsmCsrSigner
|
||||||
import com.r3.corda.networkmanage.hsm.signer.HsmSigner
|
import com.r3.corda.networkmanage.hsm.signer.HsmSigner
|
||||||
import net.corda.core.crypto.Crypto.generateKeyPair
|
import net.corda.core.crypto.Crypto.generateKeyPair
|
||||||
import net.corda.core.crypto.secureRandomBytes
|
|
||||||
import net.corda.core.identity.CordaX500Name.Companion.parse
|
import net.corda.core.identity.CordaX500Name.Companion.parse
|
||||||
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_INTERMEDIATE_CA
|
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_INTERMEDIATE_CA
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA
|
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME
|
import net.corda.nodeapi.internal.crypto.X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities.createCertificateSigningRequest
|
import net.corda.nodeapi.internal.crypto.X509Utilities.createCertificateSigningRequest
|
||||||
|
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||||
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertNotNull
|
import kotlin.test.assertNotNull
|
||||||
|
|
||||||
class HsmSigningServiceTest : HsmCertificateTest() {
|
class HsmSigningServiceTest : HsmBaseTest() {
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `HSM signing service can sign network map data`() {
|
|
||||||
// when root cert is created
|
|
||||||
run(rootCertParameters)
|
|
||||||
// when network map cert is created
|
|
||||||
run(rootCertParameters.copy(
|
|
||||||
certConfig = rootCertParameters.certConfig.copy(
|
|
||||||
keyGroup = NETWORK_MAP_CERT_KEY_GROUP,
|
|
||||||
rootKeyGroup = ROOT_CERT_KEY_GROUP,
|
|
||||||
certificateType = CertificateType.NETWORK_MAP,
|
|
||||||
subject = NETWORK_MAP_CERT_SUBJECT
|
|
||||||
)
|
|
||||||
))
|
|
||||||
// when doorman cert is created
|
|
||||||
run(rootCertParameters.copy(
|
|
||||||
certConfig = rootCertParameters.certConfig.copy(
|
|
||||||
keyGroup = DOORMAN_CERT_KEY_GROUP,
|
|
||||||
rootKeyGroup = ROOT_CERT_KEY_GROUP,
|
|
||||||
certificateType = CertificateType.INTERMEDIATE_CA,
|
|
||||||
subject = DOORMAN_CERT_SUBJECT
|
|
||||||
)
|
|
||||||
))
|
|
||||||
|
|
||||||
// given authenticated user
|
|
||||||
val userInput = givenHsmUserAuthenticationInput()
|
|
||||||
|
|
||||||
// given HSM network map signer
|
|
||||||
val signer = HsmSigner(Authenticator(
|
|
||||||
provider = hsmSigningServiceConfig.createProvider(hsmSigningServiceConfig.networkMapKeyGroup),
|
|
||||||
inputReader = userInput))
|
|
||||||
|
|
||||||
// give random data to sign
|
|
||||||
val toSign = secureRandomBytes(10)
|
|
||||||
|
|
||||||
// when
|
|
||||||
signer.signBytes(toSign)
|
|
||||||
|
|
||||||
// No exception is thrown
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `HSM signing service can sign CSR data`() {
|
fun `HSM signing service can sign CSR data`() {
|
||||||
// when root cert is created
|
// when root cert is created
|
||||||
run(rootCertParameters)
|
run(createGeneratorParameters(
|
||||||
|
keyGroup = ROOT_CERT_KEY_GROUP,
|
||||||
|
rootKeyGroup = null,
|
||||||
|
certificateType = CertificateType.ROOT_CA,
|
||||||
|
subject = ROOT_CERT_SUBJECT
|
||||||
|
))
|
||||||
// when network map cert is created
|
// when network map cert is created
|
||||||
run(rootCertParameters.copy(
|
run(createGeneratorParameters(
|
||||||
certConfig = rootCertParameters.certConfig.copy(
|
keyGroup = NETWORK_MAP_CERT_KEY_GROUP,
|
||||||
keyGroup = NETWORK_MAP_CERT_KEY_GROUP,
|
rootKeyGroup = ROOT_CERT_KEY_GROUP,
|
||||||
rootKeyGroup = ROOT_CERT_KEY_GROUP,
|
certificateType = CertificateType.NETWORK_MAP,
|
||||||
certificateType = CertificateType.NETWORK_MAP,
|
subject = NETWORK_MAP_CERT_SUBJECT
|
||||||
subject = NETWORK_MAP_CERT_SUBJECT
|
|
||||||
)
|
|
||||||
))
|
))
|
||||||
// when doorman cert is created
|
// when doorman cert is created
|
||||||
run(rootCertParameters.copy(
|
run(createGeneratorParameters(
|
||||||
certConfig = rootCertParameters.certConfig.copy(
|
keyGroup = DOORMAN_CERT_KEY_GROUP,
|
||||||
keyGroup = DOORMAN_CERT_KEY_GROUP,
|
rootKeyGroup = ROOT_CERT_KEY_GROUP,
|
||||||
rootKeyGroup = ROOT_CERT_KEY_GROUP,
|
certificateType = CertificateType.INTERMEDIATE_CA,
|
||||||
certificateType = CertificateType.INTERMEDIATE_CA,
|
subject = DOORMAN_CERT_SUBJECT
|
||||||
subject = DOORMAN_CERT_SUBJECT
|
|
||||||
)
|
|
||||||
))
|
))
|
||||||
|
|
||||||
// given authenticated user
|
// given authenticated user
|
||||||
val userInput = givenHsmUserAuthenticationInput()
|
val userInput = givenHsmUserAuthenticationInput()
|
||||||
|
|
||||||
@ -117,4 +85,56 @@ class HsmSigningServiceTest : HsmCertificateTest() {
|
|||||||
val certificates = toSign.certPath!!.certificates
|
val certificates = toSign.certPath!!.certificates
|
||||||
assertEquals(3, certificates.size)
|
assertEquals(3, certificates.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `HSM signing service can sign and serialize network map data to the Doorman DB`() {
|
||||||
|
// when root cert is created
|
||||||
|
run(createGeneratorParameters(
|
||||||
|
keyGroup = ROOT_CERT_KEY_GROUP,
|
||||||
|
rootKeyGroup = null,
|
||||||
|
certificateType = CertificateType.ROOT_CA,
|
||||||
|
subject = ROOT_CERT_SUBJECT
|
||||||
|
))
|
||||||
|
// when network map cert is created
|
||||||
|
run(createGeneratorParameters(
|
||||||
|
keyGroup = NETWORK_MAP_CERT_KEY_GROUP,
|
||||||
|
rootKeyGroup = ROOT_CERT_KEY_GROUP,
|
||||||
|
certificateType = CertificateType.NETWORK_MAP,
|
||||||
|
subject = NETWORK_MAP_CERT_SUBJECT
|
||||||
|
))
|
||||||
|
// when doorman cert is created
|
||||||
|
run(createGeneratorParameters(
|
||||||
|
keyGroup = DOORMAN_CERT_KEY_GROUP,
|
||||||
|
rootKeyGroup = ROOT_CERT_KEY_GROUP,
|
||||||
|
certificateType = CertificateType.INTERMEDIATE_CA,
|
||||||
|
subject = DOORMAN_CERT_SUBJECT
|
||||||
|
))
|
||||||
|
|
||||||
|
// given authenticated user
|
||||||
|
val userInput = givenHsmUserAuthenticationInput()
|
||||||
|
|
||||||
|
// given HSM network map signer
|
||||||
|
val hsmDataSigner = HsmSigner(Authenticator(
|
||||||
|
provider = hsmSigningServiceConfig.createProvider(hsmSigningServiceConfig.networkMapKeyGroup),
|
||||||
|
inputReader = userInput))
|
||||||
|
|
||||||
|
val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true))
|
||||||
|
val networkMapStorage = PersistentNetworkMapStorage(database)
|
||||||
|
|
||||||
|
// given network map parameters
|
||||||
|
val networkMapParameters = testNetworkParameters(emptyList())
|
||||||
|
val networkMapSigner = NetworkMapSigner(networkMapStorage, hsmDataSigner)
|
||||||
|
|
||||||
|
// when
|
||||||
|
initialiseSerialization()
|
||||||
|
networkMapStorage.saveNetworkParameters(networkMapParameters, hsmDataSigner.signBytes(networkMapParameters.serialize().bytes))
|
||||||
|
networkMapSigner.signNetworkMap()
|
||||||
|
|
||||||
|
// then
|
||||||
|
val signedNetworkMap = networkMapStorage.getCurrentNetworkMap()
|
||||||
|
assertNotNull(signedNetworkMap)
|
||||||
|
val persistedNetworkMap = signedNetworkMap!!.verified()
|
||||||
|
assertEquals(networkMapParameters.serialize().hash, persistedNetworkMap.networkParameterHash)
|
||||||
|
assertThat(persistedNetworkMap.nodeInfoHashes).isEmpty()
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,6 +1,8 @@
|
|||||||
package com.r3.corda.networkmanage.hsm
|
package com.r3.corda.networkmanage.hsm
|
||||||
|
|
||||||
import com.nhaarman.mockito_kotlin.*
|
import com.nhaarman.mockito_kotlin.*
|
||||||
|
import com.r3.corda.networkmanage.common.HOST
|
||||||
|
import com.r3.corda.networkmanage.common.HsmBaseTest
|
||||||
import com.r3.corda.networkmanage.common.persistence.configureDatabase
|
import com.r3.corda.networkmanage.common.persistence.configureDatabase
|
||||||
import com.r3.corda.networkmanage.doorman.DoormanConfig
|
import com.r3.corda.networkmanage.doorman.DoormanConfig
|
||||||
import com.r3.corda.networkmanage.doorman.NetworkManagementServer
|
import com.r3.corda.networkmanage.doorman.NetworkManagementServer
|
||||||
@ -31,22 +33,13 @@ import org.junit.After
|
|||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.rules.TemporaryFolder
|
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.persistence.PersistenceException
|
import javax.persistence.PersistenceException
|
||||||
import kotlin.concurrent.scheduleAtFixedRate
|
import kotlin.concurrent.scheduleAtFixedRate
|
||||||
|
|
||||||
class SigningServiceIntegrationTest {
|
class SigningServiceIntegrationTest : HsmBaseTest() {
|
||||||
companion object {
|
|
||||||
private val HOST = "localhost"
|
|
||||||
private val DB_NAME = "test_db"
|
|
||||||
}
|
|
||||||
|
|
||||||
@Rule
|
|
||||||
@JvmField
|
|
||||||
val tempFolder = TemporaryFolder()
|
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
@JvmField
|
@JvmField
|
||||||
@ -160,13 +153,4 @@ class SigningServiceIntegrationTest {
|
|||||||
// doReturn(trustStore).whenever(it).loadTrustStore(any())
|
// doReturn(trustStore).whenever(it).loadTrustStore(any())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeTestDataSourceProperties(): Properties {
|
|
||||||
val props = Properties()
|
|
||||||
props.setProperty("dataSourceClassName", "org.h2.jdbcx.JdbcDataSource")
|
|
||||||
props.setProperty("dataSource.url", "jdbc:h2:mem:${SigningServiceIntegrationTest.DB_NAME};DB_CLOSE_DELAY=-1")
|
|
||||||
props.setProperty("dataSource.user", "sa")
|
|
||||||
props.setProperty("dataSource.password", "")
|
|
||||||
return props
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ class NetworkMapSigner(private val networkMapStorage: NetworkMapStorage, private
|
|||||||
// in current network map.
|
// in current network map.
|
||||||
val latestNetworkParameters = networkMapStorage.getLatestNetworkParameters()
|
val latestNetworkParameters = networkMapStorage.getLatestNetworkParameters()
|
||||||
if (latestNetworkParameters == null) {
|
if (latestNetworkParameters == null) {
|
||||||
logger.debug("No network parameters present")
|
logger.info("No network parameters present")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val currentNetworkParameters = networkMapStorage.getNetworkParametersOfNetworkMap()
|
val currentNetworkParameters = networkMapStorage.getNetworkParametersOfNetworkMap()
|
||||||
|
@ -7,10 +7,15 @@ import joptsimple.ArgumentAcceptingOptionSpec
|
|||||||
import joptsimple.OptionParser
|
import joptsimple.OptionParser
|
||||||
import net.corda.core.crypto.sha256
|
import net.corda.core.crypto.sha256
|
||||||
import net.corda.core.internal.SignedDataWithCert
|
import net.corda.core.internal.SignedDataWithCert
|
||||||
|
import net.corda.core.serialization.internal.SerializationEnvironmentImpl
|
||||||
|
import net.corda.core.serialization.internal.nodeSerializationEnv
|
||||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||||
import net.corda.nodeapi.internal.crypto.X509KeyStore
|
import net.corda.nodeapi.internal.crypto.X509KeyStore
|
||||||
import net.corda.nodeapi.internal.network.NetworkMap
|
import net.corda.nodeapi.internal.network.NetworkMap
|
||||||
import net.corda.nodeapi.internal.network.NetworkParameters
|
import net.corda.nodeapi.internal.network.NetworkParameters
|
||||||
|
import net.corda.nodeapi.internal.serialization.AMQP_P2P_CONTEXT
|
||||||
|
import net.corda.nodeapi.internal.serialization.SerializationFactoryImpl
|
||||||
|
import net.corda.nodeapi.internal.serialization.amqp.AMQPClientSerializationScheme
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.PrivateKey
|
import java.security.PrivateKey
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
@ -57,6 +62,15 @@ fun X509KeyStore.getCertPathAndKey(alias: String, privateKeyPassword: String): C
|
|||||||
return CertPathAndKey(getCertificateChain(alias), getPrivateKey(alias, privateKeyPassword))
|
return CertPathAndKey(getCertificateChain(alias), getPrivateKey(alias, privateKeyPassword))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun initialiseSerialization() {
|
||||||
|
val context = AMQP_P2P_CONTEXT
|
||||||
|
nodeSerializationEnv = SerializationEnvironmentImpl(
|
||||||
|
SerializationFactoryImpl().apply {
|
||||||
|
registerScheme(AMQPClientSerializationScheme())
|
||||||
|
},
|
||||||
|
context)
|
||||||
|
}
|
||||||
|
|
||||||
private fun String.toCamelcase(): String {
|
private fun String.toCamelcase(): String {
|
||||||
return if (contains('_') || contains('-')) {
|
return if (contains('_') || contains('-')) {
|
||||||
CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, this.replace("-", "_"))
|
CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, this.replace("-", "_"))
|
||||||
|
@ -3,20 +3,12 @@ package com.r3.corda.networkmanage.doorman
|
|||||||
import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage
|
import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage
|
||||||
import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage.Companion.DOORMAN_SIGNATURE
|
import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage.Companion.DOORMAN_SIGNATURE
|
||||||
import com.r3.corda.networkmanage.common.persistence.configureDatabase
|
import com.r3.corda.networkmanage.common.persistence.configureDatabase
|
||||||
import com.r3.corda.networkmanage.common.utils.CORDA_NETWORK_MAP
|
import com.r3.corda.networkmanage.common.utils.*
|
||||||
import com.r3.corda.networkmanage.common.utils.CertPathAndKey
|
|
||||||
import com.r3.corda.networkmanage.common.utils.ShowHelpException
|
|
||||||
import com.r3.corda.networkmanage.common.utils.getCertPathAndKey
|
|
||||||
import com.r3.corda.networkmanage.doorman.signer.LocalSigner
|
import com.r3.corda.networkmanage.doorman.signer.LocalSigner
|
||||||
import com.r3.corda.networkmanage.hsm.configuration.Parameters.Companion.DEFAULT_CSR_CERTIFICATE_NAME
|
import com.r3.corda.networkmanage.hsm.configuration.Parameters.Companion.DEFAULT_CSR_CERTIFICATE_NAME
|
||||||
import net.corda.core.serialization.internal.SerializationEnvironmentImpl
|
|
||||||
import net.corda.core.serialization.internal.nodeSerializationEnv
|
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.nodeapi.internal.crypto.X509KeyStore
|
import net.corda.nodeapi.internal.crypto.X509KeyStore
|
||||||
import net.corda.nodeapi.internal.network.NetworkParameters
|
import net.corda.nodeapi.internal.network.NetworkParameters
|
||||||
import net.corda.nodeapi.internal.serialization.AMQP_P2P_CONTEXT
|
|
||||||
import net.corda.nodeapi.internal.serialization.SerializationFactoryImpl
|
|
||||||
import net.corda.nodeapi.internal.serialization.amqp.AMQPClientSerializationScheme
|
|
||||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
@ -100,12 +92,3 @@ fun main(args: Array<String>) {
|
|||||||
e.parser.printHelpOn(System.out)
|
e.parser.printHelpOn(System.out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initialiseSerialization() {
|
|
||||||
val context = AMQP_P2P_CONTEXT
|
|
||||||
nodeSerializationEnv = SerializationEnvironmentImpl(
|
|
||||||
SerializationFactoryImpl().apply {
|
|
||||||
registerScheme(AMQPClientSerializationScheme())
|
|
||||||
},
|
|
||||||
context)
|
|
||||||
}
|
|
||||||
|
@ -6,6 +6,7 @@ import com.r3.corda.networkmanage.common.persistence.PersistentNetworkMapStorage
|
|||||||
import com.r3.corda.networkmanage.common.persistence.configureDatabase
|
import com.r3.corda.networkmanage.common.persistence.configureDatabase
|
||||||
import com.r3.corda.networkmanage.common.signer.NetworkMapSigner
|
import com.r3.corda.networkmanage.common.signer.NetworkMapSigner
|
||||||
import com.r3.corda.networkmanage.common.utils.ShowHelpException
|
import com.r3.corda.networkmanage.common.utils.ShowHelpException
|
||||||
|
import com.r3.corda.networkmanage.common.utils.initialiseSerialization
|
||||||
import com.r3.corda.networkmanage.hsm.authentication.AuthMode
|
import com.r3.corda.networkmanage.hsm.authentication.AuthMode
|
||||||
import com.r3.corda.networkmanage.hsm.authentication.Authenticator
|
import com.r3.corda.networkmanage.hsm.authentication.Authenticator
|
||||||
import com.r3.corda.networkmanage.hsm.authentication.createProvider
|
import com.r3.corda.networkmanage.hsm.authentication.createProvider
|
||||||
@ -54,6 +55,7 @@ fun run(parameters: Parameters) {
|
|||||||
|
|
||||||
// Create DB connection.
|
// Create DB connection.
|
||||||
checkNotNull(dataSourceProperties)
|
checkNotNull(dataSourceProperties)
|
||||||
|
initialiseSerialization()
|
||||||
val database = configureDatabase(dataSourceProperties, databaseConfig)
|
val database = configureDatabase(dataSourceProperties, databaseConfig)
|
||||||
val csrStorage = DBSignedCertificateRequestStorage(database)
|
val csrStorage = DBSignedCertificateRequestStorage(database)
|
||||||
val hsmSigner = HsmSigner(
|
val hsmSigner = HsmSigner(
|
||||||
|
Loading…
Reference in New Issue
Block a user