Validating the entire cert path in node registration, rather just checking the root cert. (#2298)

Also reduced duplicate code when creating the node CA cert path for testing, and renamed IdentityGenerator to DevIdentityGenerator.
This commit is contained in:
Shams Asari 2017-12-29 14:38:30 +00:00 committed by GitHub
parent 39d25958e2
commit 4a2f157118
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 205 additions and 189 deletions

View File

@ -1,9 +1,9 @@
package net.corda.core.crypto package net.corda.core.crypto
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.toTypedArray
import net.corda.core.internal.cert import net.corda.core.internal.cert
import net.corda.nodeapi.internal.crypto.* import net.corda.nodeapi.internal.crypto.*
import net.corda.testing.internal.createDevIntermediateCaCertPath
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.asn1.x509.GeneralName import org.bouncycastle.asn1.x509.GeneralName
import org.bouncycastle.asn1.x509.GeneralSubtree import org.bouncycastle.asn1.x509.GeneralSubtree
@ -11,35 +11,40 @@ import org.bouncycastle.asn1.x509.NameConstraints
import org.bouncycastle.jce.provider.BouncyCastleProvider import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.junit.Test import org.junit.Test
import java.security.KeyStore import java.security.KeyStore
import java.security.cert.* import java.security.cert.CertPathValidator
import java.util.stream.Stream import java.security.cert.CertPathValidatorException
import java.security.cert.PKIXParameters
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
import kotlin.test.assertTrue import kotlin.test.assertTrue
class X509NameConstraintsTest { class X509NameConstraintsTest {
private fun makeKeyStores(subjectName: X500Name, nameConstraints: NameConstraints): Pair<KeyStore, KeyStore> { private fun makeKeyStores(subjectName: X500Name, nameConstraints: NameConstraints): Pair<KeyStore, KeyStore> {
val rootKeys = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Root CA", organisation = "R3 Ltd", locality = "London", country = "GB"), rootKeys) val nodeCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val nodeCaCert = X509Utilities.createCertificate(
val intermediateCAKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) CertificateType.NODE_CA,
val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootKeys, CordaX500Name(commonName = "Corda Intermediate CA", organisation = "R3 Ltd", locality = "London", country = "GB"), intermediateCAKeyPair.public) intermediateCa.certificate,
intermediateCa.keyPair,
val clientCAKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) CordaX500Name("Corda Client CA", "R3 Ltd", "London", "GB"),
val clientCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, intermediateCACert, intermediateCAKeyPair, CordaX500Name(commonName = "Corda Client CA", organisation = "R3 Ltd", locality = "London", country = "GB"), clientCAKeyPair.public, nameConstraints = nameConstraints) nodeCaKeyPair.public,
nameConstraints = nameConstraints)
val keyPass = "password" val keyPass = "password"
val trustStore = KeyStore.getInstance(KEYSTORE_TYPE) val trustStore = KeyStore.getInstance(KEYSTORE_TYPE)
trustStore.load(null, keyPass.toCharArray()) trustStore.load(null, keyPass.toCharArray())
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCACert.cert) trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCa.certificate.cert)
val tlsKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, clientCACert, clientCAKeyPair, subjectName, tlsKey.public) val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, subjectName, tlsKeyPair.public)
val keyStore = KeyStore.getInstance(KEYSTORE_TYPE) val keyStore = KeyStore.getInstance(KEYSTORE_TYPE)
keyStore.load(null, keyPass.toCharArray()) keyStore.load(null, keyPass.toCharArray())
keyStore.addOrReplaceKey(X509Utilities.CORDA_CLIENT_TLS, tlsKey.private, keyPass.toCharArray(), keyStore.addOrReplaceKey(
Stream.of(tlsCert, clientCACert, intermediateCACert, rootCACert).map { it.cert }.toTypedArray<Certificate>()) X509Utilities.CORDA_CLIENT_TLS,
tlsKeyPair.private,
keyPass.toCharArray(),
arrayOf(tlsCert, nodeCaCert, intermediateCa.certificate, rootCa.certificate))
return Pair(keyStore, trustStore) return Pair(keyStore, trustStore)
} }

View File

@ -19,10 +19,9 @@ import java.security.cert.X509Certificate
/** /**
* Contains utility methods for generating identities for a node. * Contains utility methods for generating identities for a node.
* *
* WARNING: This is not application for production use and must never called by the node. * WARNING: This is not application for production use.
*/ */
// TODO Rename to DevIdentityGenerator object DevIdentityGenerator {
object IdentityGenerator {
private val log = LoggerFactory.getLogger(javaClass) private val log = LoggerFactory.getLogger(javaClass)
// TODO These don't need to be prefixes but can be the full aliases // TODO These don't need to be prefixes but can be the full aliases

View File

@ -15,14 +15,7 @@ import org.bouncycastle.cert.X509CertificateHolder
* the given legal name), and the SSL key store will store the TLS cert which is a sub-cert of the node CA. * the given legal name), and the SSL key store will store the TLS cert which is a sub-cert of the node CA.
*/ */
fun SSLConfiguration.createDevKeyStores(rootCert: X509CertificateHolder, intermediateCa: CertificateAndKeyPair, legalName: CordaX500Name) { fun SSLConfiguration.createDevKeyStores(rootCert: X509CertificateHolder, intermediateCa: CertificateAndKeyPair, legalName: CordaX500Name) {
val nodeCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val (nodeCaCert, nodeCaKeyPair) = createDevNodeCa(intermediateCa, legalName)
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.x500Name))), arrayOf())
val nodeCaCert = X509Utilities.createCertificate(CertificateType.NODE_CA,
intermediateCa.certificate,
intermediateCa.keyPair,
legalName,
nodeCaKeyPair.public,
nameConstraints = nameConstraints)
loadOrCreateKeyStore(nodeKeystore, keyStorePassword).apply { loadOrCreateKeyStore(nodeKeystore, keyStorePassword).apply {
addOrReplaceKey( addOrReplaceKey(
@ -45,3 +38,20 @@ fun SSLConfiguration.createDevKeyStores(rootCert: X509CertificateHolder, interme
save(sslKeystore, keyStorePassword) save(sslKeystore, keyStorePassword)
} }
} }
/**
* 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.
*/
fun createDevNodeCa(intermediateCa: CertificateAndKeyPair, legalName: CordaX500Name): CertificateAndKeyPair {
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.x500Name))), arrayOf())
val cert = X509Utilities.createCertificate(
CertificateType.NODE_CA,
intermediateCa.certificate,
intermediateCa.keyPair,
legalName,
keyPair.public,
nameConstraints = nameConstraints)
return CertificateAndKeyPair(cert, keyPair)
}

