mirror of
https://github.com/corda/corda.git
synced 2025-06-18 15:18:16 +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:
@ -20,6 +20,7 @@ abstract class TestBase {
|
||||
requestId: String = SecureHash.randomSHA256().toString(),
|
||||
status: RequestStatus = RequestStatus.NEW,
|
||||
legalName: String = "TestLegalName",
|
||||
publicKeyHash: SecureHash = SecureHash.randomSHA256(),
|
||||
remark: String = "Test remark",
|
||||
request: PKCS10CertificationRequest = mock(),
|
||||
certData: CertificateData = mock(),
|
||||
@ -29,6 +30,7 @@ abstract class TestBase {
|
||||
requestId = requestId,
|
||||
status = status,
|
||||
legalName = legalName,
|
||||
publicKeyHash = publicKeyHash,
|
||||
remark = remark,
|
||||
certData = certData,
|
||||
request = request,
|
||||
@ -36,11 +38,9 @@ abstract class TestBase {
|
||||
)
|
||||
}
|
||||
|
||||
protected fun certificateData(publicKeyHash: String = SecureHash.randomSHA256().toString(),
|
||||
certStatus: CertificateStatus = CertificateStatus.VALID,
|
||||
protected fun certificateData(certStatus: CertificateStatus = CertificateStatus.VALID,
|
||||
certPath: CertPath = mock()): CertificateData {
|
||||
return CertificateData(
|
||||
publicKeyHash = publicKeyHash,
|
||||
certStatus = certStatus,
|
||||
certPath = certPath
|
||||
)
|
||||
|
@ -19,6 +19,7 @@ import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.security.KeyPair
|
||||
import java.security.cert.CertPath
|
||||
import java.util.*
|
||||
import javax.security.auth.x500.X500Principal
|
||||
import kotlin.test.*
|
||||
@ -69,7 +70,7 @@ class PersistentCertificateRequestStorageTest : TestBase() {
|
||||
|
||||
@Test
|
||||
fun `sign request`() {
|
||||
val (csr, _) = createRequest("LegalName")
|
||||
val (csr, nodeKeyPair) = createRequest("LegalName")
|
||||
// Add request to DB.
|
||||
val requestId = storage.saveRequest(csr)
|
||||
// New request should equals to 1.
|
||||
@ -86,11 +87,7 @@ class PersistentCertificateRequestStorageTest : TestBase() {
|
||||
// Sign certificate
|
||||
storage.putCertificatePath(
|
||||
requestId,
|
||||
JcaPKCS10CertificationRequest(csr).run {
|
||||
// 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)
|
||||
},
|
||||
generateSignedCertPath(csr, nodeKeyPair),
|
||||
listOf(DOORMAN_SIGNATURE)
|
||||
)
|
||||
// Check request is ready
|
||||
@ -99,33 +96,51 @@ class PersistentCertificateRequestStorageTest : TestBase() {
|
||||
|
||||
@Test
|
||||
fun `sign request ignores subsequent sign requests`() {
|
||||
val (csr, _) = createRequest("LegalName")
|
||||
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,
|
||||
JcaPKCS10CertificationRequest(csr).run {
|
||||
val (rootCa, intermediateCa, nodeCa) = createDevNodeCaCertPath(CordaX500Name.build(X500Principal(subject.encoded)))
|
||||
buildCertPath(nodeCa.certificate, intermediateCa.certificate, rootCa.certificate)
|
||||
},
|
||||
generateSignedCertPath(csr, nodeKeyPair),
|
||||
listOf(DOORMAN_SIGNATURE)
|
||||
)
|
||||
// Sign certificate
|
||||
// When subsequent signature requested
|
||||
assertFailsWith(IllegalArgumentException::class) {
|
||||
storage.putCertificatePath(
|
||||
requestId,
|
||||
JcaPKCS10CertificationRequest(csr).run {
|
||||
val (rootCa, intermediateCa, nodeCa) = createDevNodeCaCertPath(CordaX500Name.build(X500Principal(subject.encoded)))
|
||||
buildCertPath(nodeCa.certificate, intermediateCa.certificate, rootCa.certificate)
|
||||
},
|
||||
generateSignedCertPath(csr, nodeKeyPair),
|
||||
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
|
||||
fun `reject request`() {
|
||||
val requestId = storage.saveRequest(createRequest("BankA").first)
|
||||
@ -159,18 +174,14 @@ class PersistentCertificateRequestStorageTest : TestBase() {
|
||||
|
||||
@Test
|
||||
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)
|
||||
storage.markRequestTicketCreated(requestId)
|
||||
storage.approveRequest(requestId, DOORMAN_SIGNATURE)
|
||||
// Sign certificate
|
||||
storage.putCertificatePath(
|
||||
requestId,
|
||||
JcaPKCS10CertificationRequest(csr).run {
|
||||
// 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)
|
||||
},
|
||||
generateSignedCertPath(csr, nodeKeyPair),
|
||||
listOf(DOORMAN_SIGNATURE)
|
||||
)
|
||||
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 {
|
||||
val props = Properties()
|
||||
props.setProperty("dataSourceClassName", "org.h2.jdbcx.JdbcDataSource")
|
||||
@ -225,8 +244,7 @@ class PersistentCertificateRequestStorageTest : TestBase() {
|
||||
}
|
||||
}
|
||||
|
||||
internal fun createRequest(organisation: String): Pair<PKCS10CertificationRequest, KeyPair> {
|
||||
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
internal fun createRequest(organisation: String, keyPair: KeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)): Pair<PKCS10CertificationRequest, KeyPair> {
|
||||
val request = X509Utilities.createCertificateSigningRequest(X500Principal("O=$organisation,L=London,C=GB"), "my@mail.com", keyPair)
|
||||
return Pair(request, keyPair)
|
||||
}
|
||||
}
|
@ -5,13 +5,11 @@ import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.signWithCert
|
||||
import net.corda.nodeapi.internal.createDevNetworkMapCa
|
||||
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.verifiedNetworkMapCert
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.internal.TestNodeInfoBuilder
|
||||
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
@ -50,7 +48,7 @@ class PersistentNetworkMapStorageTest : TestBase() {
|
||||
fun `saveNetworkMap and saveNetworkParameters create current network map and parameters`() {
|
||||
// given
|
||||
// Create node info.
|
||||
val signedNodeInfo = createValidSignedNodeInfo("Test")
|
||||
val (signedNodeInfo) = createValidSignedNodeInfo("Test", requestStorage)
|
||||
val nodeInfoHash = nodeInfoStorage.putNodeInfo(signedNodeInfo)
|
||||
|
||||
val networkParameters = testNetworkParameters(emptyList())
|
||||
@ -117,8 +115,8 @@ class PersistentNetworkMapStorageTest : TestBase() {
|
||||
fun `getValidNodeInfoHashes returns only valid and signed node info hashes`() {
|
||||
// given
|
||||
// Create node infos.
|
||||
val signedNodeInfoA = createValidSignedNodeInfo("TestA")
|
||||
val signedNodeInfoB = createValidSignedNodeInfo("TestB")
|
||||
val (signedNodeInfoA) = createValidSignedNodeInfo("TestA", requestStorage)
|
||||
val (signedNodeInfoB) = createValidSignedNodeInfo("TestB", requestStorage)
|
||||
|
||||
// Put signed node info data
|
||||
val nodeInfoHashA = nodeInfoStorage.putNodeInfo(signedNodeInfoA)
|
||||
@ -139,15 +137,4 @@ class PersistentNetworkMapStorageTest : TestBase() {
|
||||
// then
|
||||
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.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.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
@ -23,6 +22,7 @@ import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.security.PrivateKey
|
||||
import java.security.cert.X509Certificate
|
||||
import javax.security.auth.x500.X500Principal
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertNull
|
||||
@ -83,8 +83,8 @@ class PersistentNodeInfoStorageTest : TestBase() {
|
||||
@Test
|
||||
fun `getNodeInfo returns persisted SignedNodeInfo using the hash of just the NodeInfo`() {
|
||||
// given
|
||||
val (nodeA) = createValidSignedNodeInfo("TestA")
|
||||
val (nodeB) = createValidSignedNodeInfo("TestB")
|
||||
val (nodeA) = createValidSignedNodeInfo("TestA", requestStorage)
|
||||
val (nodeB) = createValidSignedNodeInfo("TestB", requestStorage)
|
||||
|
||||
// Put signed node info data
|
||||
nodeInfoStorage.putNodeInfo(nodeA)
|
||||
@ -102,7 +102,7 @@ class PersistentNodeInfoStorageTest : TestBase() {
|
||||
@Test
|
||||
fun `same public key with different 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 node2 = NodeInfoWithSigned(nodeInfo2.signWith(listOf(key)))
|
||||
|
||||
@ -120,7 +120,7 @@ class PersistentNodeInfoStorageTest : TestBase() {
|
||||
@Test
|
||||
fun `putNodeInfo persists SignedNodeInfo with its signature`() {
|
||||
// given
|
||||
val (nodeInfoWithSigned) = createValidSignedNodeInfo("Test")
|
||||
val (nodeInfoWithSigned) = createValidSignedNodeInfo("Test", requestStorage)
|
||||
|
||||
// when
|
||||
val nodeInfoHash = nodeInfoStorage.putNodeInfo(nodeInfoWithSigned)
|
||||
@ -129,16 +129,17 @@ class PersistentNodeInfoStorageTest : TestBase() {
|
||||
val persistedSignedNodeInfo = nodeInfoStorage.getNodeInfo(nodeInfoHash)
|
||||
assertThat(persistedSignedNodeInfo?.signatures).isEqualTo(nodeInfoWithSigned.signedNodeInfo.signatures)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createValidSignedNodeInfo(organisation: String, serial: Long = 1): Pair<NodeInfoWithSigned, PrivateKey> {
|
||||
val nodeInfoBuilder = TestNodeInfoBuilder()
|
||||
val requestId = requestStorage.saveRequest(createRequest(organisation).first)
|
||||
requestStorage.markRequestTicketCreated(requestId)
|
||||
requestStorage.approveRequest(requestId, "TestUser")
|
||||
val (identity, key) = nodeInfoBuilder.addIdentity(CordaX500Name(organisation, "London", "GB"))
|
||||
val nodeCaCertPath = X509CertificateFactory().generateCertPath(identity.certPath.certificates.drop(1))
|
||||
requestStorage.putCertificatePath(requestId, nodeCaCertPath, emptyList())
|
||||
val (_, signedNodeInfo) = nodeInfoBuilder.buildWithSigned(serial)
|
||||
return Pair(NodeInfoWithSigned(signedNodeInfo), key)
|
||||
}
|
||||
internal fun createValidSignedNodeInfo(organisation: String,
|
||||
storage: CertificationRequestStorage): Pair<NodeInfoWithSigned, PrivateKey> {
|
||||
val (csr, nodeKeyPair) = createRequest(organisation)
|
||||
val requestId = storage.saveRequest(csr)
|
||||
storage.markRequestTicketCreated(requestId)
|
||||
storage.approveRequest(requestId, "TestUser")
|
||||
val nodeInfoBuilder = TestNodeInfoBuilder()
|
||||
val (identity, key) = nodeInfoBuilder.addIdentity(CordaX500Name.build(X500Principal(csr.subject.encoded)), nodeKeyPair)
|
||||
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 {
|
||||
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"))
|
||||
}
|
||||
val requestProcessor = DefaultCsrHandler(requestStorage, null)
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.r3.corda.networkmanage.doorman.signer
|
||||
|
||||
import com.nhaarman.mockito_kotlin.*
|
||||
import com.r3.corda.networkmanage.TestBase
|
||||
import com.r3.corda.networkmanage.common.persistence.*
|
||||
import com.r3.corda.networkmanage.doorman.ApprovedRequest
|
||||
import com.r3.corda.networkmanage.doorman.JiraClient
|
||||
@ -18,7 +19,7 @@ import org.mockito.junit.MockitoRule
|
||||
import java.security.cert.CertPath
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class JiraCsrHandlerTest {
|
||||
class JiraCsrHandlerTest : TestBase() {
|
||||
@Rule
|
||||
@JvmField
|
||||
val mockitoRule: MockitoRule = MockitoJUnit.rule()
|
||||
@ -76,7 +77,11 @@ class JiraCsrHandlerTest {
|
||||
|
||||
@Test
|
||||
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))
|
||||
|
||||
// Test
|
||||
@ -90,8 +95,8 @@ class JiraCsrHandlerTest {
|
||||
fun `sync tickets status`() {
|
||||
val id1 = SecureHash.randomSHA256().toString()
|
||||
val id2 = SecureHash.randomSHA256().toString()
|
||||
val csr1 = CertificateSigningRequest(id1, "name1", RequestStatus.NEW, pkcS10CertificationRequest, null, emptyList(), null)
|
||||
val csr2 = CertificateSigningRequest(id2, "name2", RequestStatus.NEW, pkcS10CertificationRequest, null, emptyList(), null)
|
||||
val csr1 = CertificateSigningRequest(id1, "name1", SecureHash.randomSHA256(), 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)
|
||||
|
||||
@ -134,7 +139,7 @@ class JiraCsrHandlerTest {
|
||||
|
||||
// Sign request 1
|
||||
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)
|
||||
|
||||
// Process request again.
|
||||
|
Reference in New Issue
Block a user