mirror of
https://github.com/corda/corda.git
synced 2024-12-27 08:22:35 +00:00
Adding CRL signing (#528)
* Adding CRL signing * Adding CRL * Addressing review comments * Address review comments
This commit is contained in:
parent
c2b7755824
commit
6d99f7e5d4
@ -4,6 +4,8 @@ keySpecifier = -1
|
||||
|
||||
doorman {
|
||||
crlDistributionPoint = "http://test.com/revoked.crl"
|
||||
compatibilityZoneURL = "http://test.com/api"
|
||||
crlUpdatePeriod = 200000
|
||||
validDays = 3650
|
||||
rootKeyStoreFile = "dummyfile.jks"
|
||||
rootKeyStorePassword = "trustpass"
|
||||
|
@ -28,6 +28,7 @@ import net.corda.nodeapi.internal.crypto.CertificateType
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import java.net.URL
|
||||
import java.nio.file.Path
|
||||
import java.util.*
|
||||
import com.r3.corda.networkmanage.hsm.authentication.AuthMode as SigningServiceAuthMode
|
||||
@ -127,35 +128,46 @@ abstract class HsmBaseTest {
|
||||
), hsmUserConfigs)
|
||||
}
|
||||
|
||||
protected fun createHsmSigningServiceConfig(): SigningServiceConfig {
|
||||
protected fun createHsmSigningServiceConfig(doormanCertConfig: DoormanCertificateConfig?,
|
||||
networkMapCertificateConfig: NetworkMapCertificateConfig?): SigningServiceConfig {
|
||||
return SigningServiceConfig(
|
||||
dataSourceProperties = mock(),
|
||||
device = "${hsmSimulator.port}@${hsmSimulator.host}",
|
||||
keySpecifier = 1,
|
||||
doorman = DoormanCertificateConfig(
|
||||
rootKeyStoreFile = rootKeyStoreFile,
|
||||
keyGroup = DOORMAN_CERT_KEY_GROUP,
|
||||
validDays = 3650,
|
||||
rootKeyStorePassword = TRUSTSTORE_PASSWORD,
|
||||
crlDistributionPoint = "http://test.com/revoked.crl",
|
||||
authParameters = AuthParametersConfig(
|
||||
mode = SigningServiceAuthMode.PASSWORD,
|
||||
threshold = 2
|
||||
)
|
||||
),
|
||||
networkMap = NetworkMapCertificateConfig(
|
||||
username = "INTEGRATION_TEST",
|
||||
keyGroup = NETWORK_MAP_CERT_KEY_GROUP,
|
||||
authParameters = AuthParametersConfig(
|
||||
mode = SigningServiceAuthMode.PASSWORD,
|
||||
password = "INTEGRATION_TEST",
|
||||
threshold = 2
|
||||
)
|
||||
doorman = doormanCertConfig,
|
||||
networkMap = networkMapCertificateConfig
|
||||
)
|
||||
}
|
||||
|
||||
protected fun createDoormanCertificateConfig(): DoormanCertificateConfig {
|
||||
return DoormanCertificateConfig(
|
||||
rootKeyStoreFile = rootKeyStoreFile,
|
||||
keyGroup = DOORMAN_CERT_KEY_GROUP,
|
||||
validDays = 3650,
|
||||
rootKeyStorePassword = TRUSTSTORE_PASSWORD,
|
||||
crlDistributionPoint = URL("http://test.com/revoked.crl"),
|
||||
compatibilityZoneURL = URL("http://test.com/api"),
|
||||
crlUpdatePeriod = 1000,
|
||||
authParameters = AuthParametersConfig(
|
||||
mode = SigningServiceAuthMode.PASSWORD,
|
||||
threshold = 2
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
protected fun createNetworkMapCertificateConfig(): NetworkMapCertificateConfig {
|
||||
return NetworkMapCertificateConfig(
|
||||
username = "INTEGRATION_TEST",
|
||||
keyGroup = NETWORK_MAP_CERT_KEY_GROUP,
|
||||
authParameters = AuthParametersConfig(
|
||||
mode = SigningServiceAuthMode.PASSWORD,
|
||||
password = "INTEGRATION_TEST",
|
||||
threshold = 2
|
||||
)
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
protected fun givenHsmUserAuthenticationInput(username: String = HSM_USERNAME,
|
||||
password: String = HSM_PASSWORD): InputReader {
|
||||
val inputReader = mock<InputReader>()
|
||||
|
@ -23,7 +23,7 @@ class HsmAuthenticatorTest : HsmBaseTest() {
|
||||
fun `Authenticator executes the block once user is successfully authenticated`() {
|
||||
// given
|
||||
val userInput = givenHsmUserAuthenticationInput()
|
||||
val hsmSigningServiceConfig = createHsmSigningServiceConfig()
|
||||
val hsmSigningServiceConfig = createHsmSigningServiceConfig(createDoormanCertificateConfig(), null)
|
||||
val doormanCertificateConfig = hsmSigningServiceConfig.doorman!!
|
||||
val authenticator = Authenticator(provider = createProvider(
|
||||
doormanCertificateConfig.keyGroup,
|
||||
|
@ -56,7 +56,7 @@ class HsmPermissionTest : HsmBaseTest() {
|
||||
val userInput = givenHsmUserAuthenticationInput(HSM_USERNAME_OPS_CERT_)
|
||||
|
||||
// given HSM CSR signer
|
||||
val hsmSigningServiceConfig = createHsmSigningServiceConfig()
|
||||
val hsmSigningServiceConfig = createHsmSigningServiceConfig(createDoormanCertificateConfig(), null)
|
||||
val signer = HsmCsrSigner(
|
||||
mock(),
|
||||
hsmSigningServiceConfig.doorman!!.loadRootKeyStore(),
|
||||
@ -112,7 +112,7 @@ class HsmPermissionTest : HsmBaseTest() {
|
||||
val userInput = givenHsmUserAuthenticationInput(HSM_USERNAME_OPS_CERT)
|
||||
|
||||
// given HSM CSR signer
|
||||
val hsmSigningServiceConfig = createHsmSigningServiceConfig()
|
||||
val hsmSigningServiceConfig = createHsmSigningServiceConfig(createDoormanCertificateConfig(), null)
|
||||
val signer = HsmCsrSigner(
|
||||
mock(),
|
||||
hsmSigningServiceConfig.doorman!!.loadRootKeyStore(),
|
||||
|
@ -15,6 +15,7 @@ import com.r3.corda.networkmanage.common.HsmBaseTest
|
||||
import com.r3.corda.networkmanage.common.persistence.PersistentNetworkMapStorage
|
||||
import com.r3.corda.networkmanage.common.persistence.configureDatabase
|
||||
import com.r3.corda.networkmanage.common.signer.NetworkMapSigner
|
||||
import com.r3.corda.networkmanage.common.utils.CORDA_NETWORK_MAP
|
||||
import com.r3.corda.networkmanage.common.utils.initialiseSerialization
|
||||
import com.r3.corda.networkmanage.hsm.authentication.Authenticator
|
||||
import com.r3.corda.networkmanage.hsm.authentication.createProvider
|
||||
@ -54,7 +55,7 @@ class HsmSigningServiceTest : HsmBaseTest() {
|
||||
val userInput = givenHsmUserAuthenticationInput()
|
||||
|
||||
// given HSM CSR signer
|
||||
val hsmSigningServiceConfig = createHsmSigningServiceConfig()
|
||||
val hsmSigningServiceConfig = createHsmSigningServiceConfig(createDoormanCertificateConfig(), null)
|
||||
val doormanCertificateConfig = hsmSigningServiceConfig.doorman!!
|
||||
val signer = HsmCsrSigner(
|
||||
mock(),
|
||||
@ -98,7 +99,7 @@ class HsmSigningServiceTest : HsmBaseTest() {
|
||||
val userInput = givenHsmUserAuthenticationInput()
|
||||
|
||||
// given HSM CSR signer
|
||||
val hsmSigningServiceConfig = createHsmSigningServiceConfig()
|
||||
val hsmSigningServiceConfig = createHsmSigningServiceConfig(createDoormanCertificateConfig(), null)
|
||||
val doormanCertificateConfig = hsmSigningServiceConfig.doorman!!
|
||||
val signer = HsmCsrSigner(
|
||||
mock(),
|
||||
@ -143,14 +144,15 @@ class HsmSigningServiceTest : HsmBaseTest() {
|
||||
val userInput = givenHsmUserAuthenticationInput()
|
||||
|
||||
// given HSM network map signer
|
||||
val hsmSigningServiceConfig = createHsmSigningServiceConfig()
|
||||
val hsmSigningServiceConfig = createHsmSigningServiceConfig(null, createNetworkMapCertificateConfig())
|
||||
val networkMapCertificateConfig = hsmSigningServiceConfig.networkMap!!
|
||||
val hsmDataSigner = HsmSigner(Authenticator(
|
||||
provider = createProvider(
|
||||
networkMapCertificateConfig.keyGroup,
|
||||
hsmSigningServiceConfig.keySpecifier,
|
||||
hsmSigningServiceConfig.device),
|
||||
inputReader = userInput))
|
||||
inputReader = userInput),
|
||||
keyName = CORDA_NETWORK_MAP)
|
||||
|
||||
val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true))
|
||||
val networkMapStorage = PersistentNetworkMapStorage(database)
|
||||
@ -170,7 +172,7 @@ class HsmSigningServiceTest : HsmBaseTest() {
|
||||
assertThat(persistedNetworkMap.nodeInfoHashes).isEmpty()
|
||||
}
|
||||
|
||||
private fun setupCertificates(){
|
||||
private fun setupCertificates() {
|
||||
// when root cert is created
|
||||
run(createGeneratorParameters(
|
||||
keyGroup = ROOT_CERT_KEY_GROUP,
|
||||
|
@ -18,7 +18,7 @@ import com.r3.corda.networkmanage.doorman.DoormanConfig
|
||||
import com.r3.corda.networkmanage.doorman.NetworkManagementServer
|
||||
import com.r3.corda.networkmanage.hsm.persistence.ApprovedCertificateRequestData
|
||||
import com.r3.corda.networkmanage.hsm.persistence.DBSignedCertificateRequestStorage
|
||||
import com.r3.corda.networkmanage.hsm.persistence.SignedCertificateRequestStorage
|
||||
import com.r3.corda.networkmanage.hsm.persistence.SignedCertificateSigningRequestStorage
|
||||
import com.r3.corda.networkmanage.hsm.signer.HsmCsrSigner
|
||||
import net.corda.core.crypto.random63BitValue
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
@ -77,7 +77,7 @@ class SigningServiceIntegrationTest : HsmBaseTest() {
|
||||
timer.cancel()
|
||||
}
|
||||
|
||||
private fun givenSignerSigningAllRequests(storage: SignedCertificateRequestStorage): HsmCsrSigner {
|
||||
private fun givenSignerSigningAllRequests(storage: SignedCertificateSigningRequestStorage): HsmCsrSigner {
|
||||
// Mock signing logic but keep certificate persistence
|
||||
return mock {
|
||||
on { sign(any()) }.then {
|
||||
|
@ -13,7 +13,7 @@ interface CertificateRevocationListStorage {
|
||||
* @param crlIssuer CRL issuer CA type.
|
||||
* @return latest revocation list.
|
||||
*/
|
||||
fun getCertificateRevocationList(crlIssuer: CrlIssuer): X509CRL
|
||||
fun getCertificateRevocationList(crlIssuer: CrlIssuer): X509CRL?
|
||||
|
||||
/**
|
||||
* Persists a new revocation list. Upon saving, statuses
|
||||
|
@ -1,13 +1,20 @@
|
||||
package com.r3.corda.networkmanage.common.persistence
|
||||
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.nodeapi.internal.network.CertificateRevocationRequest
|
||||
import java.math.BigInteger
|
||||
import java.security.cert.CRLReason
|
||||
import java.time.Instant
|
||||
|
||||
/**
|
||||
* This data class is intended to be used internally certificate revocation request service.
|
||||
*/
|
||||
@CordaSerializable
|
||||
data class CertificateRevocationRequestData(val requestId: String, // This is a uniquely generated string
|
||||
val certificateSerialNumber: BigInteger,
|
||||
val revocationTime: Instant?,
|
||||
val legalName: String,
|
||||
val modifiedAt: Instant,
|
||||
val legalName: CordaX500Name,
|
||||
val status: RequestStatus,
|
||||
val reason: CRLReason,
|
||||
val reporter: String) // Username of the reporter
|
||||
@ -24,13 +31,11 @@ interface CertificateRevocationRequestStorage {
|
||||
* [RequestStatus.NEW], [RequestStatus.APPROVED] or [RequestStatus.REVOKED]
|
||||
* then nothing is persisted and the existing revocation request identifier is returned.
|
||||
*
|
||||
* @param certificateSerialNumber serial number of the certificate to be revoked.
|
||||
* @param reason reason for revocation. See [java.security.cert.CRLReason]
|
||||
* @param reporter who is requesting this revocation
|
||||
* @param request certificate revocation request to be stored.
|
||||
*
|
||||
* @return identifier of the newly created (or existing) revocation request.
|
||||
*/
|
||||
fun saveRevocationRequest(certificateSerialNumber: BigInteger, reason: CRLReason, reporter: String): String
|
||||
fun saveRevocationRequest(request: CertificateRevocationRequest): String
|
||||
|
||||
/**
|
||||
* Retrieves the revocation request with the given [requestId]
|
||||
|
@ -11,6 +11,7 @@
|
||||
package com.r3.corda.networkmanage.common.persistence
|
||||
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
||||
import java.security.cert.CertPath
|
||||
|
||||
@ -89,6 +90,7 @@ sealed class CertificateResponse {
|
||||
data class Unauthorised(val message: String) : CertificateResponse()
|
||||
}
|
||||
|
||||
@CordaSerializable
|
||||
enum class RequestStatus {
|
||||
/**
|
||||
* The request has been received, this is the initial state in which a request has been created.
|
||||
|
@ -11,7 +11,7 @@ import java.security.cert.X509CRL
|
||||
import java.time.Instant
|
||||
|
||||
class PersistentCertificateRevocationListStorage(private val database: CordaPersistence) : CertificateRevocationListStorage {
|
||||
override fun getCertificateRevocationList(crlIssuer: CrlIssuer): X509CRL {
|
||||
override fun getCertificateRevocationList(crlIssuer: CrlIssuer): X509CRL? {
|
||||
return database.transaction {
|
||||
val builder = session.criteriaBuilder
|
||||
val query = builder.createQuery(CertificateRevocationListEntity::class.java).run {
|
||||
@ -21,7 +21,10 @@ class PersistentCertificateRevocationListStorage(private val database: CordaPers
|
||||
}
|
||||
}
|
||||
// We just want the last signed entry
|
||||
X509CertificateFactory().delegate.generateCRL(session.createQuery(query).setMaxResults(1).singleResult.crlBytes.inputStream()) as X509CRL
|
||||
val crlEntity = session.createQuery(query).setMaxResults(1).uniqueResult()
|
||||
crlEntity?.let {
|
||||
X509CertificateFactory().delegate.generateCRL(crlEntity.crlBytes.inputStream()) as X509CRL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,7 +44,10 @@ class PersistentCertificateRevocationListStorage(private val database: CordaPers
|
||||
|
||||
private fun revokeCertificate(certificateSerialNumber: BigInteger, time: Instant, transaction: DatabaseTransaction) {
|
||||
val revocation = transaction.uniqueEntityWhere<CertificateRevocationRequestEntity> { builder, path ->
|
||||
builder.equal(path.get<BigInteger>(CertificateRevocationRequestEntity::certificateSerialNumber.name), certificateSerialNumber)
|
||||
builder.and(
|
||||
builder.equal(path.get<BigInteger>(CertificateRevocationRequestEntity::certificateSerialNumber.name), certificateSerialNumber),
|
||||
builder.notEqual(path.get<RequestStatus>(CertificateRevocationRequestEntity::status.name), RequestStatus.REJECTED)
|
||||
)
|
||||
}
|
||||
revocation ?: throw IllegalStateException("The certificate revocation request for $certificateSerialNumber does not exist")
|
||||
check(revocation.status in arrayOf(RequestStatus.APPROVED, RequestStatus.DONE)) {
|
||||
|
@ -2,39 +2,41 @@ package com.r3.corda.networkmanage.common.persistence
|
||||
|
||||
import com.r3.corda.networkmanage.common.persistence.entity.CertificateDataEntity
|
||||
import com.r3.corda.networkmanage.common.persistence.entity.CertificateRevocationRequestEntity
|
||||
import com.r3.corda.networkmanage.common.persistence.entity.CertificateSigningRequestEntity
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.nodeapi.internal.network.CertificateRevocationRequest
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseTransaction
|
||||
import net.corda.nodeapi.internal.persistence.TransactionIsolationLevel
|
||||
import java.math.BigInteger
|
||||
import java.security.cert.CRLReason
|
||||
import java.time.Instant
|
||||
|
||||
class PersistentCertificateRevocationRequestStorage(private val database: CordaPersistence) : CertificateRevocationRequestStorage {
|
||||
override fun saveRevocationRequest(certificateSerialNumber: BigInteger, reason: CRLReason, reporter: String): String {
|
||||
override fun saveRevocationRequest(request: CertificateRevocationRequest): String {
|
||||
return database.transaction(TransactionIsolationLevel.SERIALIZABLE) {
|
||||
// Get matching CSR
|
||||
val csr = retrieveCsr(request.certificateSerialNumber, request.csrRequestId, request.legalName)
|
||||
csr ?: throw IllegalArgumentException("No CSR matching the given criteria was found")
|
||||
// Check if there is an entry for the given certificate serial number
|
||||
val revocation = uniqueEntityWhere<CertificateRevocationRequestEntity> { builder, path ->
|
||||
val serialNumberEqual = builder.equal(path.get<BigInteger>(CertificateRevocationRequestEntity::certificateSerialNumber.name), certificateSerialNumber)
|
||||
val serialNumberEqual = builder.equal(path.get<BigInteger>(CertificateRevocationRequestEntity::certificateSerialNumber.name), request.certificateSerialNumber)
|
||||
val statusNotEqualRejected = builder.notEqual(path.get<RequestStatus>(CertificateRevocationRequestEntity::status.name), RequestStatus.REJECTED)
|
||||
builder.and(serialNumberEqual, statusNotEqualRejected)
|
||||
}
|
||||
if (revocation != null) {
|
||||
revocation.requestId
|
||||
} else {
|
||||
val certificateData = uniqueEntityWhere<CertificateDataEntity> { builder, path ->
|
||||
val serialNumberEqual = builder.equal(path.get<BigInteger>(CertificateDataEntity::certificateSerialNumber.name), certificateSerialNumber)
|
||||
val statusEqualValid = builder.equal(path.get<CertificateStatus>(CertificateDataEntity::certificateStatus.name), CertificateStatus.VALID)
|
||||
builder.and(serialNumberEqual, statusEqualValid)
|
||||
}
|
||||
val certificateData = csr.certificateData!!
|
||||
requireNotNull(certificateData) { "The certificate with the given serial number cannot be found." }
|
||||
val requestId = SecureHash.randomSHA256().toString()
|
||||
session.save(CertificateRevocationRequestEntity(
|
||||
certificateSerialNumber = certificateSerialNumber,
|
||||
revocationReason = reason,
|
||||
certificateSerialNumber = certificateData.certificateSerialNumber,
|
||||
revocationReason = request.reason,
|
||||
requestId = requestId,
|
||||
modifiedBy = reporter,
|
||||
certificateData = certificateData!!,
|
||||
reporter = reporter,
|
||||
modifiedBy = request.reporter,
|
||||
certificateData = certificateData,
|
||||
reporter = request.reporter,
|
||||
legalName = certificateData.legalName()
|
||||
))
|
||||
requestId
|
||||
@ -42,8 +44,62 @@ class PersistentCertificateRevocationRequestStorage(private val database: CordaP
|
||||
}
|
||||
}
|
||||
|
||||
override fun getRevocationRequest(requestId: String): CertificateRevocationRequestData? = database.transaction {
|
||||
getRevocationRequestEntity(requestId)?.toCertificateRevocationRequest()
|
||||
private fun DatabaseTransaction.retrieveCsr(certificateSerialNumber: BigInteger?, csrRequestId: String?, legalName: CordaX500Name?): CertificateSigningRequestEntity? {
|
||||
val csr = if (csrRequestId != null) {
|
||||
uniqueEntityWhere<CertificateSigningRequestEntity> { builder, path ->
|
||||
builder.and(
|
||||
builder.equal(path.get<String>(CertificateSigningRequestEntity::requestId.name), csrRequestId),
|
||||
builder.notEqual(path.get<RequestStatus>(CertificateSigningRequestEntity::status.name), RequestStatus.REJECTED),
|
||||
builder.equal(path
|
||||
.get<CertificateDataEntity>(CertificateSigningRequestEntity::certificateData.name)
|
||||
.get<CertificateStatus>(CertificateDataEntity::certificateStatus.name), CertificateStatus.VALID)
|
||||
)
|
||||
}
|
||||
} else if (legalName != null) {
|
||||
uniqueEntityWhere<CertificateSigningRequestEntity> { builder, path ->
|
||||
builder.and(
|
||||
builder.equal(path.get<String>(CertificateSigningRequestEntity::legalName.name), legalName.toString()),
|
||||
builder.notEqual(path.get<RequestStatus>(CertificateSigningRequestEntity::status.name), RequestStatus.REJECTED),
|
||||
builder.equal(path
|
||||
.get<CertificateDataEntity>(CertificateSigningRequestEntity::certificateData.name)
|
||||
.get<CertificateStatus>(CertificateDataEntity::certificateStatus.name), CertificateStatus.VALID)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val certificateData = uniqueEntityWhere<CertificateDataEntity> { builder, path ->
|
||||
builder.and(
|
||||
builder.equal(path.get<CertificateStatus>(CertificateDataEntity::certificateStatus.name), CertificateStatus.VALID),
|
||||
builder.equal(path.get<String>(CertificateDataEntity::certificateSerialNumber.name), certificateSerialNumber)
|
||||
)
|
||||
}
|
||||
certificateData?.certificateSigningRequest
|
||||
}
|
||||
return csr?.let {
|
||||
validateCsrConsistency(certificateSerialNumber, csrRequestId, legalName, it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun validateCsrConsistency(certificateSerialNumber: BigInteger?,
|
||||
csrRequestId: String?,
|
||||
legalName: CordaX500Name?,
|
||||
result: CertificateSigningRequestEntity): CertificateSigningRequestEntity {
|
||||
val certData = result.certificateData!!
|
||||
require(legalName == null || result.legalName == legalName.toString()) {
|
||||
"The legal name does not match."
|
||||
}
|
||||
require(csrRequestId == null || result.requestId == csrRequestId) {
|
||||
"The CSR request ID does not match."
|
||||
}
|
||||
require(certificateSerialNumber == null || certData.certificateSerialNumber == certificateSerialNumber) {
|
||||
"The certificate serial number does not match."
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
override fun getRevocationRequest(requestId: String): CertificateRevocationRequestData? {
|
||||
return database.transaction {
|
||||
getRevocationRequestEntity(requestId)?.toCertificateRevocationRequest()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getRevocationRequests(revocationStatus: RequestStatus): List<CertificateRevocationRequestData> {
|
||||
@ -89,9 +145,11 @@ class PersistentCertificateRevocationRequestStorage(private val database: CordaP
|
||||
}
|
||||
}
|
||||
|
||||
private fun getRevocationRequestEntity(requestId: String): CertificateRevocationRequestEntity? = database.transaction {
|
||||
uniqueEntityWhere { builder, path ->
|
||||
builder.equal(path.get<String>(CertificateRevocationRequestEntity::requestId.name), requestId)
|
||||
private fun getRevocationRequestEntity(requestId: String): CertificateRevocationRequestEntity? {
|
||||
return database.transaction {
|
||||
uniqueEntityWhere { builder, path ->
|
||||
builder.equal(path.get<String>(CertificateRevocationRequestEntity::requestId.name), requestId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,8 +157,8 @@ class PersistentCertificateRevocationRequestStorage(private val database: CordaP
|
||||
return CertificateRevocationRequestData(
|
||||
requestId,
|
||||
certificateSerialNumber,
|
||||
if (status == RequestStatus.DONE) modifiedAt else null,
|
||||
legalName,
|
||||
modifiedAt,
|
||||
CordaX500Name.parse(legalName),
|
||||
status,
|
||||
revocationReason,
|
||||
reporter)
|
||||
|
@ -1,7 +1,6 @@
|
||||
package com.r3.corda.networkmanage.common.persistence.entity
|
||||
|
||||
import com.r3.corda.networkmanage.common.persistence.CrlIssuer
|
||||
import org.hibernate.envers.Audited
|
||||
import java.time.Instant
|
||||
import javax.persistence.*
|
||||
|
||||
@ -19,11 +18,9 @@ class CertificateRevocationListEntity(
|
||||
@Column(name = "crl_bytes", nullable = false)
|
||||
val crlBytes: ByteArray,
|
||||
|
||||
@Audited
|
||||
@Column(name = "signed_by", length = 512)
|
||||
val signedBy: String,
|
||||
|
||||
@Audited
|
||||
@Column(name = "modified_at", nullable = false)
|
||||
val modifiedAt: Instant = Instant.now()
|
||||
)
|
@ -0,0 +1,57 @@
|
||||
package com.r3.corda.networkmanage.common.signer
|
||||
|
||||
import com.r3.corda.networkmanage.common.persistence.CertificateRevocationListStorage
|
||||
import com.r3.corda.networkmanage.common.persistence.CertificateRevocationRequestData
|
||||
import com.r3.corda.networkmanage.common.persistence.CrlIssuer
|
||||
import com.r3.corda.networkmanage.common.persistence.RequestStatus
|
||||
import com.r3.corda.networkmanage.common.utils.createSignedCrl
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.core.utilities.debug
|
||||
import net.corda.core.utilities.trace
|
||||
import java.net.URL
|
||||
import java.security.cert.X509CRL
|
||||
import java.security.cert.X509Certificate
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
|
||||
class CertificateRevocationListSigner(
|
||||
private val revocationListStorage: CertificateRevocationListStorage,
|
||||
private val issuerCertificate: X509Certificate,
|
||||
private val updateInterval: Duration,
|
||||
private val endpoint: URL,
|
||||
private val signer: Signer) {
|
||||
private companion object {
|
||||
private val logger = contextLogger()
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds, signs and persists a new certificate revocation list.
|
||||
* It happens only if there are new entries to be added to the current list.
|
||||
*
|
||||
* @param newCRRs list of approved certificate revocation requests. An approved certificate revocation request
|
||||
* is a request that has status [RequestStatus.APPROVED] - i.e. it has not yet been included in any CRL.
|
||||
* @param existingCRRs list of revoked certificate revocation requests. A revoked certificate revocation request
|
||||
* is a request that has status [RequestStatus.DONE] - i.e. it has already been included in another CRL.
|
||||
* @param signedBy who signs this CRL.
|
||||
*
|
||||
* @return A new signed CRL containing both revoked and approved certificate revocation requests.
|
||||
*/
|
||||
fun createSignedCRL(newCRRs: List<CertificateRevocationRequestData>,
|
||||
existingCRRs: List<CertificateRevocationRequestData>,
|
||||
signedBy: String): X509CRL {
|
||||
require(existingCRRs.all { it.status == RequestStatus.DONE }) { "All existing CRRs need to be in the ${RequestStatus.DONE} state" }
|
||||
require(newCRRs.all { it.status == RequestStatus.APPROVED }) { "All newly included CRRs need to be in the ${RequestStatus.APPROVED} state" }
|
||||
logger.info("Signing a new Certificate Revocation List...")
|
||||
logger.debug("Retrieving approved Certificate Revocation Requests...")
|
||||
val revocationTime = Instant.now()
|
||||
val approvedWithTimestamp = newCRRs.map { it.copy(modifiedAt = revocationTime) }
|
||||
logger.trace { "Approved Certificate Revocation Requests to be included in the new Certificate Revocation List: $approvedWithTimestamp" }
|
||||
logger.debug("Retrieving revoked Certificate Revocation Requests...")
|
||||
logger.trace { "Revoked Certificate Revocation Requests to be included in the new Certificate Revocation List: $existingCRRs" }
|
||||
val crl = createSignedCrl(issuerCertificate, endpoint, updateInterval, signer, existingCRRs + approvedWithTimestamp)
|
||||
logger.debug { "Created a new Certificate Revocation List $crl" }
|
||||
revocationListStorage.saveCertificateRevocationList(crl, CrlIssuer.DOORMAN, signedBy, revocationTime)
|
||||
logger.info("A new Certificate Revocation List has been persisted.")
|
||||
return crl
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package com.r3.corda.networkmanage.common.utils
|
||||
|
||||
import com.r3.corda.networkmanage.common.persistence.CertificateRevocationRequestData
|
||||
import com.r3.corda.networkmanage.common.signer.Signer
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import org.bouncycastle.asn1.x509.*
|
||||
import org.bouncycastle.cert.X509v2CRLBuilder
|
||||
import org.bouncycastle.cert.jcajce.JcaX509CRLConverter
|
||||
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
||||
import org.bouncycastle.operator.ContentSigner
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.OutputStream
|
||||
import java.net.URL
|
||||
import java.security.cert.X509CRL
|
||||
import java.security.cert.X509Certificate
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
|
||||
fun createSignedCrl(issuerCertificate: X509Certificate,
|
||||
endpointUrl: URL,
|
||||
nextUpdateInterval: Duration,
|
||||
signer: Signer,
|
||||
includeInCrl: List<CertificateRevocationRequestData>): X509CRL {
|
||||
val extensionUtils = JcaX509ExtensionUtils()
|
||||
val builder = X509v2CRLBuilder(X500Name.getInstance(issuerCertificate.issuerX500Principal.encoded), Date())
|
||||
builder.addExtension(Extension.authorityKeyIdentifier, false, extensionUtils.createAuthorityKeyIdentifier(issuerCertificate))
|
||||
val issuingDistributionPointName = GeneralName(GeneralName.uniformResourceIdentifier, endpointUrl.toString())
|
||||
val issuingDistributionPoint = IssuingDistributionPoint(DistributionPointName(GeneralNames(issuingDistributionPointName)), false, false)
|
||||
builder.addExtension(Extension.issuingDistributionPoint, true, issuingDistributionPoint)
|
||||
builder.setNextUpdate(Date((Instant.now() + nextUpdateInterval).toEpochMilli()))
|
||||
includeInCrl.forEach {
|
||||
builder.addCRLEntry(it.certificateSerialNumber, Date(it.modifiedAt.toEpochMilli()), it.reason.ordinal)
|
||||
}
|
||||
val crlHolder = builder.build(CrlContentSigner(signer))
|
||||
return JcaX509CRLConverter().setProvider(BouncyCastleProvider()).getCRL(crlHolder)
|
||||
}
|
||||
|
||||
private class CrlContentSigner(private val signer: Signer) : ContentSigner {
|
||||
|
||||
private val outputStream = ByteArrayOutputStream()
|
||||
|
||||
override fun getAlgorithmIdentifier(): AlgorithmIdentifier = X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME.signatureOID
|
||||
override fun getOutputStream(): OutputStream = outputStream
|
||||
override fun getSignature(): ByteArray = signer.signBytes(outputStream.toByteArray()).bytes
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package com.r3.corda.networkmanage.doorman.webservice
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Caffeine
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache
|
||||
import com.r3.corda.networkmanage.common.persistence.CertificateRevocationListStorage
|
||||
import com.r3.corda.networkmanage.common.persistence.CrlIssuer
|
||||
import com.r3.corda.networkmanage.doorman.webservice.CertificateRevocationListWebService.Companion.CRL_PATH
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import java.util.concurrent.ExecutionException
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.ws.rs.GET
|
||||
import javax.ws.rs.Path
|
||||
import javax.ws.rs.Produces
|
||||
import javax.ws.rs.core.Response
|
||||
import javax.ws.rs.core.Response.ok
|
||||
import javax.ws.rs.core.Response.status
|
||||
|
||||
@Path(CRL_PATH)
|
||||
class CertificateRevocationListWebService(private val revocationListStorage: CertificateRevocationListStorage,
|
||||
cacheTimeout: Long) {
|
||||
companion object {
|
||||
private val logger = contextLogger()
|
||||
const val CRL_PATH = "certificate-revocation-list"
|
||||
const val CRL_DATA_TYPE = "application/pkcs7-crl"
|
||||
const val DOORMAN = "doorman"
|
||||
const val ROOT = "root"
|
||||
}
|
||||
|
||||
private val crlCache: LoadingCache<CrlIssuer, ByteArray> = Caffeine.newBuilder()
|
||||
.expireAfterWrite(cacheTimeout, TimeUnit.MILLISECONDS)
|
||||
.build({ key ->
|
||||
revocationListStorage.getCertificateRevocationList(key)?.encoded
|
||||
})
|
||||
|
||||
@GET
|
||||
@Path(DOORMAN)
|
||||
@Produces(CRL_DATA_TYPE)
|
||||
fun getDoormanRevocationList(): Response {
|
||||
return getCrlResponse(CrlIssuer.DOORMAN)
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path(ROOT)
|
||||
@Produces(CRL_DATA_TYPE)
|
||||
fun getRootRevocationList(): Response {
|
||||
return getCrlResponse(CrlIssuer.ROOT)
|
||||
}
|
||||
|
||||
private fun getCrlResponse(issuer: CrlIssuer): Response {
|
||||
return try {
|
||||
val crlBytes = crlCache.get(issuer)
|
||||
if (crlBytes != null) {
|
||||
ok(crlBytes).build()
|
||||
} else {
|
||||
status(Response.Status.NOT_FOUND).build()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logger.error("Error when retrieving the ${issuer.name} crl.", e)
|
||||
status(Response.Status.INTERNAL_SERVER_ERROR).build()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package com.r3.corda.networkmanage.doorman.webservice
|
||||
|
||||
import com.r3.corda.networkmanage.common.persistence.CertificateRevocationRequestStorage
|
||||
import com.r3.corda.networkmanage.doorman.webservice.CertificateRevocationRequestWebService.Companion.CRR_PATH
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.nodeapi.internal.network.CertificateRevocationRequest
|
||||
import java.io.InputStream
|
||||
import javax.ws.rs.Consumes
|
||||
import javax.ws.rs.POST
|
||||
import javax.ws.rs.Path
|
||||
import javax.ws.rs.Produces
|
||||
import javax.ws.rs.core.MediaType
|
||||
import javax.ws.rs.core.Response
|
||||
import javax.ws.rs.core.Response.ok
|
||||
|
||||
@Path(CRR_PATH)
|
||||
class CertificateRevocationRequestWebService(private val revocationRequestStorage: CertificateRevocationRequestStorage) {
|
||||
|
||||
companion object {
|
||||
const val CRR_PATH = "certificate-revocation-request"
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_OCTET_STREAM)
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
fun submitRequest(input: InputStream): Response {
|
||||
val request = input.readBytes().deserialize<CertificateRevocationRequest>()
|
||||
val requestId = revocationRequestStorage.saveRevocationRequest(request)
|
||||
return ok(requestId).build()
|
||||
}
|
||||
}
|
@ -17,6 +17,7 @@ import com.r3.corda.networkmanage.common.utils.initialiseSerialization
|
||||
import com.r3.corda.networkmanage.common.utils.parseConfig
|
||||
import com.r3.corda.networkmanage.hsm.configuration.SigningServiceArgsParser
|
||||
import com.r3.corda.networkmanage.hsm.configuration.SigningServiceConfig
|
||||
import com.r3.corda.networkmanage.hsm.processor.CrrProcessor
|
||||
import com.r3.corda.networkmanage.hsm.processor.CsrProcessor
|
||||
import com.r3.corda.networkmanage.hsm.processor.NetworkMapProcessor
|
||||
import org.apache.logging.log4j.LogManager
|
||||
@ -54,12 +55,13 @@ fun main(args: Array<String>) {
|
||||
}
|
||||
|
||||
initialiseSerialization()
|
||||
|
||||
// Create DB connection.
|
||||
val persistence = configureDatabase(config.dataSourceProperties, config.database)
|
||||
if (config.networkMap != null) {
|
||||
NetworkMapProcessor(config.networkMap, config.device, config.keySpecifier, persistence).run()
|
||||
} else {
|
||||
CsrProcessor(config.doorman!!, config.device, config.keySpecifier, persistence).showMenu()
|
||||
} else if (config.doorman != null) {
|
||||
CsrProcessor(config.doorman, config.device, config.keySpecifier, persistence).showMenu()
|
||||
} else if (config.doorman != null) {
|
||||
CrrProcessor(config.doorman, config.device, config.keySpecifier, persistence).showMenu()
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import net.corda.core.internal.div
|
||||
import net.corda.nodeapi.internal.config.parseAs
|
||||
import net.corda.nodeapi.internal.crypto.X509KeyStore
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import java.net.URL
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.util.*
|
||||
@ -54,7 +55,9 @@ data class NetworkMapCertificateConfig(val username: String,
|
||||
/**
|
||||
* Certificate signing requests process specific parameters.
|
||||
*/
|
||||
data class DoormanCertificateConfig(val crlDistributionPoint: String,
|
||||
data class DoormanCertificateConfig(val crlDistributionPoint: URL,
|
||||
val compatibilityZoneURL: URL,
|
||||
val crlUpdatePeriod: Long,
|
||||
val keyGroup:String,
|
||||
val validDays: Int,
|
||||
val rootKeyStoreFile: Path,
|
||||
|
@ -19,7 +19,7 @@ import java.security.cert.CertPath
|
||||
|
||||
data class ApprovedCertificateRequestData(val requestId: String, val request: PKCS10CertificationRequest, var certPath: CertPath? = null)
|
||||
|
||||
class DBSignedCertificateRequestStorage(database: CordaPersistence) : SignedCertificateRequestStorage {
|
||||
class DBSignedCertificateRequestStorage(database: CordaPersistence) : SignedCertificateSigningRequestStorage {
|
||||
|
||||
private val storage = PersistentCertificateSigningRequestStorage(database)
|
||||
|
||||
|
@ -13,7 +13,7 @@ package com.r3.corda.networkmanage.hsm.persistence
|
||||
/**
|
||||
* Provides an API for storing signed CSRs (Certificate Signing Requests).
|
||||
*/
|
||||
interface SignedCertificateRequestStorage {
|
||||
interface SignedCertificateSigningRequestStorage {
|
||||
|
||||
/**
|
||||
* Returns all certificate signing requests that have been approved for signing.
|
@ -0,0 +1,74 @@
|
||||
package com.r3.corda.networkmanage.hsm.processor
|
||||
|
||||
import com.google.common.net.HostAndPort
|
||||
import com.r3.corda.networkmanage.common.persistence.CrlIssuer
|
||||
import com.r3.corda.networkmanage.common.persistence.PersistentCertificateRevocationListStorage
|
||||
import com.r3.corda.networkmanage.common.persistence.PersistentCertificateRevocationRequestStorage
|
||||
import com.r3.corda.networkmanage.common.persistence.RequestStatus
|
||||
import com.r3.corda.networkmanage.common.signer.CertificateRevocationListSigner
|
||||
import com.r3.corda.networkmanage.hsm.authentication.Authenticator
|
||||
import com.r3.corda.networkmanage.hsm.authentication.createProvider
|
||||
import com.r3.corda.networkmanage.hsm.configuration.DoormanCertificateConfig
|
||||
import com.r3.corda.networkmanage.hsm.menu.Menu
|
||||
import com.r3.corda.networkmanage.hsm.signer.HsmSigner
|
||||
import com.r3.corda.networkmanage.hsm.utils.HsmX509Utilities.getAndInitializeKeyStore
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_INTERMEDIATE_CA
|
||||
import net.corda.nodeapi.internal.crypto.getX509Certificate
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import java.time.Duration
|
||||
|
||||
class CrrProcessor(private val config: DoormanCertificateConfig,
|
||||
private val device: String,
|
||||
private val keySpecifier: Int,
|
||||
private val persistance: CordaPersistence) : Processor() {
|
||||
private companion object {
|
||||
private const val RESET = "\u001B[0m"
|
||||
private const val BLACK = "\u001B[30m"
|
||||
private const val RED = "\u001B[31m"
|
||||
private const val GREEN = "\u001B[32m"
|
||||
private const val YELLOW = "\u001B[33m"
|
||||
private const val BLUE = "\u001B[34m"
|
||||
private const val CYAN = "\u001B[36m"
|
||||
private const val WHITE = "\u001B[37m"
|
||||
}
|
||||
|
||||
private val auth = config.authParameters
|
||||
|
||||
fun showMenu() {
|
||||
val authenticator = Authenticator(
|
||||
provider = createProvider(config.keyGroup, keySpecifier, device),
|
||||
mode = auth.mode,
|
||||
authStrengthThreshold = auth.threshold)
|
||||
Menu().withExceptionHandler(this::processError).setExitOption("2", "Quit").addItem("1", "View current and sign a new certificate revocation list", {
|
||||
val crlStorage = PersistentCertificateRevocationListStorage(persistance)
|
||||
val currentCrl = crlStorage.getCertificateRevocationList(CrlIssuer.DOORMAN)
|
||||
printlnColor("Current CRL:")
|
||||
printlnColor(currentCrl.toString(), YELLOW)
|
||||
val crrStorage = PersistentCertificateRevocationRequestStorage(persistance)
|
||||
val approvedRequests = crrStorage.getRevocationRequests(RequestStatus.APPROVED)
|
||||
if (approvedRequests.isEmpty()) {
|
||||
printlnColor("There are no approved Certificate Revocation Requests.", GREEN)
|
||||
} else {
|
||||
printlnColor("Following are the approved Certificate Revocation Requests which will be added to the CRL:")
|
||||
approvedRequests.forEach {
|
||||
printlnColor("Certificate DN: ${it.legalName}, Certificate serial number: ${it.certificateSerialNumber}", GREEN)
|
||||
}
|
||||
}
|
||||
Menu().withExceptionHandler(this::processError).setExitOption("2", "Go back").
|
||||
addItem("1", "Create and sign a new Certificate Revocation List", {
|
||||
authenticator.connectAndAuthenticate { provider, signers ->
|
||||
val keyStore = getAndInitializeKeyStore(provider)
|
||||
val issuerCertificate = keyStore.getX509Certificate(CORDA_INTERMEDIATE_CA)
|
||||
val crlSigner = CertificateRevocationListSigner(
|
||||
revocationListStorage = crlStorage,
|
||||
issuerCertificate = issuerCertificate,
|
||||
updateInterval = Duration.ofMillis(config.crlUpdatePeriod),
|
||||
endpoint = config.crlDistributionPoint,
|
||||
signer = HsmSigner(provider = provider, keyName = CORDA_INTERMEDIATE_CA))
|
||||
val currentRequests = crrStorage.getRevocationRequests(RequestStatus.DONE)
|
||||
crlSigner.createSignedCRL(approvedRequests, currentRequests, signers.toString())
|
||||
}
|
||||
})
|
||||
}).showMenu()
|
||||
}
|
||||
}
|
@ -16,81 +16,84 @@ import com.r3.corda.networkmanage.hsm.configuration.DoormanCertificateConfig
|
||||
import com.r3.corda.networkmanage.hsm.menu.Menu
|
||||
import com.r3.corda.networkmanage.hsm.persistence.ApprovedCertificateRequestData
|
||||
import com.r3.corda.networkmanage.hsm.persistence.DBSignedCertificateRequestStorage
|
||||
import com.r3.corda.networkmanage.hsm.persistence.SignedCertificateSigningRequestStorage
|
||||
import com.r3.corda.networkmanage.hsm.signer.HsmCsrSigner
|
||||
import com.r3.corda.networkmanage.hsm.utils.mapCryptoServerException
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
|
||||
class CsrProcessor(private val config: DoormanCertificateConfig,
|
||||
private val device: String,
|
||||
private val keySpecifier: Int,
|
||||
private val database: CordaPersistence) {
|
||||
companion object {
|
||||
val logger = contextLogger()
|
||||
private val database: CordaPersistence) : Processor() {
|
||||
private companion object {
|
||||
private val logger = contextLogger()
|
||||
}
|
||||
|
||||
private val auth = config.authParameters
|
||||
|
||||
fun showMenu() {
|
||||
val csrStorage = DBSignedCertificateRequestStorage(database)
|
||||
val sign: (List<ApprovedCertificateRequestData>) -> Unit = {
|
||||
val signer = config.run {
|
||||
val authenticator = Authenticator(
|
||||
provider = createProvider(config.keyGroup, keySpecifier, device),
|
||||
mode = auth.mode,
|
||||
authStrengthThreshold = auth.threshold)
|
||||
val signCsr: (List<ApprovedCertificateRequestData>) -> Unit = {
|
||||
val csrSigner = config.run {
|
||||
HsmCsrSigner(
|
||||
csrStorage,
|
||||
loadRootKeyStore(),
|
||||
crlDistributionPoint,
|
||||
crlDistributionPoint.toString(),
|
||||
null,
|
||||
validDays,
|
||||
Authenticator(
|
||||
provider = createProvider(config.keyGroup, keySpecifier, device),
|
||||
mode = auth.mode,
|
||||
authStrengthThreshold = auth.threshold))
|
||||
authenticator)
|
||||
}
|
||||
logger.debug("Signing requests: $it")
|
||||
signer.sign(it)
|
||||
csrSigner.sign(it)
|
||||
}
|
||||
Menu().withExceptionHandler(this::processError).setExitOption("3", "Quit").addItem("1", "Sign all approved and unsigned CSRs",
|
||||
{
|
||||
logger.debug("Fetching approved requests...")
|
||||
val approved = csrStorage.getApprovedRequests()
|
||||
logger.debug("Approved requests fetched: $approved")
|
||||
if (approved.isNotEmpty()) {
|
||||
if (confirmedSign(approved)) {
|
||||
sign(approved)
|
||||
}
|
||||
} else {
|
||||
println("There is no approved CSR")
|
||||
}
|
||||
}).addItem("2", "List all approved and unsigned CSRs",
|
||||
{
|
||||
logger.debug("Fetching approved requests...")
|
||||
val approved = csrStorage.getApprovedRequests()
|
||||
logger.debug("Approved requests fetched: $approved")
|
||||
if (approved.isNotEmpty()) {
|
||||
println("Approved CSRs:")
|
||||
approved.forEachIndexed { index, item -> println("${index + 1}. ${item.request.subject}") }
|
||||
Menu().withExceptionHandler(this::processError).setExitOption("3", "Go back").
|
||||
addItem("1", "Sign all listed CSRs", {
|
||||
if (confirmedSign(approved)) {
|
||||
sign(approved)
|
||||
}
|
||||
}, isTerminating = true).
|
||||
addItem("2", "Select and sign CSRs", {
|
||||
val selectedItems = getSelection(approved)
|
||||
if (confirmedSign(selectedItems)) {
|
||||
sign(selectedItems)
|
||||
}
|
||||
}, isTerminating = true).showMenu()
|
||||
} else {
|
||||
println("There is no approved and unsigned CSR")
|
||||
}
|
||||
}).showMenu()
|
||||
showMenu(csrStorage, signCsr)
|
||||
}
|
||||
|
||||
private fun showMenu(csrStorage: SignedCertificateSigningRequestStorage, signCsr: (List<ApprovedCertificateRequestData>) -> Unit) {
|
||||
Menu().withExceptionHandler(this::processError).setExitOption("3", "Quit").addItem("1", "Sign all approved and unsigned CSRs", {
|
||||
logger.debug("Fetching approved requests...")
|
||||
val approved = csrStorage.getApprovedRequests()
|
||||
logger.debug("Approved requests fetched: $approved")
|
||||
if (approved.isNotEmpty()) {
|
||||
if (confirmedSign(approved)) {
|
||||
signCsr(approved)
|
||||
}
|
||||
} else {
|
||||
printlnColor("There are no approved CSRs")
|
||||
}
|
||||
}).addItem("2", "List all approved and unsigned CSRs", {
|
||||
logger.debug("Fetching approved requests...")
|
||||
val approved = csrStorage.getApprovedRequests()
|
||||
logger.debug("Approved requests fetched: $approved")
|
||||
if (approved.isNotEmpty()) {
|
||||
printlnColor("Approved CSRs:")
|
||||
approved.forEachIndexed { index, item -> printlnColor("${index + 1}. ${item.request.subject}") }
|
||||
Menu().withExceptionHandler(this::processError).setExitOption("3", "Go back").
|
||||
addItem("1", "Sign all listed CSRs", {
|
||||
if (confirmedSign(approved)) {
|
||||
signCsr(approved)
|
||||
}
|
||||
}, isTerminating = true).
|
||||
addItem("2", "Select and sign CSRs", {
|
||||
val selectedItems = getSelection(approved)
|
||||
if (confirmedSign(selectedItems)) {
|
||||
signCsr(selectedItems)
|
||||
}
|
||||
}, isTerminating = true).showMenu()
|
||||
} else {
|
||||
printlnColor("There is no approved and unsigned CSR")
|
||||
}
|
||||
}).showMenu()
|
||||
}
|
||||
|
||||
private fun confirmedSign(selectedItems: List<ApprovedCertificateRequestData>): Boolean {
|
||||
println("Are you sure you want to sign the following requests:")
|
||||
printlnColor("Are you sure you want to sign the following requests:")
|
||||
selectedItems.forEachIndexed { index, data ->
|
||||
println("${index + 1} ${data.request.subject}")
|
||||
printlnColor("${index + 1} ${data.request.subject}")
|
||||
}
|
||||
var result = false
|
||||
Menu().addItem("Y", "Yes", { result = true }, true).setExitOption("N", "No").showMenu()
|
||||
@ -101,7 +104,7 @@ class CsrProcessor(private val config: DoormanCertificateConfig,
|
||||
print("CSRs to be signed (comma separated list): ")
|
||||
val line = readLine()
|
||||
if (line == null) {
|
||||
println("EOF reached")
|
||||
printlnColor("EOF reached")
|
||||
return emptyList()
|
||||
}
|
||||
return try {
|
||||
@ -114,14 +117,8 @@ class CsrProcessor(private val config: DoormanCertificateConfig,
|
||||
}
|
||||
}
|
||||
} catch (exception: Exception) {
|
||||
println(exception.message)
|
||||
printlnColor(exception.message)
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
fun processError(exception: Exception) {
|
||||
val processed = mapCryptoServerException(exception)
|
||||
System.err.println("An error occurred:")
|
||||
processed.printStackTrace()
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ package com.r3.corda.networkmanage.hsm.processor
|
||||
|
||||
import com.r3.corda.networkmanage.common.persistence.PersistentNetworkMapStorage
|
||||
import com.r3.corda.networkmanage.common.signer.NetworkMapSigner
|
||||
import com.r3.corda.networkmanage.common.utils.CORDA_NETWORK_MAP
|
||||
import com.r3.corda.networkmanage.hsm.authentication.AuthMode
|
||||
import com.r3.corda.networkmanage.hsm.authentication.Authenticator
|
||||
import com.r3.corda.networkmanage.hsm.authentication.createProvider
|
||||
@ -49,7 +50,8 @@ class NetworkMapProcessor(private val config: NetworkMapCertificateConfig,
|
||||
authParameters.keyFilePath,
|
||||
authParameters.password,
|
||||
authParameters.threshold,
|
||||
provider = createProvider(keyGroup, keySpecifier, device)))
|
||||
provider = createProvider(keyGroup, keySpecifier, device)),
|
||||
keyName = CORDA_NETWORK_MAP)
|
||||
val networkMapSigner = NetworkMapSigner(networkMapStorage, signer)
|
||||
try {
|
||||
logger.info("Executing network map signing...")
|
||||
|
@ -0,0 +1,16 @@
|
||||
package com.r3.corda.networkmanage.hsm.processor
|
||||
|
||||
import com.r3.corda.networkmanage.hsm.utils.mapCryptoServerException
|
||||
|
||||
abstract class Processor {
|
||||
|
||||
protected fun processError(exception: Exception) {
|
||||
val processed = mapCryptoServerException(exception)
|
||||
System.err.println("An error occurred:")
|
||||
processed.printStackTrace()
|
||||
}
|
||||
|
||||
protected fun printlnColor(line: String?, color: String = "") {
|
||||
println(color + line)
|
||||
}
|
||||
}
|
@ -13,13 +13,11 @@ package com.r3.corda.networkmanage.hsm.signer
|
||||
import com.r3.corda.networkmanage.common.utils.getCertRole
|
||||
import com.r3.corda.networkmanage.hsm.authentication.Authenticator
|
||||
import com.r3.corda.networkmanage.hsm.persistence.ApprovedCertificateRequestData
|
||||
import com.r3.corda.networkmanage.hsm.persistence.SignedCertificateRequestStorage
|
||||
import com.r3.corda.networkmanage.hsm.persistence.SignedCertificateSigningRequestStorage
|
||||
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.retrieveCertAndKeyPair
|
||||
import net.corda.core.internal.CertRole
|
||||
import net.corda.core.utilities.contextLogger
|
||||
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_ROOT_CA
|
||||
@ -31,7 +29,7 @@ import java.io.PrintStream
|
||||
/**
|
||||
* Encapsulates certificate signing logic
|
||||
*/
|
||||
class HsmCsrSigner(private val storage: SignedCertificateRequestStorage,
|
||||
class HsmCsrSigner(private val storage: SignedCertificateSigningRequestStorage,
|
||||
private val rootKeyStore: X509KeyStore,
|
||||
private val csrCertCrlDistPoint: String,
|
||||
private val csrCertCrlIssuer: String?,
|
||||
|
@ -10,8 +10,9 @@
|
||||
|
||||
package com.r3.corda.networkmanage.hsm.signer
|
||||
|
||||
import CryptoServerJCE.CryptoServerProvider
|
||||
import com.google.common.primitives.Booleans
|
||||
import com.r3.corda.networkmanage.common.signer.Signer
|
||||
import com.r3.corda.networkmanage.common.utils.CORDA_NETWORK_MAP
|
||||
import com.r3.corda.networkmanage.hsm.authentication.Authenticator
|
||||
import com.r3.corda.networkmanage.hsm.utils.HsmX509Utilities
|
||||
import com.r3.corda.networkmanage.hsm.utils.HsmX509Utilities.getAndInitializeKeyStore
|
||||
@ -22,26 +23,42 @@ import java.security.PrivateKey
|
||||
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] and provider to sign bytes.
|
||||
*/
|
||||
class HsmSigner(private val authenticator: Authenticator) : Signer {
|
||||
class HsmSigner(private val authenticator: Authenticator? = null,
|
||||
private val provider: CryptoServerProvider? = null,
|
||||
private val keyName: String) : Signer {
|
||||
/**
|
||||
* Signs given data using [CryptoServerJCE.CryptoServerProvider], which connects to the underlying HSM.
|
||||
*/
|
||||
override fun signBytes(data: ByteArray): DigitalSignatureWithCert {
|
||||
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, null) as PrivateKey
|
||||
val signature = Signature.getInstance(HsmX509Utilities.SIGNATURE_ALGORITHM, provider).run {
|
||||
initSign(key)
|
||||
update(data)
|
||||
sign()
|
||||
}
|
||||
verify(data, signature, certificate.publicKey)
|
||||
DigitalSignatureWithCert(certificate, signature)
|
||||
init {
|
||||
require(Booleans.countTrue(authenticator != null, provider != null) == 1) {
|
||||
"Either authenticator or provider needs to be non-null."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun signBytes(data: ByteArray): DigitalSignatureWithCert {
|
||||
if (provider == null) {
|
||||
return authenticator!!.connectAndAuthenticate { provider, _ ->
|
||||
signBytes(data, provider)
|
||||
}
|
||||
} else {
|
||||
return signBytes(data, provider)
|
||||
}
|
||||
}
|
||||
|
||||
private fun signBytes(data: ByteArray, provider: CryptoServerProvider): DigitalSignatureWithCert {
|
||||
val keyStore = getAndInitializeKeyStore(provider)
|
||||
val certificate = keyStore.getX509Certificate(keyName)
|
||||
// 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(keyName, null) as PrivateKey
|
||||
val signature = Signature.getInstance(HsmX509Utilities.SIGNATURE_ALGORITHM, provider).run {
|
||||
initSign(key)
|
||||
update(data)
|
||||
sign()
|
||||
}
|
||||
verify(data, signature, certificate.publicKey)
|
||||
return DigitalSignatureWithCert(certificate, signature)
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ package com.r3.corda.networkmanage.common.persistence
|
||||
|
||||
import com.r3.corda.networkmanage.TestBase
|
||||
import net.corda.core.utilities.minutes
|
||||
import net.corda.nodeapi.internal.network.CertificateRevocationRequest
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import net.corda.testing.internal.DEV_INTERMEDIATE_CA
|
||||
@ -52,7 +53,10 @@ class PersistentCertificateRevocationListStorageTest : TestBase() {
|
||||
fun `Saving CRL persists it in the DB and changes the status of the certificate revocation requests to DONE`() {
|
||||
// given
|
||||
val certificate = createNodeCertificate(csrStorage)
|
||||
val requestId = crrStorage.saveRevocationRequest(certificate.serialNumber, REVOCATION_REASON, REPORTER)
|
||||
val requestId = crrStorage.saveRevocationRequest(CertificateRevocationRequest(
|
||||
certificateSerialNumber = certificate.serialNumber,
|
||||
reason = REVOCATION_REASON,
|
||||
reporter = REPORTER))
|
||||
crrStorage.approveRevocationRequest(requestId, "Approver")
|
||||
val revocationRequest = crrStorage.getRevocationRequest(requestId)!!
|
||||
val crl = createDummyCertificateRevocationList(listOf(revocationRequest.certificateSerialNumber))
|
||||
@ -72,16 +76,25 @@ class PersistentCertificateRevocationListStorageTest : TestBase() {
|
||||
@Test
|
||||
fun `Saving CRL does not change the status of other requests`() {
|
||||
// given
|
||||
val done = crrStorage.saveRevocationRequest(createNodeCertificate(csrStorage, legalName = "Bank A").serialNumber, REVOCATION_REASON, REPORTER)
|
||||
val done = crrStorage.saveRevocationRequest(CertificateRevocationRequest(
|
||||
certificateSerialNumber = createNodeCertificate(csrStorage, legalName = "Bank A").serialNumber,
|
||||
reason = REVOCATION_REASON,
|
||||
reporter = REPORTER))
|
||||
crrStorage.approveRevocationRequest(done, "Approver")
|
||||
val doneRevocationRequest = crrStorage.getRevocationRequest(done)!!
|
||||
|
||||
val new = crrStorage.saveRevocationRequest(createNodeCertificate(csrStorage, legalName = "Bank B").serialNumber, REVOCATION_REASON, REPORTER)
|
||||
val new = crrStorage.saveRevocationRequest(CertificateRevocationRequest(
|
||||
certificateSerialNumber = createNodeCertificate(csrStorage, legalName = "Bank B").serialNumber,
|
||||
reason = REVOCATION_REASON,
|
||||
reporter = REPORTER))
|
||||
|
||||
val crl = createDummyCertificateRevocationList(listOf(doneRevocationRequest.certificateSerialNumber))
|
||||
crlStorage.saveCertificateRevocationList(crl, CrlIssuer.DOORMAN, "TestSigner", Instant.now())
|
||||
|
||||
val approved = crrStorage.saveRevocationRequest(createNodeCertificate(csrStorage, legalName = "Bank C").serialNumber, REVOCATION_REASON, REPORTER)
|
||||
val approved = crrStorage.saveRevocationRequest(CertificateRevocationRequest(
|
||||
certificateSerialNumber = createNodeCertificate(csrStorage, legalName = "Bank C").serialNumber,
|
||||
reason = REVOCATION_REASON,
|
||||
reporter = REPORTER))
|
||||
crrStorage.approveRevocationRequest(approved, "Approver")
|
||||
val approvedRevocationRequest = crrStorage.getRevocationRequest(approved)!!
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.r3.corda.networkmanage.common.persistence
|
||||
|
||||
import com.r3.corda.networkmanage.TestBase
|
||||
import net.corda.nodeapi.internal.network.CertificateRevocationRequest
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import net.corda.testing.node.MockServices
|
||||
@ -41,7 +42,10 @@ class PersistentCertificateRevocationRequestStorageTest : TestBase() {
|
||||
val certificate = createNodeCertificate(csrStorage)
|
||||
|
||||
// when
|
||||
val requestId = crrStorage.saveRevocationRequest(certificate.serialNumber, REVOCATION_REASON, REPORTER)
|
||||
val requestId = crrStorage.saveRevocationRequest(CertificateRevocationRequest(
|
||||
certificateSerialNumber = certificate.serialNumber,
|
||||
reason = REVOCATION_REASON,
|
||||
reporter = REPORTER))
|
||||
|
||||
// then
|
||||
assertNotNull(crrStorage.getRevocationRequest(requestId)).apply {
|
||||
@ -55,7 +59,10 @@ class PersistentCertificateRevocationRequestStorageTest : TestBase() {
|
||||
fun `Retrieving a certificate revocation request succeeds`() {
|
||||
// given
|
||||
val certificate = createNodeCertificate(csrStorage)
|
||||
val requestId = crrStorage.saveRevocationRequest(certificate.serialNumber, REVOCATION_REASON, REPORTER)
|
||||
val requestId = crrStorage.saveRevocationRequest(CertificateRevocationRequest(
|
||||
certificateSerialNumber = certificate.serialNumber,
|
||||
reason = REVOCATION_REASON,
|
||||
reporter = REPORTER))
|
||||
|
||||
// when
|
||||
val request = crrStorage.getRevocationRequest(requestId)
|
||||
@ -68,10 +75,16 @@ class PersistentCertificateRevocationRequestStorageTest : TestBase() {
|
||||
fun `Retrieving a certificate revocation requests by status returns correct data`() {
|
||||
// given
|
||||
(1..10).forEach {
|
||||
crrStorage.saveRevocationRequest(createNodeCertificate(csrStorage, "LegalName" + it.toString()).serialNumber, REVOCATION_REASON, REPORTER)
|
||||
crrStorage.saveRevocationRequest(CertificateRevocationRequest(
|
||||
certificateSerialNumber = createNodeCertificate(csrStorage, "LegalName" + it.toString()).serialNumber,
|
||||
reason = REVOCATION_REASON,
|
||||
reporter = REPORTER))
|
||||
}
|
||||
(11..15).forEach {
|
||||
val requestId = crrStorage.saveRevocationRequest(createNodeCertificate(csrStorage, "LegalName" + it.toString()).serialNumber, REVOCATION_REASON, REPORTER)
|
||||
val requestId = crrStorage.saveRevocationRequest(CertificateRevocationRequest(
|
||||
certificateSerialNumber = createNodeCertificate(csrStorage, "LegalName" + it.toString()).serialNumber,
|
||||
reason = REVOCATION_REASON,
|
||||
reporter = REPORTER))
|
||||
crrStorage.approveRevocationRequest(requestId, "Approver")
|
||||
}
|
||||
|
||||
@ -89,7 +102,10 @@ class PersistentCertificateRevocationRequestStorageTest : TestBase() {
|
||||
// then
|
||||
assertFailsWith(IllegalArgumentException::class) {
|
||||
// when
|
||||
crrStorage.saveRevocationRequest(BigInteger.TEN, REVOCATION_REASON, REPORTER)
|
||||
crrStorage.saveRevocationRequest(CertificateRevocationRequest(
|
||||
certificateSerialNumber = BigInteger.TEN,
|
||||
reason = REVOCATION_REASON,
|
||||
reporter = REPORTER))
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,7 +113,10 @@ class PersistentCertificateRevocationRequestStorageTest : TestBase() {
|
||||
fun `Approving a certificate revocation request changes its status`() {
|
||||
// given
|
||||
val certificate = createNodeCertificate(csrStorage)
|
||||
val requestId = crrStorage.saveRevocationRequest(certificate.serialNumber, REVOCATION_REASON, REPORTER)
|
||||
val requestId = crrStorage.saveRevocationRequest(CertificateRevocationRequest(
|
||||
certificateSerialNumber = certificate.serialNumber,
|
||||
reason = REVOCATION_REASON,
|
||||
reporter = REPORTER))
|
||||
|
||||
// when
|
||||
crrStorage.approveRevocationRequest(requestId, "Approver")
|
||||
@ -112,7 +131,10 @@ class PersistentCertificateRevocationRequestStorageTest : TestBase() {
|
||||
fun `Rejecting a certificate revocation request changes its status`() {
|
||||
// given
|
||||
val certificate = createNodeCertificate(csrStorage)
|
||||
val requestId = crrStorage.saveRevocationRequest(certificate.serialNumber, REVOCATION_REASON, REPORTER)
|
||||
val requestId = crrStorage.saveRevocationRequest(CertificateRevocationRequest(
|
||||
certificateSerialNumber = certificate.serialNumber,
|
||||
reason = REVOCATION_REASON,
|
||||
reporter = REPORTER))
|
||||
|
||||
// when
|
||||
crrStorage.rejectRevocationRequest(requestId, "Rejector", "No reason")
|
||||
|
@ -0,0 +1,73 @@
|
||||
package com.r3.corda.networkmanage.common.signer
|
||||
|
||||
import com.nhaarman.mockito_kotlin.*
|
||||
import com.r3.corda.networkmanage.TestBase
|
||||
import com.r3.corda.networkmanage.common.persistence.CertificateRevocationListStorage
|
||||
import com.r3.corda.networkmanage.common.persistence.CertificateRevocationRequestData
|
||||
import com.r3.corda.networkmanage.common.persistence.CrlIssuer
|
||||
import com.r3.corda.networkmanage.common.persistence.RequestStatus
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.random63BitValue
|
||||
import net.corda.core.crypto.sign
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.DigitalSignatureWithCert
|
||||
import net.corda.core.utilities.millis
|
||||
import net.corda.nodeapi.internal.DEV_INTERMEDIATE_CA
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.math.BigInteger
|
||||
import java.net.URL
|
||||
import java.security.cert.CRLReason
|
||||
import java.security.cert.X509CRL
|
||||
import java.time.Instant
|
||||
import kotlin.test.assertNotNull
|
||||
|
||||
class CertificateRevocationListSignerTest : TestBase() {
|
||||
private val signer = TestSigner()
|
||||
private lateinit var crlStorage: CertificateRevocationListStorage
|
||||
private lateinit var crlSigner: CertificateRevocationListSigner
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
crlStorage = mock()
|
||||
crlSigner = CertificateRevocationListSigner(crlStorage, DEV_INTERMEDIATE_CA.certificate, 600.millis, URL("http://dummy.com"), signer)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `signCertificateRevocationList creates correct CRL and saves c`() {
|
||||
// given
|
||||
val approvedReq = givenCertificateRevocationRequest(RequestStatus.APPROVED)
|
||||
val revokedReq = givenCertificateRevocationRequest(RequestStatus.DONE)
|
||||
val signedBy = "Signer"
|
||||
|
||||
// when
|
||||
crlSigner.createSignedCRL(listOf(approvedReq), listOf(revokedReq), signedBy)
|
||||
|
||||
// then
|
||||
argumentCaptor<X509CRL>().apply {
|
||||
verify(crlStorage).saveCertificateRevocationList(capture(), eq(CrlIssuer.DOORMAN), eq(signedBy), any())
|
||||
val crl = firstValue
|
||||
crl.verify(DEV_INTERMEDIATE_CA.keyPair.public)
|
||||
assertNotNull(crl.getRevokedCertificate(approvedReq.certificateSerialNumber))
|
||||
assertNotNull(crl.getRevokedCertificate(revokedReq.certificateSerialNumber))
|
||||
}
|
||||
}
|
||||
|
||||
private class TestSigner : Signer {
|
||||
override fun signBytes(data: ByteArray): DigitalSignatureWithCert {
|
||||
return DigitalSignatureWithCert(DEV_INTERMEDIATE_CA.certificate, DEV_INTERMEDIATE_CA.keyPair.private.sign(data).bytes)
|
||||
}
|
||||
}
|
||||
|
||||
private fun givenCertificateRevocationRequest(status: RequestStatus): CertificateRevocationRequestData {
|
||||
return CertificateRevocationRequestData(
|
||||
SecureHash.randomSHA256().toString(),
|
||||
BigInteger.valueOf(random63BitValue()),
|
||||
Instant.now(),
|
||||
CordaX500Name.parse("CN=Bank A, O=$status, L=London, C=GB"),
|
||||
status,
|
||||
CRLReason.KEY_COMPROMISE,
|
||||
"Reporter")
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package net.corda.nodeapi.internal.network
|
||||
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import java.math.BigInteger
|
||||
import java.security.cert.CRLReason
|
||||
|
||||
/**
|
||||
* This data class is intended to be used by the certificate revocation request (CRR) service client to create a new
|
||||
* CRR submission.
|
||||
*/
|
||||
@CordaSerializable
|
||||
data class CertificateRevocationRequest(val certificateSerialNumber: BigInteger? = null,
|
||||
val csrRequestId: String? = null,
|
||||
val legalName: CordaX500Name? = null,
|
||||
val reason: CRLReason,
|
||||
val reporter: String) {
|
||||
init {
|
||||
require(certificateSerialNumber != null || csrRequestId != null || legalName != null) {
|
||||
"At least one of the following needs to be specified: certificateSerialNumber, csrRequestId, legalName."
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,13 @@
|
||||
/*
|
||||
* R3 Proprietary and Confidential
|
||||
*
|
||||
* Copyright (c) 2018 R3 Limited. All rights reserved.
|
||||
*
|
||||
* The intellectual and technical concepts contained herein are proprietary to R3 and its suppliers and are protected by trade secret law.
|
||||
*
|
||||
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
|
||||
*/
|
||||
|
||||
package net.corda.nodeapi.internal.serialization.amqp.custom
|
||||
|
||||
import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer
|
||||
|
Loading…
Reference in New Issue
Block a user