View File

@ -21,6 +21,7 @@ import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1
import net.corda.testing.ALICE_NAME import net.corda.testing.ALICE_NAME
import net.corda.testing.BOB_NAME import net.corda.testing.BOB_NAME
import net.corda.testing.TestIdentity import net.corda.testing.TestIdentity
import net.corda.testing.internal.createDevIntermediateCaCertPath
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.asn1.x509.BasicConstraints import org.bouncycastle.asn1.x509.BasicConstraints
@ -169,10 +170,10 @@ class X509UtilitiesTest {
override val trustStorePassword = "trustpass" override val trustStorePassword = "trustpass"
} }
val (rootCert, intermediateCa) = createRootCertAndIntermediateCa() val (rootCert, intermediateCa) = createDevIntermediateCaCertPath()
// Generate server cert and private key and populate another keystore suitable for SSL // Generate server cert and private key and populate another keystore suitable for SSL
sslConfig.createDevKeyStores(rootCert, intermediateCa, MEGA_CORP.name) sslConfig.createDevKeyStores(rootCert.certificate, intermediateCa, MEGA_CORP.name)
// Load back server certificate // Load back server certificate
val serverKeyStore = loadKeyStore(sslConfig.nodeKeystore, sslConfig.keyStorePassword) val serverKeyStore = loadKeyStore(sslConfig.nodeKeystore, sslConfig.keyStorePassword)
@ -205,11 +206,11 @@ class X509UtilitiesTest {
override val trustStorePassword = "trustpass" override val trustStorePassword = "trustpass"
} }
val (rootCert, intermediateCa) = createRootCertAndIntermediateCa() val (rootCert, intermediateCa) = createDevIntermediateCaCertPath()
// Generate server cert and private key and populate another keystore suitable for SSL // Generate server cert and private key and populate another keystore suitable for SSL
sslConfig.createDevKeyStores(rootCert, intermediateCa, MEGA_CORP.name) sslConfig.createDevKeyStores(rootCert.certificate, intermediateCa, MEGA_CORP.name)
sslConfig.createTrustStore(rootCert.cert) sslConfig.createTrustStore(rootCert.certificate.cert)
val keyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword) val keyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword)
val trustStore = loadKeyStore(sslConfig.trustStoreFile, sslConfig.trustStorePassword) val trustStore = loadKeyStore(sslConfig.trustStoreFile, sslConfig.trustStorePassword)
@ -293,22 +294,6 @@ class X509UtilitiesTest {
private fun tempFile(name: String): Path = tempFolder.root.toPath() / name private fun tempFile(name: String): Path = tempFolder.root.toPath() / name
private fun createRootCertAndIntermediateCa(): Pair<X509CertificateHolder, CertificateAndKeyPair> {
val rootKeyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val baseName = CordaX500Name(organisation = "R3 Ltd", locality = "London", country = "GB")
val rootCert = X509Utilities.createSelfSignedCACertificate(baseName.copy(commonName = "Corda Root CA"), rootKeyPair)
val intermediateCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val intermediateCaCert = X509Utilities.createCertificate(
CertificateType.INTERMEDIATE_CA,
rootCert,
rootKeyPair,
baseName.copy(commonName = "Corda Intermediate CA"),
intermediateCaKeyPair.public)
return Pair(rootCert, CertificateAndKeyPair(intermediateCaCert, intermediateCaKeyPair))
}
private fun SSLConfiguration.createTrustStore(rootCert: X509Certificate) { private fun SSLConfiguration.createTrustStore(rootCert: X509Certificate) {
val trustStore = loadOrCreateKeyStore(trustStoreFile, trustStorePassword) val trustStore = loadOrCreateKeyStore(trustStoreFile, trustStorePassword)
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCert) trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCert)

View File

@ -10,23 +10,23 @@ import net.corda.nodeapi.internal.config.SSLConfiguration
import net.corda.nodeapi.internal.crypto.* import net.corda.nodeapi.internal.crypto.*
import net.corda.testing.ALICE_NAME import net.corda.testing.ALICE_NAME
import net.corda.testing.driver.driver import net.corda.testing.driver.driver
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Test import org.junit.Test
import java.nio.file.Path import java.nio.file.Path
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertTrue
class NodeKeystoreCheckTest { class NodeKeystoreCheckTest {
@Test
fun `starting node in non-dev mode with no key store`() {
driver(startNodesInProcess = true) {
assertThatThrownBy {
startNode(customOverrides = mapOf("devMode" to false)).getOrThrow()
}.hasMessageContaining("Identity certificate not found")
}
}
@Test @Test
fun `node should throw exception if cert path doesn't chain to the trust root`() { fun `node should throw exception if cert path doesn't chain to the trust root`() {
driver(startNodesInProcess = true) { driver(startNodesInProcess = true) {
// This will fail because there are no keystore configured.
assertFailsWith(IllegalArgumentException::class) {
startNode(customOverrides = mapOf("devMode" to false)).getOrThrow()
}.apply {
assertTrue(message?.startsWith("Identity certificate not found. ") ?: false)
}
// Create keystores // Create keystores
val keystorePassword = "password" val keystorePassword = "password"
val config = object : SSLConfiguration { val config = object : SSLConfiguration {
@ -37,9 +37,12 @@ class NodeKeystoreCheckTest {
config.configureDevKeyAndTrustStores(ALICE_NAME) config.configureDevKeyAndTrustStores(ALICE_NAME)
// This should pass with correct keystore. // This should pass with correct keystore.
val node = startNode(providedName = ALICE_NAME, customOverrides = mapOf("devMode" to false, val node = startNode(
"keyStorePassword" to keystorePassword, providedName = ALICE_NAME,
"trustStorePassword" to keystorePassword)).get() customOverrides = mapOf("devMode" to false,
"keyStorePassword" to keystorePassword,
"trustStorePassword" to keystorePassword)
).getOrThrow()
node.stop() node.stop()
// Fiddle with node keystore. // Fiddle with node keystore.
@ -53,11 +56,9 @@ class NodeKeystoreCheckTest {
keystore.setKeyEntry(X509Utilities.CORDA_CLIENT_CA, nodeCA.keyPair.private, config.keyStorePassword.toCharArray(), arrayOf(badNodeCACert.cert, badRoot.cert)) keystore.setKeyEntry(X509Utilities.CORDA_CLIENT_CA, nodeCA.keyPair.private, config.keyStorePassword.toCharArray(), arrayOf(badNodeCACert.cert, badRoot.cert))
keystore.save(config.nodeKeystore, config.keyStorePassword) keystore.save(config.nodeKeystore, config.keyStorePassword)
assertFailsWith(IllegalArgumentException::class) { assertThatThrownBy {
startNode(providedName = ALICE_NAME, customOverrides = mapOf("devMode" to false)).getOrThrow() startNode(providedName = ALICE_NAME, customOverrides = mapOf("devMode" to false)).getOrThrow()
}.apply { }.hasMessage("Client CA certificate must chain to the trusted root.")
assertEquals("Client CA certificate must chain to the trusted root.", message)
}
} }
} }
} }

View File

@ -23,7 +23,7 @@ import net.corda.node.services.config.BFTSMaRtConfiguration
import net.corda.node.services.config.NotaryConfig import net.corda.node.services.config.NotaryConfig
import net.corda.node.services.transactions.minClusterSize import net.corda.node.services.transactions.minClusterSize
import net.corda.node.services.transactions.minCorrectReplicas import net.corda.node.services.transactions.minCorrectReplicas
import net.corda.nodeapi.internal.IdentityGenerator import net.corda.nodeapi.internal.DevIdentityGenerator
import net.corda.nodeapi.internal.network.NetworkParametersCopier import net.corda.nodeapi.internal.network.NetworkParametersCopier
import net.corda.nodeapi.internal.network.NotaryInfo import net.corda.nodeapi.internal.network.NotaryInfo
import net.corda.testing.chooseIdentity import net.corda.testing.chooseIdentity
@ -60,7 +60,7 @@ class BFTNotaryServiceTests {
(Paths.get("config") / "currentView").deleteIfExists() // XXX: Make config object warn if this exists? (Paths.get("config") / "currentView").deleteIfExists() // XXX: Make config object warn if this exists?
val replicaIds = (0 until clusterSize) val replicaIds = (0 until clusterSize)
notary = IdentityGenerator.generateDistributedNotaryIdentity( notary = DevIdentityGenerator.generateDistributedNotaryIdentity(
replicaIds.map { mockNet.baseDirectory(mockNet.nextNodeId + it) }, replicaIds.map { mockNet.baseDirectory(mockNet.nextNodeId + it) },
CordaX500Name("BFT", "Zurich", "CH")) CordaX500Name("BFT", "Zurich", "CH"))

View File

@ -6,7 +6,10 @@ import net.corda.core.internal.cert
import net.corda.core.internal.concurrent.transpose import net.corda.core.internal.concurrent.transpose
import net.corda.core.internal.toX509CertHolder import net.corda.core.internal.toX509CertHolder
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
import net.corda.core.utilities.* import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.minutes
import net.corda.finance.DOLLARS import net.corda.finance.DOLLARS
import net.corda.finance.flows.CashIssueAndPaymentFlow import net.corda.finance.flows.CashIssueAndPaymentFlow
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
@ -16,7 +19,6 @@ import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_CA import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_CA
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_INTERMEDIATE_CA import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_INTERMEDIATE_CA
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA
import net.corda.nodeapi.internal.network.NotaryInfo
import net.corda.testing.ROOT_CA import net.corda.testing.ROOT_CA
import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.SerializationEnvironmentRule
import net.corda.testing.driver.PortAllocation import net.corda.testing.driver.PortAllocation
@ -25,7 +27,6 @@ import net.corda.testing.node.internal.CompatibilityZoneParams
import net.corda.testing.node.internal.internalDriver import net.corda.testing.node.internal.internalDriver
import net.corda.testing.node.internal.network.NetworkMapServer import net.corda.testing.node.internal.network.NetworkMapServer
import net.corda.testing.singleIdentity import net.corda.testing.singleIdentity
import net.corda.testing.singleIdentityAndCert
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy import org.assertj.core.api.Assertions.assertThatThrownBy
import org.bouncycastle.pkcs.PKCS10CertificationRequest import org.bouncycastle.pkcs.PKCS10CertificationRequest
@ -39,6 +40,7 @@ import java.io.InputStream
import java.net.URL import java.net.URL
import java.security.KeyPair import java.security.KeyPair
import java.security.cert.CertPath import java.security.cert.CertPath
import java.security.cert.CertPathValidatorException
import java.security.cert.Certificate import java.security.cert.Certificate
import java.util.zip.ZipEntry import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream import java.util.zip.ZipOutputStream
@ -50,8 +52,10 @@ class NodeRegistrationTest {
@Rule @Rule
@JvmField @JvmField
val testSerialization = SerializationEnvironmentRule(true) val testSerialization = SerializationEnvironmentRule(true)
private val portAllocation = PortAllocation.Incremental(13000) private val portAllocation = PortAllocation.Incremental(13000)
private val registrationHandler = RegistrationHandler(ROOT_CA) private val registrationHandler = RegistrationHandler(ROOT_CA)
private lateinit var server: NetworkMapServer private lateinit var server: NetworkMapServer
private lateinit var serverHostAndPort: NetworkHostAndPort private lateinit var serverHostAndPort: NetworkHostAndPort
@ -73,72 +77,63 @@ class NodeRegistrationTest {
portAllocation = portAllocation, portAllocation = portAllocation,
compatibilityZone = compatibilityZone, compatibilityZone = compatibilityZone,
initialiseSerialization = false, initialiseSerialization = false,
notarySpecs = listOf(NotarySpec(CordaX500Name(organisation = "NotaryService", locality = "Zurich", country = "CH"), validating = false)), notarySpecs = listOf(NotarySpec(CordaX500Name("NotaryService", "Zurich", "CH"), validating = false)),
extraCordappPackagesToScan = listOf("net.corda.finance"), extraCordappPackagesToScan = listOf("net.corda.finance"),
onNetworkParametersGeneration = { server.networkParameters = it } onNetworkParametersGeneration = { server.networkParameters = it }
) { ) {
val notary = defaultNotaryNode.get() val aliceName = "Alice"
val genevieveName = "Genevieve"
val ALICE_NAME = "Alice" val nodes = listOf(
val GENEVIEVE_NAME = "Genevieve" startNode(providedName = CordaX500Name(aliceName, "London", "GB")),
val nodesFutures = listOf(startNode(providedName = CordaX500Name(ALICE_NAME, "London", "GB")), startNode(providedName = CordaX500Name(genevieveName, "London", "GB")),
startNode(providedName = CordaX500Name(GENEVIEVE_NAME, "London", "GB"))) defaultNotaryNode
).transpose().getOrThrow()
val (alice, genevieve) = nodes
val (alice, genevieve) = nodesFutures.transpose().get() assertThat(registrationHandler.idsPolled).contains(aliceName, genevieveName)
val nodes = listOf(alice, genevieve, notary)
assertThat(registrationHandler.idsPolled).contains(ALICE_NAME, GENEVIEVE_NAME)
// Notary identities are generated beforehand hence notary nodes don't go through registration. // Notary identities are generated beforehand hence notary nodes don't go through registration.
// This test isn't specifically testing this, or relying on this behavior, though if this check fail, // This test isn't specifically testing this, or relying on this behavior, though if this check fail,
// this will probably lead to the rest of the test to fail. // this will probably lead to the rest of the test to fail.
assertThat(registrationHandler.idsPolled).doesNotContain("NotaryService") assertThat(registrationHandler.idsPolled).doesNotContain("NotaryService")
// Check each node has each other identity in their network map cache. // Check each node has each other identity in their network map cache.
val nodeIdentities = nodes.map { it.nodeInfo.singleIdentity() }
for (node in nodes) { for (node in nodes) {
assertThat(node.rpc.networkMapSnapshot().map { it.singleIdentity() }).containsAll(nodeIdentities) assertThat(node.rpc.networkMapSnapshot()).containsOnlyElementsOf(nodes.map { it.nodeInfo })
} }
// Check we nodes communicate among themselves (and the notary). // Check we nodes communicate among themselves (and the notary).
val anonymous = false val anonymous = false
genevieve.rpc.startFlow(::CashIssueAndPaymentFlow, 1000.DOLLARS, OpaqueBytes.of(12), genevieve.rpc.startFlow(
::CashIssueAndPaymentFlow,
1000.DOLLARS,
OpaqueBytes.of(12),
alice.nodeInfo.singleIdentity(), alice.nodeInfo.singleIdentity(),
anonymous, anonymous,
notary.nodeInfo.singleIdentity()) defaultNotaryIdentity
.returnValue ).returnValue.getOrThrow()
.getOrThrow()
} }
} }
@Test @Test
fun `node registration wrong root cert`() { fun `node registration wrong root cert`() {
val someCert = createSelfKeyAndSelfSignedCertificate().certificate.cert val someRootCert = X509Utilities.createSelfSignedCACertificate(
val compatibilityZone = CompatibilityZoneParams(URL("http://$serverHostAndPort"), rootCert = someCert) CordaX500Name("Integration Test Corda Node Root CA", "R3 Ltd", "London", "GB"),
Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
val compatibilityZone = CompatibilityZoneParams(URL("http://$serverHostAndPort"), rootCert = someRootCert.cert)
internalDriver( internalDriver(
portAllocation = portAllocation, portAllocation = portAllocation,
notarySpecs = emptyList(), notarySpecs = emptyList(),
compatibilityZone = compatibilityZone, compatibilityZone = compatibilityZone,
initialiseSerialization = false, initialiseSerialization = false,
// Changing the content of the truststore makes the node fail in a number of ways if started out process. startNodesInProcess = true // We need to run the nodes in the same process so that we can capture the correct exception
startNodesInProcess = true
) { ) {
assertThatThrownBy { assertThatThrownBy {
startNode(providedName = CordaX500Name("Alice", "London", "GB")).getOrThrow() startNode(providedName = CordaX500Name("Alice", "London", "GB")).getOrThrow()
}.isInstanceOf(WrongRootCertException::class.java) }.isInstanceOf(CertPathValidatorException::class.java)
} }
} }
private fun createSelfKeyAndSelfSignedCertificate(): CertificateAndKeyPair {
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val rootCACert = X509Utilities.createSelfSignedCACertificate(
CordaX500Name(
commonName = "Integration Test Corda Node Root CA",
organisation = "R3 Ltd",
locality = "London",
country = "GB"),
rootCAKey)
return CertificateAndKeyPair(rootCACert, rootCAKey)
}
} }
@Path("certificate") @Path("certificate")
@ -185,13 +180,14 @@ class RegistrationHandler(private val rootCertAndKeyPair: CertificateAndKeyPair)
caCertPath: Array<Certificate>): Pair<CertPath, CordaX500Name> { caCertPath: Array<Certificate>): Pair<CertPath, CordaX500Name> {
val request = JcaPKCS10CertificationRequest(certificationRequest) val request = JcaPKCS10CertificationRequest(certificationRequest)
val name = CordaX500Name.parse(request.subject.toString()) val name = CordaX500Name.parse(request.subject.toString())
val x509CertificateHolder = X509Utilities.createCertificate(CertificateType.NODE_CA, val nodeCaCert = X509Utilities.createCertificate(
CertificateType.NODE_CA,
caCertPath.first().toX509CertHolder(), caCertPath.first().toX509CertHolder(),
caKeyPair, caKeyPair,
name, name,
request.publicKey, request.publicKey,
nameConstraints = null) nameConstraints = null)
val certPath = X509CertificateFactory().generateCertPath(x509CertificateHolder.cert, *caCertPath) val certPath = X509CertificateFactory().generateCertPath(nodeCaCert.cert, *caCertPath)
return Pair(certPath, name) return Pair(certPath, name)
} }
} }

