Change party to hold an X.500 name

Change the legal name of parties to be an X500 name. This ensures that we aren't converting between
common names and X500 names in various places, eliminating substantial scope for error in the conversion
process. As a result, all node names must now be full X500 names, which has impact on most configurations.
This commit is contained in:
Ross Nicoll
2017-05-05 15:16:44 +01:00
parent b64e7f51f6
commit 25dbac0f07
80 changed files with 352 additions and 311 deletions

View File

@ -5,6 +5,7 @@ import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.StateRef
import net.corda.core.contracts.TransactionType
import net.corda.core.crypto.Party
import net.corda.core.crypto.commonName
import net.corda.core.div
import net.corda.core.getOrThrow
import net.corda.core.node.services.ServiceInfo
@ -19,6 +20,9 @@ import net.corda.node.services.transactions.BFTNonValidatingNotaryService
import net.corda.node.utilities.ServiceIdentityGenerator
import net.corda.node.utilities.transaction
import net.corda.testing.node.NodeBasedTest
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.asn1.x500.X500NameBuilder
import org.bouncycastle.asn1.x500.style.BCStyle
import org.junit.Test
import java.security.KeyPair
import java.util.*
@ -27,7 +31,19 @@ import kotlin.test.assertFailsWith
class BFTNotaryServiceTests : NodeBasedTest() {
private companion object {
val notaryCommonName = "BFT Notary Server"
val notaryCommonName = X500Name("CN=BFT Notary Server,O=R3,OU=corda,L=Zurich,C=CH")
fun buildNodeName(it: Int, notaryName: X500Name): X500Name {
val builder = X500NameBuilder()
notaryName.rdNs.map { it.first }.forEach { attr ->
if (attr.type == BCStyle.CN) {
builder.addRDN(BCStyle.CN, "${attr.value}-$it")
} else {
builder.addRDN(attr)
}
}
return builder.build()
}
}
@Test
@ -73,13 +89,13 @@ class BFTNotaryServiceTests : NodeBasedTest() {
}
}
private fun startBFTNotaryCluster(notaryName: String,
private fun startBFTNotaryCluster(notaryName: X500Name,
clusterSize: Int,
serviceType: ServiceType): List<Node> {
require(clusterSize > 0)
val quorum = (2 * clusterSize + 1) / 3
ServiceIdentityGenerator.generateToDisk(
(0 until clusterSize).map { tempFolder.root.toPath() / "$notaryName-$it" },
(0 until clusterSize).map { tempFolder.root.toPath() / "${notaryName.commonName}-$it" },
serviceType.id,
notaryName,
quorum)
@ -87,7 +103,7 @@ class BFTNotaryServiceTests : NodeBasedTest() {
val serviceInfo = ServiceInfo(serviceType, notaryName)
val nodes = (0 until clusterSize).map {
startNode(
"$notaryName-$it",
buildNodeName(it, notaryName),
advertisedServices = setOf(serviceInfo),
configOverrides = mapOf("notaryNodeId" to it)
).getOrThrow()

View File

@ -15,6 +15,7 @@ import net.corda.flows.NotaryFlow
import net.corda.node.internal.AbstractNode
import net.corda.node.utilities.transaction
import net.corda.testing.node.NodeBasedTest
import org.bouncycastle.asn1.x500.X500Name
import org.junit.Test
import java.security.KeyPair
import java.util.*
@ -22,7 +23,7 @@ import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
class RaftNotaryServiceTests : NodeBasedTest() {
private val notaryName = "CN=RAFT Notary Service,O=R3,OU=corda,L=London,C=UK"
private val notaryName = X500Name("CN=RAFT Notary Service,O=R3,OU=corda,L=London,C=UK")
@Test
fun `detect double spend`() {

View File

@ -5,6 +5,8 @@ import com.google.common.util.concurrent.Futures
import net.corda.core.crypto.Party
import net.corda.core.flows.FlowLogic
import net.corda.core.getOrThrow
import net.corda.core.utilities.ALICE
import net.corda.core.utilities.BOB
import net.corda.core.utilities.unwrap
import net.corda.testing.node.NodeBasedTest
import org.assertj.core.api.Assertions.assertThat
@ -14,8 +16,8 @@ class FlowVersioningTest : NodeBasedTest() {
@Test
fun `core flows receive platform version of initiator`() {
val (alice, bob) = Futures.allAsList(
startNode("Alice", platformVersion = 2),
startNode("Bob", platformVersion = 3)).getOrThrow()
startNode(ALICE.name, platformVersion = 2),
startNode(BOB.name, platformVersion = 3)).getOrThrow()
bob.installCoreFlow(ClientFlow::class, ::SendBackPlatformVersionFlow)
val resultFuture = alice.services.startFlow(ClientFlow(bob.info.legalIdentity)).resultFuture
assertThat(resultFuture.getOrThrow()).isEqualTo(2)

View File

@ -4,6 +4,7 @@ import com.google.common.util.concurrent.Futures
import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.*
import net.corda.core.crypto.X509Utilities
import net.corda.core.crypto.commonName
import net.corda.core.messaging.MessageRecipients
import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.node.services.DEFAULT_SESSION_ID
@ -18,6 +19,7 @@ import net.corda.node.services.transactions.RaftValidatingNotaryService
import net.corda.node.services.transactions.SimpleNotaryService
import net.corda.node.utilities.ServiceIdentityGenerator
import net.corda.testing.freeLocalHostAndPort
import net.corda.testing.getTestX509Name
import net.corda.testing.node.NodeBasedTest
import org.assertj.core.api.Assertions.assertThat
import org.bouncycastle.asn1.x500.X500Name
@ -30,8 +32,8 @@ import java.util.concurrent.atomic.AtomicInteger
class P2PMessagingTest : NodeBasedTest() {
private companion object {
val DISTRIBUTED_SERVICE_NAME = X509Utilities.getDevX509Name("DistributedService")
val SERVICE_2_NAME = X509Utilities.getDevX509Name("Service Node 2")
val DISTRIBUTED_SERVICE_NAME = getTestX509Name("DistributedService")
val SERVICE_2_NAME = getTestX509Name("Service Node 2")
}
@Test
@ -65,7 +67,7 @@ class P2PMessagingTest : NodeBasedTest() {
val root = tempFolder.root.toPath()
ServiceIdentityGenerator.generateToDisk(
listOf(root / DUMMY_MAP.name.toString(), root / SERVICE_2_NAME.toString()),
listOf(root / DUMMY_MAP.name.commonName, root / SERVICE_2_NAME.commonName),
RaftValidatingNotaryService.type.id,
DISTRIBUTED_SERVICE_NAME)
@ -96,7 +98,7 @@ class P2PMessagingTest : NodeBasedTest() {
@Test
fun `distributed service requests are retried if one of the nodes in the cluster goes down without sending a response`() {
val distributedServiceNodes = startNotaryCluster("DistributedService", 2).getOrThrow()
val distributedServiceNodes = startNotaryCluster(DISTRIBUTED_SERVICE_NAME, 2).getOrThrow()
val alice = startNode(ALICE.name, configOverrides = mapOf("messageRedeliveryDelaySeconds" to 1)).getOrThrow()
val serviceAddress = alice.services.networkMapCache.run {
alice.net.getAddressOfParty(getPartyInfo(getAnyNotary()!!)!!)
@ -121,7 +123,7 @@ class P2PMessagingTest : NodeBasedTest() {
@Test
fun `distributed service request retries are persisted across client node restarts`() {
val distributedServiceNodes = startNotaryCluster("DistributedService", 2).getOrThrow()
val distributedServiceNodes = startNotaryCluster(DISTRIBUTED_SERVICE_NAME, 2).getOrThrow()
val alice = startNode(ALICE.name, configOverrides = mapOf("messageRedeliveryDelaySeconds" to 1)).getOrThrow()
val serviceAddress = alice.services.networkMapCache.run {
alice.net.getAddressOfParty(getPartyInfo(getAnyNotary()!!)!!)
@ -194,7 +196,7 @@ class P2PMessagingTest : NodeBasedTest() {
node.respondWith(node.info)
}
val serviceAddress = originatingNode.services.networkMapCache.run {
originatingNode.net.getAddressOfParty(getPartyInfo(getNotary(serviceName.toString())!!)!!)
originatingNode.net.getAddressOfParty(getPartyInfo(getNotary(serviceName)!!)!!)
}
val participatingNodes = HashSet<Any>()
// Try several times so that we can be fairly sure that any node not participating is not due to Artemis' selection

View File

@ -2,6 +2,7 @@ package net.corda.services.messaging
import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.crypto.Party
import net.corda.core.crypto.X509Utilities
import net.corda.core.crypto.commonName
import net.corda.core.div
import net.corda.core.getOrThrow
@ -33,22 +34,22 @@ class P2PSecurityTest : NodeBasedTest() {
@Test
fun `incorrect legal name for the network map service config`() {
val incorrectNetworkMapName = random63BitValue().toString()
val incorrectNetworkMapName = X509Utilities.getDevX509Name(random63BitValue().toString())
val node = startNode(BOB.name, configOverrides = mapOf(
"networkMapService" to mapOf(
"address" to networkMapNode.configuration.p2pAddress.toString(),
"legalName" to incorrectNetworkMapName
"legalName" to incorrectNetworkMapName.toString()
)
))
// The connection will be rejected as the legal name doesn't match
assertThatThrownBy { node.getOrThrow() }.hasMessageContaining(incorrectNetworkMapName)
assertThatThrownBy { node.getOrThrow() }.hasMessageContaining(incorrectNetworkMapName.toString())
}
@Test
fun `register with the network map service using a legal name different from the TLS CN`() {
startSimpleNode(X500Name(DUMMY_BANK_A.name)).use {
startSimpleNode(DUMMY_BANK_A.name).use {
// Register with the network map using a different legal name
val response = it.registerWithNetworkMap(X500Name(DUMMY_BANK_B.name))
val response = it.registerWithNetworkMap(DUMMY_BANK_B.name)
// We don't expect a response because the network map's host verification will prevent a connection back
// to the attacker as the TLS CN will not match the legal name it has just provided
assertThatExceptionOfType(TimeoutException::class.java).isThrownBy {

View File

@ -63,23 +63,6 @@ private val log: Logger = loggerFor<DriverDSL>()
* This is the interface that's exposed to DSL users.
*/
interface DriverDSLExposedInterface {
/**
* Starts a [net.corda.node.internal.Node] in a separate process.
*
* @param providedName Name of the node, which will be its legal name in [Party].
* Note that this must be unique as the driver uses it as a primary key!
* @param advertisedServices The set of services to be advertised by the node. Defaults to empty set.
* @param verifierType The type of transaction verifier to use. See: [VerifierType]
* @param rpcUsers List of users who are authorised to use the RPC system. Defaults to empty list.
* @return The [NodeInfo] of the started up node retrieved from the network map service.
*/
@Deprecated("To be removed once X500Name is used as legal name everywhere")
fun startNode(providedName: String?,
advertisedServices: Set<ServiceInfo> = emptySet(),
rpcUsers: List<User> = emptyList(),
verifierType: VerifierType = VerifierType.InMemory,
customOverrides: Map<String, Any?> = emptyMap()): ListenableFuture<NodeHandle>
/**
* Starts a [net.corda.node.internal.Node] in a separate process.
*
@ -107,7 +90,7 @@ interface DriverDSLExposedInterface {
* @return The [Party] identity of the distributed notary service, and the [NodeInfo]s of the notaries in the cluster.
*/
fun startNotaryCluster(
notaryName: String,
notaryName: X500Name,
clusterSize: Int = 3,
type: ServiceType = RaftValidatingNotaryService.type,
verifierType: VerifierType = VerifierType.InMemory,
@ -444,25 +427,13 @@ class DriverDSL(
verifierType: VerifierType,
customOverrides: Map<String, Any?>
): ListenableFuture<NodeHandle> {
return startNode(providedName?.toString(), advertisedServices, rpcUsers, verifierType, customOverrides)
}
override fun startNode(providedName: String?,
advertisedServices: Set<ServiceInfo>,
rpcUsers: List<User>,
verifierType: VerifierType,
customOverrides: Map<String, Any?>): ListenableFuture<NodeHandle> {
val p2pAddress = portAllocation.nextHostAndPort()
val rpcAddress = portAllocation.nextHostAndPort()
val webAddress = portAllocation.nextHostAndPort()
val debugPort = if (isDebug) debugPortAllocation.nextPort() else null
val name = providedName.toString() ?: X509Utilities.getDevX509Name("${pickA(name).commonName}-${p2pAddress.port}").toString()
val commonName = try {
X500Name(name).commonName
} catch(ex: IllegalArgumentException) {
name
}
val baseDirectory = driverDirectory / commonName
// TODO: Derive name from the full picked name, don't just wrap the common name
val name = providedName ?: X509Utilities.getDevX509Name("${pickA(name).commonName}-${p2pAddress.port}")
val baseDirectory = driverDirectory / name.commonName
val configOverrides = mapOf(
"myLegalName" to name.toString(),
"p2pAddress" to p2pAddress.toString(),
@ -471,7 +442,7 @@ class DriverDSL(
"extraAdvertisedServiceIds" to advertisedServices.map { it.toString() },
"networkMapService" to mapOf(
"address" to networkMapAddress.toString(),
"legalName" to networkMapLegalName
"legalName" to networkMapLegalName.toString()
),
"useTestClock" to useTestClock,
"rpcUsers" to rpcUsers.map {
@ -503,14 +474,23 @@ class DriverDSL(
}
override fun startNotaryCluster(
notaryName: String,
notaryName: X500Name,
clusterSize: Int,
type: ServiceType,
verifierType: VerifierType,
rpcUsers: List<User>
): ListenableFuture<Pair<Party, List<NodeHandle>>> {
val nodeNames = (1..clusterSize).map { "Notary Node $it" }
val paths = nodeNames.map { driverDirectory / it }
val nodeNames = (1..clusterSize).map {
val nameBuilder = X500NameBuilder(BCStyle.INSTANCE)
nameBuilder.addRDN(BCStyle.CN, "${DUMMY_NOTARY.name.commonName} $it")
DUMMY_NOTARY.name.rdNs.forEach { rdn ->
if (rdn.first.type != BCStyle.CN) {
nameBuilder.addRDN(rdn.first)
}
}
nameBuilder.build()
}
val paths = nodeNames.map { driverDirectory / it.commonName }
ServiceIdentityGenerator.generateToDisk(paths, type.id, notaryName)
val serviceInfo = ServiceInfo(type, notaryName)
@ -567,17 +547,12 @@ class DriverDSL(
override fun startNetworkMapService() {
val debugPort = if (isDebug) debugPortAllocation.nextPort() else null
val apiAddress = portAllocation.nextHostAndPort().toString()
val nodeDirectoryName = try {
X500Name(networkMapLegalName).commonName
} catch(ex: IllegalArgumentException) {
networkMapLegalName
}
val baseDirectory = driverDirectory / nodeDirectoryName
val baseDirectory = driverDirectory / networkMapLegalName.commonName
val config = ConfigHelper.loadConfig(
baseDirectory = baseDirectory,
allowMissingConfig = true,
configOverrides = mapOf(
"myLegalName" to networkMapLegalName,
"myLegalName" to networkMapLegalName.toString(),
// TODO: remove the webAddress as NMS doesn't need to run a web server. This will cause all
// node port numbers to be shifted, so all demos and docs need to be updated accordingly.
"webAddress" to apiAddress,
@ -593,9 +568,9 @@ class DriverDSL(
companion object {
val name = arrayOf(
X500Name(ALICE.name),
X500Name(BOB.name),
X500Name(DUMMY_BANK_A.name)
ALICE.name,
BOB.name,
DUMMY_BANK_A.name
)
fun <A> pickA(array: Array<A>): A = array[Math.abs(Random().nextInt()) % array.size]

View File

@ -58,6 +58,7 @@ import net.corda.node.utilities.AffinityExecutor
import net.corda.node.utilities.configureDatabase
import net.corda.node.utilities.transaction
import org.apache.activemq.artemis.utils.ReusableLatch
import org.bouncycastle.asn1.x500.X500Name
import org.jetbrains.exposed.sql.Database
import org.slf4j.Logger
import java.io.IOException
@ -338,7 +339,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
protected open fun makeServiceEntries(): List<ServiceEntry> {
return advertisedServices.map {
val serviceId = it.type.id
val serviceName = it.name ?: "ou=$serviceId,${configuration.myLegalName}"
val serviceName = it.name ?: X500Name("CN=$serviceId,${configuration.myLegalName}")
val identity = obtainKeyPair(configuration.baseDirectory, serviceId + "-private-key", serviceId + "-public", serviceName).first
ServiceEntry(it, identity)
}
@ -558,7 +559,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
protected fun obtainLegalIdentity(): Party = obtainKeyPair(configuration.baseDirectory, PRIVATE_KEY_FILE_NAME, PUBLIC_IDENTITY_FILE_NAME).first
protected fun obtainLegalIdentityKey(): KeyPair = obtainKeyPair(configuration.baseDirectory, PRIVATE_KEY_FILE_NAME, PUBLIC_IDENTITY_FILE_NAME).second
private fun obtainKeyPair(dir: Path, privateKeyFileName: String, publicKeyFileName: String, serviceName: String? = null): Pair<Party, KeyPair> {
private fun obtainKeyPair(dir: Path, privateKeyFileName: String, publicKeyFileName: String, serviceName: X500Name? = null): Pair<Party, KeyPair> {
// Load the private identity key, creating it if necessary. The identity key is a long term well known key that
// is distributed to other peers and we use it (or a key signed by it) when we need to do something
// "permissioned". The identity file is what gets distributed and contains the node's legal name along with
@ -566,13 +567,13 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
// the legal name is actually validated in some way.
val privKeyFile = dir / privateKeyFileName
val pubIdentityFile = dir / publicKeyFileName
val identityName = serviceName ?: configuration.myLegalName.toString()
val identityPrincipal: X500Name = serviceName ?: configuration.myLegalName
val identityAndKey = if (!privKeyFile.exists()) {
log.info("Identity key not found, generating fresh key!")
val keyPair: KeyPair = generateKeyPair()
keyPair.serialize().writeToFile(privKeyFile)
val myIdentity = Party(identityName, keyPair.public)
val myIdentity = Party(identityPrincipal, keyPair.public)
// We include the Party class with the file here to help catch mixups when admins provide files of the
// wrong type by mistake.
myIdentity.serialize().writeToFile(pubIdentityFile)
@ -582,9 +583,9 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
// This is just a sanity check. It shouldn't fail unless the admin has fiddled with the files and messed
// things up for us.
val myIdentity = pubIdentityFile.readAll().deserialize<Party>()
if (myIdentity.name != identityName)
if (myIdentity.name != identityPrincipal)
throw ConfigurationException("The legal name in the config file doesn't match the stored identity file:" +
"$identityName vs ${myIdentity.name}")
"$identityPrincipal vs ${myIdentity.name}")
// Load the private key.
val keyPair = privKeyFile.readAll().deserialize<KeyPair>()
Pair(myIdentity, keyPair)

View File

@ -173,6 +173,7 @@ class CordaRPCOpsImpl(
override fun waitUntilRegisteredWithNetworkMap() = services.networkMapCache.mapServiceRegistered
override fun partyFromKey(key: PublicKey) = services.identityService.partyFromKey(key)
@Deprecated("Use partyFromX500Name instead")
override fun partyFromName(name: String) = services.identityService.partyFromName(name)
override fun partyFromX500Name(x500Name: X500Name)= services.identityService.partyFromX500Name(x500Name)

View File

@ -30,6 +30,7 @@ import net.corda.node.services.transactions.RaftValidatingNotaryService
import net.corda.node.utilities.AddressUtils
import net.corda.node.utilities.AffinityExecutor
import net.corda.nodeapi.ArtemisMessagingComponent.NetworkMapAddress
import org.bouncycastle.asn1.x500.X500Name
import org.slf4j.Logger
import java.io.RandomAccessFile
import java.lang.management.ManagementFactory
@ -317,4 +318,4 @@ class Node(override val configuration: FullNodeConfiguration,
class ConfigurationException(message: String) : Exception(message)
data class NetworkMapInfo(val address: HostAndPort, val legalName: String)
data class NetworkMapInfo(val address: HostAndPort, val legalName: X500Name)

View File

@ -7,8 +7,8 @@ import net.corda.core.node.services.IdentityService
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.utilities.loggerFor
import net.corda.core.utilities.trace
import java.security.PublicKey
import org.bouncycastle.asn1.x500.X500Name
import java.security.PublicKey
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import javax.annotation.concurrent.ThreadSafe
@ -23,20 +23,21 @@ class InMemoryIdentityService : SingletonSerializeAsToken(), IdentityService {
}
private val keyToParties = ConcurrentHashMap<PublicKey, Party>()
private val nameToParties = ConcurrentHashMap<String, Party>()
private val principalToParties = ConcurrentHashMap<X500Name, Party>()
override fun registerIdentity(party: Party) {
log.trace { "Registering identity $party" }
keyToParties[party.owningKey] = party
nameToParties[party.name] = party
principalToParties[party.name] = party
}
// We give the caller a copy of the data set to avoid any locking problems
override fun getAllIdentities(): Iterable<Party> = ArrayList(keyToParties.values)
override fun partyFromKey(key: PublicKey): Party? = keyToParties[key]
override fun partyFromName(name: String): Party? = nameToParties[name]
override fun partyFromX500Name(principal: X500Name): Party? = nameToParties[principal.toString()]
@Deprecated("Use partyFromX500Name")
override fun partyFromName(name: String): Party? = principalToParties[X500Name(name)]
override fun partyFromX500Name(principal: X500Name): Party? = principalToParties[principal]
override fun partyFromAnonymous(party: AnonymousParty): Party? = partyFromKey(party.owningKey)
override fun partyFromAnonymous(partyRef: PartyAndReference) = partyFromAnonymous(partyRef.party)
}

View File

@ -238,7 +238,7 @@ class ArtemisMessagingServer(override val config: NodeConfiguration,
.loadCertificateFromKeyStore(config.keyStoreFile, config.keyStorePassword, CORDA_CLIENT_CA)
val ourSubjectDN = X500Name(ourCertificate.subjectDN.name)
// This is a sanity check and should not fail unless things have been misconfigured
require(ourSubjectDN.commonName == config.myLegalName.commonName) {
require(ourSubjectDN == config.myLegalName) {
"Legal name does not match with our subject CN: $ourSubjectDN"
}
val defaultCertPolicies = mapOf(
@ -346,7 +346,7 @@ class ArtemisMessagingServer(override val config: NodeConfiguration,
}
}
private fun deployBridge(address: ArtemisPeerAddress, legalName: String) {
private fun deployBridge(address: ArtemisPeerAddress, legalName: X500Name) {
deployBridge(address.queueName, address.hostAndPort, legalName)
}
@ -359,7 +359,7 @@ class ArtemisMessagingServer(override val config: NodeConfiguration,
* as defined by ArtemisAddress.queueName. A bridge is then created to forward messages from this queue to the node's
* P2P address.
*/
private fun deployBridge(queueName: String, target: HostAndPort, legalName: String) {
private fun deployBridge(queueName: String, target: HostAndPort, legalName: X500Name) {
val connectionDirection = ConnectionDirection.Outbound(
connectorFactoryClassName = VerifyingNettyConnectorFactory::class.java.name,
expectedCommonName = legalName
@ -401,7 +401,7 @@ class ArtemisMessagingServer(override val config: NodeConfiguration,
internal fun hostVerificationFail(peerLegalName: X500Name, expectedLegalName: X500Name) {
log.error("Peer has wrong CN - expected $expectedLegalName but got $peerLegalName. This is either a fatal " +
"misconfiguration by the remote peer or an SSL man-in-the-middle attack!")
if (expectedLegalName.toString() == config.networkMapService?.legalName) {
if (expectedLegalName == config.networkMapService?.legalName) {
// If the peer that failed host verification was the network map node then we're in big trouble and need to bail!
_networkMapConnectionFuture!!.setException(IOException("${config.networkMapService} failed host verification check"))
}
@ -409,7 +409,7 @@ class ArtemisMessagingServer(override val config: NodeConfiguration,
// This is called on one of Artemis' background threads
internal fun onTcpConnection(peerLegalName: X500Name) {
if (peerLegalName.toString() == config.networkMapService?.legalName) {
if (peerLegalName == config.networkMapService?.legalName) {
_networkMapConnectionFuture!!.set(Unit)
}
}
@ -437,14 +437,12 @@ private class VerifyingNettyConnector(configuration: MutableMap<String, Any>?,
protocolManager: ClientProtocolManager?) :
NettyConnector(configuration, handler, listener, closeExecutor, threadPool, scheduledThreadPool, protocolManager) {
private val server = configuration?.get(ArtemisMessagingServer::class.java.name) as? ArtemisMessagingServer
private val expectedCommonName = (configuration?.get(ArtemisTcpTransport.VERIFY_PEER_COMMON_NAME) as? String)?.let {
X500Name(it)
}
private val expecteLegalName: X500Name? = configuration?.get(ArtemisTcpTransport.VERIFY_PEER_LEGAL_NAME) as X500Name?
override fun createConnection(): Connection? {
val connection = super.createConnection() as NettyConnection?
if (connection != null && expectedCommonName != null) {
val peerLegalName = connection
if (connection != null && expecteLegalName != null) {
val peerLegalName: X500Name = connection
.channel
.pipeline()
.get(SslHandler::class.java)
@ -453,10 +451,9 @@ private class VerifyingNettyConnector(configuration: MutableMap<String, Any>?,
.peerPrincipal
.name
.let(::X500Name)
// TODO Verify on the entire principle (subject)
if (peerLegalName.commonName != expectedCommonName.commonName) {
if (peerLegalName != expecteLegalName) {
connection.close()
server!!.hostVerificationFail(peerLegalName, expectedCommonName)
server!!.hostVerificationFail(peerLegalName, expecteLegalName)
return null // Artemis will keep trying to reconnect until it's told otherwise
} else {
server!!.onTcpConnection(peerLegalName)

View File

@ -5,6 +5,7 @@ import net.corda.core.crypto.Party
import net.corda.core.messaging.SingleMessageRecipient
import net.corda.node.services.api.ServiceHubInternal
import net.corda.node.utilities.*
import org.bouncycastle.asn1.x500.X500Name
import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.sql.statements.InsertStatement
import java.util.Collections.synchronizedMap
@ -26,12 +27,13 @@ class PersistentNetworkMapService(services: ServiceHubInternal, minimumPlatformV
}
override val nodeRegistrations: MutableMap<Party, NodeRegistrationInfo> = synchronizedMap(object : AbstractJDBCHashMap<Party, NodeRegistrationInfo, Table>(Table, loadOnInit = true) {
override fun keyFromRow(row: ResultRow): Party = Party(row[table.nodeParty.name], row[table.nodeParty.owningKey])
// TODO: We should understand an X500Name database field type, rather than manually doing the conversion ourselves
override fun keyFromRow(row: ResultRow): Party = Party(X500Name(row[table.nodeParty.name]), row[table.nodeParty.owningKey])
override fun valueFromRow(row: ResultRow): NodeRegistrationInfo = deserializeFromBlob(row[table.registrationInfo])
override fun addKeyToInsert(insert: InsertStatement, entry: Map.Entry<Party, NodeRegistrationInfo>, finalizables: MutableList<() -> Unit>) {
insert[table.nodeParty.name] = entry.key.name
insert[table.nodeParty.name] = entry.key.name.toString()
insert[table.nodeParty.owningKey] = entry.key.owningKey
}

View File

@ -281,8 +281,7 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
private fun onSessionMessage(message: ReceivedMessage) {
val sessionMessage = message.data.deserialize<SessionMessage>()
// TODO Look up the party with the full X.500 name instead of just the legal name
val sender = serviceHub.networkMapCache.getNodeByLegalName(message.peer.commonName)?.legalIdentity
val sender = serviceHub.networkMapCache.getNodeByLegalName(message.peer)?.legalIdentity
if (sender != null) {
when (sessionMessage) {
is ExistingSessionMessage -> onExistingSessionMessage(sessionMessage, sender)

View File

@ -54,7 +54,7 @@ class PersistentUniquenessProvider : UniquenessProvider, SingletonSerializeAsTok
finalizables: MutableList<() -> Unit>) {
insert[table.consumingTxHash] = entry.value.id
insert[table.consumingIndex] = entry.value.inputIndex
insert[table.requestingParty.name] = entry.value.requestingParty.name
insert[table.requestingParty.name] = entry.value.requestingParty.name.toString()
insert[table.requestingParty.owningKey] = entry.value.requestingParty.owningKey
}
})

View File

@ -91,7 +91,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
stateStatus = Vault.StateStatus.UNCONSUMED
contractStateClassName = it.value.state.data.javaClass.name
contractState = it.value.state.serialize(storageKryo()).bytes
notaryName = it.value.state.notary.name
notaryName = it.value.state.notary.name.toString()
notaryKey = it.value.state.notary.owningKey.toBase58String()
recordedTime = services.clock.instant()
}

View File

@ -6,6 +6,7 @@ import net.corda.core.crypto.generateKeyPair
import net.corda.core.serialization.serialize
import net.corda.core.utilities.loggerFor
import net.corda.core.utilities.trace
import org.bouncycastle.asn1.x500.X500Name
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
@ -23,7 +24,7 @@ object ServiceIdentityGenerator {
* @param serviceName The legal name of the distributed service.
* @param threshold The threshold for the generated group [CompositeKey].
*/
fun generateToDisk(dirs: List<Path>, serviceId: String, serviceName: String, threshold: Int = 1) {
fun generateToDisk(dirs: List<Path>, serviceId: String, serviceName: X500Name, threshold: Int = 1) {
log.trace { "Generating a group identity \"serviceName\" for nodes: ${dirs.joinToString()}" }
val keyPairs = (1..dirs.size).map { generateKeyPair() }
@ -43,7 +44,7 @@ object ServiceIdentityGenerator {
fun main(args: Array<String>) {
val dirs = args[0].split("|").map { Paths.get(it) }
val serviceId = args[1]
val serviceName = args[2]
val serviceName = X500Name(args[2])
val quorumSize = args.getOrNull(3)?.toInt() ?: 1
println("Generating service identity for \"$serviceName\"")

View File

@ -12,6 +12,7 @@ import net.corda.core.serialization.*;
import net.corda.core.transactions.*;
import net.corda.node.services.vault.schemas.*;
import net.corda.testing.node.*;
import org.bouncycastle.asn1.x500.X500Name;
import org.jetbrains.annotations.*;
import org.jetbrains.exposed.sql.*;
import org.junit.*;
@ -129,7 +130,7 @@ public class VaultQueryJavaTests {
QueryCriteria vaultCriteria = new VaultQueryCriteria(status, null, contractStateTypes);
List<UniqueIdentifier> linearIds = Arrays.asList(uid);
List<String> dealPartyNames = Arrays.asList(getMEGA_CORP().getName());
List<X500Name> dealPartyNames = Arrays.asList(getMEGA_CORP().getName());
QueryCriteria dealCriteriaAll = new LinearStateQueryCriteria(linearIds, false, dealIds, dealPartyNames);
QueryCriteria compositeCriteria = and(dealCriteriaAll, vaultCriteria);
@ -198,7 +199,7 @@ public class VaultQueryJavaTests {
QueryCriteria vaultCriteria = new VaultQueryCriteria(Vault.StateStatus.UNCONSUMED, null, contractStateTypes);
List<UniqueIdentifier> linearIds = Arrays.asList(uid);
List<String> dealPartyNames = Arrays.asList(getMEGA_CORP().getName());
List<X500Name> dealPartyNames = Arrays.asList(getMEGA_CORP().getName());
QueryCriteria dealCriteriaAll = new LinearStateQueryCriteria(linearIds, false, dealIds, dealPartyNames);
QueryCriteria compositeCriteria = and(dealCriteriaAll, vaultCriteria);

View File

@ -29,7 +29,7 @@ class InteractiveShellTest {
constructor(b: Int, c: String) : this(b.toString() + c)
constructor(amount: Amount<Currency>) : this(amount.toString())
constructor(pair: Pair<Amount<Currency>, SecureHash.SHA256>) : this(pair.toString())
constructor(party: Party) : this(party.name)
constructor(party: Party) : this(party.name.toString())
override fun call() = a
}
@ -67,7 +67,7 @@ class InteractiveShellTest {
fun flowTooManyParams() = check("b: 12, c: Yo, d: Bar", "")
@Test
fun party() = check("party: \"$someCorpLegalName\"", someCorpLegalName)
fun party() = check("party: \"$someCorpLegalName\"", someCorpLegalName.toString())
class DummyFSM(val logic: FlowA) : FlowStateMachine<Any?> {
override fun <T : Any> sendAndReceive(receiveType: Class<T>, otherParty: Party, payload: Any, sessionFlow: FlowLogic<*>, retrySend: Boolean): UntrustworthyData<T> {

View File

@ -266,7 +266,7 @@ class TwoPartyTradeFlowTests {
// Creates a mock node with an overridden storage service that uses a RecordingMap, that lets us test the order
// of gets and puts.
private fun makeNodeWithTracking(networkMapAddr: SingleMessageRecipient?, name: String, overrideServices: Map<ServiceInfo, KeyPair>? = null): MockNetwork.MockNode {
private fun makeNodeWithTracking(networkMapAddr: SingleMessageRecipient?, name: X500Name, overrideServices: Map<ServiceInfo, KeyPair>? = null): MockNetwork.MockNode {
// Create a node in the mock network ...
return net.createNode(networkMapAddr, -1, object : MockNetwork.Factory {
override fun create(config: NodeConfiguration,

View File

@ -12,7 +12,7 @@ class FullNodeConfigurationTest {
@Test
fun `Artemis special characters not permitted in RPC usernames`() {
fun configWithRPCUsername(username: String): FullNodeConfiguration {
return testConfiguration(Paths.get("."), X500Name(ALICE.name), 0).copy(
return testConfiguration(Paths.get("."), ALICE.name, 0).copy(
rpcUsers = listOf(User(username, "pass", emptySet())))
}

View File

@ -130,7 +130,7 @@ class RequeryConfigurationTest {
stateStatus = Vault.StateStatus.UNCONSUMED
contractStateClassName = DummyContract.SingleOwnerState::class.java.name
contractState = DummyContract.SingleOwnerState(owner = DUMMY_PUBKEY_1).serialize(storageKryo()).bytes
notaryName = txn.tx.notary!!.name
notaryName = txn.tx.notary!!.name.toString()
notaryKey = txn.tx.notary!!.owningKey.toBase58String()
recordedTime = Instant.now()
}

View File

@ -22,6 +22,7 @@ import net.corda.testing.node.MockKeyManagementService
import net.corda.testing.node.TestClock
import net.corda.testing.node.makeTestDataSourceProperties
import org.assertj.core.api.Assertions.assertThat
import org.bouncycastle.asn1.x500.X500Name
import org.jetbrains.exposed.sql.Database
import org.junit.After
import org.junit.Before
@ -79,7 +80,8 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
database.transaction {
val kms = MockKeyManagementService(ALICE_KEY)
val mockMessagingService = InMemoryMessagingNetwork(false).InMemoryMessaging(false, InMemoryMessagingNetwork.PeerHandle(0, "None"), AffinityExecutor.ServiceAffinityExecutor("test", 1), database)
val nullIdentity = X500Name("cn=None")
val mockMessagingService = InMemoryMessagingNetwork(false).InMemoryMessaging(false, InMemoryMessagingNetwork.PeerHandle(0, nullIdentity), AffinityExecutor.ServiceAffinityExecutor("test", 1), database)
services = object : MockServiceHubInternal(overrideClock = testClock, keyManagement = kms, net = mockMessagingService), TestReference {
override val vaultService: VaultService = NodeVaultService(this, dataSourceProps)
override val testReference = this@NodeSchedulerServiceTest

View File

@ -72,7 +72,7 @@ class ArtemisMessagingTests {
userService = RPCUserServiceImpl(emptyList())
config = TestNodeConfiguration(
baseDirectory = baseDirectory,
myLegalName = X500Name(ALICE.name),
myLegalName = ALICE.name,
networkMapService = null)
LogHelper.setLevel(PersistentUniquenessProvider::class)
val dataSourceAndDatabase = configureDatabase(makeTestDataSourceProperties())

View File

@ -29,6 +29,7 @@ import net.corda.node.utilities.AddOrRemove.REMOVE
import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockNetwork.MockNode
import org.assertj.core.api.Assertions.assertThat
import org.bouncycastle.asn1.x500.X500Name
import org.eclipse.jetty.util.BlockingArrayQueue
import org.junit.After
import org.junit.Before
@ -43,7 +44,7 @@ abstract class AbstractNetworkMapServiceTest<out S : AbstractNetworkMapService>
lateinit var alice: MockNode
companion object {
val subscriberLegalName = "CN=Subscriber,OU=Corda QA Department,O=R3 CEV,L=New York,C=US"
val subscriberLegalName = X500Name("CN=Subscriber,OU=Corda QA Department,O=R3 CEV,L=New York,C=US")
}
@Before
@ -246,14 +247,14 @@ abstract class AbstractNetworkMapServiceTest<out S : AbstractNetworkMapService>
network.runNetwork()
}
private fun addNewNodeToNetworkMap(legalName: String): MockNode {
private fun addNewNodeToNetworkMap(legalName: X500Name): MockNode {
val node = network.createNode(networkMapAddress = mapServiceNode.info.address, legalName = legalName)
network.runNetwork()
lastSerial = System.currentTimeMillis()
return node
}
private fun newNodeSeparateFromNetworkMap(legalName: String): MockNode {
private fun newNodeSeparateFromNetworkMap(legalName: X500Name): MockNode {
return network.createNode(legalName = legalName, nodeFactory = NoNMSNodeFactory)
}

View File

@ -46,7 +46,7 @@ class InMemoryIdentityServiceTests {
@Test
fun `get identity by name with no registered identities`() {
val service = InMemoryIdentityService()
assertNull(service.partyFromName(ALICE.name))
assertNull(service.partyFromX500Name(ALICE.name))
}
@Test
@ -54,8 +54,8 @@ class InMemoryIdentityServiceTests {
val service = InMemoryIdentityService()
val identities = listOf("Node A", "Node B", "Node C")
.map { Party(X500Name("CN=$it,O=R3,OU=corda,L=London,C=UK"), generateKeyPair().public) }
assertNull(service.partyFromName(identities.first().name))
assertNull(service.partyFromX500Name(identities.first().name))
identities.forEach { service.registerIdentity(it) }
identities.forEach { assertEquals(it, service.partyFromName(it.name)) }
identities.forEach { assertEquals(it, service.partyFromX500Name(it.name)) }
}
}

View File

@ -33,6 +33,7 @@ import net.corda.node.services.transactions.ValidatingNotaryService
import net.corda.node.utilities.transaction
import net.corda.testing.expect
import net.corda.testing.expectEvents
import net.corda.testing.getTestX509Name
import net.corda.testing.initiateSingleShotFlow
import net.corda.testing.node.InMemoryMessagingNetwork
import net.corda.testing.node.InMemoryMessagingNetwork.MessageTransfer
@ -43,6 +44,7 @@ import net.corda.testing.sequence
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType
import org.bouncycastle.asn1.x500.X500Name
import org.junit.After
import org.junit.Before
import org.junit.Test
@ -74,7 +76,7 @@ class StateMachineManagerTests {
node1 = nodes.first
node2 = nodes.second
val notaryKeyPair = generateKeyPair()
val notaryService = ServiceInfo(ValidatingNotaryService.type, "CN=notary-service-2000,O=R3,OU=corda,L=London,C=UK")
val notaryService = ServiceInfo(ValidatingNotaryService.type, getTestX509Name("notary-service-2000"))
val overrideServices = mapOf(Pair(notaryService, notaryKeyPair))
// Note that these notaries don't operate correctly as they don't share their state. They are only used for testing
// service addressing.

View File

@ -184,7 +184,7 @@ class VaultQueryTests {
val CASH_NOTARY_KEY: KeyPair by lazy { entropyToKeyPair(BigInteger.valueOf(20)) }
val CASH_NOTARY: Party get() = Party("Notary Service", CASH_NOTARY_KEY.public)
val CASH_NOTARY: Party get() = Party(DUMMY_NOTARY.name, CASH_NOTARY_KEY.public)
@Test
fun `unconsumed states by notary`() {

View File

@ -8,6 +8,7 @@ import net.corda.core.crypto.X509Utilities
import net.corda.core.exists
import net.corda.core.utilities.ALICE
import net.corda.testing.TestNodeConfiguration
import net.corda.testing.getTestX509Name
import org.bouncycastle.asn1.x500.X500Name
import org.junit.Rule
import org.junit.Test
@ -28,7 +29,7 @@ class NetworkRegistrationHelperTest {
val identities = listOf("CORDA_CLIENT_CA",
"CORDA_INTERMEDIATE_CA",
"CORDA_ROOT_CA")
.map { X500Name("CN=${it},O=R3,OU=corda,L=London,C=UK") }
.map { getTestX509Name(it) }
val certs = identities.map { X509Utilities.createSelfSignedCACert(it).certificate }
.toTypedArray()
@ -39,7 +40,7 @@ class NetworkRegistrationHelperTest {
val config = TestNodeConfiguration(
baseDirectory = tempFolder.root.toPath(),
myLegalName = X500Name(ALICE.name),
myLegalName = ALICE.name,
networkMapService = null)
assertFalse(config.keyStoreFile.exists())