mirror of
https://github.com/corda/corda.git
synced 2024-12-26 16:11:12 +00:00
Addressing multiple key groups. Removing redundant config parameters for private key passwords (#409)
This commit is contained in:
parent
5f1590d97f
commit
83ea4611ca
@ -46,6 +46,8 @@ Certificate Configuration
|
||||
:certificateType: Type of the certificate to be created. Allowed values are:
|
||||
ROOT_CA, INTERMEDIATE_CA, NETWORK_MAP.
|
||||
|
||||
:rootKeyGroup: This is an HSM specific parameter that corresponds to key name spacing for the root key. It is ignored if the certificateType value is ROOT_CA. See Utimaco documentation for more details.
|
||||
|
||||
:subject: X500Name formatted string to be used as the certificate public key subject.
|
||||
|
||||
:validDays: Days number for certificate validity.
|
||||
@ -64,7 +66,7 @@ Certificate Configuration
|
||||
|
||||
:keySpecifier: This is an HSM specific parameter that corresponds to key name spacing. See Utimaco documentation for more details.
|
||||
|
||||
:keyGroup: This is an HSM specific parameter that corresponds to key name spacing. See Utimaco documentation for more details.
|
||||
:keyGroup: This is an HSM specific parameter that corresponds to key name spacing for the generated key. See Utimaco documentation for more details.
|
||||
|
||||
|
||||
User Authentication Configuration
|
||||
@ -78,6 +80,6 @@ Allowed parameters are:
|
||||
CARD_READER - Smart card reader authentication
|
||||
KEY_FILE - Key file based authentication.
|
||||
|
||||
:authToken: Depending on the authMode it is either user's password or path to the authentication key file.
|
||||
:authToken: Depending on the authMode it is either user's password or path to the authentication key file. In case of the CARD_READER authMode value, this can be omitted.
|
||||
|
||||
:keyFilePassword: Only relevant, if authMode == KEY_FILE. It is the key file password.
|
@ -28,7 +28,11 @@ Allowed parameters are:
|
||||
:device: HSM connection string. It is of the following format 3001@127.0.0.1, where 3001 is the port number.
|
||||
Default value: "3001@127.0.0.1"
|
||||
|
||||
:keyGroup: HSM key group. This parameter is vendor specific (see Utimaco docs).
|
||||
:rootKeyGroup: HSM key group for the root certificate key. This parameter is vendor specific (see Utimaco docs).
|
||||
|
||||
:networkMapKeyGroup: HSM key group for the network map certificate key. This parameter is vendor specific (see Utimaco docs).
|
||||
|
||||
:doormanKeyGroup: HSM key group for the doorman certificate key. This parameter is vendor specific (see Utimaco docs).
|
||||
|
||||
:keySpecifier: HSM key specifier. This parameter is vendor specific (see Utimaco docs). Default value: 1.
|
||||
|
||||
|
@ -4,7 +4,7 @@ trustStoreDirectory = "."
|
||||
trustStorePassword = "trustpass"
|
||||
|
||||
certConfig {
|
||||
subject = "CN=Corda Root, O=R3Cev, L=London, C=GB"
|
||||
subject = "CN=Corda Root, O=R3 HoldCo LLC, OU=Corda, L=New York, C=US"
|
||||
certificateType = ROOT_CA
|
||||
validDays = 3650
|
||||
keyOverride = 0
|
||||
|
@ -1,15 +1,14 @@
|
||||
basedir = "."
|
||||
device = "3001@127.0.0.1"
|
||||
keyGroup = "DEV.DOORMAN"
|
||||
device = "3001@192.168.0.1"
|
||||
keySpecifier = -1
|
||||
authMode = PASSWORD
|
||||
rootCertificateName = "corda_root_ca"
|
||||
rootPrivateKeyPassword = "Password"
|
||||
csrPrivateKeyPassword = "Password"
|
||||
csrCertificateName = "intermediate_ca"
|
||||
csrCertCrlDistPoint = "http://test.com/revoked.crl"
|
||||
networkMapCertificateName = "intermediate_ca"
|
||||
networkMapPrivateKeyPassword = "Password"
|
||||
rootKeyGroup = "DEV.CORDACONNECT.ROOT"
|
||||
doormanKeyGroup = "DEV.CORDACONNECT.OPS.CERT"
|
||||
networkMapKeyGroup = "DEV.CORDACONNECT.OPS.NETMAP"
|
||||
validDays = 3650
|
||||
signAuthThreshold = 2
|
||||
keyGenAuthThreshold = 2
|
||||
|
@ -0,0 +1,24 @@
|
||||
package com.r3.corda.networkmanage.hsm
|
||||
|
||||
import com.r3.corda.networkmanage.hsm.authentication.Authenticator
|
||||
import com.r3.corda.networkmanage.hsm.authentication.createProvider
|
||||
import org.junit.Test
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class HsmAuthenticatorTest : HsmCertificateTest() {
|
||||
|
||||
@Test
|
||||
fun `Authenticator executes the block once user is successfully authenticated`() {
|
||||
// given
|
||||
val userInput = givenHsmUserAuthenticationInput()
|
||||
val authenticator = Authenticator(provider = hsmSigningServiceConfig.createProvider(hsmSigningServiceConfig.rootKeyGroup), inputReader = userInput)
|
||||
val executed = AtomicBoolean(false)
|
||||
|
||||
// when
|
||||
authenticator.connectAndAuthenticate { _, _, _ -> executed.set(true) }
|
||||
|
||||
// then
|
||||
assertTrue(executed.get())
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
package com.r3.corda.networkmanage.hsm
|
||||
|
||||
import com.nhaarman.mockito_kotlin.any
|
||||
import com.nhaarman.mockito_kotlin.mock
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
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.configuration.Parameters
|
||||
import com.r3.corda.networkmanage.hsm.generator.AuthMode
|
||||
import com.r3.corda.networkmanage.hsm.generator.CertificateConfiguration
|
||||
import com.r3.corda.networkmanage.hsm.generator.GeneratorParameters
|
||||
import com.r3.corda.networkmanage.hsm.generator.UserAuthenticationParameters
|
||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||
import org.junit.Rule
|
||||
import org.junit.rules.TemporaryFolder
|
||||
|
||||
abstract class HsmCertificateTest {
|
||||
companion object {
|
||||
val ROOT_CERT_KEY_GROUP = "DEV.CORDACONNECT.ROOT"
|
||||
val NETWORK_MAP_CERT_KEY_GROUP = "DEV.CORDACONNECT.OPS.NETMAP"
|
||||
val DOORMAN_CERT_KEY_GROUP = "DEV.CORDACONNECT.OPS.CERT"
|
||||
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 DOORMAN_CERT_SUBJECT = "CN=Corda Doorman CA, O=R3 HoldCo LLC, OU=Corda, L=New York, C=US"
|
||||
}
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
val tempFolder = TemporaryFolder()
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
val hsmSimulator: HsmSimulator = HsmSimulator()
|
||||
|
||||
protected val rootCertParameters: GeneratorParameters by lazy {
|
||||
GeneratorParameters(
|
||||
hsmHost = hsmSimulator.host,
|
||||
hsmPort = hsmSimulator.port,
|
||||
trustStoreDirectory = tempFolder.root.toPath(),
|
||||
trustStorePassword = "",
|
||||
userConfigs = listOf(UserAuthenticationParameters(
|
||||
username = "INTEGRATION_TEST",
|
||||
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 {
|
||||
CryptoServerProviderConfig(
|
||||
Device = "${rootCertParameters.hsmPort}@${rootCertParameters.hsmHost}",
|
||||
KeySpecifier = rootCertParameters.certConfig.keySpecifier,
|
||||
KeyGroup = rootCertParameters.certConfig.keyGroup,
|
||||
StoreKeysExternal = rootCertParameters.certConfig.storeKeysExternal)
|
||||
}
|
||||
|
||||
protected val hsmSigningServiceConfig = Parameters(
|
||||
dataSourceProperties = mock(),
|
||||
device = "${hsmSimulator.port}@${hsmSimulator.host}",
|
||||
keySpecifier = 1,
|
||||
rootKeyGroup = ROOT_CERT_KEY_GROUP,
|
||||
doormanKeyGroup = DOORMAN_CERT_KEY_GROUP,
|
||||
networkMapKeyGroup = NETWORK_MAP_CERT_KEY_GROUP,
|
||||
validDays = 3650,
|
||||
csrCertCrlDistPoint = "http://test.com/revoked.crl"
|
||||
)
|
||||
|
||||
protected fun givenHsmUserAuthenticationInput(): InputReader {
|
||||
val inputReader = mock<InputReader>()
|
||||
whenever(inputReader.readLine()).thenReturn(hsmSimulator.cryptoUserCredentials().username)
|
||||
whenever(inputReader.readPassword(any())).thenReturn(hsmSimulator.cryptoUserCredentials().password)
|
||||
return inputReader
|
||||
}
|
||||
}
|
@ -1,111 +1,82 @@
|
||||
package com.r3.corda.networkmanage.hsm
|
||||
|
||||
import com.r3.corda.networkmanage.HsmSimulator
|
||||
import com.nhaarman.mockito_kotlin.any
|
||||
import com.nhaarman.mockito_kotlin.mock
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import com.r3.corda.networkmanage.common.utils.CORDA_NETWORK_MAP
|
||||
import com.r3.corda.networkmanage.hsm.authentication.CryptoServerProviderConfig
|
||||
import com.r3.corda.networkmanage.hsm.generator.*
|
||||
import com.r3.corda.networkmanage.hsm.authentication.InputReader
|
||||
import com.r3.corda.networkmanage.hsm.generator.AutoAuthenticator
|
||||
import com.r3.corda.networkmanage.hsm.generator.run
|
||||
import com.r3.corda.networkmanage.hsm.utils.HsmX509Utilities
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
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_ROOT_CA
|
||||
import org.junit.Rule
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import java.security.cert.X509Certificate
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
|
||||
class HsmKeyGenerationTest {
|
||||
class HsmKeyGenerationTest : HsmCertificateTest() {
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
val tempFolder = TemporaryFolder()
|
||||
private lateinit var inputReader: InputReader
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
val hsmSimulator: HsmSimulator = HsmSimulator()
|
||||
|
||||
private val rootCertParameters: GeneratorParameters by lazy {
|
||||
GeneratorParameters(
|
||||
hsmHost = hsmSimulator.host,
|
||||
hsmPort = hsmSimulator.port,
|
||||
trustStoreDirectory = tempFolder.root.toPath(),
|
||||
trustStorePassword = "",
|
||||
userConfigs = listOf(UserAuthenticationParameters(
|
||||
username = "INTEGRATION_TEST",
|
||||
authMode = AuthMode.PASSWORD,
|
||||
authToken = "INTEGRATION_TEST",
|
||||
keyFilePassword = null
|
||||
)),
|
||||
certConfig = CertificateConfiguration(
|
||||
keySpecifier = 1,
|
||||
keyGroup = "DEV.DOORMAN",
|
||||
storeKeysExternal = false,
|
||||
subject = "CN=Corda Root, O=R3Cev, L=London, C=GB",
|
||||
validDays = 3650,
|
||||
keyCurve = "NIST-P256",
|
||||
certificateType = CertificateType.ROOT_CA,
|
||||
keyExport = 0,
|
||||
keyGenMechanism = 4,
|
||||
keyOverride = 0,
|
||||
crlIssuer = null,
|
||||
crlDistributionUrl = null
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private val providerConfig: CryptoServerProviderConfig by lazy {
|
||||
CryptoServerProviderConfig(
|
||||
Device = "${rootCertParameters.hsmPort}@${rootCertParameters.hsmHost}",
|
||||
KeySpecifier = rootCertParameters.certConfig.keySpecifier,
|
||||
KeyGroup = rootCertParameters.certConfig.keyGroup,
|
||||
StoreKeysExternal = rootCertParameters.certConfig.storeKeysExternal)
|
||||
@Before
|
||||
fun setUp() {
|
||||
inputReader = mock()
|
||||
whenever(inputReader.readLine()).thenReturn(hsmSimulator.cryptoUserCredentials().username)
|
||||
whenever(inputReader.readPassword(any())).thenReturn(hsmSimulator.cryptoUserCredentials().password)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Authenticator executes the block once user is successfully authenticated`() {
|
||||
// given
|
||||
val authenticator = AutoAuthenticator(providerConfig, rootCertParameters.userConfigs)
|
||||
val rootCertGenerator = KeyCertificateGenerator(rootCertParameters)
|
||||
fun `Root and network map certificates have different namespace`() {
|
||||
// when root cert is created
|
||||
authenticator.connectAndAuthenticate { provider ->
|
||||
rootCertGenerator.generate(provider)
|
||||
// then
|
||||
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
|
||||
)
|
||||
))
|
||||
|
||||
// then root cert is persisted in the HSM
|
||||
AutoAuthenticator(providerConfig, rootCertParameters.userConfigs).connectAndAuthenticate { provider ->
|
||||
val keyStore = HsmX509Utilities.getAndInitializeKeyStore(provider)
|
||||
val rootCert = keyStore.getCertificate(CORDA_ROOT_CA) as X509Certificate
|
||||
assertEquals(rootCert.issuerX500Principal, rootCert.subjectX500Principal)
|
||||
}
|
||||
// when network map cert is created
|
||||
val networkMapCertGenerator = KeyCertificateGenerator(rootCertParameters.copy(
|
||||
certConfig = rootCertParameters.certConfig.copy(
|
||||
certificateType = CertificateType.NETWORK_MAP,
|
||||
subject = "CN=Corda NM, O=R3Cev, L=London, C=GB"
|
||||
)
|
||||
))
|
||||
authenticator.connectAndAuthenticate { provider ->
|
||||
networkMapCertGenerator.generate(provider)
|
||||
// then
|
||||
val keyStore = HsmX509Utilities.getAndInitializeKeyStore(provider)
|
||||
val rootCert = keyStore.getCertificate(CORDA_ROOT_CA) as X509Certificate
|
||||
val networkMapCert = keyStore.getCertificate(CORDA_NETWORK_MAP) as X509Certificate
|
||||
assertNotNull(networkMapCert)
|
||||
assertEquals(rootCert.subjectX500Principal, networkMapCert.issuerX500Principal)
|
||||
}
|
||||
// when csr cert is created
|
||||
val csrCertGenerator = KeyCertificateGenerator(rootCertParameters.copy(
|
||||
certConfig = rootCertParameters.certConfig.copy(
|
||||
certificateType = CertificateType.INTERMEDIATE_CA,
|
||||
subject = "CN=Corda CSR, O=R3Cev, L=London, C=GB"
|
||||
)
|
||||
))
|
||||
authenticator.connectAndAuthenticate { provider ->
|
||||
csrCertGenerator.generate(provider)
|
||||
// then
|
||||
val keyStore = HsmX509Utilities.getAndInitializeKeyStore(provider)
|
||||
val rootCert = keyStore.getCertificate(CORDA_ROOT_CA) as X509Certificate
|
||||
val csrCert = keyStore.getCertificate(CORDA_INTERMEDIATE_CA) as X509Certificate
|
||||
assertNotNull(csrCert)
|
||||
assertEquals(rootCert.subjectX500Principal, csrCert.issuerX500Principal)
|
||||
}
|
||||
|
||||
// then network map cert is persisted in the HSM
|
||||
|
||||
AutoAuthenticator(providerConfig.copy(KeyGroup = NETWORK_MAP_CERT_KEY_GROUP), rootCertParameters.userConfigs)
|
||||
.connectAndAuthenticate { provider ->
|
||||
val keyStore = HsmX509Utilities.getAndInitializeKeyStore(provider)
|
||||
val networkMapCert = keyStore.getCertificate(CORDA_NETWORK_MAP) as X509Certificate
|
||||
assertNotNull(networkMapCert)
|
||||
assertEquals(CordaX500Name.parse(ROOT_CERT_SUBJECT).x500Principal, networkMapCert.issuerX500Principal)
|
||||
}
|
||||
|
||||
// then doorman cert is persisted in the HSM
|
||||
|
||||
AutoAuthenticator(providerConfig.copy(KeyGroup = DOORMAN_CERT_KEY_GROUP), rootCertParameters.userConfigs)
|
||||
.connectAndAuthenticate { provider ->
|
||||
val keyStore = HsmX509Utilities.getAndInitializeKeyStore(provider)
|
||||
val networkMapCert = keyStore.getCertificate(CORDA_INTERMEDIATE_CA) as X509Certificate
|
||||
assertNotNull(networkMapCert)
|
||||
assertEquals(CordaX500Name.parse(ROOT_CERT_SUBJECT).x500Principal, networkMapCert.issuerX500Principal)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
package com.r3.corda.networkmanage.hsm
|
||||
|
||||
import com.nhaarman.mockito_kotlin.mock
|
||||
import com.r3.corda.networkmanage.hsm.authentication.Authenticator
|
||||
import com.r3.corda.networkmanage.hsm.authentication.createProvider
|
||||
import com.r3.corda.networkmanage.hsm.generator.run
|
||||
import com.r3.corda.networkmanage.hsm.persistence.ApprovedCertificateRequestData
|
||||
import com.r3.corda.networkmanage.hsm.signer.HsmCsrSigner
|
||||
import com.r3.corda.networkmanage.hsm.signer.HsmNetworkMapSigner
|
||||
import net.corda.core.crypto.Crypto.generateKeyPair
|
||||
import net.corda.core.crypto.secureRandomBytes
|
||||
import net.corda.core.identity.CordaX500Name.Companion.parse
|
||||
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_ROOT_CA
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities.createCertificateSigningRequest
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
|
||||
class HsmSigningServiceTest : HsmCertificateTest() {
|
||||
|
||||
@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 = HsmNetworkMapSigner(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
|
||||
fun `HSM signing service can sign CSR 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 CSR signer
|
||||
val signer = HsmCsrSigner(
|
||||
mock(),
|
||||
CORDA_INTERMEDIATE_CA,
|
||||
"",
|
||||
null,
|
||||
CORDA_ROOT_CA,
|
||||
3650,
|
||||
Authenticator(
|
||||
provider = hsmSigningServiceConfig.createProvider(hsmSigningServiceConfig.doormanKeyGroup),
|
||||
rootProvider = hsmSigningServiceConfig.createProvider(hsmSigningServiceConfig.rootKeyGroup),
|
||||
inputReader = userInput)
|
||||
)
|
||||
|
||||
// give random data to sign
|
||||
val toSign = ApprovedCertificateRequestData(
|
||||
"test",
|
||||
createCertificateSigningRequest(
|
||||
parse("O=R3Cev,L=London,C=GB").x500Principal,
|
||||
"my@mail.com",
|
||||
generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME)))
|
||||
|
||||
// when
|
||||
signer.sign(listOf(toSign))
|
||||
|
||||
// then
|
||||
assertNotNull(toSign.certPath)
|
||||
val certificates = toSign.certPath!!.certificates
|
||||
assertEquals(3, certificates.size)
|
||||
}
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
package com.r3.corda.networkmanage.hsm
|
||||
|
||||
import com.nhaarman.mockito_kotlin.any
|
||||
import com.nhaarman.mockito_kotlin.mock
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import com.r3.corda.networkmanage.HsmSimulator
|
||||
import com.r3.corda.networkmanage.hsm.authentication.Authenticator
|
||||
import com.r3.corda.networkmanage.hsm.authentication.InputReader
|
||||
import com.r3.corda.networkmanage.hsm.authentication.createProvider
|
||||
import com.r3.corda.networkmanage.hsm.configuration.Parameters
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class HsmTest {
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
val hsmSimulator: HsmSimulator = HsmSimulator()
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
val tempFolder = TemporaryFolder()
|
||||
|
||||
private val testParameters = Parameters(
|
||||
dataSourceProperties = mock(),
|
||||
device = "${hsmSimulator.port}@${hsmSimulator.host}",
|
||||
keySpecifier = 1,
|
||||
csrPrivateKeyPassword = "",
|
||||
networkMapPrivateKeyPassword = "",
|
||||
rootPrivateKeyPassword = "",
|
||||
keyGroup = "DEV.DOORMAN",
|
||||
validDays = 3650,
|
||||
csrCertCrlDistPoint = "http://test.com/revoked.crl"
|
||||
)
|
||||
|
||||
private lateinit var inputReader: InputReader
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
inputReader = mock()
|
||||
whenever(inputReader.readLine()).thenReturn(hsmSimulator.cryptoUserCredentials().username)
|
||||
whenever(inputReader.readPassword(any())).thenReturn(hsmSimulator.cryptoUserCredentials().password)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Authenticator executes the block once user is successfully authenticated`() {
|
||||
// given
|
||||
val authenticator = Authenticator(testParameters.createProvider(), inputReader = inputReader)
|
||||
val executed = AtomicBoolean(false)
|
||||
|
||||
// when
|
||||
authenticator.connectAndAuthenticate { _, _ -> executed.set(true) }
|
||||
|
||||
// then
|
||||
assertTrue(executed.get())
|
||||
}
|
||||
}
|
@ -57,8 +57,13 @@ fun run(parameters: Parameters) {
|
||||
val database = configureDatabase(dataSourceProperties, databaseConfig)
|
||||
val csrStorage = DBSignedCertificateRequestStorage(database)
|
||||
val hsmSigner = HsmNetworkMapSigner(
|
||||
networkMapPrivateKeyPassword,
|
||||
Authenticator(createProvider(), AuthMode.KEY_FILE, autoUsername, authKeyFilePath, authKeyFilePassword, signAuthThreshold))
|
||||
Authenticator(
|
||||
AuthMode.KEY_FILE,
|
||||
autoUsername,
|
||||
authKeyFilePath,
|
||||
authKeyFilePassword,
|
||||
signAuthThreshold,
|
||||
provider = createProvider(networkMapKeyGroup)))
|
||||
|
||||
val networkMapStorage = PersistentNetworkMapStorage(database)
|
||||
val scheduler = Executors.newSingleThreadScheduledExecutor()
|
||||
@ -68,12 +73,18 @@ fun run(parameters: Parameters) {
|
||||
val signer = HsmCsrSigner(
|
||||
csrStorage,
|
||||
csrCertificateName,
|
||||
csrPrivateKeyPassword,
|
||||
csrCertCrlDistPoint,
|
||||
csrCertCrlIssuer,
|
||||
rootCertificateName,
|
||||
validDays,
|
||||
Authenticator(createProvider(), authMode, autoUsername, authKeyFilePath, authKeyFilePassword, signAuthThreshold))
|
||||
Authenticator(
|
||||
authMode,
|
||||
autoUsername,
|
||||
authKeyFilePath,
|
||||
authKeyFilePassword,
|
||||
signAuthThreshold,
|
||||
provider = createProvider(doormanKeyGroup),
|
||||
rootProvider = createProvider(rootKeyGroup)))
|
||||
signer.sign(it)
|
||||
}
|
||||
Menu().withExceptionHandler(::processError).addItem("1", "Sign all approved and unsigned CSRs", {
|
||||
@ -141,13 +152,6 @@ private fun confirmedSign(selectedItems: List<ApprovedCertificateRequestData>):
|
||||
return result
|
||||
}
|
||||
|
||||
private fun confirmedKeyGen(): Boolean {
|
||||
println("Are you sure you want to generate new keys/certificates (it will overwrite the existing ones):")
|
||||
var result = false
|
||||
Menu().addItem("Y", "Yes", { result = true }, true).setExitOption("N", "No").showMenu()
|
||||
return result
|
||||
}
|
||||
|
||||
private fun getSelection(toSelect: List<ApprovedCertificateRequestData>): List<ApprovedCertificateRequestData> {
|
||||
print("CSRs to be signed (comma separated list): ")
|
||||
val line = readLine()
|
||||
|
@ -11,22 +11,24 @@ import kotlin.reflect.full.memberProperties
|
||||
/**
|
||||
* Performs user authentication against the HSM
|
||||
*/
|
||||
class Authenticator(private val provider: CryptoServerProvider,
|
||||
private val mode: AuthMode = AuthMode.PASSWORD,
|
||||
class Authenticator(private val mode: AuthMode = AuthMode.PASSWORD,
|
||||
private val autoUsername: String? = null,
|
||||
private val authKeyFilePath: Path? = null,
|
||||
private val authKeyFilePass: String? = null,
|
||||
private val authStrengthThreshold: Int = 2,
|
||||
inputReader: InputReader = ConsoleInputReader()) : InputReader by inputReader {
|
||||
inputReader: InputReader = ConsoleInputReader(),
|
||||
private val provider: CryptoServerProvider,
|
||||
private val rootProvider: CryptoServerProvider? = null) : InputReader by inputReader {
|
||||
|
||||
/**
|
||||
* Interactively (using console) authenticates a user against the HSM. Once authentication is successful the
|
||||
* [block] is executed.
|
||||
* @param block to be executed once the authentication process succeeds. The block should take 2 parameters:
|
||||
* 1) [CryptoServerProvider] instance
|
||||
* @param block to be executed once the authentication process succeeds. The block should take 3 parameters:
|
||||
* 1) [CryptoServerProvider] instance of the certificate provider
|
||||
* 2) [CryptoServerProvider] instance of the root certificate provider
|
||||
* 2) List of strings that corresponds to user names authenticated against the HSM.
|
||||
*/
|
||||
fun <T : Any> connectAndAuthenticate(block: (CryptoServerProvider, List<String>) -> T): T {
|
||||
fun <T : Any> connectAndAuthenticate(block: (CryptoServerProvider, CryptoServerProvider?, List<String>) -> T): T {
|
||||
return try {
|
||||
val authenticated = mutableListOf<String>()
|
||||
loop@ while (true) {
|
||||
@ -45,7 +47,12 @@ class Authenticator(private val provider: CryptoServerProvider,
|
||||
when (mode) {
|
||||
AuthMode.CARD_READER -> {
|
||||
println("Authenticating using card reader")
|
||||
println("Accessing the certificate key group data...")
|
||||
provider.loginSign(user, ":cs2:cyb:USB0", null)
|
||||
if (rootProvider != null) {
|
||||
println("Accessing the root certificate key group data...")
|
||||
rootProvider.loginSign(user, ":cs2:cyb:USB0", null)
|
||||
}
|
||||
}
|
||||
AuthMode.KEY_FILE -> {
|
||||
println("Authenticating using preconfigured key file $authKeyFilePath")
|
||||
@ -60,7 +67,12 @@ class Authenticator(private val provider: CryptoServerProvider,
|
||||
} else {
|
||||
authKeyFilePass
|
||||
}
|
||||
println("Accessing the certificate key group data...")
|
||||
provider.loginSign(user, authKeyFilePath.toString(), password)
|
||||
if (rootProvider != null) {
|
||||
println("Accessing the root certificate key group data...")
|
||||
rootProvider.loginSign(user, authKeyFilePath.toString(), password)
|
||||
}
|
||||
}
|
||||
AuthMode.PASSWORD -> {
|
||||
println("Authenticating using password")
|
||||
@ -69,7 +81,12 @@ class Authenticator(private val provider: CryptoServerProvider,
|
||||
authenticated.clear()
|
||||
break@loop
|
||||
}
|
||||
println("Accessing the certificate key group data...")
|
||||
provider.loginPassword(user, password)
|
||||
if (rootProvider != null) {
|
||||
println("Accessing the root certificate key group data...")
|
||||
rootProvider.loginPassword(user, password)
|
||||
}
|
||||
}
|
||||
}
|
||||
authenticated.add(user!!)
|
||||
@ -82,7 +99,7 @@ class Authenticator(private val provider: CryptoServerProvider,
|
||||
}
|
||||
}
|
||||
if (!authenticated.isEmpty()) {
|
||||
block(provider, authenticated)
|
||||
block(provider, rootProvider, authenticated)
|
||||
} else {
|
||||
throw AuthenticationException()
|
||||
}
|
||||
@ -114,7 +131,7 @@ data class CryptoServerProviderConfig(
|
||||
/**
|
||||
* Creates an instance of [CryptoServerProvider] that corresponds to the HSM.
|
||||
*/
|
||||
fun Parameters.createProvider(): CryptoServerProvider {
|
||||
fun Parameters.createProvider(keyGroup: String): CryptoServerProvider {
|
||||
val config = CryptoServerProviderConfig(
|
||||
Device = device,
|
||||
KeyGroup = keyGroup,
|
||||
|
@ -20,15 +20,14 @@ data class Parameters(val dataSourceProperties: Properties,
|
||||
val databaseConfig: DatabaseConfig = DatabaseConfig(),
|
||||
val device: String = DEFAULT_DEVICE,
|
||||
// TODO this needs cleaning up after the config-file-only support is implemented
|
||||
val keyGroup: String,
|
||||
val rootKeyGroup: String,
|
||||
val doormanKeyGroup:String,
|
||||
val networkMapKeyGroup: String,
|
||||
val keySpecifier: Int = DEFAULT_KEY_SPECIFIER,
|
||||
val rootPrivateKeyPassword: String,
|
||||
val csrPrivateKeyPassword: String,
|
||||
val csrCertificateName: String = DEFAULT_CSR_CERTIFICATE_NAME,
|
||||
val csrCertCrlDistPoint: String,
|
||||
val csrCertCrlIssuer: String? = DEFAULT_CSR_CERT_CRL_ISSUER, // X500 name of the issuing authority e.g. "L=New York, C=US, OU=Org Unit, CN=Service Name",
|
||||
// if null parent CA is is considered as an issuer.
|
||||
val networkMapPrivateKeyPassword: String,
|
||||
val rootCertificateName: String = DEFAULT_ROOT_CERTIFICATE_NAME,
|
||||
val validDays: Int,
|
||||
val signAuthThreshold: Int = DEFAULT_SIGN_AUTH_THRESHOLD,
|
||||
|
@ -19,7 +19,7 @@ class AutoAuthenticator(providerConfig: CryptoServerProviderConfig,
|
||||
fun connectAndAuthenticate(block: (CryptoServerProvider) -> Unit) {
|
||||
try {
|
||||
for (userConfig in userConfigs) {
|
||||
when(userConfig.authMode) {
|
||||
when (userConfig.authMode) {
|
||||
AuthMode.PASSWORD -> provider.loginPassword(userConfig.username, userConfig.authToken)
|
||||
AuthMode.CARD_READER -> provider.loginSign(userConfig.username, ":cs2:cyb:USB0", null)
|
||||
AuthMode.KEY_FILE -> provider.loginSign(userConfig.username, userConfig.keyFilePassword, userConfig.authToken)
|
||||
|
@ -15,7 +15,7 @@ import java.nio.file.Paths
|
||||
*/
|
||||
data class UserAuthenticationParameters(val username: String,
|
||||
val authMode: AuthMode,
|
||||
val authToken: String, // password or path to the key file, depending on the [authMode]
|
||||
val authToken: String?, // password or path to the key file, depending on the [authMode]
|
||||
val keyFilePassword: String?) // used only if authMode == [AuthMode.KEY_FILE]
|
||||
|
||||
/**
|
||||
@ -42,6 +42,7 @@ data class CertificateConfiguration(val keyGroup: String,
|
||||
val keySpecifier: Int,
|
||||
val storeKeysExternal: Boolean,
|
||||
val certificateType: CertificateType,
|
||||
val rootKeyGroup: String?,
|
||||
val subject: String, // it is certificate [X500Name] subject
|
||||
val validDays: Int,
|
||||
val crlDistributionUrl: String?,
|
||||
|
@ -4,7 +4,6 @@ import CryptoServerCXI.CryptoServerCXI.KEY_ALGO_ECDSA
|
||||
import CryptoServerCXI.CryptoServerCXI.KeyAttributes
|
||||
import CryptoServerJCE.CryptoServerProvider
|
||||
import com.r3.corda.networkmanage.common.utils.CORDA_NETWORK_MAP
|
||||
import com.r3.corda.networkmanage.doorman.NETWORK_ROOT_TRUSTSTORE_FILENAME
|
||||
import com.r3.corda.networkmanage.hsm.utils.HsmX509Utilities.createIntermediateCert
|
||||
import com.r3.corda.networkmanage.hsm.utils.HsmX509Utilities.createSelfSignedCACert
|
||||
import com.r3.corda.networkmanage.hsm.utils.HsmX509Utilities.getAndInitializeKeyStore
|
||||
@ -15,10 +14,13 @@ import net.corda.core.internal.div
|
||||
import net.corda.core.internal.isDirectory
|
||||
import net.corda.core.internal.x500Name
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.nodeapi.internal.crypto.*
|
||||
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||
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_ROOT_CA
|
||||
import net.corda.nodeapi.internal.crypto.addOrReplaceCertificate
|
||||
import net.corda.nodeapi.internal.crypto.loadOrCreateKeyStore
|
||||
import net.corda.nodeapi.internal.crypto.save
|
||||
import java.nio.file.Path
|
||||
import java.security.Key
|
||||
import java.security.KeyPair
|
||||
@ -27,7 +29,6 @@ import java.security.PrivateKey
|
||||
import java.security.cert.Certificate
|
||||
import java.security.cert.X509Certificate
|
||||
|
||||
data class CertificateNameAndPass(val certificateName: String, val privateKeyPassword: String)
|
||||
/**
|
||||
* Encapsulates logic for key and certificate generation.
|
||||
*
|
||||
@ -37,7 +38,7 @@ class KeyCertificateGenerator(private val parameters: GeneratorParameters) {
|
||||
val logger = contextLogger()
|
||||
}
|
||||
|
||||
fun generate(provider: CryptoServerProvider) {
|
||||
fun generate(provider: CryptoServerProvider, rootProvider: CryptoServerProvider? = null) {
|
||||
parameters.run {
|
||||
require(trustStoreDirectory.isDirectory()) { "trustStoreDirectory must point to a directory." }
|
||||
val keyName = when (certConfig.certificateType) {
|
||||
@ -48,10 +49,11 @@ class KeyCertificateGenerator(private val parameters: GeneratorParameters) {
|
||||
}
|
||||
val keyStore = getAndInitializeKeyStore(provider)
|
||||
val keyPair = certConfig.generateEcdsaKeyPair(keyName, provider, keyStore)
|
||||
val certChain = if (certConfig.certificateType == ROOT_CA) {
|
||||
val certChain = if (rootProvider == null) {
|
||||
certConfig.generateRootCert(provider, keyPair, trustStoreDirectory, trustStorePassword)
|
||||
} else {
|
||||
certConfig.generateIntermediateCert(provider, keyPair, keyStore)
|
||||
val rootKeyStore = getAndInitializeKeyStore(rootProvider)
|
||||
certConfig.generateIntermediateCert(rootProvider, keyPair, rootKeyStore)
|
||||
}
|
||||
keyStore.addOrReplaceKey(keyName, keyPair.private, null, certChain)
|
||||
logger.info("New certificate and key pair named $keyName have been generated and stored in HSM")
|
||||
@ -77,12 +79,13 @@ class KeyCertificateGenerator(private val parameters: GeneratorParameters) {
|
||||
provider,
|
||||
crlDistributionUrl,
|
||||
crlIssuer).certificate
|
||||
val networkRootTruststorePath = networkRootTrustStoreDirectory / NETWORK_ROOT_TRUSTSTORE_FILENAME
|
||||
val networkRootTruststore = loadOrCreateKeyStore(networkRootTruststorePath, networkRootTrustStorePassword)
|
||||
logger.info("Trust store for distribution to nodes created in $networkRootTruststorePath")
|
||||
networkRootTruststore.addOrReplaceCertificate(CORDA_ROOT_CA, certificate)
|
||||
logger.info("Certificate $CORDA_ROOT_CA has been added to $networkRootTruststorePath")
|
||||
networkRootTruststore.save(networkRootTruststorePath, networkRootTrustStorePassword)
|
||||
logger.info("Certificate for $subject created.")
|
||||
val trustStorePath = networkRootTrustStoreDirectory / "truststore.jks"
|
||||
val trustStore = loadOrCreateKeyStore(trustStorePath, networkRootTrustStorePassword)
|
||||
logger.info("Trust store for distribution to nodes created in $trustStore")
|
||||
trustStore.addOrReplaceCertificate(CORDA_ROOT_CA, certificate)
|
||||
logger.info("Certificate $CORDA_ROOT_CA has been added to $trustStore")
|
||||
trustStore.save(trustStorePath, networkRootTrustStorePassword)
|
||||
logger.info("Trust store has been persisted. Ready for distribution.")
|
||||
return arrayOf(certificate)
|
||||
}
|
||||
@ -90,8 +93,10 @@ class KeyCertificateGenerator(private val parameters: GeneratorParameters) {
|
||||
private fun CertificateConfiguration.generateIntermediateCert(
|
||||
provider: CryptoServerProvider,
|
||||
keyPair: KeyPair,
|
||||
keyStore: KeyStore): Array<X509Certificate> {
|
||||
val rootKeysAndCertChain = retrieveKeysAndCertificateChain(CORDA_ROOT_CA, keyStore)
|
||||
rootKeyStore: KeyStore): Array<X509Certificate> {
|
||||
logger.info("Retrieving the root key pair.")
|
||||
val rootKeysAndCertChain = retrieveKeysAndCertificateChain(CORDA_ROOT_CA,
|
||||
rootKeyStore)
|
||||
val certificateAndKeyPair = createIntermediateCert(
|
||||
certificateType,
|
||||
CordaX500Name.parse(subject).x500Name,
|
||||
@ -101,6 +106,7 @@ class KeyCertificateGenerator(private val parameters: GeneratorParameters) {
|
||||
provider,
|
||||
crlDistributionUrl,
|
||||
crlIssuer)
|
||||
logger.info("Certificate for $subject created.")
|
||||
return arrayOf(certificateAndKeyPair.certificate, *rootKeysAndCertChain.certificateChain)
|
||||
}
|
||||
|
||||
@ -114,8 +120,9 @@ class KeyCertificateGenerator(private val parameters: GeneratorParameters) {
|
||||
name = keyName
|
||||
setCurve(keyCurve)
|
||||
}
|
||||
logger.info("Generating key $keyName")
|
||||
logger.info("Generating key $keyName.")
|
||||
provider.cryptoServer.generateKey(keyOverride, keyAttributes, keyGenMechanism)
|
||||
logger.info("$keyName key generated.")
|
||||
}
|
||||
|
||||
private fun CertificateConfiguration.generateEcdsaKeyPair(keyName: String, provider: CryptoServerProvider, keyStore: KeyStore): KeyPair {
|
||||
|
@ -2,24 +2,40 @@ package com.r3.corda.networkmanage.hsm.generator
|
||||
|
||||
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
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import java.nio.file.Paths
|
||||
|
||||
private val log = LogManager.getLogger("com.r3.corda.networkmanage.hsm.generator.Main")
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val commandLineOptions = parseCommandLine(*args)
|
||||
parseParameters(commandLineOptions.configFile).run {
|
||||
run(parseParameters(parseCommandLine(*args).configFile))
|
||||
}
|
||||
|
||||
fun run(parameters: GeneratorParameters) {
|
||||
parameters.run {
|
||||
val providerConfig = CryptoServerProviderConfig(
|
||||
Device = "$hsmPort@$hsmHost",
|
||||
KeySpecifier = certConfig.keySpecifier,
|
||||
KeyGroup = certConfig.keyGroup,
|
||||
StoreKeysExternal = certConfig.storeKeysExternal)
|
||||
try {
|
||||
val authenticator = AutoAuthenticator(providerConfig, userConfigs)
|
||||
authenticator.connectAndAuthenticate { provider ->
|
||||
AutoAuthenticator(providerConfig, userConfigs).connectAndAuthenticate { provider ->
|
||||
val generator = KeyCertificateGenerator(this)
|
||||
generator.generate(provider)
|
||||
if (certConfig.certificateType == ROOT_CA) {
|
||||
generator.generate(provider)
|
||||
} else {
|
||||
requireNotNull(certConfig.rootKeyGroup)
|
||||
val rootProviderConfig = CryptoServerProviderConfig(
|
||||
Device = "$hsmPort@$hsmHost",
|
||||
KeySpecifier = certConfig.keySpecifier,
|
||||
KeyGroup = certConfig.rootKeyGroup!!,
|
||||
StoreKeysExternal = certConfig.storeKeysExternal)
|
||||
AutoAuthenticator(rootProviderConfig, userConfigs).connectAndAuthenticate { rootProvider ->
|
||||
generator.generate(provider, rootProvider)
|
||||
rootProvider.logoff()
|
||||
}
|
||||
}
|
||||
provider.logoff()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
log.error(mapCryptoServerException(e))
|
||||
|
@ -14,7 +14,6 @@ import net.corda.nodeapi.internal.crypto.CertificateType
|
||||
*/
|
||||
class HsmCsrSigner(private val storage: SignedCertificateRequestStorage,
|
||||
private val intermediateCertAlias: String,
|
||||
private val intermediateCertPrivateKeyPass: String?,
|
||||
private val csrCertCrlDistPoint: String,
|
||||
private val csrCertCrlIssuer: String?,
|
||||
private val rootCertAlias: String,
|
||||
@ -32,22 +31,22 @@ class HsmCsrSigner(private val storage: SignedCertificateRequestStorage,
|
||||
* @param toSign list of approved certificates to be signed
|
||||
*/
|
||||
override fun sign(toSign: List<ApprovedCertificateRequestData>) {
|
||||
authenticator.connectAndAuthenticate { provider, signers ->
|
||||
val keyStore = getAndInitializeKeyStore(provider)
|
||||
authenticator.connectAndAuthenticate { provider, rootProvider, signers ->
|
||||
val rootKeyStore = getAndInitializeKeyStore(rootProvider!!)
|
||||
// This should be changed once we allow for more certificates in the chain. Preferably we should use
|
||||
// keyStore.getCertificateChain(String) and assume entire chain is stored in the HSM (depending on the support).
|
||||
val rootCert = keyStore.getCertificate(rootCertAlias)
|
||||
val intermediatePrivateKeyPass = intermediateCertPrivateKeyPass ?: authenticator.readPassword("CA Private Key Password: ")
|
||||
val intermediateCertAndKey = retrieveCertificateAndKeys(intermediateCertAlias, intermediatePrivateKeyPass, keyStore)
|
||||
val rootCert = rootKeyStore.getCertificate(rootCertAlias)
|
||||
val keyStore = getAndInitializeKeyStore(provider)
|
||||
val doormanCertAndKey = retrieveCertificateAndKeys(intermediateCertAlias, keyStore)
|
||||
toSign.forEach {
|
||||
it.certPath = buildCertPath(createClientCertificate(
|
||||
CertificateType.NODE_CA,
|
||||
intermediateCertAndKey,
|
||||
doormanCertAndKey,
|
||||
it.request,
|
||||
validDays,
|
||||
provider,
|
||||
csrCertCrlDistPoint,
|
||||
csrCertCrlIssuer), rootCert)
|
||||
csrCertCrlIssuer), doormanCertAndKey.certificate, rootCert)
|
||||
}
|
||||
storage.store(toSign, signers)
|
||||
println("The following certificates have been signed by $signers:")
|
||||
|
@ -15,18 +15,17 @@ import java.security.Signature
|
||||
* Signer which connects to a HSM using the given [authenticator] to sign bytes.
|
||||
*/
|
||||
// TODO Rename this to HsmSigner
|
||||
class HsmNetworkMapSigner(private val privateKeyPassword: String,
|
||||
private val authenticator: Authenticator) : Signer {
|
||||
class HsmNetworkMapSigner(private val authenticator: Authenticator) : Signer {
|
||||
/**
|
||||
* Signs given data using [CryptoServerJCE.CryptoServerProvider], which connects to the underlying HSM.
|
||||
*/
|
||||
override fun signBytes(data: ByteArray): DigitalSignatureWithCert {
|
||||
return authenticator.connectAndAuthenticate { provider, _ ->
|
||||
return authenticator.connectAndAuthenticate { provider, _, _ ->
|
||||
val keyStore = getAndInitializeKeyStore(provider)
|
||||
val certificate = keyStore.getX509Certificate(CORDA_NETWORK_MAP)
|
||||
// Don't worry this is not a real private key but a pointer to one that resides in the HSM. It only works
|
||||
// when used with the given provider.
|
||||
val key = keyStore.getKey(CORDA_NETWORK_MAP, privateKeyPassword.toCharArray()) as PrivateKey
|
||||
val key = keyStore.getKey(CORDA_NETWORK_MAP, null) as PrivateKey
|
||||
val signature = Signature.getInstance(HsmX509Utilities.SIGNATURE_ALGORITHM, provider).run {
|
||||
initSign(key)
|
||||
update(data)
|
||||
|
@ -101,12 +101,11 @@ object HsmX509Utilities {
|
||||
* Retrieves a certificate and keys from the given key store. Also, the keys retrieved are cleaned in a sense of the
|
||||
* [getCleanEcdsaKeyPair] method.
|
||||
* @param certificateKeyName certificate and key name (alias) to be used when querying the key store.
|
||||
* @param privateKeyPassword password for the private key.
|
||||
* @param keyStore key store that holds the certificate with its keys.
|
||||
* @return instance of [CertificateAndKeyPair] holding the retrieved certificate with its keys.
|
||||
*/
|
||||
fun retrieveCertificateAndKeys(certificateKeyName: String, privateKeyPassword: String, keyStore: KeyStore): CertificateAndKeyPair {
|
||||
val privateKey = keyStore.getKey(certificateKeyName, privateKeyPassword.toCharArray()) as PrivateKey
|
||||
fun retrieveCertificateAndKeys(certificateKeyName: String, keyStore: KeyStore): CertificateAndKeyPair {
|
||||
val privateKey = keyStore.getKey(certificateKeyName, null) as PrivateKey
|
||||
val publicKey = keyStore.getCertificate(certificateKeyName).publicKey
|
||||
val certificate = keyStore.getX509Certificate(certificateKeyName)
|
||||
return CertificateAndKeyPair(certificate, getCleanEcdsaKeyPair(publicKey, privateKey))
|
||||
|
@ -1,17 +0,0 @@
|
||||
device = "3001@127.0.0.1"
|
||||
keyGroup = "*"
|
||||
keySpecifier = -1
|
||||
authMode = PASSWORD
|
||||
csrPrivateKeyPassword = ""
|
||||
networkMapPrivateKeyPassword = ""
|
||||
rootPrivateKeyPassword = ""
|
||||
keyGroup = "DEV.DOORMAN"
|
||||
validDays = 3650
|
||||
|
||||
h2port = 0
|
||||
dataSourceProperties {
|
||||
"dataSourceClassName" = org.h2.jdbcx.JdbcDataSource
|
||||
"dataSource.url" = "jdbc:h2:file:"${basedir}"/persistence;DB_CLOSE_ON_EXIT=FALSE;LOCK_TIMEOUT=10000;WRITE_DELAY=0;AUTO_SERVER_PORT="${h2port}
|
||||
"dataSource.user" = sa
|
||||
"dataSource.password" = ""
|
||||
}
|
@ -28,7 +28,7 @@ class AuthenticatorTest : TestBase() {
|
||||
|
||||
// when
|
||||
assertFailsWith<AuthenticationException> {
|
||||
Authenticator(provider = provider, inputReader = inputReader).connectAndAuthenticate { _, _ -> }
|
||||
Authenticator(provider = provider, inputReader = inputReader).connectAndAuthenticate { _, _, _ -> }
|
||||
}
|
||||
|
||||
//then
|
||||
@ -47,7 +47,7 @@ class AuthenticatorTest : TestBase() {
|
||||
var executed = false
|
||||
|
||||
// when
|
||||
Authenticator(provider = provider, inputReader = inputReader).connectAndAuthenticate { _, _ -> executed = true }
|
||||
Authenticator(provider = provider, inputReader = inputReader).connectAndAuthenticate { _, _, _ -> executed = true }
|
||||
|
||||
// then
|
||||
verify(provider).loginPassword(username, password)
|
||||
@ -64,7 +64,7 @@ class AuthenticatorTest : TestBase() {
|
||||
var executed = false
|
||||
|
||||
// when
|
||||
Authenticator(provider = provider, inputReader = inputReader, mode = AuthMode.CARD_READER).connectAndAuthenticate { _, _ -> executed = true }
|
||||
Authenticator(provider = provider, inputReader = inputReader, mode = AuthMode.CARD_READER).connectAndAuthenticate { _, _, _ -> executed = true }
|
||||
|
||||
// then
|
||||
verify(provider).loginSign(username, ":cs2:cyb:USB0", null)
|
||||
@ -83,7 +83,7 @@ class AuthenticatorTest : TestBase() {
|
||||
var executed = false
|
||||
|
||||
// when
|
||||
Authenticator(provider = provider, inputReader = inputReader).connectAndAuthenticate { _, _ -> executed = true }
|
||||
Authenticator(provider = provider, inputReader = inputReader).connectAndAuthenticate { _, _, _ -> executed = true }
|
||||
|
||||
// then
|
||||
verify(provider, times(3)).loginPassword(username, password)
|
||||
|
@ -10,7 +10,7 @@ import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
|
||||
class ConfigurationTest : TestBase() {
|
||||
private val validConfigPath = File(javaClass.getResource("/hsm.conf").toURI()).absolutePath
|
||||
private val validConfigPath = File("./hsm.conf").absolutePath
|
||||
private val invalidConfigPath = File(javaClass.getResource("/hsm_fail.conf").toURI()).absolutePath
|
||||
|
||||
@Test
|
||||
|
@ -1,18 +0,0 @@
|
||||
device = "3001@192.168.0.1"
|
||||
keyGroup = "DEV.DOORMAN"
|
||||
keySpecifier = -1
|
||||
authMode = PASSWORD
|
||||
csrPrivateKeyPassword = ""
|
||||
csrCertCrlDistPoint = "http://test.com/revoked.crl"
|
||||
networkMapPrivateKeyPassword = ""
|
||||
rootPrivateKeyPassword = ""
|
||||
keyGroup = "DEV.DOORMAN"
|
||||
validDays = 3650
|
||||
|
||||
h2port = 0
|
||||
dataSourceProperties {
|
||||
"dataSourceClassName" = org.h2.jdbcx.JdbcDataSource
|
||||
"dataSource.url" = "jdbc:h2:file:"${basedir}"/persistence;DB_CLOSE_ON_EXIT=FALSE;LOCK_TIMEOUT=10000;WRITE_DELAY=0;AUTO_SERVER_PORT="${h2port}
|
||||
"dataSource.user" = sa
|
||||
"dataSource.password" = ""
|
||||
}
|
Loading…
Reference in New Issue
Block a user