Removed all remaining special treatment of the X500 common name.

With network parameters the CN is no longer needed to identify notaries. This frees it up to be used in the node's name alongside the other attributes.

Also, the identity generation logic has been simplified, removing the need to have magic string values for storing distributed identities in the keystore. Now there are just two alias prefixes: "identity" as it was previously, and "distributed-notary".
This commit is contained in:
Shams Asari
2017-11-30 19:02:44 +00:00
parent f7445a5e6a
commit e1e715ee81
30 changed files with 200 additions and 226 deletions

View File

@ -13,7 +13,6 @@ import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.internal.deleteIfExists
import net.corda.core.internal.div
import net.corda.core.node.services.NotaryService
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.NetworkHostAndPort
@ -24,10 +23,10 @@ import net.corda.node.services.config.BFTSMaRtConfiguration
import net.corda.node.services.config.NotaryConfig
import net.corda.node.services.transactions.minClusterSize
import net.corda.node.services.transactions.minCorrectReplicas
import net.corda.nodeapi.internal.ServiceIdentityGenerator
import net.corda.nodeapi.internal.IdentityGenerator
import net.corda.nodeapi.internal.network.NetworkParametersCopier
import net.corda.nodeapi.internal.network.NotaryInfo
import net.corda.testing.chooseIdentity
import net.corda.nodeapi.internal.network.NetworkParametersCopier
import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.contracts.DummyContract
import net.corda.testing.dummyCommand
@ -55,10 +54,9 @@ class BFTNotaryServiceTests {
(Paths.get("config") / "currentView").deleteIfExists() // XXX: Make config object warn if this exists?
val replicaIds = (0 until clusterSize)
notary = ServiceIdentityGenerator.generateToDisk(
notary = IdentityGenerator.generateDistributedNotaryIdentity(
replicaIds.map { mockNet.baseDirectory(mockNet.nextNodeId + it) },
CordaX500Name("BFT", "Zurich", "CH"),
NotaryService.constructId(validating = false, bft = true))
CordaX500Name("BFT", "Zurich", "CH"))
val networkParameters = NetworkParametersCopier(testNetworkParameters(listOf(NotaryInfo(notary, false))))

View File

@ -11,30 +11,30 @@ import net.corda.core.internal.concurrent.map
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.getOrThrow
import net.corda.node.internal.StartedNode
import net.corda.node.services.transactions.RaftValidatingNotaryService
import net.corda.testing.*
import net.corda.testing.DUMMY_BANK_A_NAME
import net.corda.testing.chooseIdentity
import net.corda.testing.contracts.DummyContract
import net.corda.testing.driver.NodeHandle
import net.corda.testing.driver.driver
import net.corda.testing.dummyCommand
import net.corda.testing.node.startFlow
import net.corda.testing.node.ClusterSpec
import net.corda.testing.node.NotarySpec
import net.corda.testing.node.startFlow
import org.junit.Test
import java.util.*
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
class RaftNotaryServiceTests {
private val notaryName = CordaX500Name(RaftValidatingNotaryService.id, "RAFT Notary Service", "London", "GB")
private val notaryName = CordaX500Name("RAFT Notary Service", "London", "GB")
@Test
fun `detect double spend`() {
driver(
startNodesInProcess = true,
extraCordappPackagesToScan = listOf("net.corda.testing.contracts"),
notarySpecs = listOf(NotarySpec(notaryName, cluster = ClusterSpec.Raft(clusterSize = 3))))
{
notarySpecs = listOf(NotarySpec(notaryName, cluster = ClusterSpec.Raft(clusterSize = 3)))
) {
val bankA = startNode(providedName = DUMMY_BANK_A_NAME).map { (it as NodeHandle.InProcess).node }.getOrThrow()
val inputState = issueState(bankA, defaultNotaryIdentity)

View File

@ -15,7 +15,6 @@ import net.corda.core.utilities.seconds
import net.corda.node.internal.Node
import net.corda.node.internal.StartedNode
import net.corda.node.services.messaging.*
import net.corda.node.services.transactions.RaftValidatingNotaryService
import net.corda.testing.ALICE_NAME
import net.corda.testing.chooseIdentity
import net.corda.testing.driver.DriverDSL
@ -32,7 +31,7 @@ import java.util.concurrent.atomic.AtomicInteger
class P2PMessagingTest {
private companion object {
val DISTRIBUTED_SERVICE_NAME = CordaX500Name(RaftValidatingNotaryService.id, "DistributedService", "London", "GB")
val DISTRIBUTED_SERVICE_NAME = CordaX500Name("DistributedService", "London", "GB")
}
@Test

View File

@ -59,8 +59,12 @@ import net.corda.node.services.vault.NodeVaultService
import net.corda.node.services.vault.VaultSoftLockManager
import net.corda.node.shell.InteractiveShell
import net.corda.node.utilities.AffinityExecutor
import net.corda.nodeapi.internal.IdentityGenerator
import net.corda.nodeapi.internal.SignedNodeInfo
import net.corda.nodeapi.internal.crypto.*
import net.corda.nodeapi.internal.crypto.KeyStoreWrapper
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.nodeapi.internal.crypto.loadKeyStore
import net.corda.nodeapi.internal.network.NETWORK_PARAMS_FILE_NAME
import net.corda.nodeapi.internal.network.NetworkParameters
import net.corda.nodeapi.internal.persistence.CordaPersistence
@ -133,25 +137,19 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
protected val services: ServiceHubInternal get() = _services
private lateinit var _services: ServiceHubInternalImpl
protected var myNotaryIdentity: PartyAndCertificate? = null
protected lateinit var checkpointStorage: CheckpointStorage
private lateinit var checkpointStorage: CheckpointStorage
private lateinit var tokenizableServices: List<Any>
protected lateinit var attachments: NodeAttachmentService
protected lateinit var network: MessagingService
protected val runOnStop = ArrayList<() -> Any?>()
protected val _nodeReadyFuture = openFuture<Unit>()
private val _nodeReadyFuture = openFuture<Unit>()
protected var networkMapClient: NetworkMapClient? = null
lateinit var securityManager: RPCSecurityManager get
/** Completes once the node has successfully registered with the network map service
* or has loaded network map data from local database */
val nodeReadyFuture: CordaFuture<Unit>
get() = _nodeReadyFuture
/** A [CordaX500Name] with null common name. */
protected val myLegalName: CordaX500Name by lazy {
val cert = loadKeyStore(configuration.nodeKeystore, configuration.keyStorePassword).getX509Certificate(X509Utilities.CORDA_CLIENT_CA)
CordaX500Name.build(cert.subjectX500Principal).copy(commonName = null)
}
val nodeReadyFuture: CordaFuture<Unit> get() = _nodeReadyFuture
open val serializationWhitelists: List<SerializationWhitelist> by lazy {
cordappLoader.cordapps.flatMap { it.serializationWhitelists }
@ -308,7 +306,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
)
// Check if we have already stored a version of 'our own' NodeInfo, this is to avoid regenerating it with
// a different timestamp.
networkMapCache.getNodesByLegalName(myLegalName).firstOrNull()?.let {
networkMapCache.getNodesByLegalName(configuration.myLegalName).firstOrNull()?.let {
if (info.copy(serial = it.serial) == it) {
info = it
}
@ -728,13 +726,10 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
val (id, singleName) = if (notaryConfig == null || !notaryConfig.isClusterConfig) {
// Node's main identity or if it's a single node notary
Pair("identity", myLegalName)
Pair(IdentityGenerator.NODE_IDENTITY_ALIAS_PREFIX, configuration.myLegalName)
} else {
val notaryId = notaryConfig.run {
NotaryService.constructId(validating, raft != null, bftSMaRt != null, custom)
}
// The node is part of a distributed notary whose identity must already be generated beforehand.
Pair(notaryId, null)
Pair(IdentityGenerator.DISTRIBUTED_NOTARY_ALIAS_PREFIX, null)
}
// TODO: Integrate with Key management service?
val privateKeyAlias = "$id-private-key"
@ -742,7 +737,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
if (!keyStore.containsAlias(privateKeyAlias)) {
singleName ?: throw IllegalArgumentException(
"Unable to find in the key store the identity of the distributed notary ($id) the node is part of")
// TODO: Remove use of [ServiceIdentityGenerator.generateToDisk].
// TODO: Remove use of [IdentityGenerator.generateToDisk].
log.info("$privateKeyAlias not found in key store ${configuration.nodeKeystore}, generating fresh key!")
keyStore.signAndSaveNewKeyPair(singleName, privateKeyAlias, generateKeyPair())
}

View File

@ -56,7 +56,7 @@ fun SSLConfiguration.configureDevKeyAndTrustStores(myLegalName: CordaX500Name) {
val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
createKeystoreForCordaNode(sslKeystore, nodeKeystore, keyStorePassword, keyStorePassword, caKeyStore, "cordacadevkeypass", myLegalName)
// Move distributed service composite key (generated by ServiceIdentityGenerator.generateToDisk) to keystore if exists.
// Move distributed service composite key (generated by IdentityGenerator.generateToDisk) to keystore if exists.
val distributedServiceKeystore = certificatesDirectory / "distributedService.jks"
if (distributedServiceKeystore.exists()) {
val serviceKeystore = loadKeyStore(distributedServiceKeystore, "cordacadevpass")
@ -98,18 +98,17 @@ fun createKeystoreForCordaNode(sslKeyStorePath: Path,
val (intermediateCACert, intermediateCAKeyPair) = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, caKeyPassword)
val clientKey = Crypto.generateKeyPair(signatureScheme)
val clientName = legalName.copy(commonName = null)
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, clientName.x500Name))), arrayOf())
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.x500Name))), arrayOf())
val clientCACert = X509Utilities.createCertificate(CertificateType.NODE_CA,
intermediateCACert,
intermediateCAKeyPair,
clientName.copy(commonName = X509Utilities.CORDA_CLIENT_CA_CN),
legalName,
clientKey.public,
nameConstraints = nameConstraints)
val tlsKey = Crypto.generateKeyPair(signatureScheme)
val clientTLSCert = X509Utilities.createCertificate(CertificateType.TLS, clientCACert, clientKey, clientName, tlsKey.public)
val clientTLSCert = X509Utilities.createCertificate(CertificateType.TLS, clientCACert, clientKey, legalName, tlsKey.public)
val keyPass = keyPassword.toCharArray()

View File

@ -24,6 +24,7 @@ import javax.annotation.concurrent.ThreadSafe
*
* @param identities initial set of identities for the service, typically only used for unit tests.
*/
// TODO There is duplicated logic between this and PersistentIdentityService
@ThreadSafe
class InMemoryIdentityService(identities: Array<out PartyAndCertificate>,
trustRoot: X509CertificateHolder) : SingletonSerializeAsToken(), IdentityServiceInternal {

View File

@ -26,6 +26,7 @@ import javax.persistence.Entity
import javax.persistence.Id
import javax.persistence.Lob
// TODO There is duplicated logic between this and InMemoryIdentityService
@ThreadSafe
class PersistentIdentityService(override val trustRoot: X509Certificate,
vararg caCertificates: X509Certificate) : SingletonSerializeAsToken(), IdentityServiceInternal {

View File

@ -34,12 +34,13 @@ import kotlin.concurrent.thread
*
* A transaction is notarised when the consensus is reached by the cluster on its uniqueness, and time-window validity.
*/
class BFTNonValidatingNotaryService(override val services: ServiceHubInternal,
override val notaryIdentityKey: PublicKey,
private val bftSMaRtConfig: BFTSMaRtConfiguration,
cluster: BFTSMaRt.Cluster) : NotaryService() {
class BFTNonValidatingNotaryService(
override val services: ServiceHubInternal,
override val notaryIdentityKey: PublicKey,
private val bftSMaRtConfig: BFTSMaRtConfiguration,
cluster: BFTSMaRt.Cluster
) : NotaryService() {
companion object {
val id = constructId(validating = false, bft = true)
private val log = contextLogger()
}

View File

@ -8,14 +8,13 @@ import net.corda.core.node.services.TrustedAuthorityNotaryService
import java.security.PublicKey
/** A non-validating notary service operated by a group of mutually trusting parties, uses the Raft algorithm to achieve consensus. */
class RaftNonValidatingNotaryService(override val services: ServiceHub,
override val notaryIdentityKey: PublicKey,
override val uniquenessProvider: RaftUniquenessProvider) : TrustedAuthorityNotaryService() {
companion object {
val id = constructId(validating = false, raft = true)
}
class RaftNonValidatingNotaryService(
override val services: ServiceHub,
override val notaryIdentityKey: PublicKey,
override val uniquenessProvider: RaftUniquenessProvider
) : TrustedAuthorityNotaryService() {
override val timeWindowChecker: TimeWindowChecker = TimeWindowChecker(services.clock)
override fun createServiceFlow(otherPartySession: FlowSession): NotaryFlow.Service {
return NonValidatingNotaryFlow(otherPartySession, this)
}

View File

@ -8,14 +8,13 @@ import net.corda.core.node.services.TrustedAuthorityNotaryService
import java.security.PublicKey
/** A validating notary service operated by a group of mutually trusting parties, uses the Raft algorithm to achieve consensus. */
class RaftValidatingNotaryService(override val services: ServiceHub,
override val notaryIdentityKey: PublicKey,
override val uniquenessProvider: RaftUniquenessProvider) : TrustedAuthorityNotaryService() {
companion object {
val id = constructId(validating = true, raft = true)
}
class RaftValidatingNotaryService(
override val services: ServiceHub,
override val notaryIdentityKey: PublicKey,
override val uniquenessProvider: RaftUniquenessProvider
) : TrustedAuthorityNotaryService() {
override val timeWindowChecker: TimeWindowChecker = TimeWindowChecker(services.clock)
override fun createServiceFlow(otherPartySession: FlowSession): NotaryFlow.Service {
return ValidatingNotaryFlow(otherPartySession, this)
}

View File

@ -9,10 +9,8 @@ import java.security.PublicKey
/** A Notary service that validates the transaction chain of the submitted transaction before committing it */
class ValidatingNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() {
companion object {
val id = constructId(validating = true)
}
override val timeWindowChecker = TimeWindowChecker(services.clock)
override val uniquenessProvider = PersistentUniquenessProvider()
override fun createServiceFlow(otherPartySession: FlowSession): NotaryFlow.Service = ValidatingNotaryFlow(otherPartySession, this)

View File

@ -103,10 +103,9 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v
println("Generating SSL certificate for node messaging service.")
val sslKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val caCert = caKeyStore.getX509Certificate(CORDA_CLIENT_CA).toX509CertHolder()
val sslCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, keyPair, CordaX500Name.build(caCert.cert.subjectX500Principal).copy(commonName = null), sslKey.public)
val sslCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, keyPair, CordaX500Name.build(caCert.cert.subjectX500Principal), sslKey.public)
val sslKeyStore = loadOrCreateKeyStore(config.sslKeystore, keystorePassword)
sslKeyStore.addOrReplaceKey(CORDA_CLIENT_TLS, sslKey.private, privateKeyPassword.toCharArray(),
arrayOf(sslCert.cert, *certificates))
sslKeyStore.addOrReplaceKey(CORDA_CLIENT_TLS, sslKey.private, privateKeyPassword.toCharArray(), arrayOf(sslCert.cert, *certificates))
sslKeyStore.save(config.sslKeystore, config.keyStorePassword)
println("SSL private key and certificate stored in ${config.sslKeystore}.")
// All done, clean up temp files.

View File

@ -659,7 +659,7 @@ class FlowFrameworkTests {
private inline fun <reified P : FlowLogic<*>> StartedNode<MockNode>.restartAndGetRestoredFlow() = internals.run {
disableDBCloseOnStop() // Handover DB to new node copy
stop()
val newNode = mockNet.createNode(MockNodeParameters(id))
val newNode = mockNet.createNode(MockNodeParameters(id, configuration.myLegalName))
newNode.internals.acceptableLiveFiberCountOnStop = 1
manuallyCloseDB()
mockNet.runNetwork()

View File

@ -1,5 +1,7 @@
package net.corda.node.utilities.registration
import com.google.common.jimfs.Configuration.unix
import com.google.common.jimfs.Jimfs
import com.nhaarman.mockito_kotlin.any
import com.nhaarman.mockito_kotlin.doReturn
import com.nhaarman.mockito_kotlin.eq
@ -7,68 +9,61 @@ import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SecureHash
import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.*
import net.corda.core.internal.cert
import net.corda.core.internal.createDirectories
import net.corda.node.services.config.NodeConfiguration
import net.corda.nodeapi.internal.crypto.*
import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.nodeapi.internal.crypto.getX509Certificate
import net.corda.nodeapi.internal.crypto.loadKeyStore
import net.corda.testing.ALICE_NAME
import net.corda.testing.internal.rigorousMock
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import java.security.cert.Certificate
import kotlin.test.assertEquals
import java.security.cert.X509Certificate
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class NetworkRegistrationHelperTest {
@Rule
@JvmField
val tempFolder = TemporaryFolder()
private val fs = Jimfs.newFileSystem(unix())
private val requestId = SecureHash.randomSHA256().toString()
private val nodeLegalName = ALICE_NAME
private val intermediateCaName = CordaX500Name("CORDA_INTERMEDIATE_CA", "R3 Ltd", "London", "GB")
private val rootCaName = CordaX500Name("CORDA_ROOT_CA", "R3 Ltd", "London", "GB")
private val nodeCaCert = createCaCert(nodeLegalName)
private val intermediateCaCert = createCaCert(intermediateCaName)
private val rootCaCert = createCaCert(rootCaName)
private lateinit var config: NodeConfiguration
private val identities = listOf("CORDA_CLIENT_CA",
"CORDA_INTERMEDIATE_CA",
"CORDA_ROOT_CA")
.map { CordaX500Name(commonName = it, organisation = "R3 Ltd", locality = "London", country = "GB") }
private val certs = identities.map { X509Utilities.createSelfSignedCACertificate(it, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) }
.map { it.cert }.toTypedArray()
private val certService = mockRegistrationResponse(*certs)
@Before
fun init() {
val baseDirectory = fs.getPath("/baseDir").createDirectories()
abstract class AbstractNodeConfiguration : NodeConfiguration
config = rigorousMock<AbstractNodeConfiguration>().also {
doReturn(tempFolder.root.toPath()).whenever(it).baseDirectory
doReturn(baseDirectory).whenever(it).baseDirectory
doReturn("trustpass").whenever(it).trustStorePassword
doReturn("cordacadevpass").whenever(it).keyStorePassword
doReturn(ALICE_NAME).whenever(it).myLegalName
doReturn(nodeLegalName).whenever(it).myLegalName
doReturn("").whenever(it).emailAddress
}
}
@After
fun cleanUp() {
fs.close()
}
@Test
fun `successful registration`() {
assertFalse(config.nodeKeystore.exists())
assertFalse(config.sslKeystore.exists())
config.trustStoreFile.parent.createDirectories()
loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also {
it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, certs.last())
it.save(config.trustStoreFile, config.trustStorePassword)
}
assertThat(config.nodeKeystore).doesNotExist()
assertThat(config.sslKeystore).doesNotExist()
assertThat(config.trustStoreFile).doesNotExist()
NetworkRegistrationHelper(config, certService).buildKeystore()
saveTrustStoreWithRootCa(rootCaCert)
assertTrue(config.nodeKeystore.exists())
assertTrue(config.sslKeystore.exists())
assertTrue(config.trustStoreFile.exists())
createRegistrationHelper().buildKeystore()
val nodeKeystore = loadKeyStore(config.nodeKeystore, config.keyStorePassword)
val sslKeystore = loadKeyStore(config.sslKeystore, config.keyStorePassword)
@ -79,9 +74,8 @@ class NetworkRegistrationHelperTest {
assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA))
assertFalse(containsAlias(X509Utilities.CORDA_ROOT_CA))
assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_TLS))
val certificateChain = getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
assertEquals(3, certificateChain.size)
assertEquals(listOf("CORDA_CLIENT_CA", "CORDA_INTERMEDIATE_CA", "CORDA_ROOT_CA"), certificateChain.map { it.toX509CertHolder().subject.commonName })
val nodeCaCertChain = getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
assertThat(nodeCaCertChain).containsExactly(nodeCaCert, intermediateCaCert, rootCaCert)
}
sslKeystore.run {
@ -89,46 +83,55 @@ class NetworkRegistrationHelperTest {
assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA))
assertFalse(containsAlias(X509Utilities.CORDA_ROOT_CA))
assertTrue(containsAlias(X509Utilities.CORDA_CLIENT_TLS))
val certificateChain = getCertificateChain(X509Utilities.CORDA_CLIENT_TLS)
assertEquals(4, certificateChain.size)
assertEquals(listOf(CordaX500Name(organisation = "R3 Ltd", locality = "London", country = "GB").x500Name) + identities.map { it.x500Name },
certificateChain.map { it.toX509CertHolder().subject })
assertEquals(CordaX500Name(organisation = "R3 Ltd", locality = "London", country = "GB").x500Principal,
getX509Certificate(X509Utilities.CORDA_CLIENT_TLS).subjectX500Principal)
val nodeTlsCertChain = getCertificateChain(X509Utilities.CORDA_CLIENT_TLS)
assertThat(nodeTlsCertChain).hasSize(4)
// The TLS cert has the same subject as the node CA cert
assertThat(CordaX500Name.build((nodeTlsCertChain[0] as X509Certificate).subjectX500Principal)).isEqualTo(nodeLegalName)
assertThat(nodeTlsCertChain.drop(1)).containsExactly(nodeCaCert, intermediateCaCert, rootCaCert)
}
trustStore.run {
assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_CA))
assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA))
assertTrue(containsAlias(X509Utilities.CORDA_ROOT_CA))
val trustStoreRootCaCert = getCertificate(X509Utilities.CORDA_ROOT_CA)
assertThat(trustStoreRootCaCert).isEqualTo(rootCaCert)
}
}
@Test
fun `missing truststore`() {
assertThatThrownBy {
NetworkRegistrationHelper(config, certService).buildKeystore()
createRegistrationHelper()
}.hasMessageContaining("This file must contain the root CA cert of your compatibility zone. Please contact your CZ operator.")
.isInstanceOf(IllegalArgumentException::class.java)
}
@Test
fun `wrong root cert in truststore`() {
val someCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name("Foo", "MU", "GB"), Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)).cert
config.trustStoreFile.parent.createDirectories()
loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also {
it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, someCert)
it.save(config.trustStoreFile, config.trustStorePassword)
}
saveTrustStoreWithRootCa(createCaCert(CordaX500Name("Foo", "MU", "GB")))
val registrationHelper = createRegistrationHelper()
assertThatThrownBy {
NetworkRegistrationHelper(config, certService).buildKeystore()
registrationHelper.buildKeystore()
}.isInstanceOf(WrongRootCertException::class.java)
}
private fun mockRegistrationResponse(vararg response: Certificate): NetworkRegistrationService {
return rigorousMock<NetworkRegistrationService>().also {
private fun createRegistrationHelper(): NetworkRegistrationHelper {
val certService = rigorousMock<NetworkRegistrationService>().also {
doReturn(requestId).whenever(it).submitRequest(any())
doReturn(response).whenever(it).retrieveCertificates(eq(requestId))
doReturn(arrayOf<Certificate>(nodeCaCert, intermediateCaCert, rootCaCert)).whenever(it).retrieveCertificates(eq(requestId))
}
return NetworkRegistrationHelper(config, certService)
}
private fun saveTrustStoreWithRootCa(rootCa: X509Certificate) {
config.trustStoreFile.parent.createDirectories()
loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also {
it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCa)
it.save(config.trustStoreFile, config.trustStorePassword)
}
}
private fun createCaCert(name: CordaX500Name): X509Certificate {
return X509Utilities.createSelfSignedCACertificate(name, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)).cert
}
}