mirror of
https://github.com/corda/corda.git
synced 2025-01-13 08:20:01 +00:00
Clean up of network-management to make more use of the existing X509 utilities (#419)
This commit is contained in:
parent
3d32760dcc
commit
2432b1380e
2
.idea/compiler.xml
generated
2
.idea/compiler.xml
generated
@ -17,6 +17,8 @@
|
|||||||
<module name="business-network-demo_integrationTest" target="1.8" />
|
<module name="business-network-demo_integrationTest" target="1.8" />
|
||||||
<module name="business-network-demo_main" target="1.8" />
|
<module name="business-network-demo_main" target="1.8" />
|
||||||
<module name="business-network-demo_test" target="1.8" />
|
<module name="business-network-demo_test" target="1.8" />
|
||||||
|
<module name="capsule-hsm-cert-generator_main" target="1.8" />
|
||||||
|
<module name="capsule-hsm-cert-generator_test" target="1.8" />
|
||||||
<module name="capsule-hsm_main" target="1.8" />
|
<module name="capsule-hsm_main" target="1.8" />
|
||||||
<module name="capsule-hsm_test" target="1.8" />
|
<module name="capsule-hsm_test" target="1.8" />
|
||||||
<module name="client_main" target="1.8" />
|
<module name="client_main" target="1.8" />
|
||||||
|
@ -6,7 +6,7 @@ import com.r3.corda.networkmanage.hsm.authentication.createProvider
|
|||||||
import com.r3.corda.networkmanage.hsm.generator.run
|
import com.r3.corda.networkmanage.hsm.generator.run
|
||||||
import com.r3.corda.networkmanage.hsm.persistence.ApprovedCertificateRequestData
|
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.HsmNetworkMapSigner
|
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.crypto.secureRandomBytes
|
||||||
import net.corda.core.identity.CordaX500Name.Companion.parse
|
import net.corda.core.identity.CordaX500Name.Companion.parse
|
||||||
@ -48,7 +48,7 @@ class HsmSigningServiceTest : HsmCertificateTest() {
|
|||||||
val userInput = givenHsmUserAuthenticationInput()
|
val userInput = givenHsmUserAuthenticationInput()
|
||||||
|
|
||||||
// given HSM network map signer
|
// given HSM network map signer
|
||||||
val signer = HsmNetworkMapSigner(Authenticator(
|
val signer = HsmSigner(Authenticator(
|
||||||
provider = hsmSigningServiceConfig.createProvider(hsmSigningServiceConfig.networkMapKeyGroup),
|
provider = hsmSigningServiceConfig.createProvider(hsmSigningServiceConfig.networkMapKeyGroup),
|
||||||
inputReader = userInput))
|
inputReader = userInput))
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@ package com.r3.corda.networkmanage.hsm
|
|||||||
|
|
||||||
import com.nhaarman.mockito_kotlin.*
|
import com.nhaarman.mockito_kotlin.*
|
||||||
import com.r3.corda.networkmanage.common.persistence.configureDatabase
|
import com.r3.corda.networkmanage.common.persistence.configureDatabase
|
||||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
|
||||||
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
|
||||||
import com.r3.corda.networkmanage.hsm.persistence.ApprovedCertificateRequestData
|
import com.r3.corda.networkmanage.hsm.persistence.ApprovedCertificateRequestData
|
||||||
@ -19,7 +18,9 @@ import net.corda.node.services.config.NodeConfiguration
|
|||||||
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
|
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
|
||||||
import net.corda.node.utilities.registration.NetworkRegistrationHelper
|
import net.corda.node.utilities.registration.NetworkRegistrationHelper
|
||||||
import net.corda.nodeapi.internal.createDevNodeCa
|
import net.corda.nodeapi.internal.createDevNodeCa
|
||||||
import net.corda.nodeapi.internal.crypto.*
|
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||||
|
import net.corda.nodeapi.internal.crypto.X509KeyStore
|
||||||
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||||
import net.corda.testing.core.ALICE_NAME
|
import net.corda.testing.core.ALICE_NAME
|
||||||
import net.corda.testing.core.SerializationEnvironmentRule
|
import net.corda.testing.core.SerializationEnvironmentRule
|
||||||
@ -76,7 +77,7 @@ class SigningServiceIntegrationTest {
|
|||||||
for (approvedRequest in approvedRequests) {
|
for (approvedRequest in approvedRequests) {
|
||||||
JcaPKCS10CertificationRequest(approvedRequest.request).run {
|
JcaPKCS10CertificationRequest(approvedRequest.request).run {
|
||||||
val nodeCa = createDevNodeCa(intermediateCa, CordaX500Name.parse(subject.toString()))
|
val nodeCa = createDevNodeCa(intermediateCa, CordaX500Name.parse(subject.toString()))
|
||||||
approvedRequest.certPath = buildCertPath(nodeCa.certificate, intermediateCa.certificate, rootCaCert)
|
approvedRequest.certPath = X509Utilities.buildCertPath(nodeCa.certificate, intermediateCa.certificate, rootCaCert)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
storage.store(approvedRequests, listOf("TEST"))
|
storage.store(approvedRequests, listOf("TEST"))
|
||||||
|
@ -4,11 +4,11 @@ import com.r3.corda.networkmanage.common.persistence.CertificateData
|
|||||||
import com.r3.corda.networkmanage.common.persistence.CertificateSigningRequest
|
import com.r3.corda.networkmanage.common.persistence.CertificateSigningRequest
|
||||||
import com.r3.corda.networkmanage.common.persistence.CertificateStatus
|
import com.r3.corda.networkmanage.common.persistence.CertificateStatus
|
||||||
import com.r3.corda.networkmanage.common.persistence.RequestStatus
|
import com.r3.corda.networkmanage.common.persistence.RequestStatus
|
||||||
|
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
||||||
import org.hibernate.envers.Audited
|
import org.hibernate.envers.Audited
|
||||||
import java.security.cert.CertPath
|
import java.security.cert.CertPath
|
||||||
import java.security.cert.CertificateFactory
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import javax.persistence.*
|
import javax.persistence.*
|
||||||
|
|
||||||
@ -114,5 +114,5 @@ class CertificateDataEntity(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun toCertificatePath(): CertPath = CertificateFactory.getInstance("X.509").generateCertPath(certificatePathBytes.inputStream())
|
private fun toCertificatePath(): CertPath = buildCertPath(certificatePathBytes)
|
||||||
}
|
}
|
@ -8,6 +8,7 @@ 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.nodeapi.internal.crypto.X509CertificateFactory
|
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||||
|
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 java.security.KeyPair
|
import java.security.KeyPair
|
||||||
@ -50,10 +51,12 @@ fun Array<out String>.toConfigWithOptions(registerOptions: OptionParser.() -> Un
|
|||||||
|
|
||||||
class ShowHelpException(val parser: OptionParser, val errorMessage: String? = null) : Exception()
|
class ShowHelpException(val parser: OptionParser, val errorMessage: String? = null) : Exception()
|
||||||
|
|
||||||
fun buildCertPath(vararg certificates: X509Certificate): CertPath = X509CertificateFactory().generateCertPath(certificates.asList())
|
|
||||||
|
|
||||||
fun buildCertPath(certPathBytes: ByteArray): CertPath = X509CertificateFactory().delegate.generateCertPath(certPathBytes.inputStream())
|
fun buildCertPath(certPathBytes: ByteArray): CertPath = X509CertificateFactory().delegate.generateCertPath(certPathBytes.inputStream())
|
||||||
|
|
||||||
|
fun X509KeyStore.getCertPathAndKey(alias: String, privateKeyPassword: String): CertPathAndKey {
|
||||||
|
return CertPathAndKey(getCertificateChain(alias), getPrivateKey(alias, privateKeyPassword))
|
||||||
|
}
|
||||||
|
|
||||||
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("-", "_"))
|
||||||
|
@ -94,6 +94,7 @@ fun parseParameters(vararg args: String): NetworkManagementServerParameters {
|
|||||||
val configFile = if (argConfig.hasPath("configFile")) {
|
val configFile = if (argConfig.hasPath("configFile")) {
|
||||||
Paths.get(argConfig.getString("configFile"))
|
Paths.get(argConfig.getString("configFile"))
|
||||||
} else {
|
} else {
|
||||||
|
// TODO Remove this default
|
||||||
Paths.get(".") / "network-management.conf"
|
Paths.get(".") / "network-management.conf"
|
||||||
}
|
}
|
||||||
require(configFile.isRegularFile()) { "Config file $configFile does not exist" }
|
require(configFile.isRegularFile()) { "Config file $configFile does not exist" }
|
||||||
|
@ -6,20 +6,18 @@ 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.CORDA_NETWORK_MAP
|
||||||
import com.r3.corda.networkmanage.common.utils.CertPathAndKey
|
import com.r3.corda.networkmanage.common.utils.CertPathAndKey
|
||||||
import com.r3.corda.networkmanage.common.utils.ShowHelpException
|
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.SerializationEnvironmentImpl
|
||||||
import net.corda.core.serialization.internal.nodeSerializationEnv
|
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.getCertificateAndKeyPair
|
import net.corda.nodeapi.internal.crypto.X509KeyStore
|
||||||
import net.corda.nodeapi.internal.crypto.getSupportedKey
|
|
||||||
import net.corda.nodeapi.internal.crypto.loadOrCreateKeyStore
|
|
||||||
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.AMQP_P2P_CONTEXT
|
||||||
import net.corda.nodeapi.internal.serialization.SerializationFactoryImpl
|
import net.corda.nodeapi.internal.serialization.SerializationFactoryImpl
|
||||||
import net.corda.nodeapi.internal.serialization.amqp.AMQPClientSerializationScheme
|
import net.corda.nodeapi.internal.serialization.amqp.AMQPClientSerializationScheme
|
||||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
||||||
import java.security.cert.X509Certificate
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
@ -33,17 +31,9 @@ private fun processKeyStore(parameters: NetworkManagementServerParameters): Pair
|
|||||||
// Get password from console if not in config.
|
// Get password from console if not in config.
|
||||||
val keyStorePassword = parameters.keystorePassword ?: readPassword("Key store password: ")
|
val keyStorePassword = parameters.keystorePassword ?: readPassword("Key store password: ")
|
||||||
val privateKeyPassword = parameters.caPrivateKeyPassword ?: readPassword("Private key password: ")
|
val privateKeyPassword = parameters.caPrivateKeyPassword ?: readPassword("Private key password: ")
|
||||||
val keyStore = loadOrCreateKeyStore(parameters.keystorePath, keyStorePassword)
|
val keyStore = X509KeyStore.fromFile(parameters.keystorePath, keyStorePassword)
|
||||||
|
val csrCertPathAndKey = keyStore.getCertPathAndKey(DEFAULT_CSR_CERTIFICATE_NAME, privateKeyPassword)
|
||||||
val csrCertPathAndKey = keyStore.run {
|
|
||||||
CertPathAndKey(
|
|
||||||
keyStore.getCertificateChain(DEFAULT_CSR_CERTIFICATE_NAME).map { it as X509Certificate },
|
|
||||||
keyStore.getSupportedKey(DEFAULT_CSR_CERTIFICATE_NAME, privateKeyPassword)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val networkMapSigner = LocalSigner(keyStore.getCertificateAndKeyPair(CORDA_NETWORK_MAP, privateKeyPassword))
|
val networkMapSigner = LocalSigner(keyStore.getCertificateAndKeyPair(CORDA_NETWORK_MAP, privateKeyPassword))
|
||||||
|
|
||||||
return Pair(csrCertPathAndKey, networkMapSigner)
|
return Pair(csrCertPathAndKey, networkMapSigner)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,8 +11,8 @@ import java.nio.file.Path
|
|||||||
import javax.security.auth.x500.X500Principal
|
import javax.security.auth.x500.X500Principal
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
private val cordaX500Name = "OU=Corda,O=R3 Ltd,L=London,C=GB"
|
// TODO The cert subjects need to be configurable
|
||||||
|
private const val CORDA_X500_BASE = "O=R3 HoldCo LLC,OU=Corda,L=New York,C=US"
|
||||||
const val NETWORK_ROOT_TRUSTSTORE_FILENAME = "network-root-truststore.jks"
|
const val NETWORK_ROOT_TRUSTSTORE_FILENAME = "network-root-truststore.jks"
|
||||||
|
|
||||||
/** Read password from console, do a readLine instead if console is null (e.g. when debugging in IDE). */
|
/** Read password from console, do a readLine instead if console is null (e.g. when debugging in IDE). */
|
||||||
@ -32,10 +32,10 @@ fun generateRootKeyPair(rootStoreFile: Path, rootKeystorePass: String?, rootPriv
|
|||||||
val rootKeystorePassword = rootKeystorePass ?: readPassword("Root Keystore Password: ")
|
val rootKeystorePassword = rootKeystorePass ?: readPassword("Root Keystore Password: ")
|
||||||
// Ensure folder exists.
|
// Ensure folder exists.
|
||||||
rootStoreFile.parent.createDirectories()
|
rootStoreFile.parent.createDirectories()
|
||||||
val rootStore = loadOrCreateKeyStore(rootStoreFile, rootKeystorePassword)
|
val rootStore = X509KeyStore.fromFile(rootStoreFile, rootKeystorePassword, createNew = true)
|
||||||
val rootPrivateKeyPassword = rootPrivateKeyPass ?: readPassword("Root Private Key Password: ")
|
val rootPrivateKeyPassword = rootPrivateKeyPass ?: readPassword("Root Private Key Password: ")
|
||||||
|
|
||||||
if (rootStore.containsAlias(X509Utilities.CORDA_ROOT_CA)) {
|
if (X509Utilities.CORDA_ROOT_CA in rootStore) {
|
||||||
println("${X509Utilities.CORDA_ROOT_CA} already exists in keystore, process will now terminate.")
|
println("${X509Utilities.CORDA_ROOT_CA} already exists in keystore, process will now terminate.")
|
||||||
println(rootStore.getCertificate(X509Utilities.CORDA_ROOT_CA))
|
println(rootStore.getCertificate(X509Utilities.CORDA_ROOT_CA))
|
||||||
exitProcess(1)
|
exitProcess(1)
|
||||||
@ -43,22 +43,24 @@ fun generateRootKeyPair(rootStoreFile: Path, rootKeystorePass: String?, rootPriv
|
|||||||
|
|
||||||
val selfSignKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val selfSignKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
// TODO Make the cert subject configurable
|
// TODO Make the cert subject configurable
|
||||||
val selfSignCert = X509Utilities.createSelfSignedCACertificate(
|
val rootCert = X509Utilities.createSelfSignedCACertificate(
|
||||||
X500Principal("CN=Corda Root CA,$cordaX500Name"),
|
X500Principal("CN=Corda Root CA,$CORDA_X500_BASE"),
|
||||||
selfSignKey)
|
selfSignKey)
|
||||||
rootStore.addOrReplaceKey(X509Utilities.CORDA_ROOT_CA, selfSignKey.private, rootPrivateKeyPassword.toCharArray(), arrayOf(selfSignCert))
|
rootStore.update {
|
||||||
rootStore.save(rootStoreFile, rootKeystorePassword)
|
setPrivateKey(X509Utilities.CORDA_ROOT_CA, selfSignKey.private, listOf(rootCert), rootPrivateKeyPassword)
|
||||||
|
}
|
||||||
|
|
||||||
val trustStorePath = (rootStoreFile.parent / "distribute-nodes").createDirectories() / NETWORK_ROOT_TRUSTSTORE_FILENAME
|
val trustStorePath = (rootStoreFile.parent / "distribute-nodes").createDirectories() / NETWORK_ROOT_TRUSTSTORE_FILENAME
|
||||||
|
|
||||||
val networkRootTrustPassword = networkRootTrustPass ?: readPassword("Network Root Trust Store Password: ")
|
val networkRootTrustPassword = networkRootTrustPass ?: readPassword("Network Root Trust Store Password: ")
|
||||||
val networkRootTrustStore = loadOrCreateKeyStore(trustStorePath, networkRootTrustPassword)
|
|
||||||
networkRootTrustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, selfSignCert)
|
|
||||||
networkRootTrustStore.save(trustStorePath, networkRootTrustPassword)
|
|
||||||
println("Trust store for distribution to nodes created in $networkRootTrustStore")
|
|
||||||
|
|
||||||
|
X509KeyStore.fromFile(trustStorePath, networkRootTrustPassword, createNew = true).update {
|
||||||
|
setCertificate(X509Utilities.CORDA_ROOT_CA, rootCert)
|
||||||
|
}
|
||||||
|
|
||||||
|
println("Trust store for distribution to nodes created in $trustStorePath")
|
||||||
println("Root CA keypair and certificate stored in ${rootStoreFile.toAbsolutePath()}.")
|
println("Root CA keypair and certificate stored in ${rootStoreFile.toAbsolutePath()}.")
|
||||||
println(selfSignCert)
|
println(rootCert)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun generateSigningKeyPairs(keystoreFile: Path, rootStoreFile: Path, rootKeystorePass: String?, rootPrivateKeyPass: String?, keystorePass: String?, caPrivateKeyPass: String?) {
|
fun generateSigningKeyPairs(keystoreFile: Path, rootStoreFile: Path, rootKeystorePass: String?, rootPrivateKeyPass: String?, keystorePass: String?, caPrivateKeyPass: String?) {
|
||||||
@ -66,7 +68,7 @@ fun generateSigningKeyPairs(keystoreFile: Path, rootStoreFile: Path, rootKeystor
|
|||||||
// Get password from console if not in config.
|
// Get password from console if not in config.
|
||||||
val rootKeystorePassword = rootKeystorePass ?: readPassword("Root key store password: ")
|
val rootKeystorePassword = rootKeystorePass ?: readPassword("Root key store password: ")
|
||||||
val rootPrivateKeyPassword = rootPrivateKeyPass ?: readPassword("Root private key password: ")
|
val rootPrivateKeyPassword = rootPrivateKeyPass ?: readPassword("Root private key password: ")
|
||||||
val rootKeyStore = loadKeyStore(rootStoreFile, rootKeystorePassword)
|
val rootKeyStore = X509KeyStore.fromFile(rootStoreFile, rootKeystorePassword)
|
||||||
|
|
||||||
val rootKeyPairAndCert = rootKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_ROOT_CA, rootPrivateKeyPassword)
|
val rootKeyPairAndCert = rootKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_ROOT_CA, rootPrivateKeyPassword)
|
||||||
|
|
||||||
@ -74,10 +76,10 @@ fun generateSigningKeyPairs(keystoreFile: Path, rootStoreFile: Path, rootKeystor
|
|||||||
val privateKeyPassword = caPrivateKeyPass ?: readPassword("Private key Password: ")
|
val privateKeyPassword = caPrivateKeyPass ?: readPassword("Private key Password: ")
|
||||||
// Ensure folder exists.
|
// Ensure folder exists.
|
||||||
keystoreFile.parent.createDirectories()
|
keystoreFile.parent.createDirectories()
|
||||||
val keyStore = loadOrCreateKeyStore(keystoreFile, keyStorePassword)
|
val keyStore = X509KeyStore.fromFile(keystoreFile, keyStorePassword, createNew = true)
|
||||||
|
|
||||||
fun storeCertIfAbsent(alias: String, certificateType: CertificateType, subject: X500Principal, signatureScheme: SignatureScheme) {
|
fun storeCertIfAbsent(alias: String, certificateType: CertificateType, subject: X500Principal, signatureScheme: SignatureScheme) {
|
||||||
if (keyStore.containsAlias(alias)) {
|
if (alias in keyStore) {
|
||||||
println("$alias already exists in keystore:")
|
println("$alias already exists in keystore:")
|
||||||
println(keyStore.getCertificate(alias))
|
println(keyStore.getCertificate(alias))
|
||||||
return
|
return
|
||||||
@ -91,13 +93,10 @@ fun generateSigningKeyPairs(keystoreFile: Path, rootStoreFile: Path, rootKeystor
|
|||||||
subject,
|
subject,
|
||||||
keyPair.public
|
keyPair.public
|
||||||
)
|
)
|
||||||
keyStore.addOrReplaceKey(
|
|
||||||
alias,
|
keyStore.update {
|
||||||
keyPair.private,
|
setPrivateKey(alias, keyPair.private, listOf(cert, rootKeyPairAndCert.certificate), privateKeyPassword)
|
||||||
privateKeyPassword.toCharArray(),
|
}
|
||||||
arrayOf(cert, rootKeyPairAndCert.certificate)
|
|
||||||
)
|
|
||||||
keyStore.save(keystoreFile, keyStorePassword)
|
|
||||||
|
|
||||||
println("$certificateType key pair and certificate stored in $keystoreFile.")
|
println("$certificateType key pair and certificate stored in $keystoreFile.")
|
||||||
println(cert)
|
println(cert)
|
||||||
@ -106,12 +105,12 @@ fun generateSigningKeyPairs(keystoreFile: Path, rootStoreFile: Path, rootKeystor
|
|||||||
storeCertIfAbsent(
|
storeCertIfAbsent(
|
||||||
DEFAULT_CSR_CERTIFICATE_NAME,
|
DEFAULT_CSR_CERTIFICATE_NAME,
|
||||||
CertificateType.INTERMEDIATE_CA,
|
CertificateType.INTERMEDIATE_CA,
|
||||||
X500Principal("CN=Corda Intermediate CA,$cordaX500Name"),
|
X500Principal("CN=Corda Doorman CA,$CORDA_X500_BASE"),
|
||||||
X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
|
|
||||||
storeCertIfAbsent(
|
storeCertIfAbsent(
|
||||||
CORDA_NETWORK_MAP,
|
CORDA_NETWORK_MAP,
|
||||||
CertificateType.NETWORK_MAP,
|
CertificateType.NETWORK_MAP,
|
||||||
X500Principal("CN=Corda Network Map,$cordaX500Name"),
|
X500Principal("CN=Corda Network Map,$CORDA_X500_BASE"),
|
||||||
Crypto.EDDSA_ED25519_SHA512)
|
Crypto.EDDSA_ED25519_SHA512)
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ import com.r3.corda.networkmanage.hsm.menu.Menu
|
|||||||
import com.r3.corda.networkmanage.hsm.persistence.ApprovedCertificateRequestData
|
import com.r3.corda.networkmanage.hsm.persistence.ApprovedCertificateRequestData
|
||||||
import com.r3.corda.networkmanage.hsm.persistence.DBSignedCertificateRequestStorage
|
import com.r3.corda.networkmanage.hsm.persistence.DBSignedCertificateRequestStorage
|
||||||
import com.r3.corda.networkmanage.hsm.signer.HsmCsrSigner
|
import com.r3.corda.networkmanage.hsm.signer.HsmCsrSigner
|
||||||
import com.r3.corda.networkmanage.hsm.signer.HsmNetworkMapSigner
|
import com.r3.corda.networkmanage.hsm.signer.HsmSigner
|
||||||
import com.r3.corda.networkmanage.hsm.utils.mapCryptoServerException
|
import com.r3.corda.networkmanage.hsm.utils.mapCryptoServerException
|
||||||
import net.corda.core.utilities.minutes
|
import net.corda.core.utilities.minutes
|
||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
@ -56,7 +56,7 @@ fun run(parameters: Parameters) {
|
|||||||
checkNotNull(dataSourceProperties)
|
checkNotNull(dataSourceProperties)
|
||||||
val database = configureDatabase(dataSourceProperties, databaseConfig)
|
val database = configureDatabase(dataSourceProperties, databaseConfig)
|
||||||
val csrStorage = DBSignedCertificateRequestStorage(database)
|
val csrStorage = DBSignedCertificateRequestStorage(database)
|
||||||
val hsmSigner = HsmNetworkMapSigner(
|
val hsmSigner = HsmSigner(
|
||||||
Authenticator(
|
Authenticator(
|
||||||
AuthMode.KEY_FILE,
|
AuthMode.KEY_FILE,
|
||||||
autoUsername,
|
autoUsername,
|
||||||
@ -123,7 +123,7 @@ fun run(parameters: Parameters) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun startNetworkingMapSigningPolling(networkMapStorage: NetworkMapStorage,
|
private fun startNetworkingMapSigningPolling(networkMapStorage: NetworkMapStorage,
|
||||||
signer: HsmNetworkMapSigner,
|
signer: HsmSigner,
|
||||||
executor: ScheduledExecutorService,
|
executor: ScheduledExecutorService,
|
||||||
signingPeriod: Duration) {
|
signingPeriod: Duration) {
|
||||||
val networkMapSigner = NetworkMapSigner(networkMapStorage, signer)
|
val networkMapSigner = NetworkMapSigner(networkMapStorage, signer)
|
||||||
|
@ -4,23 +4,21 @@ import CryptoServerCXI.CryptoServerCXI.KEY_ALGO_ECDSA
|
|||||||
import CryptoServerCXI.CryptoServerCXI.KeyAttributes
|
import CryptoServerCXI.CryptoServerCXI.KeyAttributes
|
||||||
import CryptoServerJCE.CryptoServerProvider
|
import CryptoServerJCE.CryptoServerProvider
|
||||||
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.utils.HsmX509Utilities.cleanEcdsaPublicKey
|
||||||
import com.r3.corda.networkmanage.hsm.utils.HsmX509Utilities.createIntermediateCert
|
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.createSelfSignedCert
|
||||||
import com.r3.corda.networkmanage.hsm.utils.HsmX509Utilities.getAndInitializeKeyStore
|
import com.r3.corda.networkmanage.hsm.utils.HsmX509Utilities.getAndInitializeKeyStore
|
||||||
import com.r3.corda.networkmanage.hsm.utils.HsmX509Utilities.getCleanEcdsaKeyPair
|
import com.r3.corda.networkmanage.hsm.utils.HsmX509Utilities.retrieveCertAndKeyPair
|
||||||
import com.r3.corda.networkmanage.hsm.utils.HsmX509Utilities.retrieveKeysAndCertificateChain
|
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.internal.isDirectory
|
import net.corda.core.internal.isDirectory
|
||||||
import net.corda.core.internal.x500Name
|
import net.corda.core.internal.x500Name
|
||||||
import net.corda.core.utilities.contextLogger
|
import net.corda.core.utilities.contextLogger
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateType.*
|
import net.corda.nodeapi.internal.crypto.CertificateType.*
|
||||||
|
import net.corda.nodeapi.internal.crypto.X509KeyStore
|
||||||
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.addOrReplaceCertificate
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
import net.corda.nodeapi.internal.crypto.loadOrCreateKeyStore
|
|
||||||
import net.corda.nodeapi.internal.crypto.save
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.security.Key
|
import java.security.Key
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
@ -50,10 +48,10 @@ class KeyCertificateGenerator(private val parameters: GeneratorParameters) {
|
|||||||
val keyStore = getAndInitializeKeyStore(provider)
|
val keyStore = getAndInitializeKeyStore(provider)
|
||||||
val keyPair = certConfig.generateEcdsaKeyPair(keyName, provider, keyStore)
|
val keyPair = certConfig.generateEcdsaKeyPair(keyName, provider, keyStore)
|
||||||
val certChain = if (rootProvider == null) {
|
val certChain = if (rootProvider == null) {
|
||||||
certConfig.generateRootCert(provider, keyPair, trustStoreDirectory, trustStorePassword)
|
arrayOf(certConfig.generateRootCert(provider, keyPair, trustStoreDirectory, trustStorePassword))
|
||||||
} else {
|
} else {
|
||||||
val rootKeyStore = getAndInitializeKeyStore(rootProvider)
|
val rootKeyStore = getAndInitializeKeyStore(rootProvider)
|
||||||
certConfig.generateIntermediateCert(rootProvider, keyPair, rootKeyStore)
|
certConfig.generateIntermediateCertChain(rootProvider, keyPair, rootKeyStore)
|
||||||
}
|
}
|
||||||
keyStore.addOrReplaceKey(keyName, keyPair.private, null, certChain)
|
keyStore.addOrReplaceKey(keyName, keyPair.private, null, certChain)
|
||||||
logger.info("New certificate and key pair named $keyName have been generated and stored in HSM")
|
logger.info("New certificate and key pair named $keyName have been generated and stored in HSM")
|
||||||
@ -71,43 +69,41 @@ class KeyCertificateGenerator(private val parameters: GeneratorParameters) {
|
|||||||
private fun CertificateConfiguration.generateRootCert(provider: CryptoServerProvider,
|
private fun CertificateConfiguration.generateRootCert(provider: CryptoServerProvider,
|
||||||
keyPair: KeyPair,
|
keyPair: KeyPair,
|
||||||
networkRootTrustStoreDirectory: Path,
|
networkRootTrustStoreDirectory: Path,
|
||||||
networkRootTrustStorePassword: String): Array<X509Certificate> {
|
networkRootTrustStorePassword: String): X509Certificate {
|
||||||
val certificate = createSelfSignedCACert(ROOT_CA,
|
val rootCert = createSelfSignedCert(
|
||||||
|
ROOT_CA,
|
||||||
CordaX500Name.parse(subject).x500Name,
|
CordaX500Name.parse(subject).x500Name,
|
||||||
keyPair,
|
keyPair,
|
||||||
validDays,
|
validDays,
|
||||||
provider,
|
provider,
|
||||||
crlDistributionUrl,
|
crlDistributionUrl,
|
||||||
crlIssuer).certificate
|
crlIssuer?.let { X500Name(it) })
|
||||||
logger.info("Certificate for $subject created.")
|
logger.info("Created root cert:\n$rootCert")
|
||||||
val trustStorePath = networkRootTrustStoreDirectory / "truststore.jks"
|
val trustStorePath = networkRootTrustStoreDirectory / "truststore.jks"
|
||||||
val trustStore = loadOrCreateKeyStore(trustStorePath, networkRootTrustStorePassword)
|
X509KeyStore.fromFile(trustStorePath, networkRootTrustStorePassword, createNew = true).update {
|
||||||
logger.info("Trust store for distribution to nodes created in $trustStore")
|
setCertificate(CORDA_ROOT_CA, rootCert)
|
||||||
trustStore.addOrReplaceCertificate(CORDA_ROOT_CA, certificate)
|
}
|
||||||
logger.info("Certificate $CORDA_ROOT_CA has been added to $trustStore")
|
logger.info("Trust store containing the root for distribution to the nodes created in $trustStorePath")
|
||||||
trustStore.save(trustStorePath, networkRootTrustStorePassword)
|
return rootCert
|
||||||
logger.info("Trust store has been persisted. Ready for distribution.")
|
|
||||||
return arrayOf(certificate)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CertificateConfiguration.generateIntermediateCert(
|
private fun CertificateConfiguration.generateIntermediateCertChain(
|
||||||
provider: CryptoServerProvider,
|
provider: CryptoServerProvider,
|
||||||
keyPair: KeyPair,
|
keyPair: KeyPair,
|
||||||
rootKeyStore: KeyStore): Array<X509Certificate> {
|
rootKeyStore: KeyStore): Array<X509Certificate> {
|
||||||
logger.info("Retrieving the root key pair.")
|
logger.info("Retrieving the root key pair.")
|
||||||
val rootKeysAndCertChain = retrieveKeysAndCertificateChain(CORDA_ROOT_CA,
|
val rootKeysAndCertChain = retrieveCertAndKeyPair(CORDA_ROOT_CA, rootKeyStore)
|
||||||
rootKeyStore)
|
|
||||||
val certificateAndKeyPair = createIntermediateCert(
|
val certificateAndKeyPair = createIntermediateCert(
|
||||||
certificateType,
|
certificateType,
|
||||||
CordaX500Name.parse(subject).x500Name,
|
CordaX500Name.parse(subject).x500Name,
|
||||||
CertificateAndKeyPair(rootKeysAndCertChain.certificateChain.first(), rootKeysAndCertChain.keyPair),
|
rootKeysAndCertChain,
|
||||||
keyPair,
|
keyPair,
|
||||||
validDays,
|
validDays,
|
||||||
provider,
|
provider,
|
||||||
crlDistributionUrl,
|
crlDistributionUrl,
|
||||||
crlIssuer)
|
crlIssuer?.let { X500Name(it) })
|
||||||
logger.info("Certificate for $subject created.")
|
logger.info("Certificate for $subject created.")
|
||||||
return arrayOf(certificateAndKeyPair.certificate, *rootKeysAndCertChain.certificateChain)
|
return arrayOf(certificateAndKeyPair.certificate, rootKeysAndCertChain.certificate)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CertificateConfiguration.generateECDSAKey(keyName: String, provider: CryptoServerProvider) {
|
private fun CertificateConfiguration.generateECDSAKey(keyName: String, provider: CryptoServerProvider) {
|
||||||
@ -129,6 +125,6 @@ class KeyCertificateGenerator(private val parameters: GeneratorParameters) {
|
|||||||
generateECDSAKey(keyName, provider)
|
generateECDSAKey(keyName, provider)
|
||||||
val privateKey = keyStore.getKey(keyName, null) as PrivateKey
|
val privateKey = keyStore.getKey(keyName, null) as PrivateKey
|
||||||
val publicKey = keyStore.getCertificate(keyName).publicKey
|
val publicKey = keyStore.getCertificate(keyName).publicKey
|
||||||
return getCleanEcdsaKeyPair(publicKey, privateKey)
|
return KeyPair(cleanEcdsaPublicKey(publicKey), privateKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,11 +3,13 @@ package com.r3.corda.networkmanage.hsm.signer
|
|||||||
import com.r3.corda.networkmanage.hsm.authentication.Authenticator
|
import com.r3.corda.networkmanage.hsm.authentication.Authenticator
|
||||||
import com.r3.corda.networkmanage.hsm.persistence.ApprovedCertificateRequestData
|
import com.r3.corda.networkmanage.hsm.persistence.ApprovedCertificateRequestData
|
||||||
import com.r3.corda.networkmanage.hsm.persistence.SignedCertificateRequestStorage
|
import com.r3.corda.networkmanage.hsm.persistence.SignedCertificateRequestStorage
|
||||||
import com.r3.corda.networkmanage.hsm.utils.HsmX509Utilities.buildCertPath
|
|
||||||
import com.r3.corda.networkmanage.hsm.utils.HsmX509Utilities.createClientCertificate
|
import com.r3.corda.networkmanage.hsm.utils.HsmX509Utilities.createClientCertificate
|
||||||
import com.r3.corda.networkmanage.hsm.utils.HsmX509Utilities.getAndInitializeKeyStore
|
import com.r3.corda.networkmanage.hsm.utils.HsmX509Utilities.getAndInitializeKeyStore
|
||||||
import com.r3.corda.networkmanage.hsm.utils.HsmX509Utilities.retrieveCertificateAndKeys
|
import com.r3.corda.networkmanage.hsm.utils.HsmX509Utilities.retrieveCertAndKeyPair
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
|
import net.corda.nodeapi.internal.crypto.getX509Certificate
|
||||||
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encapsulates certificate signing logic
|
* Encapsulates certificate signing logic
|
||||||
@ -33,20 +35,19 @@ class HsmCsrSigner(private val storage: SignedCertificateRequestStorage,
|
|||||||
override fun sign(toSign: List<ApprovedCertificateRequestData>) {
|
override fun sign(toSign: List<ApprovedCertificateRequestData>) {
|
||||||
authenticator.connectAndAuthenticate { provider, rootProvider, signers ->
|
authenticator.connectAndAuthenticate { provider, rootProvider, signers ->
|
||||||
val rootKeyStore = getAndInitializeKeyStore(rootProvider!!)
|
val rootKeyStore = getAndInitializeKeyStore(rootProvider!!)
|
||||||
// This should be changed once we allow for more certificates in the chain. Preferably we should use
|
val rootCert = rootKeyStore.getX509Certificate(rootCertAlias)
|
||||||
// keyStore.getCertificateChain(String) and assume entire chain is stored in the HSM (depending on the support).
|
|
||||||
val rootCert = rootKeyStore.getCertificate(rootCertAlias)
|
|
||||||
val keyStore = getAndInitializeKeyStore(provider)
|
val keyStore = getAndInitializeKeyStore(provider)
|
||||||
val doormanCertAndKey = retrieveCertificateAndKeys(intermediateCertAlias, keyStore)
|
val doormanCertAndKey = retrieveCertAndKeyPair(intermediateCertAlias, keyStore)
|
||||||
toSign.forEach {
|
toSign.forEach {
|
||||||
it.certPath = buildCertPath(createClientCertificate(
|
val nodeCaCert = createClientCertificate(
|
||||||
CertificateType.NODE_CA,
|
CertificateType.NODE_CA,
|
||||||
doormanCertAndKey,
|
doormanCertAndKey,
|
||||||
it.request,
|
it.request,
|
||||||
validDays,
|
validDays,
|
||||||
provider,
|
provider,
|
||||||
csrCertCrlDistPoint,
|
csrCertCrlDistPoint,
|
||||||
csrCertCrlIssuer), doormanCertAndKey.certificate, rootCert)
|
csrCertCrlIssuer?.let { X500Name(it) })
|
||||||
|
it.certPath = X509Utilities.buildCertPath(nodeCaCert, doormanCertAndKey.certificate, rootCert)
|
||||||
}
|
}
|
||||||
storage.store(toSign, signers)
|
storage.store(toSign, signers)
|
||||||
println("The following certificates have been signed by $signers:")
|
println("The following certificates have been signed by $signers:")
|
||||||
|
@ -14,8 +14,7 @@ import java.security.Signature
|
|||||||
/**
|
/**
|
||||||
* Signer which connects to a HSM using the given [authenticator] to sign bytes.
|
* Signer which connects to a HSM using the given [authenticator] to sign bytes.
|
||||||
*/
|
*/
|
||||||
// TODO Rename this to HsmSigner
|
class HsmSigner(private val authenticator: Authenticator) : Signer {
|
||||||
class HsmNetworkMapSigner(private val authenticator: Authenticator) : Signer {
|
|
||||||
/**
|
/**
|
||||||
* Signs given data using [CryptoServerJCE.CryptoServerProvider], which connects to the underlying HSM.
|
* Signs given data using [CryptoServerJCE.CryptoServerProvider], which connects to the underlying HSM.
|
||||||
*/
|
*/
|
@ -24,8 +24,6 @@ import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
|||||||
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
|
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.security.*
|
import java.security.*
|
||||||
import java.security.cert.Certificate
|
|
||||||
import java.security.cert.CertificateFactory
|
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
import java.security.spec.X509EncodedKeySpec
|
import java.security.spec.X509EncodedKeySpec
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
@ -34,7 +32,7 @@ import java.util.*
|
|||||||
|
|
||||||
object HsmX509Utilities {
|
object HsmX509Utilities {
|
||||||
|
|
||||||
val SIGNATURE_ALGORITHM = "SHA256withECDSA"
|
const val SIGNATURE_ALGORITHM = "SHA256withECDSA"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a de novo root self-signed X509 v3 CA cert for the specified [KeyPair].
|
* Create a de novo root self-signed X509 v3 CA cert for the specified [KeyPair].
|
||||||
@ -45,27 +43,26 @@ object HsmX509Utilities {
|
|||||||
* @param provider provider to be used during the certificate signing process
|
* @param provider provider to be used during the certificate signing process
|
||||||
* @param crlDistPoint url to the certificate revocation list of this certificate
|
* @param crlDistPoint url to the certificate revocation list of this certificate
|
||||||
* @param crlIssuer issuer of the certificate revocation list of this certificate
|
* @param crlIssuer issuer of the certificate revocation list of this certificate
|
||||||
* @return an instance of [CertificateAndKeyPair] class is returned containing the new root CA Cert and its [KeyPair] for signing downstream certificates.
|
* @return the new root cert.
|
||||||
* Note the generated certificate tree is capped at max depth of 2 to be in line with commercially available certificates
|
* Note the generated certificate tree is capped at max depth of 2 to be in line with commercially available certificates
|
||||||
*/
|
*/
|
||||||
fun createSelfSignedCACert(type: CertificateType,
|
fun createSelfSignedCert(type: CertificateType,
|
||||||
subject: X500Name,
|
subject: X500Name,
|
||||||
keyPair: KeyPair,
|
keyPair: KeyPair,
|
||||||
validDays: Int,
|
validDays: Int,
|
||||||
provider: Provider,
|
provider: Provider,
|
||||||
crlDistPoint: String? = null,
|
crlDistPoint: String? = null,
|
||||||
crlIssuer: String? = null): CertificateAndKeyPair {
|
crlIssuer: X500Name? = null): X509Certificate {
|
||||||
// TODO this needs to be chaneged
|
// TODO this needs to be changed
|
||||||
val serial = BigInteger.valueOf(random63BitValue(provider))
|
val serial = BigInteger.valueOf(random63BitValue(provider))
|
||||||
val pubKey = keyPair.public
|
|
||||||
|
|
||||||
// Ten year certificate validity
|
// Ten year certificate validity
|
||||||
// TODO how do we manage certificate expiry, revocation and loss
|
// TODO how do we manage certificate expiry, revocation and loss
|
||||||
val window = getCertificateValidityWindow(0, validDays)
|
val window = getCertificateValidityWindow(0, validDays)
|
||||||
val keyPurposes = DERSequence(ASN1EncodableVector().apply { type.purposes.forEach { add(it) } })
|
val keyPurposes = DERSequence(ASN1EncodableVector().apply { type.purposes.forEach { add(it) } })
|
||||||
|
|
||||||
val builder = JcaX509v3CertificateBuilder(subject, serial, window.first, window.second, subject, pubKey)
|
val builder = JcaX509v3CertificateBuilder(subject, serial, window.first, window.second, subject, keyPair.public)
|
||||||
builder.addExtension(Extension.subjectKeyIdentifier, false, createSubjectKeyIdentifier(pubKey))
|
builder.addExtension(Extension.subjectKeyIdentifier, false, createSubjectKeyIdentifier(keyPair.public))
|
||||||
builder.addExtension(Extension.basicConstraints, true, BasicConstraints(type.isCA))
|
builder.addExtension(Extension.basicConstraints, true, BasicConstraints(type.isCA))
|
||||||
builder.addExtension(Extension.keyUsage, false, type.keyUsage)
|
builder.addExtension(Extension.keyUsage, false, type.keyUsage)
|
||||||
builder.addExtension(Extension.extendedKeyUsage, false, keyPurposes)
|
builder.addExtension(Extension.extendedKeyUsage, false, keyPurposes)
|
||||||
@ -77,52 +74,33 @@ object HsmX509Utilities {
|
|||||||
|
|
||||||
val cert = signCertificate(builder, keyPair.private, provider)
|
val cert = signCertificate(builder, keyPair.private, provider)
|
||||||
|
|
||||||
cert.checkValidity(Date())
|
cert.checkValidity()
|
||||||
cert.verify(pubKey)
|
cert.verify(keyPair.public)
|
||||||
|
|
||||||
return CertificateAndKeyPair(cert, KeyPair(pubKey, keyPair.private))
|
return cert
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a helper function, which purpose is to workaround a bug in the bouncycastle library
|
* This is a helper function, which purpose is to workaround a bug in the bouncycastle library
|
||||||
* that is associated with the incorrect encoded byte production when the EC algorithm is used with the passed keys.
|
* that is associated with the incorrect encoded byte production when the EC algorithm is used with the passed keys.
|
||||||
* @param publicKey public key
|
* @param publicKey public key
|
||||||
* @param privateKey private key
|
* @return cleaned [PublicKey] instance
|
||||||
* @return cleaned [KeyPair] instance
|
|
||||||
*/
|
*/
|
||||||
fun getCleanEcdsaKeyPair(publicKey: PublicKey, privateKey: PrivateKey): KeyPair {
|
fun cleanEcdsaPublicKey(publicKey: PublicKey): PublicKey {
|
||||||
val rawPublicKeyBytes = publicKey.encoded
|
return KeyFactory.getInstance("EC").generatePublic(X509EncodedKeySpec(publicKey.encoded))
|
||||||
val kf = KeyFactory.getInstance("EC")
|
|
||||||
val cleanPublicKey = kf.generatePublic(X509EncodedKeySpec(rawPublicKeyBytes))
|
|
||||||
return KeyPair(cleanPublicKey, privateKey)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a certificate and keys from the given key store. Also, the keys retrieved are cleaned in a sense of the
|
* Retrieves key pair and certificate from the given key store. Also, the keys retrieved are cleaned in a sense of the
|
||||||
* [getCleanEcdsaKeyPair] method.
|
* [cleanEcdsaPublicKey] method.
|
||||||
* @param certificateKeyName certificate and key name (alias) to be used when querying the key store.
|
* @param alias certificate and key name (alias) to be used when querying the key store.
|
||||||
* @param keyStore key store that holds the certificate with its keys.
|
* @param keyStore key store that holds the certificate with its keys.
|
||||||
* @return instance of [CertificateAndKeyPair] holding the retrieved certificate with its keys.
|
* @return instance of [CertificateAndKeyPair] holding the key pair and the certificate.
|
||||||
*/
|
*/
|
||||||
fun retrieveCertificateAndKeys(certificateKeyName: String, keyStore: KeyStore): CertificateAndKeyPair {
|
fun retrieveCertAndKeyPair(alias: String, keyStore: KeyStore): CertificateAndKeyPair {
|
||||||
val privateKey = keyStore.getKey(certificateKeyName, null) as PrivateKey
|
val privateKey = keyStore.getKey(alias, null) as PrivateKey
|
||||||
val publicKey = keyStore.getCertificate(certificateKeyName).publicKey
|
val certificate = keyStore.getX509Certificate(alias)
|
||||||
val certificate = keyStore.getX509Certificate(certificateKeyName)
|
return CertificateAndKeyPair(certificate, KeyPair(cleanEcdsaPublicKey(certificate.publicKey), privateKey))
|
||||||
return CertificateAndKeyPair(certificate, getCleanEcdsaKeyPair(publicKey, privateKey))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves key pair and certificate chain 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 keyStore key store that holds the certificate with its keys.
|
|
||||||
* @return instance of [KeyPairAndCertificateChain] holding the key pair and the certificate chain.
|
|
||||||
*/
|
|
||||||
fun retrieveKeysAndCertificateChain(certificateKeyName: String, keyStore: KeyStore): KeyPairAndCertificateChain {
|
|
||||||
val privateKey = keyStore.getKey(certificateKeyName, null) as PrivateKey
|
|
||||||
val publicKey = keyStore.getCertificate(certificateKeyName).publicKey
|
|
||||||
val certificateChain = keyStore.getCertificateChain(certificateKeyName).map { it as X509Certificate }
|
|
||||||
return KeyPairAndCertificateChain(getCleanEcdsaKeyPair(publicKey, privateKey), certificateChain.toTypedArray())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -145,7 +123,7 @@ object HsmX509Utilities {
|
|||||||
validDays: Int,
|
validDays: Int,
|
||||||
provider: Provider,
|
provider: Provider,
|
||||||
crlDistPoint: String?,
|
crlDistPoint: String?,
|
||||||
crlIssuer: String?): CertificateAndKeyPair {
|
crlIssuer: X500Name?): CertificateAndKeyPair {
|
||||||
|
|
||||||
val issuer = X509CertificateHolder(certificateAuthority.certificate.encoded).subject
|
val issuer = X509CertificateHolder(certificateAuthority.certificate.encoded).subject
|
||||||
val serial = BigInteger.valueOf(random63BitValue(provider))
|
val serial = BigInteger.valueOf(random63BitValue(provider))
|
||||||
@ -192,7 +170,7 @@ object HsmX509Utilities {
|
|||||||
validDays: Int,
|
validDays: Int,
|
||||||
provider: Provider,
|
provider: Provider,
|
||||||
crlDistPoint: String?,
|
crlDistPoint: String?,
|
||||||
crlIssuer: String?): Certificate {
|
crlIssuer: X500Name?): X509Certificate {
|
||||||
val jcaRequest = JcaPKCS10CertificationRequest(request)
|
val jcaRequest = JcaPKCS10CertificationRequest(request)
|
||||||
// This can be adjusted more to our future needs.
|
// This can be adjusted more to our future needs.
|
||||||
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, CordaX500Name.parse(jcaRequest.subject.toString()).copy(commonName = null).x500Name))), arrayOf())
|
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, CordaX500Name.parse(jcaRequest.subject.toString()).copy(commonName = null).x500Name))), arrayOf())
|
||||||
@ -249,13 +227,6 @@ object HsmX509Utilities {
|
|||||||
return Pair(notBefore, notAfter)
|
return Pair(notBefore, notAfter)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* A utility method for transforming number of certificates into the [CertPath] instance.
|
|
||||||
* The certificates passed should be ordered starting with the leaf certificate and ending with the root one.
|
|
||||||
* @param certificates ordered certficates
|
|
||||||
*/
|
|
||||||
fun buildCertPath(vararg certificates: Certificate) = CertificateFactory.getInstance("X509").generateCertPath(certificates.asList())
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates and initializes a key store from the given crypto server provider.
|
* Creates and initializes a key store from the given crypto server provider.
|
||||||
* @param provider crypto server provider to be used for the key store creation
|
* @param provider crypto server provider to be used for the key store creation
|
||||||
@ -312,11 +283,11 @@ object HsmX509Utilities {
|
|||||||
return certificateBuilder.build(signer).toJca()
|
return certificateBuilder.build(signer).toJca()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addCrlInfo(builder: X509v3CertificateBuilder, crlDistPoint: String?, crlIssuer: String?) {
|
private fun addCrlInfo(builder: X509v3CertificateBuilder, crlDistPoint: String?, crlIssuer: X500Name?) {
|
||||||
if (crlDistPoint != null) {
|
if (crlDistPoint != null) {
|
||||||
val distPointName = DistributionPointName(GeneralNames(GeneralName(GeneralName.uniformResourceIdentifier, crlDistPoint)))
|
val distPointName = DistributionPointName(GeneralNames(GeneralName(GeneralName.uniformResourceIdentifier, crlDistPoint)))
|
||||||
val crlIssuerGeneralNames = crlIssuer?.let {
|
val crlIssuerGeneralNames = crlIssuer?.let {
|
||||||
GeneralNames(GeneralName(CordaX500Name.parse(crlIssuer).x500Name))
|
GeneralNames(GeneralName(crlIssuer))
|
||||||
}
|
}
|
||||||
// The second argument is flag that allows you to define what reason of certificate revocation is served by this distribution point see [ReasonFlags].
|
// The second argument is flag that allows you to define what reason of certificate revocation is served by this distribution point see [ReasonFlags].
|
||||||
// The idea is that you have different revocation per revocation reason. Since we won't go into such a granularity, we can skip that parameter.
|
// The idea is that you have different revocation per revocation reason. Since we won't go into such a granularity, we can skip that parameter.
|
||||||
@ -327,5 +298,3 @@ object HsmX509Utilities {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class KeyPairAndCertificateChain(val keyPair: KeyPair, val certificateChain: Array<X509Certificate>)
|
|
@ -3,14 +3,13 @@ package com.r3.corda.networkmanage.common.persistence
|
|||||||
import com.r3.corda.networkmanage.TestBase
|
import com.r3.corda.networkmanage.TestBase
|
||||||
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.entity.CertificateSigningRequestEntity
|
import com.r3.corda.networkmanage.common.persistence.entity.CertificateSigningRequestEntity
|
||||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||||
import net.corda.testing.internal.createDevNodeCaCertPath
|
|
||||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||||
|
import net.corda.testing.internal.createDevNodeCaCertPath
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
||||||
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
|
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
|
||||||
@ -228,9 +227,8 @@ class PersistentCertificateRequestStorageTest : TestBase() {
|
|||||||
|
|
||||||
private fun generateSignedCertPath(csr: PKCS10CertificationRequest, keyPair: KeyPair): CertPath {
|
private fun generateSignedCertPath(csr: PKCS10CertificationRequest, keyPair: KeyPair): CertPath {
|
||||||
return JcaPKCS10CertificationRequest(csr).run {
|
return JcaPKCS10CertificationRequest(csr).run {
|
||||||
// TODO We need a utility in InternalUtils for converting X500Name -> CordaX500Name
|
|
||||||
val (rootCa, intermediateCa, nodeCa) = createDevNodeCaCertPath(CordaX500Name.build(X500Principal(subject.encoded)), keyPair)
|
val (rootCa, intermediateCa, nodeCa) = createDevNodeCaCertPath(CordaX500Name.build(X500Principal(subject.encoded)), keyPair)
|
||||||
buildCertPath(nodeCa.certificate, intermediateCa.certificate, rootCa.certificate)
|
X509Utilities.buildCertPath(nodeCa.certificate, intermediateCa.certificate, rootCa.certificate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package com.r3.corda.networkmanage.common.persistence
|
package com.r3.corda.networkmanage.common.persistence
|
||||||
|
|
||||||
import com.r3.corda.networkmanage.TestBase
|
import com.r3.corda.networkmanage.TestBase
|
||||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
|
||||||
import com.r3.corda.networkmanage.common.utils.hashString
|
import com.r3.corda.networkmanage.common.utils.hashString
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
@ -71,7 +70,7 @@ class PersistentNodeInfoStorageTest : TestBase() {
|
|||||||
|
|
||||||
requestStorage.putCertificatePath(
|
requestStorage.putCertificatePath(
|
||||||
requestId,
|
requestId,
|
||||||
buildCertPath(nodeCaCert, intermediateCa.certificate, rootCaCert),
|
X509Utilities.buildCertPath(nodeCaCert, intermediateCa.certificate, rootCaCert),
|
||||||
listOf(CertificationRequestStorage.DOORMAN_SIGNATURE))
|
listOf(CertificationRequestStorage.DOORMAN_SIGNATURE))
|
||||||
|
|
||||||
val storedCertPath = nodeInfoStorage.getCertificatePath(SecureHash.parse(keyPair.public.hashString()))
|
val storedCertPath = nodeInfoStorage.getCertificatePath(SecureHash.parse(keyPair.public.hashString()))
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package com.r3.corda.networkmanage.doorman
|
package com.r3.corda.networkmanage.doorman
|
||||||
|
|
||||||
import com.atlassian.jira.rest.client.internal.async.AsynchronousJiraRestClientFactory
|
import com.atlassian.jira.rest.client.internal.async.AsynchronousJiraRestClientFactory
|
||||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
@ -10,6 +9,7 @@ import org.junit.Before
|
|||||||
import org.junit.Ignore
|
import org.junit.Ignore
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
|
|
||||||
@Ignore
|
@Ignore
|
||||||
// This is manual test for testing Jira API.
|
// This is manual test for testing Jira API.
|
||||||
@ -41,8 +41,10 @@ class JiraClientTest {
|
|||||||
@Test
|
@Test
|
||||||
fun updateSignedRequests() {
|
fun updateSignedRequests() {
|
||||||
val requests = jiraClient.getApprovedRequests()
|
val requests = jiraClient.getApprovedRequests()
|
||||||
val selfSignedCA = X509Utilities.createSelfSignedCACertificate(CordaX500Name("test", "london", "GB").x500Principal, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
|
val selfSignedCaCertPath = X509Utilities.buildCertPath(X509Utilities.createSelfSignedCACertificate(
|
||||||
jiraClient.updateSignedRequests(requests.map { it.requestId to buildCertPath(selfSignedCA) }.toMap())
|
X500Principal("O=test,L=london,C=GB"),
|
||||||
|
Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)))
|
||||||
|
jiraClient.updateSignedRequests(requests.associateBy({ it.requestId }, { selfSignedCaCertPath }))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -29,14 +29,17 @@ class DefaultCsrHandlerTest : TestBase() {
|
|||||||
|
|
||||||
val requestStorage: CertificationRequestStorage = mock {
|
val requestStorage: CertificationRequestStorage = mock {
|
||||||
on { getRequest("New") }.thenReturn(certificateSigningRequest())
|
on { getRequest("New") }.thenReturn(certificateSigningRequest())
|
||||||
on { getRequest("Signed") }.thenReturn(certificateSigningRequest(status = RequestStatus.SIGNED, certData = certificateData(CertificateStatus.VALID, buildCertPath(cert))))
|
on { getRequest("Signed") }.thenReturn(certificateSigningRequest(
|
||||||
|
status = RequestStatus.SIGNED,
|
||||||
|
certData = certificateData(CertificateStatus.VALID, X509Utilities.buildCertPath(cert))
|
||||||
|
))
|
||||||
on { getRequest("Rejected") }.thenReturn(certificateSigningRequest(status = RequestStatus.REJECTED, remark = "Random reason"))
|
on { getRequest("Rejected") }.thenReturn(certificateSigningRequest(status = RequestStatus.REJECTED, remark = "Random reason"))
|
||||||
}
|
}
|
||||||
val requestProcessor = DefaultCsrHandler(requestStorage, null)
|
val requestProcessor = DefaultCsrHandler(requestStorage, null)
|
||||||
|
|
||||||
assertEquals(CertificateResponse.NotReady, requestProcessor.getResponse("random"))
|
assertEquals(CertificateResponse.NotReady, requestProcessor.getResponse("random"))
|
||||||
assertEquals(CertificateResponse.NotReady, requestProcessor.getResponse("New"))
|
assertEquals(CertificateResponse.NotReady, requestProcessor.getResponse("New"))
|
||||||
assertEquals(CertificateResponse.Ready(buildCertPath(cert)), requestProcessor.getResponse("Signed"))
|
assertEquals(CertificateResponse.Ready(X509Utilities.buildCertPath(cert)), requestProcessor.getResponse("Signed"))
|
||||||
assertEquals(CertificateResponse.Unauthorised("Random reason"), requestProcessor.getResponse("Rejected"))
|
assertEquals(CertificateResponse.Unauthorised("Random reason"), requestProcessor.getResponse("Rejected"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@ package com.r3.corda.networkmanage.doorman.webservice
|
|||||||
import com.nhaarman.mockito_kotlin.*
|
import com.nhaarman.mockito_kotlin.*
|
||||||
import com.r3.corda.networkmanage.TestBase
|
import com.r3.corda.networkmanage.TestBase
|
||||||
import com.r3.corda.networkmanage.common.persistence.CertificateResponse
|
import com.r3.corda.networkmanage.common.persistence.CertificateResponse
|
||||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
|
||||||
import com.r3.corda.networkmanage.doorman.NetworkManagementWebServer
|
import com.r3.corda.networkmanage.doorman.NetworkManagementWebServer
|
||||||
import com.r3.corda.networkmanage.doorman.signer.CsrHandler
|
import com.r3.corda.networkmanage.doorman.signer.CsrHandler
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
@ -109,7 +108,7 @@ class RegistrationWebServiceTest : TestBase() {
|
|||||||
intermediateCa.keyPair,
|
intermediateCa.keyPair,
|
||||||
X500Principal(subject.encoded),
|
X500Principal(subject.encoded),
|
||||||
publicKey)
|
publicKey)
|
||||||
buildCertPath(tlsCert, intermediateCa.certificate, rootCaCert)
|
X509Utilities.buildCertPath(tlsCert, intermediateCa.certificate, rootCaCert)
|
||||||
}
|
}
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
@ -157,7 +156,7 @@ class RegistrationWebServiceTest : TestBase() {
|
|||||||
X500Principal(subject.encoded),
|
X500Principal(subject.encoded),
|
||||||
publicKey,
|
publicKey,
|
||||||
nameConstraints = nameConstraints)
|
nameConstraints = nameConstraints)
|
||||||
buildCertPath(clientCert, intermediateCa.certificate, rootCaCert)
|
X509Utilities.buildCertPath(clientCert, intermediateCa.certificate, rootCaCert)
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user