View File

@ -59,7 +59,7 @@ import net.corda.node.services.vault.NodeVaultService
import net.corda.node.services.vault.VaultSoftLockManager import net.corda.node.services.vault.VaultSoftLockManager
import net.corda.node.shell.InteractiveShell import net.corda.node.shell.InteractiveShell
import net.corda.node.utilities.AffinityExecutor import net.corda.node.utilities.AffinityExecutor
import net.corda.nodeapi.internal.IdentityGenerator import net.corda.nodeapi.internal.DevIdentityGenerator
import net.corda.nodeapi.internal.SignedNodeInfo import net.corda.nodeapi.internal.SignedNodeInfo
import net.corda.nodeapi.internal.crypto.KeyStoreWrapper import net.corda.nodeapi.internal.crypto.KeyStoreWrapper
import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509CertificateFactory
@ -726,10 +726,10 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
val (id, singleName) = if (notaryConfig == null || !notaryConfig.isClusterConfig) { val (id, singleName) = if (notaryConfig == null || !notaryConfig.isClusterConfig) {
// Node's main identity or if it's a single node notary // Node's main identity or if it's a single node notary
Pair(IdentityGenerator.NODE_IDENTITY_ALIAS_PREFIX, configuration.myLegalName) Pair(DevIdentityGenerator.NODE_IDENTITY_ALIAS_PREFIX, configuration.myLegalName)
} else { } else {
// The node is part of a distributed notary whose identity must already be generated beforehand. // The node is part of a distributed notary whose identity must already be generated beforehand.
Pair(IdentityGenerator.DISTRIBUTED_NOTARY_ALIAS_PREFIX, null) Pair(DevIdentityGenerator.DISTRIBUTED_NOTARY_ALIAS_PREFIX, null)
} }
// TODO: Integrate with Key management service? // TODO: Integrate with Key management service?
val privateKeyAlias = "$id-private-key" val privateKeyAlias = "$id-private-key"

