mirror of
https://github.com/corda/corda.git
synced 2025-06-18 23:28:21 +00:00
Adding public key constraint (#319)
* ENT-1356 Adding public key constraint * Addressing review comments * Removing SERIALIZABLE from transaction * Adding stashed changes * Removing SERIALIZABLE from node info storage * Addressing review comments * Addressing possible certificate inconsitency (design gap) + clearing whole database for new liquibase changeset * Addressing review comments
This commit is contained in:
@ -1,12 +1,14 @@
|
|||||||
package com.r3.corda.networkmanage.common.persistence
|
package com.r3.corda.networkmanage.common.persistence
|
||||||
|
|
||||||
|
import net.corda.core.crypto.SecureHash
|
||||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
||||||
import java.security.cert.CertPath
|
import java.security.cert.CertPath
|
||||||
|
|
||||||
data class CertificateData(val publicKeyHash: String, val certStatus: CertificateStatus, val certPath: CertPath)
|
data class CertificateData(val certStatus: CertificateStatus, val certPath: CertPath)
|
||||||
|
|
||||||
data class CertificateSigningRequest(val requestId: String,
|
data class CertificateSigningRequest(val requestId: String,
|
||||||
val legalName: String,
|
val legalName: String,
|
||||||
|
val publicKeyHash: SecureHash,
|
||||||
val status: RequestStatus,
|
val status: RequestStatus,
|
||||||
val request: PKCS10CertificationRequest,
|
val request: PKCS10CertificationRequest,
|
||||||
val remark: String?,
|
val remark: String?,
|
||||||
|
@ -3,6 +3,7 @@ package com.r3.corda.networkmanage.common.persistence
|
|||||||
import com.r3.corda.networkmanage.common.persistence.entity.CertificateDataEntity
|
import com.r3.corda.networkmanage.common.persistence.entity.CertificateDataEntity
|
||||||
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.hashString
|
import com.r3.corda.networkmanage.common.utils.hashString
|
||||||
|
import net.corda.core.crypto.Crypto.toSupportedPublicKey
|
||||||
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.persistence.CordaPersistence
|
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||||
@ -26,14 +27,12 @@ class PersistentCertificateRequestStorage(private val database: CordaPersistence
|
|||||||
builder.and(requestIdEq, statusEq)
|
builder.and(requestIdEq, statusEq)
|
||||||
}
|
}
|
||||||
request ?: throw IllegalArgumentException("Cannot retrieve 'APPROVED' certificate signing request for request id: $requestId")
|
request ?: throw IllegalArgumentException("Cannot retrieve 'APPROVED' certificate signing request for request id: $requestId")
|
||||||
val publicKeyHash = certificates.certificates.first().publicKey.hashString()
|
|
||||||
val certificateSigningRequest = request.copy(
|
val certificateSigningRequest = request.copy(
|
||||||
modifiedBy = signedBy,
|
modifiedBy = signedBy,
|
||||||
modifiedAt = Instant.now(),
|
modifiedAt = Instant.now(),
|
||||||
status = RequestStatus.SIGNED)
|
status = RequestStatus.SIGNED)
|
||||||
session.merge(certificateSigningRequest)
|
session.merge(certificateSigningRequest)
|
||||||
val certificateDataEntity = CertificateDataEntity(
|
val certificateDataEntity = CertificateDataEntity(
|
||||||
publicKeyHash = publicKeyHash,
|
|
||||||
certificateStatus = CertificateStatus.VALID,
|
certificateStatus = CertificateStatus.VALID,
|
||||||
certificatePathBytes = certificates.encoded,
|
certificatePathBytes = certificates.encoded,
|
||||||
certificateSigningRequest = certificateSigningRequest)
|
certificateSigningRequest = certificateSigningRequest)
|
||||||
@ -48,6 +47,7 @@ class PersistentCertificateRequestStorage(private val database: CordaPersistence
|
|||||||
session.save(CertificateSigningRequestEntity(
|
session.save(CertificateSigningRequestEntity(
|
||||||
requestId = requestId,
|
requestId = requestId,
|
||||||
legalName = legalName,
|
legalName = legalName,
|
||||||
|
publicKeyHash = toSupportedPublicKey(request.subjectPublicKeyInfo).hashString(),
|
||||||
requestBytes = request.encoded,
|
requestBytes = request.encoded,
|
||||||
remark = rejectReason,
|
remark = rejectReason,
|
||||||
modifiedBy = emptyList(),
|
modifiedBy = emptyList(),
|
||||||
@ -134,7 +134,7 @@ class PersistentCertificateRequestStorage(private val database: CordaPersistence
|
|||||||
return Pair(request.subject.toString(), "Name validation failed: ${e.message}")
|
return Pair(request.subject.toString(), "Name validation failed: ${e.message}")
|
||||||
}
|
}
|
||||||
|
|
||||||
val query = session.criteriaBuilder.run {
|
val duplicateNameQuery = session.criteriaBuilder.run {
|
||||||
val criteriaQuery = createQuery(CertificateSigningRequestEntity::class.java)
|
val criteriaQuery = createQuery(CertificateSigningRequestEntity::class.java)
|
||||||
criteriaQuery.from(CertificateSigningRequestEntity::class.java).run {
|
criteriaQuery.from(CertificateSigningRequestEntity::class.java).run {
|
||||||
criteriaQuery.where(equal(get<String>(CertificateSigningRequestEntity::legalName.name), legalName))
|
criteriaQuery.where(equal(get<String>(CertificateSigningRequestEntity::legalName.name), legalName))
|
||||||
@ -144,10 +144,27 @@ class PersistentCertificateRequestStorage(private val database: CordaPersistence
|
|||||||
// TODO consider scenario: There is a CSR that is signed but the certificate itself has expired or was revoked
|
// TODO consider scenario: There is a CSR that is signed but the certificate itself has expired or was revoked
|
||||||
// Also, at the moment we assume that once the CSR is approved it cannot be rejected.
|
// Also, at the moment we assume that once the CSR is approved it cannot be rejected.
|
||||||
// What if we approved something by mistake.
|
// What if we approved something by mistake.
|
||||||
val duplicates = session.createQuery(query).resultList.filter {
|
val nameDuplicates = session.createQuery(duplicateNameQuery).resultList.filter {
|
||||||
it.status != RequestStatus.REJECTED
|
it.status != RequestStatus.REJECTED
|
||||||
}
|
}
|
||||||
|
|
||||||
return Pair(legalName, if (duplicates.isEmpty()) null else "Duplicate legal name")
|
if (nameDuplicates.isNotEmpty()) {
|
||||||
|
return Pair(legalName, "Duplicate legal name")
|
||||||
|
}
|
||||||
|
|
||||||
|
val publicKey = toSupportedPublicKey(request.subjectPublicKeyInfo).hashString()
|
||||||
|
val duplicatePkQuery = session.criteriaBuilder.run {
|
||||||
|
val criteriaQuery = createQuery(CertificateSigningRequestEntity::class.java)
|
||||||
|
criteriaQuery.from(CertificateSigningRequestEntity::class.java).run {
|
||||||
|
criteriaQuery.where(equal(get<String>(CertificateSigningRequestEntity::publicKeyHash.name), publicKey))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO Consider following scenario: There is a CSR that is signed but the certificate itself has expired or was revoked
|
||||||
|
val pkDuplicates = session.createQuery(duplicatePkQuery).resultList.filter {
|
||||||
|
it.status != RequestStatus.REJECTED
|
||||||
|
}
|
||||||
|
|
||||||
|
return Pair(legalName, if (pkDuplicates.isEmpty()) null else "Duplicate public key")
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,5 @@
|
|||||||
package com.r3.corda.networkmanage.common.persistence
|
package com.r3.corda.networkmanage.common.persistence
|
||||||
|
|
||||||
import com.r3.corda.networkmanage.common.persistence.entity.CertificateDataEntity
|
|
||||||
import com.r3.corda.networkmanage.common.persistence.entity.CertificateSigningRequestEntity
|
import com.r3.corda.networkmanage.common.persistence.entity.CertificateSigningRequestEntity
|
||||||
import com.r3.corda.networkmanage.common.persistence.entity.NodeInfoEntity
|
import com.r3.corda.networkmanage.common.persistence.entity.NodeInfoEntity
|
||||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
||||||
@ -10,7 +9,7 @@ import net.corda.core.internal.CertRole
|
|||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.nodeapi.internal.SignedNodeInfo
|
import net.corda.nodeapi.internal.SignedNodeInfo
|
||||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||||
import net.corda.nodeapi.internal.persistence.TransactionIsolationLevel
|
import net.corda.nodeapi.internal.persistence.DatabaseTransaction
|
||||||
import java.security.cert.CertPath
|
import java.security.cert.CertPath
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -21,16 +20,13 @@ class PersistentNodeInfoStorage(private val database: CordaPersistence) : NodeIn
|
|||||||
val nodeInfo = nodeInfoWithSigned.nodeInfo
|
val nodeInfo = nodeInfoWithSigned.nodeInfo
|
||||||
val signedNodeInfo = nodeInfoWithSigned.signedNodeInfo
|
val signedNodeInfo = nodeInfoWithSigned.signedNodeInfo
|
||||||
val nodeCaCert = nodeInfo.legalIdentitiesAndCerts[0].certPath.certificates.find { CertRole.extract(it) == CertRole.NODE_CA }
|
val nodeCaCert = nodeInfo.legalIdentitiesAndCerts[0].certPath.certificates.find { CertRole.extract(it) == CertRole.NODE_CA }
|
||||||
return database.transaction(TransactionIsolationLevel.SERIALIZABLE) {
|
return database.transaction {
|
||||||
// TODO Move these checks out of data access layer
|
// TODO Move these checks out of data access layer
|
||||||
val request = nodeCaCert?.let {
|
val request = nodeCaCert?.let {
|
||||||
singleRequestWhere(CertificateDataEntity::class.java) { builder, path ->
|
getSignedRequestByPublicHash(it.publicKey.encoded.sha256(), this)
|
||||||
val certPublicKeyHashEq = builder.equal(path.get<String>(CertificateDataEntity::publicKeyHash.name), it.publicKey.encoded.sha256().toString())
|
|
||||||
val certStatusValid = builder.equal(path.get<CertificateStatus>(CertificateDataEntity::certificateStatus.name), CertificateStatus.VALID)
|
|
||||||
builder.and(certPublicKeyHashEq, certStatusValid)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
request ?: throw IllegalArgumentException("Unknown node info, this public key is not registered with the network management service.")
|
request ?: throw IllegalArgumentException("Unknown node info, this public key is not registered with the network management service.")
|
||||||
|
require(request.certificateData!!.certificateStatus == CertificateStatus.VALID) { "Certificate is no longer valid" }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Delete any previous [NodeInfoEntity] instance for this CSR
|
* Delete any previous [NodeInfoEntity] instance for this CSR
|
||||||
@ -40,13 +36,13 @@ class PersistentNodeInfoStorage(private val database: CordaPersistence) : NodeIn
|
|||||||
* but it has been confirmed that this fact has been acknowledged at the design time and we are fine with it.
|
* but it has been confirmed that this fact has been acknowledged at the design time and we are fine with it.
|
||||||
*/
|
*/
|
||||||
deleteRequest(NodeInfoEntity::class.java) { builder, path ->
|
deleteRequest(NodeInfoEntity::class.java) { builder, path ->
|
||||||
builder.equal(path.get<CertificateSigningRequestEntity>(NodeInfoEntity::certificateSigningRequest.name), request.certificateSigningRequest)
|
builder.equal(path.get<CertificateSigningRequestEntity>(NodeInfoEntity::certificateSigningRequest.name), request)
|
||||||
}
|
}
|
||||||
val hash = signedNodeInfo.raw.hash
|
val hash = signedNodeInfo.raw.hash
|
||||||
|
|
||||||
val hashedNodeInfo = NodeInfoEntity(
|
val hashedNodeInfo = NodeInfoEntity(
|
||||||
nodeInfoHash = hash.toString(),
|
nodeInfoHash = hash.toString(),
|
||||||
certificateSigningRequest = request.certificateSigningRequest,
|
certificateSigningRequest = request,
|
||||||
signedNodeInfoBytes = signedNodeInfo.serialize().bytes)
|
signedNodeInfoBytes = signedNodeInfo.serialize().bytes)
|
||||||
session.save(hashedNodeInfo)
|
session.save(hashedNodeInfo)
|
||||||
hash
|
hash
|
||||||
@ -61,16 +57,16 @@ class PersistentNodeInfoStorage(private val database: CordaPersistence) : NodeIn
|
|||||||
|
|
||||||
override fun getCertificatePath(publicKeyHash: SecureHash): CertPath? {
|
override fun getCertificatePath(publicKeyHash: SecureHash): CertPath? {
|
||||||
return database.transaction {
|
return database.transaction {
|
||||||
val builder = session.criteriaBuilder
|
val request = getSignedRequestByPublicHash(publicKeyHash, this)
|
||||||
val query = builder.createQuery(ByteArray::class.java).run {
|
request?.let { buildCertPath(it.certificateData!!.certificatePathBytes) }
|
||||||
from(CertificateSigningRequestEntity::class.java).run {
|
}
|
||||||
select(get<CertificateDataEntity>(CertificateSigningRequestEntity::certificateData.name)
|
}
|
||||||
.get<ByteArray>(CertificateDataEntity::certificatePathBytes.name))
|
|
||||||
where(builder.equal(get<CertificateDataEntity>(CertificateSigningRequestEntity::certificateData.name)
|
private fun getSignedRequestByPublicHash(publicKeyHash: SecureHash, transaction: DatabaseTransaction): CertificateSigningRequestEntity? {
|
||||||
.get<String>(CertificateDataEntity::publicKeyHash.name), publicKeyHash.toString()))
|
return transaction.singleRequestWhere(CertificateSigningRequestEntity::class.java) { builder, path ->
|
||||||
}
|
val publicKeyEq = builder.equal(path.get<String>(CertificateSigningRequestEntity::publicKeyHash.name), publicKeyHash.toString())
|
||||||
}
|
val statusEq = builder.equal(path.get<RequestStatus>(CertificateSigningRequestEntity::status.name), RequestStatus.SIGNED)
|
||||||
session.createQuery(query).uniqueResultOptional().orElseGet { null }?.let { buildCertPath(it) }
|
builder.and(publicKeyEq, statusEq)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,6 +4,7 @@ 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 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
|
||||||
@ -12,7 +13,7 @@ import java.time.Instant
|
|||||||
import javax.persistence.*
|
import javax.persistence.*
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "certificate_signing_request")
|
@Table(name = "certificate_signing_request", indexes = arrayOf(Index(name = "IDX_PUB_KEY_HASH", columnList = "public_key_hash")))
|
||||||
class CertificateSigningRequestEntity(
|
class CertificateSigningRequestEntity(
|
||||||
@Id
|
@Id
|
||||||
@Column(name = "request_id", length = 64)
|
@Column(name = "request_id", length = 64)
|
||||||
@ -22,6 +23,9 @@ class CertificateSigningRequestEntity(
|
|||||||
@Column(name = "legal_name", length = 256, nullable = false)
|
@Column(name = "legal_name", length = 256, nullable = false)
|
||||||
val legalName: String,
|
val legalName: String,
|
||||||
|
|
||||||
|
@Column(name = "public_key_hash", length = 64)
|
||||||
|
val publicKeyHash: String,
|
||||||
|
|
||||||
@Audited
|
@Audited
|
||||||
@Column(name = "status", nullable = false)
|
@Column(name = "status", nullable = false)
|
||||||
@Enumerated(EnumType.STRING)
|
@Enumerated(EnumType.STRING)
|
||||||
@ -50,6 +54,7 @@ class CertificateSigningRequestEntity(
|
|||||||
fun toCertificateSigningRequest() = CertificateSigningRequest(
|
fun toCertificateSigningRequest() = CertificateSigningRequest(
|
||||||
requestId = requestId,
|
requestId = requestId,
|
||||||
legalName = legalName,
|
legalName = legalName,
|
||||||
|
publicKeyHash = SecureHash.parse(publicKeyHash),
|
||||||
status = status,
|
status = status,
|
||||||
request = request(),
|
request = request(),
|
||||||
remark = remark,
|
remark = remark,
|
||||||
@ -59,6 +64,7 @@ class CertificateSigningRequestEntity(
|
|||||||
|
|
||||||
fun copy(requestId: String = this.requestId,
|
fun copy(requestId: String = this.requestId,
|
||||||
legalName: String = this.legalName,
|
legalName: String = this.legalName,
|
||||||
|
publicKeyHash: String = this.publicKeyHash,
|
||||||
status: RequestStatus = this.status,
|
status: RequestStatus = this.status,
|
||||||
modifiedBy: List<String> = this.modifiedBy,
|
modifiedBy: List<String> = this.modifiedBy,
|
||||||
modifiedAt: Instant = this.modifiedAt,
|
modifiedAt: Instant = this.modifiedAt,
|
||||||
@ -69,6 +75,7 @@ class CertificateSigningRequestEntity(
|
|||||||
return CertificateSigningRequestEntity(
|
return CertificateSigningRequestEntity(
|
||||||
requestId = requestId,
|
requestId = requestId,
|
||||||
legalName = legalName,
|
legalName = legalName,
|
||||||
|
publicKeyHash = publicKeyHash,
|
||||||
status = status,
|
status = status,
|
||||||
modifiedAt = modifiedAt,
|
modifiedAt = modifiedAt,
|
||||||
modifiedBy = modifiedBy,
|
modifiedBy = modifiedBy,
|
||||||
@ -82,16 +89,13 @@ class CertificateSigningRequestEntity(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "certificate_data", indexes = arrayOf(Index(name = "IDX_PUB_KEY_HASH", columnList = "public_key_hash")))
|
@Table(name = "certificate_data")
|
||||||
class CertificateDataEntity(
|
class CertificateDataEntity(
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.SEQUENCE)
|
@GeneratedValue(strategy = GenerationType.SEQUENCE)
|
||||||
val id: Long? = null,
|
val id: Long? = null,
|
||||||
|
|
||||||
@Column(name = "public_key_hash", length = 64)
|
|
||||||
val publicKeyHash: String,
|
|
||||||
|
|
||||||
@Column(name = "certificate_status")
|
@Column(name = "certificate_status")
|
||||||
val certificateStatus: CertificateStatus,
|
val certificateStatus: CertificateStatus,
|
||||||
|
|
||||||
@ -105,7 +109,6 @@ class CertificateDataEntity(
|
|||||||
) {
|
) {
|
||||||
fun toCertificateData(): CertificateData {
|
fun toCertificateData(): CertificateData {
|
||||||
return CertificateData(
|
return CertificateData(
|
||||||
publicKeyHash = publicKeyHash,
|
|
||||||
certStatus = certificateStatus,
|
certStatus = certificateStatus,
|
||||||
certPath = toCertificatePath()
|
certPath = toCertificatePath()
|
||||||
)
|
)
|
||||||
|
@ -3,5 +3,6 @@
|
|||||||
|
|
||||||
<include file="migration/network-manager.changelog-init.xml"/>
|
<include file="migration/network-manager.changelog-init.xml"/>
|
||||||
<include file="migration/network-manager.changelog-signing-network-params.xml"/>
|
<include file="migration/network-manager.changelog-signing-network-params.xml"/>
|
||||||
|
<include file="migration/network-manager.changelog-pub-key-move.xml"/>
|
||||||
|
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd">
|
||||||
|
<changeSet author="R3.Corda" id="Move public key hash">
|
||||||
|
<delete tableName="certificate_signing_request"/>
|
||||||
|
<delete tableName="certificate_data"/>
|
||||||
|
<addColumn tableName="certificate_signing_request">
|
||||||
|
<column name="public_key_hash" type="NVARCHAR(64)"/>
|
||||||
|
</addColumn>
|
||||||
|
<dropIndex indexName="IDX_PUB_KEY_HASH" tableName="certificate_data"/>
|
||||||
|
<createIndex indexName="IDX_PUB_KEY_HASH" tableName="certificate_signing_request">
|
||||||
|
<column name="public_key_hash"/>
|
||||||
|
</createIndex>
|
||||||
|
<dropColumn columnName="public_key_hash" tableName="certificate_data"/>
|
||||||
|
<delete tableName="CertificateSigningRequestEntity_modifiedBy"/>
|
||||||
|
<delete tableName="CertificateSigningRequestEntity_modifiedBy_AUD"/>
|
||||||
|
<delete tableName="certificate_signing_request_AUD"/>
|
||||||
|
<delete tableName="network_map"/>
|
||||||
|
<delete tableName="network_parameters"/>
|
||||||
|
<delete tableName="node_info"/>
|
||||||
|
<delete tableName="REVINFO"/>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
@ -20,6 +20,7 @@ abstract class TestBase {
|
|||||||
requestId: String = SecureHash.randomSHA256().toString(),
|
requestId: String = SecureHash.randomSHA256().toString(),
|
||||||
status: RequestStatus = RequestStatus.NEW,
|
status: RequestStatus = RequestStatus.NEW,
|
||||||
legalName: String = "TestLegalName",
|
legalName: String = "TestLegalName",
|
||||||
|
publicKeyHash: SecureHash = SecureHash.randomSHA256(),
|
||||||
remark: String = "Test remark",
|
remark: String = "Test remark",
|
||||||
request: PKCS10CertificationRequest = mock(),
|
request: PKCS10CertificationRequest = mock(),
|
||||||
certData: CertificateData = mock(),
|
certData: CertificateData = mock(),
|
||||||
@ -29,6 +30,7 @@ abstract class TestBase {
|
|||||||
requestId = requestId,
|
requestId = requestId,
|
||||||
status = status,
|
status = status,
|
||||||
legalName = legalName,
|
legalName = legalName,
|
||||||
|
publicKeyHash = publicKeyHash,
|
||||||
remark = remark,
|
remark = remark,
|
||||||
certData = certData,
|
certData = certData,
|
||||||
request = request,
|
request = request,
|
||||||
@ -36,11 +38,9 @@ abstract class TestBase {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun certificateData(publicKeyHash: String = SecureHash.randomSHA256().toString(),
|
protected fun certificateData(certStatus: CertificateStatus = CertificateStatus.VALID,
|
||||||
certStatus: CertificateStatus = CertificateStatus.VALID,
|
|
||||||
certPath: CertPath = mock()): CertificateData {
|
certPath: CertPath = mock()): CertificateData {
|
||||||
return CertificateData(
|
return CertificateData(
|
||||||
publicKeyHash = publicKeyHash,
|
|
||||||
certStatus = certStatus,
|
certStatus = certStatus,
|
||||||
certPath = certPath
|
certPath = certPath
|
||||||
)
|
)
|
||||||
|
@ -19,6 +19,7 @@ import org.junit.After
|
|||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
|
import java.security.cert.CertPath
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.security.auth.x500.X500Principal
|
import javax.security.auth.x500.X500Principal
|
||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
@ -69,7 +70,7 @@ class PersistentCertificateRequestStorageTest : TestBase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `sign request`() {
|
fun `sign request`() {
|
||||||
val (csr, _) = createRequest("LegalName")
|
val (csr, nodeKeyPair) = createRequest("LegalName")
|
||||||
// Add request to DB.
|
// Add request to DB.
|
||||||
val requestId = storage.saveRequest(csr)
|
val requestId = storage.saveRequest(csr)
|
||||||
// New request should equals to 1.
|
// New request should equals to 1.
|
||||||
@ -86,11 +87,7 @@ class PersistentCertificateRequestStorageTest : TestBase() {
|
|||||||
// Sign certificate
|
// Sign certificate
|
||||||
storage.putCertificatePath(
|
storage.putCertificatePath(
|
||||||
requestId,
|
requestId,
|
||||||
JcaPKCS10CertificationRequest(csr).run {
|
generateSignedCertPath(csr, nodeKeyPair),
|
||||||
// TODO We need a utility in InternalUtils for converting X500Name -> CordaX500Name
|
|
||||||
val (rootCa, intermediateCa, nodeCa) = createDevNodeCaCertPath(CordaX500Name.build(X500Principal(subject.encoded)))
|
|
||||||
buildCertPath(nodeCa.certificate, intermediateCa.certificate, rootCa.certificate)
|
|
||||||
},
|
|
||||||
listOf(DOORMAN_SIGNATURE)
|
listOf(DOORMAN_SIGNATURE)
|
||||||
)
|
)
|
||||||
// Check request is ready
|
// Check request is ready
|
||||||
@ -99,33 +96,51 @@ class PersistentCertificateRequestStorageTest : TestBase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `sign request ignores subsequent sign requests`() {
|
fun `sign request ignores subsequent sign requests`() {
|
||||||
val (csr, _) = createRequest("LegalName")
|
val (csr, nodeKeyPair) = createRequest("LegalName")
|
||||||
// Add request to DB.
|
// Add request to DB.
|
||||||
val requestId = storage.saveRequest(csr)
|
val requestId = storage.saveRequest(csr)
|
||||||
// Store certificate to DB.
|
// Store certificate to DB.
|
||||||
storage.markRequestTicketCreated(requestId)
|
storage.markRequestTicketCreated(requestId)
|
||||||
storage.approveRequest(requestId, DOORMAN_SIGNATURE)
|
storage.approveRequest(requestId, DOORMAN_SIGNATURE)
|
||||||
|
// Sign certificate
|
||||||
storage.putCertificatePath(
|
storage.putCertificatePath(
|
||||||
requestId,
|
requestId,
|
||||||
JcaPKCS10CertificationRequest(csr).run {
|
generateSignedCertPath(csr, nodeKeyPair),
|
||||||
val (rootCa, intermediateCa, nodeCa) = createDevNodeCaCertPath(CordaX500Name.build(X500Principal(subject.encoded)))
|
|
||||||
buildCertPath(nodeCa.certificate, intermediateCa.certificate, rootCa.certificate)
|
|
||||||
},
|
|
||||||
listOf(DOORMAN_SIGNATURE)
|
listOf(DOORMAN_SIGNATURE)
|
||||||
)
|
)
|
||||||
// Sign certificate
|
|
||||||
// When subsequent signature requested
|
// When subsequent signature requested
|
||||||
assertFailsWith(IllegalArgumentException::class) {
|
assertFailsWith(IllegalArgumentException::class) {
|
||||||
storage.putCertificatePath(
|
storage.putCertificatePath(
|
||||||
requestId,
|
requestId,
|
||||||
JcaPKCS10CertificationRequest(csr).run {
|
generateSignedCertPath(csr, nodeKeyPair),
|
||||||
val (rootCa, intermediateCa, nodeCa) = createDevNodeCaCertPath(CordaX500Name.build(X500Principal(subject.encoded)))
|
|
||||||
buildCertPath(nodeCa.certificate, intermediateCa.certificate, rootCa.certificate)
|
|
||||||
},
|
|
||||||
listOf(DOORMAN_SIGNATURE))
|
listOf(DOORMAN_SIGNATURE))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `sign request rejects requests with the same public key`() {
|
||||||
|
val (csr, nodeKeyPair) = createRequest("LegalName")
|
||||||
|
// Add request to DB.
|
||||||
|
val requestId = storage.saveRequest(csr)
|
||||||
|
// Store certificate to DB.
|
||||||
|
storage.markRequestTicketCreated(requestId)
|
||||||
|
storage.approveRequest(requestId, DOORMAN_SIGNATURE)
|
||||||
|
// Sign certificate
|
||||||
|
storage.putCertificatePath(
|
||||||
|
requestId,
|
||||||
|
generateSignedCertPath(csr, nodeKeyPair),
|
||||||
|
listOf(DOORMAN_SIGNATURE)
|
||||||
|
)
|
||||||
|
// Sign certificate
|
||||||
|
// When request with the same public key is requested
|
||||||
|
val (newCsr, _) = createRequest("NewLegalName", nodeKeyPair)
|
||||||
|
val duplicateRequestId = storage.saveRequest(newCsr)
|
||||||
|
assertThat(storage.getRequests(RequestStatus.NEW)).isEmpty()
|
||||||
|
val duplicateRequest = storage.getRequest(duplicateRequestId)
|
||||||
|
assertThat(duplicateRequest!!.status).isEqualTo(RequestStatus.REJECTED)
|
||||||
|
assertThat(duplicateRequest.remark).isEqualTo("Duplicate public key")
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `reject request`() {
|
fun `reject request`() {
|
||||||
val requestId = storage.saveRequest(createRequest("BankA").first)
|
val requestId = storage.saveRequest(createRequest("BankA").first)
|
||||||
@ -159,18 +174,14 @@ class PersistentCertificateRequestStorageTest : TestBase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `request with the same legal name as a previously signed request`() {
|
fun `request with the same legal name as a previously signed request`() {
|
||||||
val csr = createRequest("BankA").first
|
val (csr, nodeKeyPair) = createRequest("BankA")
|
||||||
val requestId = storage.saveRequest(csr)
|
val requestId = storage.saveRequest(csr)
|
||||||
storage.markRequestTicketCreated(requestId)
|
storage.markRequestTicketCreated(requestId)
|
||||||
storage.approveRequest(requestId, DOORMAN_SIGNATURE)
|
storage.approveRequest(requestId, DOORMAN_SIGNATURE)
|
||||||
// Sign certificate
|
// Sign certificate
|
||||||
storage.putCertificatePath(
|
storage.putCertificatePath(
|
||||||
requestId,
|
requestId,
|
||||||
JcaPKCS10CertificationRequest(csr).run {
|
generateSignedCertPath(csr, nodeKeyPair),
|
||||||
// TODO We need a utility in InternalUtils for converting X500Name -> CordaX500Name
|
|
||||||
val (rootCa, intermediateCa, nodeCa) = createDevNodeCaCertPath(CordaX500Name.build(X500Principal(subject.encoded)))
|
|
||||||
buildCertPath(nodeCa.certificate, intermediateCa.certificate, rootCa.certificate)
|
|
||||||
},
|
|
||||||
listOf(DOORMAN_SIGNATURE)
|
listOf(DOORMAN_SIGNATURE)
|
||||||
)
|
)
|
||||||
val rejectedRequestId = storage.saveRequest(createRequest("BankA").first)
|
val rejectedRequestId = storage.saveRequest(createRequest("BankA").first)
|
||||||
@ -215,6 +226,14 @@ class PersistentCertificateRequestStorageTest : TestBase() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun generateSignedCertPath(csr: PKCS10CertificationRequest, keyPair: KeyPair): CertPath {
|
||||||
|
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)
|
||||||
|
buildCertPath(nodeCa.certificate, intermediateCa.certificate, rootCa.certificate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun makeTestDataSourceProperties(nodeName: String = SecureHash.randomSHA256().toString()): Properties {
|
private fun makeTestDataSourceProperties(nodeName: String = SecureHash.randomSHA256().toString()): Properties {
|
||||||
val props = Properties()
|
val props = Properties()
|
||||||
props.setProperty("dataSourceClassName", "org.h2.jdbcx.JdbcDataSource")
|
props.setProperty("dataSourceClassName", "org.h2.jdbcx.JdbcDataSource")
|
||||||
@ -225,8 +244,7 @@ class PersistentCertificateRequestStorageTest : TestBase() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun createRequest(organisation: String): Pair<PKCS10CertificationRequest, KeyPair> {
|
internal fun createRequest(organisation: String, keyPair: KeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)): Pair<PKCS10CertificationRequest, KeyPair> {
|
||||||
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
|
||||||
val request = X509Utilities.createCertificateSigningRequest(X500Principal("O=$organisation,L=London,C=GB"), "my@mail.com", keyPair)
|
val request = X509Utilities.createCertificateSigningRequest(X500Principal("O=$organisation,L=London,C=GB"), "my@mail.com", keyPair)
|
||||||
return Pair(request, keyPair)
|
return Pair(request, keyPair)
|
||||||
}
|
}
|
@ -5,13 +5,11 @@ import net.corda.core.identity.CordaX500Name
|
|||||||
import net.corda.core.internal.signWithCert
|
import net.corda.core.internal.signWithCert
|
||||||
import net.corda.nodeapi.internal.createDevNetworkMapCa
|
import net.corda.nodeapi.internal.createDevNetworkMapCa
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
|
||||||
import net.corda.nodeapi.internal.network.NetworkMap
|
import net.corda.nodeapi.internal.network.NetworkMap
|
||||||
import net.corda.nodeapi.internal.network.verifiedNetworkMapCert
|
import net.corda.nodeapi.internal.network.verifiedNetworkMapCert
|
||||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||||
import net.corda.testing.common.internal.testNetworkParameters
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
import net.corda.testing.internal.TestNodeInfoBuilder
|
|
||||||
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
@ -50,7 +48,7 @@ class PersistentNetworkMapStorageTest : TestBase() {
|
|||||||
fun `saveNetworkMap and saveNetworkParameters create current network map and parameters`() {
|
fun `saveNetworkMap and saveNetworkParameters create current network map and parameters`() {
|
||||||
// given
|
// given
|
||||||
// Create node info.
|
// Create node info.
|
||||||
val signedNodeInfo = createValidSignedNodeInfo("Test")
|
val (signedNodeInfo) = createValidSignedNodeInfo("Test", requestStorage)
|
||||||
val nodeInfoHash = nodeInfoStorage.putNodeInfo(signedNodeInfo)
|
val nodeInfoHash = nodeInfoStorage.putNodeInfo(signedNodeInfo)
|
||||||
|
|
||||||
val networkParameters = testNetworkParameters(emptyList())
|
val networkParameters = testNetworkParameters(emptyList())
|
||||||
@ -117,8 +115,8 @@ class PersistentNetworkMapStorageTest : TestBase() {
|
|||||||
fun `getValidNodeInfoHashes returns only valid and signed node info hashes`() {
|
fun `getValidNodeInfoHashes returns only valid and signed node info hashes`() {
|
||||||
// given
|
// given
|
||||||
// Create node infos.
|
// Create node infos.
|
||||||
val signedNodeInfoA = createValidSignedNodeInfo("TestA")
|
val (signedNodeInfoA) = createValidSignedNodeInfo("TestA", requestStorage)
|
||||||
val signedNodeInfoB = createValidSignedNodeInfo("TestB")
|
val (signedNodeInfoB) = createValidSignedNodeInfo("TestB", requestStorage)
|
||||||
|
|
||||||
// Put signed node info data
|
// Put signed node info data
|
||||||
val nodeInfoHashA = nodeInfoStorage.putNodeInfo(signedNodeInfoA)
|
val nodeInfoHashA = nodeInfoStorage.putNodeInfo(signedNodeInfoA)
|
||||||
@ -139,15 +137,4 @@ class PersistentNetworkMapStorageTest : TestBase() {
|
|||||||
// then
|
// then
|
||||||
assertThat(validNodeInfoHash).containsOnly(nodeInfoHashA, nodeInfoHashB)
|
assertThat(validNodeInfoHash).containsOnly(nodeInfoHashA, nodeInfoHashB)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createValidSignedNodeInfo(organisation: String): NodeInfoWithSigned {
|
|
||||||
val nodeInfoBuilder = TestNodeInfoBuilder()
|
|
||||||
val requestId = requestStorage.saveRequest(createRequest(organisation).first)
|
|
||||||
requestStorage.markRequestTicketCreated(requestId)
|
|
||||||
requestStorage.approveRequest(requestId, "TestUser")
|
|
||||||
val (identity) = nodeInfoBuilder.addIdentity(CordaX500Name(organisation, "London", "GB"))
|
|
||||||
val nodeCaCertPath = X509CertificateFactory().generateCertPath(identity.certPath.certificates.drop(1))
|
|
||||||
requestStorage.putCertificatePath(requestId, nodeCaCertPath, emptyList())
|
|
||||||
return NodeInfoWithSigned(nodeInfoBuilder.buildWithSigned().second)
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -9,7 +9,6 @@ import net.corda.core.identity.CordaX500Name
|
|||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
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.X509CertificateFactory
|
|
||||||
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.nodeapi.internal.persistence.DatabaseConfig
|
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||||
@ -23,6 +22,7 @@ import org.junit.Before
|
|||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.security.PrivateKey
|
import java.security.PrivateKey
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertNotNull
|
import kotlin.test.assertNotNull
|
||||||
import kotlin.test.assertNull
|
import kotlin.test.assertNull
|
||||||
@ -83,8 +83,8 @@ class PersistentNodeInfoStorageTest : TestBase() {
|
|||||||
@Test
|
@Test
|
||||||
fun `getNodeInfo returns persisted SignedNodeInfo using the hash of just the NodeInfo`() {
|
fun `getNodeInfo returns persisted SignedNodeInfo using the hash of just the NodeInfo`() {
|
||||||
// given
|
// given
|
||||||
val (nodeA) = createValidSignedNodeInfo("TestA")
|
val (nodeA) = createValidSignedNodeInfo("TestA", requestStorage)
|
||||||
val (nodeB) = createValidSignedNodeInfo("TestB")
|
val (nodeB) = createValidSignedNodeInfo("TestB", requestStorage)
|
||||||
|
|
||||||
// Put signed node info data
|
// Put signed node info data
|
||||||
nodeInfoStorage.putNodeInfo(nodeA)
|
nodeInfoStorage.putNodeInfo(nodeA)
|
||||||
@ -102,7 +102,7 @@ class PersistentNodeInfoStorageTest : TestBase() {
|
|||||||
@Test
|
@Test
|
||||||
fun `same public key with different node info`() {
|
fun `same public key with different node info`() {
|
||||||
// Create node info.
|
// Create node info.
|
||||||
val (node1, key) = createValidSignedNodeInfo("Test", serial = 1)
|
val (node1, key) = createValidSignedNodeInfo("Test", requestStorage)
|
||||||
val nodeInfo2 = node1.nodeInfo.copy(serial = 2)
|
val nodeInfo2 = node1.nodeInfo.copy(serial = 2)
|
||||||
val node2 = NodeInfoWithSigned(nodeInfo2.signWith(listOf(key)))
|
val node2 = NodeInfoWithSigned(nodeInfo2.signWith(listOf(key)))
|
||||||
|
|
||||||
@ -120,7 +120,7 @@ class PersistentNodeInfoStorageTest : TestBase() {
|
|||||||
@Test
|
@Test
|
||||||
fun `putNodeInfo persists SignedNodeInfo with its signature`() {
|
fun `putNodeInfo persists SignedNodeInfo with its signature`() {
|
||||||
// given
|
// given
|
||||||
val (nodeInfoWithSigned) = createValidSignedNodeInfo("Test")
|
val (nodeInfoWithSigned) = createValidSignedNodeInfo("Test", requestStorage)
|
||||||
|
|
||||||
// when
|
// when
|
||||||
val nodeInfoHash = nodeInfoStorage.putNodeInfo(nodeInfoWithSigned)
|
val nodeInfoHash = nodeInfoStorage.putNodeInfo(nodeInfoWithSigned)
|
||||||
@ -129,16 +129,17 @@ class PersistentNodeInfoStorageTest : TestBase() {
|
|||||||
val persistedSignedNodeInfo = nodeInfoStorage.getNodeInfo(nodeInfoHash)
|
val persistedSignedNodeInfo = nodeInfoStorage.getNodeInfo(nodeInfoHash)
|
||||||
assertThat(persistedSignedNodeInfo?.signatures).isEqualTo(nodeInfoWithSigned.signedNodeInfo.signatures)
|
assertThat(persistedSignedNodeInfo?.signatures).isEqualTo(nodeInfoWithSigned.signedNodeInfo.signatures)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private fun createValidSignedNodeInfo(organisation: String, serial: Long = 1): Pair<NodeInfoWithSigned, PrivateKey> {
|
|
||||||
val nodeInfoBuilder = TestNodeInfoBuilder()
|
internal fun createValidSignedNodeInfo(organisation: String,
|
||||||
val requestId = requestStorage.saveRequest(createRequest(organisation).first)
|
storage: CertificationRequestStorage): Pair<NodeInfoWithSigned, PrivateKey> {
|
||||||
requestStorage.markRequestTicketCreated(requestId)
|
val (csr, nodeKeyPair) = createRequest(organisation)
|
||||||
requestStorage.approveRequest(requestId, "TestUser")
|
val requestId = storage.saveRequest(csr)
|
||||||
val (identity, key) = nodeInfoBuilder.addIdentity(CordaX500Name(organisation, "London", "GB"))
|
storage.markRequestTicketCreated(requestId)
|
||||||
val nodeCaCertPath = X509CertificateFactory().generateCertPath(identity.certPath.certificates.drop(1))
|
storage.approveRequest(requestId, "TestUser")
|
||||||
requestStorage.putCertificatePath(requestId, nodeCaCertPath, emptyList())
|
val nodeInfoBuilder = TestNodeInfoBuilder()
|
||||||
val (_, signedNodeInfo) = nodeInfoBuilder.buildWithSigned(serial)
|
val (identity, key) = nodeInfoBuilder.addIdentity(CordaX500Name.build(X500Principal(csr.subject.encoded)), nodeKeyPair)
|
||||||
return Pair(NodeInfoWithSigned(signedNodeInfo), key)
|
storage.putCertificatePath(requestId, identity.certPath, listOf("Test"))
|
||||||
}
|
val (_, signedNodeInfo) = nodeInfoBuilder.buildWithSigned(1)
|
||||||
|
return Pair(NodeInfoWithSigned(signedNodeInfo), key)
|
||||||
}
|
}
|
@ -28,7 +28,7 @@ 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, 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)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.r3.corda.networkmanage.doorman.signer
|
package com.r3.corda.networkmanage.doorman.signer
|
||||||
|
|
||||||
import com.nhaarman.mockito_kotlin.*
|
import com.nhaarman.mockito_kotlin.*
|
||||||
|
import com.r3.corda.networkmanage.TestBase
|
||||||
import com.r3.corda.networkmanage.common.persistence.*
|
import com.r3.corda.networkmanage.common.persistence.*
|
||||||
import com.r3.corda.networkmanage.doorman.ApprovedRequest
|
import com.r3.corda.networkmanage.doorman.ApprovedRequest
|
||||||
import com.r3.corda.networkmanage.doorman.JiraClient
|
import com.r3.corda.networkmanage.doorman.JiraClient
|
||||||
@ -18,7 +19,7 @@ import org.mockito.junit.MockitoRule
|
|||||||
import java.security.cert.CertPath
|
import java.security.cert.CertPath
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class JiraCsrHandlerTest {
|
class JiraCsrHandlerTest : TestBase() {
|
||||||
@Rule
|
@Rule
|
||||||
@JvmField
|
@JvmField
|
||||||
val mockitoRule: MockitoRule = MockitoJUnit.rule()
|
val mockitoRule: MockitoRule = MockitoJUnit.rule()
|
||||||
@ -76,7 +77,11 @@ class JiraCsrHandlerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `create tickets`() {
|
fun `create tickets`() {
|
||||||
val csr = CertificateSigningRequest(requestId, "name", RequestStatus.NEW, pkcS10CertificationRequest, null, emptyList(), null)
|
val csr = certificateSigningRequest(
|
||||||
|
requestId = requestId,
|
||||||
|
legalName = "name",
|
||||||
|
status = RequestStatus.NEW,
|
||||||
|
request = pkcS10CertificationRequest)
|
||||||
whenever(certificationRequestStorage.getRequests(RequestStatus.NEW)).thenReturn(listOf(csr))
|
whenever(certificationRequestStorage.getRequests(RequestStatus.NEW)).thenReturn(listOf(csr))
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
@ -90,8 +95,8 @@ class JiraCsrHandlerTest {
|
|||||||
fun `sync tickets status`() {
|
fun `sync tickets status`() {
|
||||||
val id1 = SecureHash.randomSHA256().toString()
|
val id1 = SecureHash.randomSHA256().toString()
|
||||||
val id2 = SecureHash.randomSHA256().toString()
|
val id2 = SecureHash.randomSHA256().toString()
|
||||||
val csr1 = CertificateSigningRequest(id1, "name1", RequestStatus.NEW, pkcS10CertificationRequest, null, emptyList(), null)
|
val csr1 = CertificateSigningRequest(id1, "name1", SecureHash.randomSHA256(), RequestStatus.NEW, pkcS10CertificationRequest, null, emptyList(), null)
|
||||||
val csr2 = CertificateSigningRequest(id2, "name2", RequestStatus.NEW, pkcS10CertificationRequest, null, emptyList(), null)
|
val csr2 = CertificateSigningRequest(id2, "name2", SecureHash.randomSHA256(), RequestStatus.NEW, pkcS10CertificationRequest, null, emptyList(), null)
|
||||||
|
|
||||||
val requests = mutableMapOf(id1 to csr1, id2 to csr2)
|
val requests = mutableMapOf(id1 to csr1, id2 to csr2)
|
||||||
|
|
||||||
@ -134,7 +139,7 @@ class JiraCsrHandlerTest {
|
|||||||
|
|
||||||
// Sign request 1
|
// Sign request 1
|
||||||
val certPath = mock<CertPath>()
|
val certPath = mock<CertPath>()
|
||||||
val certData = CertificateData("", CertificateStatus.VALID, certPath)
|
val certData = CertificateData(CertificateStatus.VALID, certPath)
|
||||||
requests[id1] = requests[id1]!!.copy(status = RequestStatus.SIGNED, certData = certData)
|
requests[id1] = requests[id1]!!.copy(status = RequestStatus.SIGNED, certData = certData)
|
||||||
|
|
||||||
// Process request again.
|
// Process request again.
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.corda.nodeapi.internal
|
package net.corda.nodeapi.internal
|
||||||
|
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
|
import net.corda.core.crypto.Crypto.generateKeyPair
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.x500Name
|
import net.corda.core.internal.x500Name
|
||||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
import net.corda.nodeapi.internal.config.SSLConfiguration
|
||||||
@ -8,6 +9,7 @@ import net.corda.nodeapi.internal.crypto.*
|
|||||||
import org.bouncycastle.asn1.x509.GeneralName
|
import org.bouncycastle.asn1.x509.GeneralName
|
||||||
import org.bouncycastle.asn1.x509.GeneralSubtree
|
import org.bouncycastle.asn1.x509.GeneralSubtree
|
||||||
import org.bouncycastle.asn1.x509.NameConstraints
|
import org.bouncycastle.asn1.x509.NameConstraints
|
||||||
|
import java.security.KeyPair
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
import javax.security.auth.x500.X500Principal
|
import javax.security.auth.x500.X500Principal
|
||||||
|
|
||||||
@ -31,7 +33,7 @@ fun SSLConfiguration.createDevKeyStores(legalName: CordaX500Name,
|
|||||||
save(nodeKeystore, keyStorePassword)
|
save(nodeKeystore, keyStorePassword)
|
||||||
}
|
}
|
||||||
|
|
||||||
val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val tlsKeyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, legalName.x500Principal, tlsKeyPair.public)
|
val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, legalName.x500Principal, tlsKeyPair.public)
|
||||||
|
|
||||||
loadOrCreateKeyStore(sslKeystore, keyStorePassword).apply {
|
loadOrCreateKeyStore(sslKeystore, keyStorePassword).apply {
|
||||||
@ -59,17 +61,18 @@ fun createDevNetworkMapCa(rootCa: CertificateAndKeyPair = DEV_ROOT_CA): Certific
|
|||||||
* Create a dev node CA cert, as a sub-cert of the given [intermediateCa], and matching key pair using the given
|
* Create a dev node CA cert, as a sub-cert of the given [intermediateCa], and matching key pair using the given
|
||||||
* [CordaX500Name] as the cert subject.
|
* [CordaX500Name] as the cert subject.
|
||||||
*/
|
*/
|
||||||
fun createDevNodeCa(intermediateCa: CertificateAndKeyPair, legalName: CordaX500Name): CertificateAndKeyPair {
|
fun createDevNodeCa(intermediateCa: CertificateAndKeyPair,
|
||||||
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
legalName: CordaX500Name,
|
||||||
|
nodeKeyPair: KeyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)): CertificateAndKeyPair {
|
||||||
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.x500Name))), arrayOf())
|
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.x500Name))), arrayOf())
|
||||||
val cert = X509Utilities.createCertificate(
|
val cert = X509Utilities.createCertificate(
|
||||||
CertificateType.NODE_CA,
|
CertificateType.NODE_CA,
|
||||||
intermediateCa.certificate,
|
intermediateCa.certificate,
|
||||||
intermediateCa.keyPair,
|
intermediateCa.keyPair,
|
||||||
legalName.x500Principal,
|
legalName.x500Principal,
|
||||||
keyPair.public,
|
nodeKeyPair.public,
|
||||||
nameConstraints = nameConstraints)
|
nameConstraints = nameConstraints)
|
||||||
return CertificateAndKeyPair(cert, keyPair)
|
return CertificateAndKeyPair(cert, nodeKeyPair)
|
||||||
}
|
}
|
||||||
|
|
||||||
val DEV_INTERMEDIATE_CA: CertificateAndKeyPair get() = DevCaHelper.loadDevCa(X509Utilities.CORDA_INTERMEDIATE_CA)
|
val DEV_INTERMEDIATE_CA: CertificateAndKeyPair get() = DevCaHelper.loadDevCa(X509Utilities.CORDA_INTERMEDIATE_CA)
|
||||||
|
@ -2,6 +2,7 @@ package net.corda.testing.internal
|
|||||||
|
|
||||||
import com.nhaarman.mockito_kotlin.doAnswer
|
import com.nhaarman.mockito_kotlin.doAnswer
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
|
import net.corda.core.crypto.Crypto.generateKeyPair
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.utilities.loggerFor
|
import net.corda.core.utilities.loggerFor
|
||||||
import net.corda.node.services.config.configureDevKeyAndTrustStores
|
import net.corda.node.services.config.configureDevKeyAndTrustStores
|
||||||
@ -15,6 +16,7 @@ import org.mockito.Mockito
|
|||||||
import org.mockito.internal.stubbing.answers.ThrowsException
|
import org.mockito.internal.stubbing.answers.ThrowsException
|
||||||
import java.lang.reflect.Modifier
|
import java.lang.reflect.Modifier
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
|
import java.security.KeyPair
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.security.auth.x500.X500Principal
|
import javax.security.auth.x500.X500Principal
|
||||||
|
|
||||||
@ -102,11 +104,12 @@ fun createDevIntermediateCaCertPath(
|
|||||||
*/
|
*/
|
||||||
fun createDevNodeCaCertPath(
|
fun createDevNodeCaCertPath(
|
||||||
legalName: CordaX500Name,
|
legalName: CordaX500Name,
|
||||||
|
nodeKeyPair: KeyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME),
|
||||||
rootCaName: X500Principal = defaultRootCaName,
|
rootCaName: X500Principal = defaultRootCaName,
|
||||||
intermediateCaName: X500Principal = defaultIntermediateCaName
|
intermediateCaName: X500Principal = defaultIntermediateCaName
|
||||||
): Triple<CertificateAndKeyPair, CertificateAndKeyPair, CertificateAndKeyPair> {
|
): Triple<CertificateAndKeyPair, CertificateAndKeyPair, CertificateAndKeyPair> {
|
||||||
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath(rootCaName, intermediateCaName)
|
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath(rootCaName, intermediateCaName)
|
||||||
val nodeCa = createDevNodeCa(intermediateCa, legalName)
|
val nodeCa = createDevNodeCa(intermediateCa, legalName, nodeKeyPair)
|
||||||
return Triple(rootCa, intermediateCa, nodeCa)
|
return Triple(rootCa, intermediateCa, nodeCa)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,16 +8,35 @@ import net.corda.core.node.NodeInfo
|
|||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.nodeapi.internal.SignedNodeInfo
|
import net.corda.nodeapi.internal.SignedNodeInfo
|
||||||
import net.corda.testing.getTestPartyAndCertificate
|
import net.corda.nodeapi.internal.createDevNodeCa
|
||||||
|
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||||
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
|
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||||
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
|
import net.corda.testing.DEV_INTERMEDIATE_CA
|
||||||
|
import net.corda.testing.DEV_ROOT_CA
|
||||||
|
import java.security.KeyPair
|
||||||
import java.security.PrivateKey
|
import java.security.PrivateKey
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
|
|
||||||
class TestNodeInfoBuilder {
|
class TestNodeInfoBuilder(private val intermediateAndRoot: Pair<CertificateAndKeyPair, X509Certificate> = DEV_INTERMEDIATE_CA to DEV_ROOT_CA.certificate) {
|
||||||
private val identitiesAndPrivateKeys = ArrayList<Pair<PartyAndCertificate, PrivateKey>>()
|
private val identitiesAndPrivateKeys = ArrayList<Pair<PartyAndCertificate, PrivateKey>>()
|
||||||
|
|
||||||
fun addIdentity(name: CordaX500Name): Pair<PartyAndCertificate, PrivateKey> {
|
fun addIdentity(name: CordaX500Name, nodeKeyPair: KeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)): Pair<PartyAndCertificate, PrivateKey> {
|
||||||
|
val nodeCertificateAndKeyPair = createDevNodeCa(intermediateAndRoot.first, name, nodeKeyPair)
|
||||||
val identityKeyPair = Crypto.generateKeyPair()
|
val identityKeyPair = Crypto.generateKeyPair()
|
||||||
val identity = getTestPartyAndCertificate(name, identityKeyPair.public)
|
val identityCert = X509Utilities.createCertificate(
|
||||||
return Pair(identity, identityKeyPair.private).also {
|
CertificateType.LEGAL_IDENTITY,
|
||||||
|
nodeCertificateAndKeyPair.certificate,
|
||||||
|
nodeCertificateAndKeyPair.keyPair,
|
||||||
|
nodeCertificateAndKeyPair.certificate.subjectX500Principal,
|
||||||
|
identityKeyPair.public)
|
||||||
|
val certPath = X509CertificateFactory()
|
||||||
|
.generateCertPath(identityCert,
|
||||||
|
nodeCertificateAndKeyPair.certificate,
|
||||||
|
intermediateAndRoot.first.certificate,
|
||||||
|
intermediateAndRoot.second)
|
||||||
|
return Pair(PartyAndCertificate(certPath), identityKeyPair.private).also {
|
||||||
identitiesAndPrivateKeys += it
|
identitiesAndPrivateKeys += it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user