View File

@ -12,10 +12,10 @@ import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA
import org.bouncycastle.openssl.jcajce.JcaPEMWriter import org.bouncycastle.openssl.jcajce.JcaPEMWriter
import org.bouncycastle.util.io.pem.PemObject import org.bouncycastle.util.io.pem.PemObject
import java.io.StringWriter import java.io.StringWriter
import java.nio.file.Path
import java.security.KeyPair import java.security.KeyPair
import java.security.KeyStore import java.security.KeyStore
import java.security.cert.Certificate import java.security.cert.Certificate
import java.security.cert.X509Certificate
/** /**
* Helper for managing the node registration process, which checks for any existing certificates and requests them if * Helper for managing the node registration process, which checks for any existing certificates and requests them if
@ -32,7 +32,7 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v
// TODO: Use different password for private key. // TODO: Use different password for private key.
private val privateKeyPassword = config.keyStorePassword private val privateKeyPassword = config.keyStorePassword
private val trustStore: KeyStore private val trustStore: KeyStore
private val rootCert: Certificate private val rootCert: X509Certificate
init { init {
require(config.trustStoreFile.exists()) { require(config.trustStoreFile.exists()) {
@ -46,7 +46,7 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v
"This file must contain the root CA cert of your compatibility zone. " + "This file must contain the root CA cert of your compatibility zone. " +
"Please contact your CZ operator." "Please contact your CZ operator."
} }
this.rootCert = rootCert this.rootCert = rootCert as X509Certificate
} }
/** /**
@ -94,12 +94,8 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v
caKeyStore.save(config.nodeKeystore, keystorePassword) caKeyStore.save(config.nodeKeystore, keystorePassword)
println("Node private key and certificate stored in ${config.nodeKeystore}.") println("Node private key and certificate stored in ${config.nodeKeystore}.")
// TODO This should actually be using X509Utilities.validateCertificateChain println("Checking root of the certificate path is what we expect.")
// Check that the root of the signed certificate matches the expected certificate in the truststore. X509Utilities.validateCertificateChain(rootCert, *certificates)
if (rootCert != certificates.last()) {
// Assumes certificate chain always starts with client certificate and end with root certificate.
throw WrongRootCertException(rootCert, certificates.last(), config.trustStoreFile)
}
println("Generating SSL certificate for node messaging service.") println("Generating SSL certificate for node messaging service.")
val sslKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val sslKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
@ -169,17 +165,3 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v
} }
} }
} }
/**
* Exception thrown when the doorman root certificate doesn't match the expected (out-of-band) root certificate.
* This usually means that there has been a Man-in-the-middle attack when contacting the doorman.
*/
class WrongRootCertException(expected: Certificate,
actual: Certificate,
expectedFilePath: Path):
Exception("""
The Root CA returned back from the registration process does not match the expected Root CA
expected: $expected
actual: $actual
the expected certificate is stored in: $expectedFilePath with alias $CORDA_ROOT_CA
""".trimMargin())

View File

@ -14,12 +14,14 @@ import net.corda.core.internal.createDirectories
import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NodeConfiguration
import net.corda.nodeapi.internal.crypto.* import net.corda.nodeapi.internal.crypto.*
import net.corda.testing.ALICE_NAME import net.corda.testing.ALICE_NAME
import net.corda.testing.internal.createDevNodeCaCertPath
import net.corda.testing.internal.rigorousMock import net.corda.testing.internal.rigorousMock
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import java.security.cert.CertPathValidatorException
import java.security.cert.Certificate import java.security.cert.Certificate
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import kotlin.test.assertFalse import kotlin.test.assertFalse
@ -29,16 +31,19 @@ class NetworkRegistrationHelperTest {
private val fs = Jimfs.newFileSystem(unix()) private val fs = Jimfs.newFileSystem(unix())
private val requestId = SecureHash.randomSHA256().toString() private val requestId = SecureHash.randomSHA256().toString()
private val nodeLegalName = ALICE_NAME 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 rootCaCert: X509Certificate
private lateinit var intermediateCaCert: X509Certificate
private lateinit var nodeCaCert: X509Certificate
private lateinit var config: NodeConfiguration private lateinit var config: NodeConfiguration
@Before @Before
fun init() { fun init() {
val (rootCa, intermediateCa, nodeCa) = createDevNodeCaCertPath(nodeLegalName)
this.rootCaCert = rootCa.certificate.cert
this.intermediateCaCert = intermediateCa.certificate.cert
this.nodeCaCert = nodeCa.certificate.cert
val baseDirectory = fs.getPath("/baseDir").createDirectories() val baseDirectory = fs.getPath("/baseDir").createDirectories()
abstract class AbstractNodeConfiguration : NodeConfiguration abstract class AbstractNodeConfiguration : NodeConfiguration
config = rigorousMock<AbstractNodeConfiguration>().also { config = rigorousMock<AbstractNodeConfiguration>().also {
@ -108,11 +113,13 @@ class NetworkRegistrationHelperTest {
@Test @Test
fun `wrong root cert in truststore`() { fun `wrong root cert in truststore`() {
saveTrustStoreWithRootCa(createCaCert(CordaX500Name("Foo", "MU", "GB"))) val rootKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val rootCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name("Foo", "MU", "GB"), rootKeyPair)
saveTrustStoreWithRootCa(rootCert.cert)
val registrationHelper = createRegistrationHelper() val registrationHelper = createRegistrationHelper()
assertThatThrownBy { assertThatThrownBy {
registrationHelper.buildKeystore() registrationHelper.buildKeystore()
}.isInstanceOf(WrongRootCertException::class.java) }.isInstanceOf(CertPathValidatorException::class.java)
} }
private fun createRegistrationHelper(): NetworkRegistrationHelper { private fun createRegistrationHelper(): NetworkRegistrationHelper {
@ -123,15 +130,11 @@ class NetworkRegistrationHelperTest {
return NetworkRegistrationHelper(config, certService) return NetworkRegistrationHelper(config, certService)
} }
private fun saveTrustStoreWithRootCa(rootCa: X509Certificate) { private fun saveTrustStoreWithRootCa(rootCert: X509Certificate) {
config.trustStoreFile.parent.createDirectories() config.certificatesDirectory.createDirectories()
loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also { loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also {
it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCa) it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCert)
it.save(config.trustStoreFile, config.trustStorePassword) it.save(config.trustStoreFile, config.trustStorePassword)
} }
} }
private fun createCaCert(name: CordaX500Name): X509Certificate {
return X509Utilities.createSelfSignedCACertificate(name, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)).cert
}
} }

View File

@ -8,7 +8,7 @@ import net.corda.core.utilities.NetworkHostAndPort
import net.corda.node.services.config.BFTSMaRtConfiguration import net.corda.node.services.config.BFTSMaRtConfiguration
import net.corda.node.services.config.NotaryConfig import net.corda.node.services.config.NotaryConfig
import net.corda.node.services.transactions.minCorrectReplicas import net.corda.node.services.transactions.minCorrectReplicas
import net.corda.nodeapi.internal.IdentityGenerator import net.corda.nodeapi.internal.DevIdentityGenerator
import net.corda.testing.node.internal.demorun.* import net.corda.testing.node.internal.demorun.*
import net.corda.testing.ALICE_NAME import net.corda.testing.ALICE_NAME
import net.corda.testing.BOB_NAME import net.corda.testing.BOB_NAME
@ -62,7 +62,7 @@ class BFTNotaryCordform : CordformDefinition() {
} }
override fun setup(context: CordformContext) { override fun setup(context: CordformContext) {
IdentityGenerator.generateDistributedNotaryIdentity( DevIdentityGenerator.generateDistributedNotaryIdentity(
notaryNames.map { context.baseDirectory(it.toString()) }, notaryNames.map { context.baseDirectory(it.toString()) },
clusterName, clusterName,
minCorrectReplicas(clusterSize) minCorrectReplicas(clusterSize)

View File

@ -7,7 +7,7 @@ import net.corda.core.identity.CordaX500Name
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.node.services.config.NotaryConfig import net.corda.node.services.config.NotaryConfig
import net.corda.node.services.config.RaftConfig import net.corda.node.services.config.RaftConfig
import net.corda.nodeapi.internal.IdentityGenerator import net.corda.nodeapi.internal.DevIdentityGenerator
import net.corda.testing.node.internal.demorun.* import net.corda.testing.node.internal.demorun.*
import net.corda.testing.ALICE_NAME import net.corda.testing.ALICE_NAME
import net.corda.testing.BOB_NAME import net.corda.testing.BOB_NAME
@ -58,7 +58,7 @@ class RaftNotaryCordform : CordformDefinition() {
} }
override fun setup(context: CordformContext) { override fun setup(context: CordformContext) {
IdentityGenerator.generateDistributedNotaryIdentity( DevIdentityGenerator.generateDistributedNotaryIdentity(
notaryNames.map { context.baseDirectory(it.toString()) }, notaryNames.map { context.baseDirectory(it.toString()) },
clusterName clusterName
) )

View File

@ -38,7 +38,7 @@ import net.corda.node.services.transactions.BFTSMaRt
import net.corda.node.services.transactions.InMemoryTransactionVerifierService import net.corda.node.services.transactions.InMemoryTransactionVerifierService
import net.corda.node.utilities.AffinityExecutor import net.corda.node.utilities.AffinityExecutor
import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor
import net.corda.nodeapi.internal.IdentityGenerator import net.corda.nodeapi.internal.DevIdentityGenerator
import net.corda.nodeapi.internal.config.User import net.corda.nodeapi.internal.config.User
import net.corda.nodeapi.internal.network.NetworkParametersCopier import net.corda.nodeapi.internal.network.NetworkParametersCopier
import net.corda.nodeapi.internal.network.NotaryInfo import net.corda.nodeapi.internal.network.NotaryInfo
@ -235,7 +235,7 @@ open class MockNetwork(private val cordappPackages: List<String>,
private fun generateNotaryIdentities(): List<NotaryInfo> { private fun generateNotaryIdentities(): List<NotaryInfo> {
return notarySpecs.mapIndexed { index, (name, validating) -> return notarySpecs.mapIndexed { index, (name, validating) ->
val identity = IdentityGenerator.installKeyStoreWithNodeIdentity(baseDirectory(nextNodeId + index), name) val identity = DevIdentityGenerator.installKeyStoreWithNodeIdentity(baseDirectory(nextNodeId + index), name)
NotaryInfo(identity, validating) NotaryInfo(identity, validating)
} }
} }

View File

@ -10,10 +10,12 @@ import net.corda.cordform.CordformContext
import net.corda.cordform.CordformNode import net.corda.cordform.CordformNode
import net.corda.core.concurrent.CordaFuture import net.corda.core.concurrent.CordaFuture
import net.corda.core.concurrent.firstOf import net.corda.core.concurrent.firstOf
import net.corda.core.crypto.random63BitValue
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.* import net.corda.core.internal.ThreadBox
import net.corda.core.internal.concurrent.* import net.corda.core.internal.concurrent.*
import net.corda.core.internal.copyTo
import net.corda.core.internal.createDirectories
import net.corda.core.internal.div
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.core.node.services.NetworkMapCache import net.corda.core.node.services.NetworkMapCache
import net.corda.core.toFuture import net.corda.core.toFuture
@ -28,7 +30,7 @@ import net.corda.node.services.Permissions
import net.corda.node.services.config.* import net.corda.node.services.config.*
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
import net.corda.node.utilities.registration.NetworkRegistrationHelper import net.corda.node.utilities.registration.NetworkRegistrationHelper
import net.corda.nodeapi.internal.IdentityGenerator import net.corda.nodeapi.internal.DevIdentityGenerator
import net.corda.nodeapi.internal.addShutdownHook import net.corda.nodeapi.internal.addShutdownHook
import net.corda.nodeapi.internal.config.User import net.corda.nodeapi.internal.config.User
import net.corda.nodeapi.internal.config.parseAs import net.corda.nodeapi.internal.config.parseAs
@ -37,13 +39,13 @@ import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.nodeapi.internal.crypto.addOrReplaceCertificate import net.corda.nodeapi.internal.crypto.addOrReplaceCertificate
import net.corda.nodeapi.internal.crypto.loadOrCreateKeyStore import net.corda.nodeapi.internal.crypto.loadOrCreateKeyStore
import net.corda.nodeapi.internal.crypto.save import net.corda.nodeapi.internal.crypto.save
import net.corda.nodeapi.internal.network.NetworkParameters
import net.corda.nodeapi.internal.network.NetworkParametersCopier import net.corda.nodeapi.internal.network.NetworkParametersCopier
import net.corda.nodeapi.internal.network.NodeInfoFilesCopier import net.corda.nodeapi.internal.network.NodeInfoFilesCopier
import net.corda.nodeapi.internal.network.NotaryInfo import net.corda.nodeapi.internal.network.NotaryInfo
import net.corda.testing.ALICE_NAME import net.corda.testing.ALICE_NAME
import net.corda.testing.BOB_NAME import net.corda.testing.BOB_NAME
import net.corda.testing.DUMMY_BANK_A_NAME import net.corda.testing.DUMMY_BANK_A_NAME
import net.corda.nodeapi.internal.network.NetworkParameters
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.driver.* import net.corda.testing.driver.*
import net.corda.testing.node.ClusterSpec import net.corda.testing.node.ClusterSpec
@ -66,7 +68,7 @@ import java.nio.file.StandardCopyOption
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import java.time.Duration import java.time.Duration
import java.time.Instant import java.time.Instant
import java.time.ZoneOffset import java.time.ZoneOffset.UTC
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import java.util.* import java.util.*
import java.util.concurrent.Executors import java.util.concurrent.Executors
@ -266,13 +268,13 @@ class DriverDSLImpl(
clusterNodes.put(ClusterType.NON_VALIDATING_BFT, name) clusterNodes.put(ClusterType.NON_VALIDATING_BFT, name)
} else { } else {
// We have all we need here to generate the identity for single node notaries // We have all we need here to generate the identity for single node notaries
val identity = IdentityGenerator.installKeyStoreWithNodeIdentity(baseDirectory(name), legalName = name) val identity = DevIdentityGenerator.installKeyStoreWithNodeIdentity(baseDirectory(name), legalName = name)
notaryInfos += NotaryInfo(identity, notaryConfig.validating) notaryInfos += NotaryInfo(identity, notaryConfig.validating)
} }
} }
clusterNodes.asMap().forEach { type, nodeNames -> clusterNodes.asMap().forEach { type, nodeNames ->
val identity = IdentityGenerator.generateDistributedNotaryIdentity( val identity = DevIdentityGenerator.generateDistributedNotaryIdentity(
dirs = nodeNames.map { baseDirectory(it) }, dirs = nodeNames.map { baseDirectory(it) },
notaryName = type.clusterName notaryName = type.clusterName
) )
@ -360,9 +362,9 @@ class DriverDSLImpl(
private fun generateNotaryIdentities(): List<NotaryInfo> { private fun generateNotaryIdentities(): List<NotaryInfo> {
return notarySpecs.map { spec -> return notarySpecs.map { spec ->
val identity = if (spec.cluster == null) { val identity = if (spec.cluster == null) {
IdentityGenerator.installKeyStoreWithNodeIdentity(baseDirectory(spec.name), spec.name, compatibilityZone?.rootCert) DevIdentityGenerator.installKeyStoreWithNodeIdentity(baseDirectory(spec.name), spec.name, compatibilityZone?.rootCert)
} else { } else {
IdentityGenerator.generateDistributedNotaryIdentity( DevIdentityGenerator.generateDistributedNotaryIdentity(
dirs = generateNodeNames(spec).map { baseDirectory(it) }, dirs = generateNodeNames(spec).map { baseDirectory(it) },
notaryName = spec.name, notaryName = spec.name,
customRootCert = compatibilityZone?.rootCert customRootCert = compatibilityZone?.rootCert
@ -890,8 +892,7 @@ fun <A> internalDriver(
} }
fun getTimestampAsDirectoryName(): String { fun getTimestampAsDirectoryName(): String {
// Add a random number in case 2 tests are started in the same instant. return DateTimeFormatter.ofPattern("yyyyMMdd-HHmmss.SSS").withZone(UTC).format(Instant.now())
return DateTimeFormatter.ofPattern("yyyyMMddHHmmss").withZone(ZoneOffset.UTC).format(Instant.now()) + random63BitValue()
} }
fun writeConfig(path: Path, filename: String, config: Config) { fun writeConfig(path: Path, filename: String, config: Config) {

View File

@ -1,26 +1,21 @@
package net.corda.testing.node.internal.network package net.corda.testing.node.internal.network
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.SignedData
import net.corda.core.crypto.sha256
import net.corda.core.identity.CordaX500Name
import net.corda.core.crypto.* import net.corda.core.crypto.*
import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.cert import net.corda.core.internal.cert
import net.corda.core.internal.toX509CertHolder import net.corda.core.internal.toX509CertHolder
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.deserialize import net.corda.core.serialization.deserialize
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.crypto.CertificateAndKeyPair
import net.corda.nodeapi.internal.crypto.CertificateType
import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.nodeapi.internal.network.DigitalSignatureWithCert import net.corda.nodeapi.internal.network.DigitalSignatureWithCert
import net.corda.nodeapi.internal.network.NetworkMap import net.corda.nodeapi.internal.network.NetworkMap
import net.corda.nodeapi.internal.network.NetworkParameters import net.corda.nodeapi.internal.network.NetworkParameters
import net.corda.nodeapi.internal.network.SignedNetworkMap import net.corda.nodeapi.internal.network.SignedNetworkMap
import net.corda.nodeapi.internal.*
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
import net.corda.nodeapi.internal.crypto.CertificateType
import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.testing.ROOT_CA import net.corda.testing.ROOT_CA
import org.eclipse.jetty.server.Server import org.eclipse.jetty.server.Server
import org.eclipse.jetty.server.ServerConnector import org.eclipse.jetty.server.ServerConnector
@ -50,7 +45,7 @@ class NetworkMapServer(cacheTimeout: Duration,
private fun networkMapKeyAndCert(rootCAKeyAndCert: CertificateAndKeyPair): CertificateAndKeyPair { private fun networkMapKeyAndCert(rootCAKeyAndCert: CertificateAndKeyPair): CertificateAndKeyPair {
val networkMapKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val networkMapKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val networkMapCert = X509Utilities.createCertificate( val networkMapCert = X509Utilities.createCertificate(
CertificateType.INTERMEDIATE_CA, CertificateType.NETWORK_MAP,
rootCAKeyAndCert.certificate, rootCAKeyAndCert.certificate,
rootCAKeyAndCert.keyPair, rootCAKeyAndCert.keyPair,
CordaX500Name("Corda Network Map", "R3 Ltd", "London","GB"), CordaX500Name("Corda Network Map", "R3 Ltd", "London","GB"),

View File

@ -5,24 +5,24 @@ package net.corda.testing
import net.corda.core.contracts.PartyAndReference import net.corda.core.contracts.PartyAndReference
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.crypto.* import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.entropyToKeyPair
import net.corda.core.crypto.generateKeyPair
import net.corda.core.crypto.toStringShort
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate import net.corda.core.identity.PartyAndCertificate
import net.corda.core.internal.cert import net.corda.core.internal.cert
import net.corda.core.internal.unspecifiedCountry import net.corda.core.internal.unspecifiedCountry
import net.corda.core.internal.x500Name
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.node.services.config.configureDevKeyAndTrustStores import net.corda.node.services.config.configureDevKeyAndTrustStores
import net.corda.nodeapi.internal.config.SSLConfiguration import net.corda.nodeapi.internal.config.SSLConfiguration
import net.corda.nodeapi.internal.createDevNodeCa
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.X509CertificateFactory
import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities
import org.bouncycastle.asn1.x509.GeneralName
import org.bouncycastle.asn1.x509.GeneralSubtree
import org.bouncycastle.asn1.x509.NameConstraints
import org.bouncycastle.cert.X509CertificateHolder import org.bouncycastle.cert.X509CertificateHolder
import java.math.BigInteger import java.math.BigInteger
import java.nio.file.Files import java.nio.file.Files
@ -92,15 +92,7 @@ fun getTestPartyAndCertificate(party: Party): PartyAndCertificate {
val trustRoot: X509CertificateHolder = DEV_TRUST_ROOT val trustRoot: X509CertificateHolder = DEV_TRUST_ROOT
val intermediate: CertificateAndKeyPair = DEV_CA val intermediate: CertificateAndKeyPair = DEV_CA
val (nodeCaCert, nodeCaKeyPair) = createDevNodeCa(intermediate, party.name)
val nodeCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val nodeCaCert = X509Utilities.createCertificate(
CertificateType.NODE_CA,
intermediate.certificate,
intermediate.keyPair,
party.name,
nodeCaKeyPair.public,
nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, party.name.x500Name))), arrayOf()))
val identityCert = X509Utilities.createCertificate( val identityCert = X509Utilities.createCertificate(
CertificateType.LEGAL_IDENTITY, CertificateType.LEGAL_IDENTITY,

View File

@ -1,6 +1,12 @@
package net.corda.testing.internal package net.corda.testing.internal
import net.corda.core.crypto.Crypto
import net.corda.core.identity.CordaX500Name
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
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.X509Utilities
import net.corda.nodeapi.internal.serialization.amqp.AMQP_ENABLED import net.corda.nodeapi.internal.serialization.amqp.AMQP_ENABLED
import org.mockito.Mockito import org.mockito.Mockito
import org.mockito.internal.stubbing.answers.ThrowsException import org.mockito.internal.stubbing.answers.ThrowsException
@ -42,3 +48,44 @@ fun <T> rigorousMock(clazz: Class<T>): T = Mockito.mock(clazz) {
it.callRealMethod() it.callRealMethod()
} }
} }
private val defaultRootCaName = CordaX500Name("Corda Root CA", "R3 Ltd", "London", "GB")
private val defaultIntermediateCaName = CordaX500Name("Corda Intermediate CA", "R3 Ltd", "London", "GB")
/**
* Returns a pair of [CertificateAndKeyPair]s, the first being the root CA and the second the intermediate CA.
* @param rootCaName The subject name for the root CA cert.
* @param intermediateCaName The subject name for the intermediate CA cert.
*/
fun createDevIntermediateCaCertPath(
rootCaName: CordaX500Name = defaultRootCaName,
intermediateCaName: CordaX500Name = defaultIntermediateCaName
): Pair<CertificateAndKeyPair, CertificateAndKeyPair> {
val rootKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val rootCert = X509Utilities.createSelfSignedCACertificate(rootCaName, rootKeyPair)
val intermediateCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val intermediateCaCert = X509Utilities.createCertificate(
CertificateType.INTERMEDIATE_CA,
rootCert,
rootKeyPair,
intermediateCaName,
intermediateCaKeyPair.public)
return Pair(CertificateAndKeyPair(rootCert, rootKeyPair), CertificateAndKeyPair(intermediateCaCert, intermediateCaKeyPair))
}
/**
* Returns a triple of [CertificateAndKeyPair]s, the first being the root CA, the second the intermediate CA and the third
* the node CA.
* @param legalName The subject name for the node CA cert.
*/
fun createDevNodeCaCertPath(
legalName: CordaX500Name,
rootCaName: CordaX500Name = defaultRootCaName,
intermediateCaName: CordaX500Name = defaultIntermediateCaName
): Triple<CertificateAndKeyPair, CertificateAndKeyPair, CertificateAndKeyPair> {
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath(rootCaName, intermediateCaName)
val nodeCa = createDevNodeCa(intermediateCa, legalName)
return Triple(rootCa, intermediateCa, nodeCa)
}

View File

@ -13,7 +13,7 @@ import net.corda.demobench.pty.R3Pty
import net.corda.nodeapi.internal.network.NetworkParameters import net.corda.nodeapi.internal.network.NetworkParameters
import net.corda.nodeapi.internal.network.NetworkParametersCopier import net.corda.nodeapi.internal.network.NetworkParametersCopier
import net.corda.nodeapi.internal.network.NotaryInfo import net.corda.nodeapi.internal.network.NotaryInfo
import net.corda.nodeapi.internal.IdentityGenerator import net.corda.nodeapi.internal.DevIdentityGenerator
import tornadofx.* import tornadofx.*
import java.io.IOException import java.io.IOException
import java.lang.management.ManagementFactory import java.lang.management.ManagementFactory
@ -153,7 +153,7 @@ class NodeController(check: atRuntime = ::checkExists) : Controller() {
// Generate notary identity and save it into node's directory. This identity will be used in network parameters. // Generate notary identity and save it into node's directory. This identity will be used in network parameters.
private fun getNotaryIdentity(config: NodeConfigWrapper): Party { private fun getNotaryIdentity(config: NodeConfigWrapper): Party {
return IdentityGenerator.installKeyStoreWithNodeIdentity(config.nodeDir, config.nodeConfig.myLegalName) return DevIdentityGenerator.installKeyStoreWithNodeIdentity(config.nodeDir, config.nodeConfig.myLegalName)
} }
fun reset() { fun reset() {