Reduce use of X500Name bridges (#1483)

* Change to using strings in CordformContext; X500Name is an external API dependency we're trying to avoid, X500Principal rearranges the order of the elements in a way we don't want, and trying to put CordaX500Name into Groovy in the time available is impractical. As such having pre-formatted strings used in the Cordform plugin makes most sense right now.
* Remove uses of CordaX500Name.x500
* Remove old X.500 parsing tools
* Move CordaX500Name.x500 into X500NameUtils
* Move X500NameUtils into internal
This commit is contained in:
Ross Nicoll 2017-09-14 11:56:14 +01:00 committed by GitHub
parent 8f0c0c784b
commit ae2183c8a1
35 changed files with 229 additions and 158 deletions

View File

@ -7,7 +7,6 @@ import net.corda.core.messaging.CordaRPCOps;
import net.corda.core.messaging.FlowHandle;
import net.corda.core.node.NodeInfo;
import net.corda.core.utilities.OpaqueBytes;
import net.corda.core.utilities.X500NameUtils;
import net.corda.finance.flows.AbstractCashFlow;
import net.corda.finance.flows.CashIssueFlow;
import net.corda.nodeapi.User;

View File

@ -1,4 +1,4 @@
gradlePluginsVersion=0.16.2
gradlePluginsVersion=0.16.3
kotlinVersion=1.1.4
guavaVersion=21.0
bouncycastleVersion=1.57

View File

@ -2,13 +2,15 @@ package net.corda.core.identity
import net.corda.core.internal.LegalNameValidator
import net.corda.core.internal.countryCodes
import net.corda.core.internal.x500Name
import net.corda.core.serialization.CordaSerializable
import org.bouncycastle.asn1.ASN1Encodable
import org.bouncycastle.asn1.ASN1ObjectIdentifier
import org.bouncycastle.asn1.x500.AttributeTypeAndValue
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.asn1.x500.X500NameBuilder
import org.bouncycastle.asn1.x500.style.BCStyle
import java.security.Principal
import javax.security.auth.x500.X500Principal
/**
* X.500 distinguished name data type customised to how Corda uses names. This restricts the attributes to those Corda
@ -33,7 +35,7 @@ data class CordaX500Name(val commonName: String?,
val locality: String,
val state: String?,
val country: String) {
constructor(commonName: String, organisation: String, locality: String, country: String) : this(commonName = commonName, organisationUnit = null, organisation = organisation, locality = locality, state = null, country = country)
constructor(commonName: String, organisation: String, locality: String, country: String) : this(commonName = commonName, organisationUnit = null, organisation = organisation, locality = locality, state = null, country = country)
/**
* @param organisation name of the organisation.
* @param locality locality of the organisation, typically nearest major city.
@ -56,11 +58,15 @@ data class CordaX500Name(val commonName: String?,
require(locality.length < MAX_LENGTH_LOCALITY) { "Locality attribute (L) must contain less then $MAX_LENGTH_LOCALITY characters." }
state?.let { require(it.length < MAX_LENGTH_STATE) { "State attribute (ST) must contain less then $MAX_LENGTH_STATE characters." } }
organisationUnit?.let { require(it.length < MAX_LENGTH_ORGANISATION_UNIT) {
"Organisation Unit attribute (OU) must contain less then $MAX_LENGTH_ORGANISATION_UNIT characters." }
organisationUnit?.let {
require(it.length < MAX_LENGTH_ORGANISATION_UNIT) {
"Organisation Unit attribute (OU) must contain less then $MAX_LENGTH_ORGANISATION_UNIT characters."
}
}
commonName?.let { require(it.length < MAX_LENGTH_COMMON_NAME) {
"Common Name attribute (CN) must contain less then $MAX_LENGTH_COMMON_NAME characters." }
commonName?.let {
require(it.length < MAX_LENGTH_COMMON_NAME) {
"Common Name attribute (CN) must contain less then $MAX_LENGTH_COMMON_NAME characters."
}
}
}
@ -74,7 +80,8 @@ data class CordaX500Name(val commonName: String?,
private val supportedAttributes = setOf(BCStyle.O, BCStyle.C, BCStyle.L, BCStyle.CN, BCStyle.ST, BCStyle.OU)
@JvmStatic
fun build(x500Name: X500Name) : CordaX500Name {
fun build(principal: X500Principal) : CordaX500Name {
val x500Name = X500Name.getInstance(principal.encoded)
val attrsMap: Map<ASN1ObjectIdentifier, ASN1Encodable> = x500Name.rdNs
.flatMap { it.typesAndValues.asList() }
.groupBy(AttributeTypeAndValue::getType, AttributeTypeAndValue::getValue)
@ -99,31 +106,26 @@ data class CordaX500Name(val commonName: String?,
val C = attrsMap[BCStyle.C]?.toString() ?: throw IllegalArgumentException("Corda X.500 names must include an C attribute")
return CordaX500Name(CN, OU, O, L, ST, C)
}
@JvmStatic
fun parse(name: String) : CordaX500Name = build(X500Name(name))
fun parse(name: String) : CordaX500Name = build(X500Principal(name))
}
@Transient
private var x500Cache: X500Name? = null
override fun toString(): String = x500Name.toString()
/**
* Return the underlying X.500 name from this Corda-safe X.500 name. These are guaranteed to have a consistent
* ordering, such that their `toString()` function returns the same value every time for the same [CordaX500Name].
*/
val x500Name: X500Name
val x500Principal: X500Principal
get() {
if (x500Cache == null) {
x500Cache = X500NameBuilder(BCStyle.INSTANCE).apply {
addRDN(BCStyle.C, country)
state?.let { addRDN(BCStyle.ST, it) }
addRDN(BCStyle.L, locality)
addRDN(BCStyle.O, organisation)
organisationUnit?.let { addRDN(BCStyle.OU, it) }
commonName?.let { addRDN(BCStyle.CN, it) }
}.build()
x500Cache = this.x500Name
}
return x500Cache!!
return X500Principal(x500Cache!!.encoded)
}
override fun toString(): String {
if (x500Cache == null) {
x500Cache = this.x500Name
}
return x500Cache.toString()
}
}

View File

@ -6,6 +6,8 @@ import net.corda.core.crypto.Crypto
import net.corda.core.utilities.OpaqueBytes
import org.bouncycastle.cert.X509CertificateHolder
import java.security.PublicKey
import java.security.cert.X509Certificate
import javax.security.auth.x500.X500Principal
/**
* The [Party] class represents an entity on the network, which is typically identified by a legal [name] and public key
@ -27,7 +29,8 @@ import java.security.PublicKey
* @see CompositeKey
*/
class Party(val name: CordaX500Name, owningKey: PublicKey) : AbstractParty(owningKey) {
constructor(certificate: X509CertificateHolder) : this(CordaX500Name.build(certificate.subject), Crypto.toSupportedPublicKey(certificate.subjectPublicKeyInfo))
constructor(certificate: X509Certificate) : this(CordaX500Name.build(certificate.subjectX500Principal), Crypto.toSupportedPublicKey(certificate.publicKey))
constructor(certificate: X509CertificateHolder) : this(CordaX500Name.build(X500Principal(certificate.subject.encoded)), Crypto.toSupportedPublicKey(certificate.subjectPublicKeyInfo))
override fun nameOrNull(): CordaX500Name = name
fun anonymise(): AnonymousParty = AnonymousParty(owningKey)
override fun ref(bytes: OpaqueBytes): PartyAndReference = PartyAndReference(this, bytes)

View File

@ -1,9 +1,11 @@
@file:JvmName("X500NameUtils")
package net.corda.core.utilities
package net.corda.core.internal
import net.corda.core.identity.CordaX500Name
import org.bouncycastle.asn1.ASN1ObjectIdentifier
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.asn1.x500.X500NameBuilder
import org.bouncycastle.asn1.x500.style.BCStyle
val X500Name.commonName: String? get() = getRDNValueString(BCStyle.CN)
@ -13,3 +15,20 @@ val X500Name.locality: String get() = getRDNValueString(BCStyle.L) ?: throw Ille
val X500Name.country: String get() = getRDNValueString(BCStyle.C) ?: throw IllegalArgumentException("Malformed X500 name, country attribute (C) cannot be empty.")
private fun X500Name.getRDNValueString(identifier: ASN1ObjectIdentifier): String? = getRDNs(identifier).firstOrNull()?.first?.value?.toString()
/**
* Return the underlying X.500 name from this Corda-safe X.500 name. These are guaranteed to have a consistent
* ordering, such that their `toString()` function returns the same value every time for the same [CordaX500Name].
*/
val CordaX500Name.x500Name: X500Name
get() {
return X500NameBuilder(BCStyle.INSTANCE).apply {
addRDN(BCStyle.C, country)
state?.let { addRDN(BCStyle.ST, it) }
addRDN(BCStyle.L, locality)
addRDN(BCStyle.O, organisation)
organisationUnit?.let { addRDN(BCStyle.OU, it) }
commonName?.let { addRDN(BCStyle.CN, it) }
}.build()
}

View File

@ -2,6 +2,7 @@ package net.corda.core.crypto
import net.corda.core.crypto.CompositeKey.NodeAndWeight
import net.corda.core.crypto.composite.CompositeSignaturesWithKeys
import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.declaredField
import net.corda.core.internal.div
import net.corda.core.serialization.serialize
@ -331,10 +332,11 @@ class CompositeKeyTests : TestDependencyInjectionBase() {
// Create self sign CA.
val caKeyPair = Crypto.generateKeyPair()
val ca = X509Utilities.createSelfSignedCACertificate(getX500Name(CN = "Test CA", O = "R3 Ltd", L = "London", C = "GB"), caKeyPair)
val caName = CordaX500Name(commonName = "Test CA", organisation = "R3 Ltd", locality = "London", country = "GB")
val ca = X509Utilities.createSelfSignedCACertificate(caName, caKeyPair)
// Sign the composite key with the self sign CA.
val compositeKeyCert = X509Utilities.createCertificate(CertificateType.IDENTITY, ca, caKeyPair, getX500Name(CN = "CompositeKey", O = "R3 Ltd", L = "London", C = "GB"), compositeKey)
val compositeKeyCert = X509Utilities.createCertificate(CertificateType.IDENTITY, ca, caKeyPair, caName.copy(commonName = "CompositeKey"), compositeKey)
// Store certificate to keystore.
val keystorePath = tempFolder.root.toPath() / "keystore.jks"

View File

@ -1,5 +1,6 @@
package net.corda.core.crypto
import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.toTypedArray
import net.corda.core.utilities.cert
import net.corda.node.utilities.*
@ -20,13 +21,13 @@ class X509NameConstraintsTest {
private fun makeKeyStores(subjectName: X500Name, nameConstraints: NameConstraints): Pair<KeyStore, KeyStore> {
val rootKeys = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val rootCACert = X509Utilities.createSelfSignedCACertificate(getX500Name(CN = "Corda Root CA", O = "R3CEV", L = "London", C = "GB"), rootKeys)
val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Root CA", organisation = "R3 Ltd", locality= "London", country = "GB"), rootKeys)
val intermediateCAKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootKeys, getX500Name(CN = "Corda Intermediate CA", O = "R3CEV", L = "London", C = "GB"), intermediateCAKeyPair.public)
val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootKeys, CordaX500Name(commonName = "Corda Intermediate CA", organisation = "R3 Ltd", locality = "London", country = "GB"), intermediateCAKeyPair.public)
val clientCAKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val clientCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, intermediateCACert, intermediateCAKeyPair, getX500Name(CN = "Corda Client CA", O = "R3CEV", L = "London", C = "GB"), clientCAKeyPair.public, nameConstraints = nameConstraints)
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)
val keyPass = "password"
val trustStore = KeyStore.getInstance(KEYSTORE_TYPE)

View File

@ -4,5 +4,5 @@ import org.bouncycastle.asn1.x500.X500Name;
import java.nio.file.Path;
public interface CordformContext {
Path baseDirectory(X500Name nodeName);
Path baseDirectory(String nodeName);
}

View File

@ -1,6 +1,5 @@
package net.corda.cordform;
import org.bouncycastle.asn1.x500.X500Name;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.function.Consumer;
@ -8,9 +7,9 @@ import java.util.function.Consumer;
public abstract class CordformDefinition {
public final Path driverDirectory;
public final ArrayList<Consumer<? super CordformNode>> nodeConfigurers = new ArrayList<>();
public final X500Name networkMapNodeName;
public final String networkMapNodeName;
public CordformDefinition(Path driverDirectory, X500Name networkMapNodeName) {
public CordformDefinition(Path driverDirectory, String networkMapNodeName) {
this.driverDirectory = driverDirectory;
this.networkMapNodeName = networkMapNodeName;
}

View File

@ -120,8 +120,8 @@ class Cordform extends DefaultTask {
}
}
cd.setup new CordformContext() {
Path baseDirectory(X500Name nodeName) {
project.projectDir.toPath().resolve(getNodeByName(nodeName.toString()).nodeDir.toPath())
Path baseDirectory(String nodeName) {
project.projectDir.toPath().resolve(getNodeByName(nodeName).nodeDir.toPath())
}
}
} else {

View File

@ -1,9 +1,7 @@
package net.corda.services.messaging
import net.corda.core.crypto.Crypto
import net.corda.core.internal.copyTo
import net.corda.core.internal.createDirectories
import net.corda.core.internal.exists
import net.corda.core.internal.*
import net.corda.node.utilities.*
import net.corda.nodeapi.ArtemisMessagingComponent.Companion.NODE_USER
import net.corda.nodeapi.ArtemisMessagingComponent.Companion.PEER_USER
@ -20,6 +18,7 @@ import org.assertj.core.api.Assertions.assertThatExceptionOfType
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.junit.Test
import java.nio.file.Files
@ -98,7 +97,7 @@ class MQSecurityAsNodeTest : MQSecurityTest() {
javaClass.classLoader.getResourceAsStream("net/corda/node/internal/certificates/cordadevcakeys.jks"),
"cordacadevpass")
val rootCACert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA)
val rootCACert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA).toX509CertHolder()
val intermediateCA = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
val clientKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)

View File

@ -64,7 +64,7 @@ class P2PMessagingTest : NodeBasedTest() {
@Test
fun `communicating with a distributed service which the network map node is part of`() {
ServiceIdentityGenerator.generateToDisk(
listOf(DUMMY_MAP.name, SERVICE_2_NAME).map { baseDirectory(it.x500Name) },
listOf(DUMMY_MAP.name, SERVICE_2_NAME).map { baseDirectory(it) },
RaftValidatingNotaryService.type.id,
DISTRIBUTED_SERVICE_NAME)

View File

@ -30,7 +30,8 @@ class P2PSecurityTest : NodeBasedTest() {
@Test
fun `incorrect legal name for the network map service config`() {
val incorrectNetworkMapName = getX500Name(O = "NetworkMap-${random63BitValue()}", L = "London", C = "GB")
val incorrectNetworkMapName = CordaX500Name(organisation = "NetworkMap-${random63BitValue()}",
locality = "London", country = "GB")
val node = startNode(BOB.name, configOverrides = mapOf(
"networkMapService" to mapOf(
"address" to networkMapNode.internals.configuration.p2pAddress.toString(),
@ -57,7 +58,7 @@ class P2PSecurityTest : NodeBasedTest() {
private fun startSimpleNode(legalName: CordaX500Name,
trustRoot: X509Certificate): SimpleNode {
val config = testNodeConfiguration(
baseDirectory = baseDirectory(legalName.x500Name),
baseDirectory = baseDirectory(legalName),
myLegalName = legalName).also {
whenever(it.networkMapService).thenReturn(NetworkMapInfo(networkMapNode.internals.configuration.p2pAddress, networkMapNode.info.legalIdentity.name))
}

View File

@ -153,7 +153,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
protected val myLegalName: CordaX500Name by lazy {
val cert = loadKeyStore(configuration.nodeKeystore, configuration.keyStorePassword).getX509Certificate(X509Utilities.CORDA_CLIENT_CA)
CordaX500Name.build(cert.subject).copy(commonName = null)
CordaX500Name.build(cert.subjectX500Principal).copy(commonName = null)
}
open val pluginRegistries: List<CordaPluginRegistry> by lazy {
@ -631,7 +631,11 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
}
}
val subject = CordaX500Name.build(certificates[0].toX509CertHolder().subject)
val nodeCertificate: X509Certificate = if (certificates[0] is X509Certificate)
certificates[0] as X509Certificate
else
throw ConfigurationException("Node certificate must be an X.509 certificate")
val subject: CordaX500Name? = CordaX500Name.build(nodeCertificate.subjectX500Principal)
if (subject != name)
throw ConfigurationException("The name for $id doesn't match what's in the key store: $name vs $subject")
@ -681,7 +685,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
val trustStore = KeyStoreWrapper(configuration.trustStoreFile, configuration.trustStorePassword)
val caKeyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword)
makeIdentityService(
trustStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA).cert,
trustStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA),
caKeyStore.certificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA),
info.legalIdentityAndCert)
}

View File

@ -7,10 +7,7 @@ import com.typesafe.config.ConfigRenderOptions
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SignatureScheme
import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.copyTo
import net.corda.core.internal.createDirectories
import net.corda.core.internal.div
import net.corda.core.internal.exists
import net.corda.core.internal.*
import net.corda.core.utilities.loggerFor
import net.corda.node.utilities.*
import net.corda.nodeapi.config.SSLConfiguration
@ -82,7 +79,7 @@ fun createKeystoreForCordaNode(sslKeyStorePath: Path,
legalName: CordaX500Name,
signatureScheme: SignatureScheme = X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) {
val rootCACert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA)
val rootCACert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA).toX509CertHolder()
val (intermediateCACert, intermediateCAKeyPair) = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, caKeyPassword)
val clientKey = Crypto.generateKeyPair(signatureScheme)
@ -92,12 +89,12 @@ fun createKeystoreForCordaNode(sslKeyStorePath: Path,
val clientCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA,
intermediateCACert,
intermediateCAKeyPair,
clientName.copy(commonName = X509Utilities.CORDA_CLIENT_CA_CN).x500Name,
clientName.copy(commonName = X509Utilities.CORDA_CLIENT_CA_CN),
clientKey.public,
nameConstraints = nameConstraints)
val tlsKey = Crypto.generateKeyPair(signatureScheme)
val clientTLSCert = X509Utilities.createCertificate(CertificateType.TLS, clientCACert, clientKey, clientName.x500Name, tlsKey.public)
val clientTLSCert = X509Utilities.createCertificate(CertificateType.TLS, clientCACert, clientKey, clientName, tlsKey.public)
val keyPass = keyPassword.toCharArray()

View File

@ -106,8 +106,8 @@ class InMemoryIdentityService(identities: Iterable<PartyAndCertificate> = emptyS
val results = LinkedHashSet<Party>()
for ((x500name, partyAndCertificate) in principalToParties) {
val party = partyAndCertificate.party
for (rdn in x500name.x500Name.rdNs) {
val component = rdn.first.value.toString()
val components = listOf(x500name.commonName, x500name.organisationUnit, x500name.organisation, x500name.locality, x500name.state, x500name.country).filterNotNull()
components.forEach { component ->
if (exactMatch && component == query) {
results += party
} else if (!exactMatch) {

View File

@ -164,8 +164,8 @@ class PersistentIdentityService(identities: Iterable<PartyAndCertificate> = empt
val results = LinkedHashSet<Party>()
for ((x500name, partyId) in principalToParties.allPersisted()) {
val party = keyToParties[partyId]!!.party
for (rdn in x500name.x500Name.rdNs) {
val component = rdn.first.value.toString()
val components = listOf(x500name.commonName, x500name.organisationUnit, x500name.organisation, x500name.locality, x500name.state, x500name.country).filterNotNull()
components.forEach { component ->
if (exactMatch && component == query) {
results += party
} else if (!exactMatch) {

View File

@ -35,7 +35,7 @@ fun freshCertificate(identityService: IdentityService,
val issuerCertificate = issuer.certificate
val window = X509Utilities.getCertificateValidityWindow(Duration.ZERO, 3650.days, issuerCertificate)
val ourCertificate = X509Utilities.createCertificate(CertificateType.IDENTITY, issuerCertificate.subject,
issuerSigner, issuer.name.x500Name, subjectPublicKey, window)
issuerSigner, issuer.name, subjectPublicKey, window)
val certFactory = CertificateFactory.getInstance("X509")
val ourCertPath = certFactory.generateCertPath(listOf(ourCertificate.cert) + issuer.certPath.certificates)
val anonymisedIdentity = PartyAndCertificate(ourCertPath)

View File

@ -11,7 +11,6 @@ import net.corda.core.internal.ThreadBox
import net.corda.core.internal.concurrent.openFuture
import net.corda.core.internal.div
import net.corda.core.internal.noneOrSingle
import net.corda.core.internal.toX509CertHolder
import net.corda.core.node.NodeInfo
import net.corda.core.node.services.NetworkMapCache
import net.corda.core.node.services.NetworkMapCache.MapChange
@ -57,6 +56,7 @@ import java.math.BigInteger
import java.security.KeyStore
import java.security.KeyStoreException
import java.security.Principal
import java.security.cert.X509Certificate
import java.util.*
import java.util.concurrent.Executor
import java.util.concurrent.ScheduledExecutorService
@ -71,8 +71,8 @@ import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag.RE
import javax.security.auth.login.FailedLoginException
import javax.security.auth.login.LoginException
import javax.security.auth.spi.LoginModule
import javax.security.auth.x500.X500Principal
import javax.security.cert.CertificateException
import javax.security.cert.X509Certificate
// TODO: Verify that nobody can connect to us and fiddle with our config over the socket due to the secman.
// TODO: Implement a discovery engine that can trigger builds of new connections when another node registers? (later)
@ -506,13 +506,12 @@ private class VerifyingNettyConnector(configuration: MutableMap<String, Any>,
"misconfiguration by the remote peer or an SSL man-in-the-middle attack!"
}
// Make sure certificate has the same name.
val peerCertificate = session.peerCertificateChain[0].toX509CertHolder()
val peerCertificateName = CordaX500Name.build(peerCertificate.subject)
val peerCertificateName = CordaX500Name.build(X500Principal(session.peerCertificateChain[0].subjectDN.name))
require(peerCertificateName == expectedLegalName) {
"Peer has wrong subject name in the certificate - expected $expectedLegalName but got $peerCertificateName. This is either a fatal " +
"misconfiguration by the remote peer or an SSL man-in-the-middle attack!"
}
X509Utilities.validateCertificateChain(session.localCertificates.last().toX509CertHolder(), *session.peerCertificates)
X509Utilities.validateCertificateChain(session.localCertificates.last() as java.security.cert.X509Certificate, *session.peerCertificates)
server.onTcpConnection(peerLegalName)
} catch (e: IllegalArgumentException) {
connection.close()
@ -528,7 +527,7 @@ sealed class CertificateChainCheckPolicy {
@FunctionalInterface
interface Check {
fun checkCertificateChain(theirChain: Array<X509Certificate>)
fun checkCertificateChain(theirChain: Array<javax.security.cert.X509Certificate>)
}
abstract fun createCheck(keyStore: KeyStore, trustStore: KeyStore): Check
@ -536,7 +535,7 @@ sealed class CertificateChainCheckPolicy {
object Any : CertificateChainCheckPolicy() {
override fun createCheck(keyStore: KeyStore, trustStore: KeyStore): Check {
return object : Check {
override fun checkCertificateChain(theirChain: Array<X509Certificate>) {
override fun checkCertificateChain(theirChain: Array<javax.security.cert.X509Certificate>) {
}
}
}
@ -546,7 +545,7 @@ sealed class CertificateChainCheckPolicy {
override fun createCheck(keyStore: KeyStore, trustStore: KeyStore): Check {
val rootPublicKey = trustStore.getCertificate(CORDA_ROOT_CA).publicKey
return object : Check {
override fun checkCertificateChain(theirChain: Array<X509Certificate>) {
override fun checkCertificateChain(theirChain: Array<javax.security.cert.X509Certificate>) {
val theirRoot = theirChain.last().publicKey
if (rootPublicKey != theirRoot) {
throw CertificateException("Root certificate mismatch, their root = $theirRoot")
@ -560,7 +559,7 @@ sealed class CertificateChainCheckPolicy {
override fun createCheck(keyStore: KeyStore, trustStore: KeyStore): Check {
val ourPublicKey = keyStore.getCertificate(CORDA_CLIENT_TLS).publicKey
return object : Check {
override fun checkCertificateChain(theirChain: Array<X509Certificate>) {
override fun checkCertificateChain(theirChain: Array<javax.security.cert.X509Certificate>) {
val theirLeaf = theirChain.first().publicKey
if (ourPublicKey != theirLeaf) {
throw CertificateException("Leaf certificate mismatch, their leaf = $theirLeaf")
@ -574,7 +573,7 @@ sealed class CertificateChainCheckPolicy {
override fun createCheck(keyStore: KeyStore, trustStore: KeyStore): Check {
val trustedPublicKeys = trustedAliases.map { trustStore.getCertificate(it).publicKey }.toSet()
return object : Check {
override fun checkCertificateChain(theirChain: Array<X509Certificate>) {
override fun checkCertificateChain(theirChain: Array<javax.security.cert.X509Certificate>) {
if (!theirChain.any { it.publicKey in trustedPublicKeys }) {
throw CertificateException("Their certificate chain contained none of the trusted ones")
}
@ -664,19 +663,19 @@ class NodeLoginModule : LoginModule {
}
}
private fun authenticateNode(certificates: Array<X509Certificate>): String {
private fun authenticateNode(certificates: Array<javax.security.cert.X509Certificate>): String {
nodeCertCheck.checkCertificateChain(certificates)
principals += RolePrincipal(NODE_ROLE)
return certificates.first().subjectDN.name
}
private fun authenticateVerifier(certificates: Array<X509Certificate>): String {
private fun authenticateVerifier(certificates: Array<javax.security.cert.X509Certificate>): String {
verifierCertCheck.checkCertificateChain(certificates)
principals += RolePrincipal(VERIFIER_ROLE)
return certificates.first().subjectDN.name
}
private fun authenticatePeer(certificates: Array<X509Certificate>): String {
private fun authenticatePeer(certificates: Array<javax.security.cert.X509Certificate>): String {
peerCertCheck.checkCertificateChain(certificates)
principals += RolePrincipal(PEER_ROLE)
return certificates.first().subjectDN.name
@ -694,7 +693,7 @@ class NodeLoginModule : LoginModule {
return username
}
private fun determineUserRole(certificates: Array<X509Certificate>?, username: String): String? {
private fun determineUserRole(certificates: Array<javax.security.cert.X509Certificate>?, username: String): String? {
fun requireTls() = require(certificates != null) { "No TLS?" }
return when (username) {
PEER_USER -> {

View File

@ -48,6 +48,7 @@ import javax.persistence.Column
import javax.persistence.Entity
import javax.persistence.Id
import javax.persistence.Lob
import javax.security.auth.x500.X500Principal
// TODO: Stop the wallet explorer and other clients from using this class and get rid of persistentInbox
@ -247,8 +248,8 @@ class NodeMessagingClient(override val config: NodeConfiguration,
}
}, {})
val myLegalName = loadKeyStore(config.sslKeystore, config.keyStorePassword).getX509Certificate(X509Utilities.CORDA_CLIENT_TLS).subject
rpcServer = RPCServer(rpcOps, NODE_USER, NODE_USER, locator, userService, CordaX500Name.build(myLegalName))
val myCert = loadKeyStore(config.sslKeystore, config.keyStorePassword).getX509Certificate(X509Utilities.CORDA_CLIENT_TLS)
rpcServer = RPCServer(rpcOps, NODE_USER, NODE_USER, locator, userService, CordaX500Name.build(myCert.subjectX500Principal))
fun checkVerifierCount() {
if (session.queueQuery(SimpleString(VERIFICATION_REQUESTS_QUEUE_NAME)).consumerCount == 0) {

View File

@ -16,6 +16,7 @@ import java.security.*
import java.security.cert.CertPath
import java.security.cert.Certificate
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
val KEYSTORE_TYPE = "JKS"
@ -136,7 +137,7 @@ fun KeyStore.getKeyPair(alias: String, keyPassword: String): KeyPair = getCertif
* @param keyPassword The password for the PrivateKey (not the store access password).
*/
fun KeyStore.getCertificateAndKeyPair(alias: String, keyPassword: String): CertificateAndKeyPair {
val cert = getX509Certificate(alias)
val cert = getX509Certificate(alias).toX509CertHolder()
val publicKey = Crypto.toSupportedPublicKey(cert.subjectPublicKeyInfo)
return CertificateAndKeyPair(cert, KeyPair(publicKey, getSupportedKey(alias, keyPassword)))
}
@ -146,9 +147,9 @@ fun KeyStore.getCertificateAndKeyPair(alias: String, keyPassword: String): Certi
* @param alias The name to lookup the Key and Certificate chain from.
* @return The X509Certificate found in the KeyStore under the specified alias.
*/
fun KeyStore.getX509Certificate(alias: String): X509CertificateHolder {
val certificate = getCertificate(alias) ?: throw IllegalArgumentException("No certificate under alias \"$alias\"")
return certificate.toX509CertHolder()
fun KeyStore.getX509Certificate(alias: String): X509Certificate {
val certificate = getCertificate(alias) ?: throw IllegalArgumentException("No certificate under alias \"$alias\".")
return certificate as? X509Certificate ?: throw IllegalArgumentException("Certificate under alias \"$alias\" is not an X.509 certificate.")
}
/**
@ -179,7 +180,7 @@ class KeyStoreWrapper(private val storePath: Path, private val storePassword: St
// Assume key password = store password.
val clientCA = certificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)
// Create new keys and store in keystore.
val cert = X509Utilities.createCertificate(CertificateType.IDENTITY, clientCA.certificate, clientCA.keyPair, serviceName.x500Name, pubKey)
val cert = X509Utilities.createCertificate(CertificateType.IDENTITY, clientCA.certificate, clientCA.keyPair, serviceName, pubKey)
val certPath = CertificateFactory.getInstance("X509").generateCertPath(listOf(cert.cert) + clientCertPath)
require(certPath.certificates.isNotEmpty()) { "Certificate path cannot be empty" }
// TODO: X509Utilities.validateCertificateChain()

View File

@ -4,6 +4,7 @@ import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SignatureScheme
import net.corda.core.crypto.random63BitValue
import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.x500Name
import net.corda.core.utilities.cert
import net.corda.core.utilities.days
import net.corda.core.utilities.millis
@ -89,37 +90,43 @@ object X509Utilities {
* Create a de novo root self-signed X509 v3 CA cert.
*/
@JvmStatic
fun createSelfSignedCACertificate(subject: X500Name, keyPair: KeyPair, validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW): X509CertificateHolder {
fun createSelfSignedCACertificate(subject: CordaX500Name, keyPair: KeyPair, validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW): X509CertificateHolder {
val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second)
return createCertificate(CertificateType.ROOT_CA, subject, keyPair, subject, keyPair.public, window)
return createCertificate(CertificateType.ROOT_CA, subject.x500Name, keyPair, subject.x500Name, keyPair.public, window)
}
/**
* Create a X509 v3 cert.
* Create a X509 v3 certificate for use as a CA or for TLS. This does not require a [CordaX500Name] because the
* constraints are inappropriate for TLS/CA usage, however as a result this is unsuitable for Corda identity
* certificate generation.
*
* @param issuerCertificate The Public certificate of the root CA above this used to sign it.
* @param issuerKeyPair The KeyPair of the root CA above this used to sign it.
* @param subject subject of the generated certificate.
* @param subjectPublicKey subject 's public key.
* @param subjectPublicKey subject's public key.
* @param validityWindow The certificate's validity window. Default to [DEFAULT_VALIDITY_WINDOW] if not provided.
* @return A data class is returned containing the new intermediate CA Cert and its KeyPair for signing downstream certificates.
* Note the generated certificate tree is capped at max depth of 1 below this to be in line with commercially available certificates.
*/
@JvmStatic
fun createCertificate(certificateType: CertificateType,
issuerCertificate: X509CertificateHolder, issuerKeyPair: KeyPair,
subject: CordaX500Name, subjectPublicKey: PublicKey,
issuerCertificate: X509CertificateHolder,
issuerKeyPair: KeyPair,
subject: CordaX500Name,
subjectPublicKey: PublicKey,
validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW,
nameConstraints: NameConstraints? = null): X509CertificateHolder {
val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second, issuerCertificate)
return createCertificate(certificateType, issuerCertificate.subject, issuerKeyPair, subject.x500Name, subjectPublicKey, window, nameConstraints)
}
nameConstraints: NameConstraints? = null): X509CertificateHolder
= createCertificate(certificateType, issuerCertificate, issuerKeyPair, subject.x500Name, subjectPublicKey, validityWindow, nameConstraints)
/**
* Create a X509 v3 cert.
* Create a X509 v3 certificate for use as a CA or for TLS. This does not require a [CordaX500Name] because the
* constraints are inappropriate for TLS/CA usage, however as a result this is unsuitable for Corda identity
* certificate generation.
*
* @param issuerCertificate The Public certificate of the root CA above this used to sign it.
* @param issuerKeyPair The KeyPair of the root CA above this used to sign it.
* @param subject subject of the generated certificate.
* @param subjectPublicKey subject 's public key.
* @param subjectPublicKey subject's public key.
* @param validityWindow The certificate's validity window. Default to [DEFAULT_VALIDITY_WINDOW] if not provided.
* @return A data class is returned containing the new intermediate CA Cert and its KeyPair for signing downstream certificates.
* Note the generated certificate tree is capped at max depth of 1 below this to be in line with commercially available certificates.
@ -137,10 +144,10 @@ object X509Utilities {
}
@Throws(CertPathValidatorException::class)
fun validateCertificateChain(trustedRoot: X509CertificateHolder, vararg certificates: Certificate) {
fun validateCertificateChain(trustedRoot: X509Certificate, vararg certificates: Certificate) {
require(certificates.isNotEmpty()) { "Certificate path must contain at least one certificate" }
val certFactory = CertificateFactory.getInstance("X509")
val params = PKIXParameters(setOf(TrustAnchor(trustedRoot.cert, null)))
val params = PKIXParameters(setOf(TrustAnchor(trustedRoot, null)))
params.isRevocationEnabled = false
val certPath = certFactory.generateCertPath(certificates.toList())
val pathValidator = CertPathValidator.getInstance("PKIX")
@ -185,16 +192,37 @@ object X509Utilities {
* @param validityWindow the time period the certificate is valid for.
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
*/
fun createCertificate(certificateType: CertificateType, issuer: X500Name,
subject: X500Name, subjectPublicKey: PublicKey,
fun createCertificate(certificateType: CertificateType,
issuer: CordaX500Name,
subject: CordaX500Name,
subjectPublicKey: PublicKey,
validityWindow: Pair<Date, Date>,
nameConstraints: NameConstraints? = null): X509v3CertificateBuilder {
return createCertificate(certificateType, issuer.x500Name, subject.x500Name, subjectPublicKey, validityWindow, nameConstraints)
}
/**
* Build a partial X.509 certificate ready for signing.
*
* @param issuer name of the issuing entity.
* @param subject name of the certificate subject.
* @param subjectPublicKey public key of the certificate subject.
* @param validityWindow the time period the certificate is valid for.
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
*/
internal fun createCertificate(certificateType: CertificateType,
issuer: X500Name,
subject: X500Name,
subjectPublicKey: PublicKey,
validityWindow: Pair<Date, Date>,
nameConstraints: NameConstraints? = null): X509v3CertificateBuilder {
val serial = BigInteger.valueOf(random63BitValue())
val keyPurposes = DERSequence(ASN1EncodableVector().apply { certificateType.purposes.forEach { add(it) } })
val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(subjectPublicKey.encoded))
val builder = JcaX509v3CertificateBuilder(issuer, serial, validityWindow.first, validityWindow.second, subject, subjectPublicKey)
val builder = JcaX509v3CertificateBuilder(issuer, serial, validityWindow.first, validityWindow.second,
subject, subjectPublicKey)
.addExtension(Extension.subjectKeyIdentifier, false, BcX509ExtensionUtils().createSubjectKeyIdentifier(subjectPublicKeyInfo))
.addExtension(Extension.basicConstraints, certificateType.isCA, BasicConstraints(certificateType.isCA))
.addExtension(Extension.keyUsage, false, certificateType.keyUsage)
@ -216,11 +244,13 @@ object X509Utilities {
* @param validityWindow the time period the certificate is valid for.
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
*/
fun createCertificate(certificateType: CertificateType, issuer: X500Name, issuerSigner: ContentSigner,
subject: X500Name, subjectPublicKey: PublicKey,
validityWindow: Pair<Date, Date>,
nameConstraints: NameConstraints? = null): X509CertificateHolder {
val builder = createCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints)
internal fun createCertificate(certificateType: CertificateType,
issuer: X500Name,
issuerSigner: ContentSigner,
subject: CordaX500Name, subjectPublicKey: PublicKey,
validityWindow: Pair<Date, Date>,
nameConstraints: NameConstraints? = null): X509CertificateHolder {
val builder = createCertificate(certificateType, issuer, subject.x500Name, subjectPublicKey, validityWindow, nameConstraints)
return builder.build(issuerSigner).apply {
require(isValidOn(Date()))
}
@ -236,7 +266,7 @@ object X509Utilities {
* @param validityWindow the time period the certificate is valid for.
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
*/
fun createCertificate(certificateType: CertificateType, issuer: X500Name, issuerKeyPair: KeyPair,
internal fun createCertificate(certificateType: CertificateType, issuer: X500Name, issuerKeyPair: KeyPair,
subject: X500Name, subjectPublicKey: PublicKey,
validityWindow: Pair<Date, Date>,
nameConstraints: NameConstraints? = null): X509CertificateHolder {
@ -255,12 +285,12 @@ object X509Utilities {
/**
* Create certificate signing request using provided information.
*/
fun createCertificateSigningRequest(subject: X500Name, email: String, keyPair: KeyPair, signatureScheme: SignatureScheme): PKCS10CertificationRequest {
internal fun createCertificateSigningRequest(subject: CordaX500Name, email: String, keyPair: KeyPair, signatureScheme: SignatureScheme): PKCS10CertificationRequest {
val signer = ContentSignerBuilder.build(signatureScheme, keyPair.private, Crypto.findProvider(signatureScheme.providerName))
return JcaPKCS10CertificationRequestBuilder(subject, keyPair.public).addAttribute(BCStyle.E, DERUTF8String(email)).build(signer)
return JcaPKCS10CertificationRequestBuilder(subject.x500Name, keyPair.public).addAttribute(BCStyle.E, DERUTF8String(email)).build(signer)
}
fun createCertificateSigningRequest(subject: X500Name, email: String, keyPair: KeyPair) = createCertificateSigningRequest(subject, email, keyPair, DEFAULT_TLS_SIGNATURE_SCHEME)
fun createCertificateSigningRequest(subject: CordaX500Name, email: String, keyPair: KeyPair) = createCertificateSigningRequest(subject, email, keyPair, DEFAULT_TLS_SIGNATURE_SCHEME)
}

View File

@ -1,8 +1,8 @@
package net.corda.node.utilities.registration
import net.corda.core.crypto.Crypto
import net.corda.core.utilities.cert
import net.corda.core.internal.*
import net.corda.core.utilities.cert
import net.corda.core.utilities.seconds
import net.corda.node.services.config.NodeConfiguration
import net.corda.node.utilities.*
@ -51,7 +51,7 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v
// We use the self sign certificate to store the key temporarily in the keystore while waiting for the request approval.
if (!caKeyStore.containsAlias(SELF_SIGNED_PRIVATE_KEY)) {
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val selfSignCert = X509Utilities.createSelfSignedCACertificate(config.myLegalName.x500Name, keyPair)
val selfSignCert = X509Utilities.createSelfSignedCACertificate(config.myLegalName, keyPair)
// Save to the key store.
caKeyStore.addOrReplaceKey(SELF_SIGNED_PRIVATE_KEY, keyPair.private, privateKeyPassword.toCharArray(),
arrayOf(selfSignCert))
@ -84,7 +84,7 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v
println("Generating SSL certificate for node messaging service.")
val sslKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val caCert = caKeyStore.getX509Certificate(CORDA_CLIENT_CA)
val caCert = caKeyStore.getX509Certificate(CORDA_CLIENT_CA).toX509CertHolder()
val sslCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, keyPair, caCert.subject, sslKey.public)
val sslKeyStore = loadOrCreateKeyStore(config.sslKeystore, keystorePassword)
sslKeyStore.addOrReplaceKey(CORDA_CLIENT_TLS, sslKey.private, privateKeyPassword.toCharArray(),
@ -124,7 +124,7 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v
private fun submitOrResumeCertificateSigningRequest(keyPair: KeyPair): String {
// Retrieve request id from file if exists, else post a request to server.
return if (!requestIdStore.exists()) {
val request = X509Utilities.createCertificateSigningRequest(config.myLegalName.x500Name, config.emailAddress, keyPair)
val request = X509Utilities.createCertificateSigningRequest(config.myLegalName, config.emailAddress, keyPair)
val writer = StringWriter()
JcaPEMWriter(writer).use {
it.writeObject(PemObject("CERTIFICATE REQUEST", request.encoded))

View File

@ -7,14 +7,15 @@ import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.services.UnknownAnonymousPartyException
import net.corda.node.utilities.CertificateAndKeyPair
import net.corda.core.utilities.cert
import net.corda.node.services.identity.InMemoryIdentityService
import net.corda.node.utilities.CertificateAndKeyPair
import net.corda.node.utilities.CertificateType
import net.corda.node.utilities.X509Utilities
import net.corda.testing.*
import org.junit.Test
import java.security.cert.CertificateFactory
import javax.security.auth.x500.X500Principal
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertNull
@ -86,7 +87,7 @@ class InMemoryIdentityServiceTests {
fun `assert unknown anonymous key is unrecognised`() {
withTestSerialization {
val rootKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Name, rootKey)
val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootKey)
val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val service = InMemoryIdentityService(trustRoot = DUMMY_CA.certificate)
// TODO: Generate certificate with an EdDSA key rather than ECDSA
@ -151,7 +152,7 @@ class InMemoryIdentityServiceTests {
assertFailsWith<IllegalArgumentException> {
val owningKey = Crypto.decodePublicKey(trustRoot.certificate.subjectPublicKeyInfo.encoded)
val subject = CordaX500Name.build(trustRoot.certificate.subject)
val subject = CordaX500Name.build(X500Principal(trustRoot.certificate.subject.encoded))
service.assertOwnership(Party(subject, owningKey), anonymousAlice.party.anonymise())
}
}
@ -162,7 +163,7 @@ class InMemoryIdentityServiceTests {
val issuerKeyPair = generateKeyPair()
val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public, ca)
val txKey = Crypto.generateKeyPair()
val txCert = X509Utilities.createCertificate(CertificateType.IDENTITY, issuer.certificate, issuerKeyPair, x500Name.x500Name, txKey.public)
val txCert = X509Utilities.createCertificate(CertificateType.IDENTITY, issuer.certificate, issuerKeyPair, x500Name, txKey.public)
val txCertPath = certFactory.generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates)
return Pair(issuer, PartyAndCertificate(txCertPath))
}

View File

@ -8,9 +8,9 @@ import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.services.IdentityService
import net.corda.core.node.services.UnknownAnonymousPartyException
import net.corda.node.utilities.CertificateAndKeyPair
import net.corda.core.utilities.cert
import net.corda.node.services.identity.PersistentIdentityService
import net.corda.node.utilities.CertificateAndKeyPair
import net.corda.node.utilities.CertificateType
import net.corda.node.utilities.CordaPersistence
import net.corda.node.utilities.X509Utilities
@ -20,6 +20,7 @@ import org.junit.After
import org.junit.Before
import org.junit.Test
import java.security.cert.CertificateFactory
import javax.security.auth.x500.X500Principal
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertNull
@ -131,7 +132,7 @@ class PersistentIdentityServiceTests {
fun `assert unknown anonymous key is unrecognised`() {
withTestSerialization {
val rootKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Name, rootKey)
val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootKey)
val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_IDENTITY_SIGNATURE_SCHEME)
val identity = Party(rootCert)
val txIdentity = AnonymousParty(txKey.public)
@ -213,7 +214,7 @@ class PersistentIdentityServiceTests {
assertFailsWith<IllegalArgumentException> {
val owningKey = Crypto.decodePublicKey(trustRoot.certificate.subjectPublicKeyInfo.encoded)
database.transaction {
val subject = CordaX500Name.build(trustRoot.certificate.subject)
val subject = CordaX500Name.build(X500Principal(trustRoot.certificate.subject.encoded))
identityService.assertOwnership(Party(subject, owningKey), anonymousAlice.party.anonymise())
}
}
@ -261,7 +262,7 @@ class PersistentIdentityServiceTests {
val issuerKeyPair = generateKeyPair()
val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public, ca)
val txKey = Crypto.generateKeyPair()
val txCert = X509Utilities.createCertificate(CertificateType.IDENTITY, issuer.certificate, issuerKeyPair, x500Name.x500Name, txKey.public)
val txCert = X509Utilities.createCertificate(CertificateType.IDENTITY, issuer.certificate, issuerKeyPair, x500Name, txKey.public)
val txCertPath = certFactory.generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates)
return Pair(issuer, PartyAndCertificate(txCertPath))
}

View File

@ -3,8 +3,10 @@ package net.corda.node.utilities
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.Crypto.EDDSA_ED25519_SHA512
import net.corda.core.crypto.Crypto.generateKeyPair
import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.div
import net.corda.core.internal.toTypedArray
import net.corda.core.internal.x500Name
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
@ -15,7 +17,10 @@ import net.corda.nodeapi.internal.serialization.AllWhitelist
import net.corda.nodeapi.internal.serialization.KryoHeaderV0_1
import net.corda.nodeapi.internal.serialization.SerializationContextImpl
import net.corda.nodeapi.internal.serialization.SerializationFactoryImpl
import net.corda.testing.*
import net.corda.testing.ALICE
import net.corda.testing.BOB
import net.corda.testing.BOB_PUBKEY
import net.corda.testing.MEGA_CORP
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.asn1.x509.BasicConstraints
import org.bouncycastle.asn1.x509.Extension
@ -44,6 +49,7 @@ import javax.net.ssl.*
import kotlin.concurrent.thread
import kotlin.test.*
class X509UtilitiesTest {
@Rule
@JvmField
@ -52,8 +58,8 @@ class X509UtilitiesTest {
@Test
fun `create valid self-signed CA certificate`() {
val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val caCert = X509Utilities.createSelfSignedCACertificate(getX500Name(CN = "Test Cert", O = "R3 Ltd", L = "London", C = "GB"), caKey)
assertEquals(X500Name("CN=Test Cert,O=R3 Ltd,L=London,C=GB"), caCert.subject)
val caCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Test Cert", organisation = "R3 Ltd", locality = "London", country = "GB"), caKey)
assertEquals(X500Name("CN=Test Cert,O=R3 Ltd,L=London,C=GB"), caCert.subject) // using our subject common name
assertEquals(caCert.issuer, caCert.subject) //self-signed
caCert.isValidOn(Date()) // throws on verification problems
caCert.isSignatureValid(JcaContentVerifierProviderBuilder().build(caKey.public)) // throws on verification problems
@ -67,7 +73,7 @@ class X509UtilitiesTest {
fun `load and save a PEM file certificate`() {
val tmpCertificateFile = tempFile("cacert.pem")
val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val caCert = X509Utilities.createSelfSignedCACertificate(getX500Name(CN = "Test Cert", O = "R3 Ltd", L = "London", C = "GB"), caKey)
val caCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Test Cert", organisation = "R3 Ltd", locality = "London", country = "GB"), caKey)
X509Utilities.saveCertificateAsPEMFile(caCert, tmpCertificateFile)
val readCertificate = X509Utilities.loadCertificateFromPEMFile(tmpCertificateFile)
assertEquals(caCert, readCertificate)
@ -76,11 +82,11 @@ class X509UtilitiesTest {
@Test
fun `create valid server certificate chain`() {
val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val caCert = X509Utilities.createSelfSignedCACertificate(getX500Name(CN = "Test CA Cert", O = "R3 Ltd", L = "London", C = "GB"), caKey)
val subject = getX500Name(CN = "Server Cert", O = "R3 Ltd", L = "London", C = "GB")
val caCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Test CA Cert", organisation = "R3 Ltd", locality = "London", country = "GB"), caKey)
val subject = CordaX500Name(commonName = "Server Cert", organisation = "R3 Ltd", locality = "London", country = "GB")
val keyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val serverCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, caKey, subject, keyPair.public)
assertTrue { serverCert.subject.toString().contains("CN=Server Cert") } // using our subject common name
assertEquals(X500Name("C=GB,L=London,O=R3 Ltd,CN=Server Cert"), serverCert.subject) // using our subject common name
assertEquals(caCert.issuer, serverCert.issuer) // Issued by our CA cert
serverCert.isValidOn(Date()) // throws on verification problems
serverCert.isSignatureValid(JcaContentVerifierProviderBuilder().build(caKey.public)) // throws on verification problems
@ -95,7 +101,8 @@ class X509UtilitiesTest {
val tmpKeyStore = tempFile("keystore.jks")
val keyPair = generateKeyPair(EDDSA_ED25519_SHA512)
val selfSignCert = X509Utilities.createSelfSignedCACertificate(X500Name("CN=Test"), keyPair)
val testName = CordaX500Name(commonName = "Test", organisation = "R3 Ltd", locality = "London", country = "GB")
val selfSignCert = X509Utilities.createSelfSignedCACertificate(testName, keyPair)
assertTrue(Arrays.equals(selfSignCert.subjectPublicKeyInfo.encoded, keyPair.public.encoded))
@ -120,7 +127,8 @@ class X509UtilitiesTest {
fun `signing EdDSA key with EcDSA certificate`() {
val tmpKeyStore = tempFile("keystore.jks")
val ecDSAKey = generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
val ecDSACert = X509Utilities.createSelfSignedCACertificate(X500Name("CN=Test"), ecDSAKey)
val testName = CordaX500Name(commonName = "Test", organisation = "R3 Ltd", locality = "London", country = "GB")
val ecDSACert = X509Utilities.createSelfSignedCACertificate(testName, ecDSAKey)
val edDSAKeypair = generateKeyPair(EDDSA_ED25519_SHA512)
val edDSACert = X509Utilities.createCertificate(CertificateType.TLS, ecDSACert, ecDSAKey, X500Name("CN=TestEdDSA"), edDSAKeypair.public)
@ -311,8 +319,7 @@ class X509UtilitiesTest {
// Double check hostname manually
val peerChain = clientSocket.session.peerCertificates
val peerX500Principal = (peerChain[0] as X509Certificate).subjectX500Principal
val x500name = X500Name(peerX500Principal.name)
assertEquals(MEGA_CORP.name.x500Name, x500name)
assertEquals(MEGA_CORP.name.x500Principal, peerX500Principal)
X509Utilities.validateCertificateChain(trustStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA), *peerChain)
val output = DataOutputStream(clientSocket.outputStream)
output.writeUTF("Hello World")
@ -353,10 +360,10 @@ class X509UtilitiesTest {
trustStorePassword: String
): KeyStore {
val rootCAKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val rootCACert = X509Utilities.createSelfSignedCACertificate(getX500Name(CN = "Corda Node Root CA", O = "R3CEV", L = "London", C = "GB"), rootCAKey)
val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", organisation = "R3CEV", locality = "London", country = "GB"), rootCAKey)
val intermediateCAKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, getX500Name(CN = "Corda Node Intermediate CA", O = "R3CEV", L = "London", C = "GB"), intermediateCAKeyPair.public)
val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, CordaX500Name(commonName = "Corda Node Intermediate CA", organisation = "R3CEV", locality = "London", country = "GB"), intermediateCAKeyPair.public)
val keyPass = keyPassword.toCharArray()
val keyStore = loadOrCreateKeyStore(keyStoreFilePath, storePassword)
@ -383,7 +390,8 @@ class X509UtilitiesTest {
@Test
fun `Get correct private key type from Keystore`() {
val keyPair = generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
val selfSignCert = X509Utilities.createSelfSignedCACertificate(X500Name("CN=Test"), keyPair)
val testName = CordaX500Name(commonName = "Test", organisation = "R3 Ltd", locality = "London", country = "GB")
val selfSignCert = X509Utilities.createSelfSignedCACertificate(testName, keyPair)
val keyStore = loadOrCreateKeyStore(tempFile("testKeystore.jks"), "keystorepassword")
keyStore.setKeyEntry("Key", keyPair.private, "keypassword".toCharArray(), arrayOf(selfSignCert.cert))
@ -403,7 +411,7 @@ class X509UtilitiesTest {
emptyMap(),
true,
SerializationContext.UseCase.P2P)
val expected: X509CertificateHolder = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Name, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
val expected: X509CertificateHolder = X509Utilities.createSelfSignedCACertificate(ALICE.name, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
val serialized = expected.serialize(factory, context).bytes
val actual: X509CertificateHolder = serialized.deserialize(factory, context)
assertEquals(expected, actual)
@ -420,7 +428,7 @@ class X509UtilitiesTest {
SerializationContext.UseCase.P2P)
val certFactory = CertificateFactory.getInstance("X509")
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val rootCACert = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Name, rootCAKey)
val rootCACert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootCAKey)
val certificate = X509Utilities.createCertificate(CertificateType.TLS, rootCACert, rootCAKey, BOB.name.x500Name, BOB_PUBKEY)
val expected = certFactory.generateCertPath(listOf(certificate.cert, rootCACert.cert))
val serialized = expected.serialize(factory, context).bytes

View File

@ -5,16 +5,18 @@ import com.nhaarman.mockito_kotlin.eq
import com.nhaarman.mockito_kotlin.mock
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SecureHash
import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.exists
import net.corda.core.internal.toTypedArray
import net.corda.core.internal.toX509CertHolder
import net.corda.core.utilities.cert
import net.corda.core.utilities.commonName
import net.corda.node.utilities.X509Utilities
import net.corda.node.utilities.loadKeyStore
import net.corda.testing.ALICE
import net.corda.testing.getX500Name
import net.corda.testing.testNodeConfiguration
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.asn1.x500.style.BCStyle
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
@ -22,6 +24,8 @@ import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
val X500Name.commonName: String? get() = getRDNs(BCStyle.CN).firstOrNull()?.first?.value?.toString()
class NetworkRegistrationHelperTest {
@Rule
@JvmField
@ -34,7 +38,7 @@ class NetworkRegistrationHelperTest {
val identities = listOf("CORDA_CLIENT_CA",
"CORDA_INTERMEDIATE_CA",
"CORDA_ROOT_CA")
.map { getX500Name(CN = it, O = "R3 Ltd", L = "London", C = "GB") }
.map { CordaX500Name(commonName = it, organisation = "R3 Ltd", locality = "London", country = "GB") }
val certs = identities.stream().map { X509Utilities.createSelfSignedCACertificate(it, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) }
.map { it.cert }.toTypedArray()

View File

@ -22,7 +22,7 @@ fun main(args: Array<String>) = BFTNotaryCordform.runNodes()
private val clusterSize = 4 // Minimum size that tolerates a faulty replica.
private val notaryNames = createNotaryNames(clusterSize)
object BFTNotaryCordform : CordformDefinition("build" / "notary-demo-nodes", notaryNames[0].x500Name) {
object BFTNotaryCordform : CordformDefinition("build" / "notary-demo-nodes", notaryNames[0].toString()) {
private val clusterName = CordaX500Name(organisation = "BFT", locality = "Zurich", country = "CH")
private val advertisedService = ServiceInfo(BFTNonValidatingNotaryService.type, clusterName)
@ -65,6 +65,6 @@ object BFTNotaryCordform : CordformDefinition("build" / "notary-demo-nodes", not
}
override fun setup(context: CordformContext) {
ServiceIdentityGenerator.generateToDisk(notaryNames.map { context.baseDirectory(it.x500Name) }, advertisedService.type.id, clusterName, minCorrectReplicas(clusterSize))
ServiceIdentityGenerator.generateToDisk(notaryNames.map(CordaX500Name::toString).map(context::baseDirectory), advertisedService.type.id, clusterName, minCorrectReplicas(clusterSize))
}
}

View File

@ -20,7 +20,7 @@ internal fun createNotaryNames(clusterSize: Int) = (0 until clusterSize).map { C
private val notaryNames = createNotaryNames(3)
object RaftNotaryCordform : CordformDefinition("build" / "notary-demo-nodes", notaryNames[0].x500Name) {
object RaftNotaryCordform : CordformDefinition("build" / "notary-demo-nodes", notaryNames[0].toString()) {
private val clusterName = CordaX500Name(organisation = "Raft", locality = "Zurich", country = "CH")
private val advertisedService = ServiceInfo(RaftValidatingNotaryService.type, clusterName)
@ -62,6 +62,6 @@ object RaftNotaryCordform : CordformDefinition("build" / "notary-demo-nodes", no
}
override fun setup(context: CordformContext) {
ServiceIdentityGenerator.generateToDisk(notaryNames.map { context.baseDirectory(it.x500Name) }, advertisedService.type.id, clusterName)
ServiceIdentityGenerator.generateToDisk(notaryNames.map(CordaX500Name::toString).map(context::baseDirectory), advertisedService.type.id, clusterName)
}
}

View File

@ -19,7 +19,7 @@ fun main(args: Array<String>) = SingleNotaryCordform.runNodes()
val notaryDemoUser = User("demou", "demop", setOf(startFlowPermission<DummyIssueAndMove>(), startFlowPermission<RPCStartableNotaryFlowClient>()))
object SingleNotaryCordform : CordformDefinition("build" / "notary-demo-nodes", DUMMY_NOTARY.name.x500Name) {
object SingleNotaryCordform : CordformDefinition("build" / "notary-demo-nodes", DUMMY_NOTARY.name.toString()) {
init {
node {
name(ALICE.name)

View File

@ -6,6 +6,7 @@ import net.corda.core.identity.CordaX500Name
import net.corda.testing.driver.NetworkMapStartStrategy
import net.corda.testing.driver.PortAllocation
import net.corda.testing.driver.driver
import javax.security.auth.x500.X500Principal
fun CordformDefinition.clean() {
System.err.println("Deleting: $driverDirectory")
@ -18,7 +19,7 @@ fun CordformDefinition.clean() {
fun CordformDefinition.runNodes() = driver(
isDebug = true,
driverDirectory = driverDirectory,
networkMapStartStrategy = NetworkMapStartStrategy.Nominated(CordaX500Name.build(networkMapNodeName)),
networkMapStartStrategy = NetworkMapStartStrategy.Nominated(CordaX500Name.parse(networkMapNodeName)),
portAllocation = PortAllocation.Incremental(10001)
) {
setup(this)

View File

@ -38,7 +38,6 @@ import net.corda.testing.*
import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO
import okhttp3.OkHttpClient
import okhttp3.Request
import org.bouncycastle.asn1.x500.X500Name
import org.slf4j.Logger
import java.io.File
import java.net.*
@ -681,7 +680,7 @@ class DriverDSL(
override fun getConfig() = configOf("p2pAddress" to p2pAddress.toString())
}))
val config = ConfigHelper.loadConfig(
baseDirectory = baseDirectory(name.x500Name),
baseDirectory = baseDirectory(name),
allowMissingConfig = true,
configOverrides = configOf(
"myLegalName" to name.toString(),
@ -706,7 +705,7 @@ class DriverDSL(
val name = CordaX500Name.parse(node.name)
val config = ConfigHelper.loadConfig(
baseDirectory = baseDirectory(name.x500Name),
baseDirectory = baseDirectory(name),
allowMissingConfig = true,
configOverrides = node.config + mapOf(
"extraAdvertisedServiceIds" to node.advertisedServices,
@ -728,7 +727,7 @@ class DriverDSL(
startInSameProcess: Boolean?
): CordaFuture<Pair<Party, List<NodeHandle>>> {
val nodeNames = (0 until clusterSize).map { CordaX500Name(organisation = "Notary Service $it", locality = "Zurich", country = "CH") }
val paths = nodeNames.map { baseDirectory(it.x500Name) }
val paths = nodeNames.map { baseDirectory(it) }
ServiceIdentityGenerator.generateToDisk(paths, type.id, notaryName)
val advertisedServices = setOf(ServiceInfo(type, notaryName))
val notaryClusterAddress = portAllocation.nextHostAndPort()
@ -793,16 +792,19 @@ class DriverDSL(
}
}
override fun baseDirectory(nodeName: X500Name): Path {
fun baseDirectory(nodeName: CordaX500Name): Path {
val nodeDirectoryName = String(nodeName.organisation.filter { !it.isWhitespace() }.toCharArray())
return driverDirectory / nodeDirectoryName
}
override fun baseDirectory(nodeName: String): Path = baseDirectory(CordaX500Name.parse(nodeName))
override fun startDedicatedNetworkMapService(startInProcess: Boolean?): CordaFuture<NodeHandle> {
val webAddress = portAllocation.nextHostAndPort()
val networkMapLegalName = networkMapStartStrategy.legalName
val config = ConfigHelper.loadConfig(
baseDirectory = baseDirectory(networkMapLegalName.x500Name),
baseDirectory = baseDirectory(networkMapLegalName),
allowMissingConfig = true,
configOverrides = configOf(
"myLegalName" to networkMapLegalName.toString(),

View File

@ -8,7 +8,6 @@ import net.corda.core.internal.div
import net.corda.core.node.services.ServiceInfo
import net.corda.core.node.services.ServiceType
import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.organisation
import net.corda.node.internal.Node
import net.corda.node.internal.StartedNode
import net.corda.node.services.config.ConfigHelper
@ -24,10 +23,8 @@ import net.corda.testing.DUMMY_MAP
import net.corda.testing.TestDependencyInjectionBase
import net.corda.testing.driver.addressMustNotBeBoundFuture
import net.corda.testing.getFreeLocalPorts
import net.corda.testing.getX500Name
import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO
import org.apache.logging.log4j.Level
import org.bouncycastle.asn1.x500.X500Name
import org.junit.After
import org.junit.Rule
import org.junit.rules.TemporaryFolder
@ -135,7 +132,7 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() {
clusterSize: Int,
serviceType: ServiceType = RaftValidatingNotaryService.type): CordaFuture<List<StartedNode<Node>>> {
ServiceIdentityGenerator.generateToDisk(
(0 until clusterSize).map { baseDirectory(getX500Name(O = "${notaryName.organisation}-$it", L = notaryName.locality, C = notaryName.country)) },
(0 until clusterSize).map { baseDirectory(notaryName.copy(organisation = "${notaryName.organisation}-$it")) },
serviceType.id,
notaryName)
@ -163,7 +160,7 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() {
}
}
protected fun baseDirectory(legalName: X500Name) = tempFolder.root.toPath() / legalName.organisation.replace(WHITESPACE, "")
protected fun baseDirectory(legalName: CordaX500Name) = tempFolder.root.toPath() / legalName.organisation.replace(WHITESPACE, "")
private fun startNodeInternal(legalName: CordaX500Name,
platformVersion: Int,
@ -171,7 +168,7 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() {
rpcUsers: List<User>,
configOverrides: Map<String, Any>,
noNetworkMap: Boolean = false): StartedNode<Node> {
val baseDirectory = baseDirectory(legalName.x500Name).createDirectories()
val baseDirectory = baseDirectory(legalName).createDirectories()
val localPort = getFreeLocalPorts("localhost", 2)
val p2pAddress = configOverrides["p2pAddress"] ?: localPort[0].toString()
val config = ConfigHelper.loadConfig(

View File

@ -128,7 +128,7 @@ fun configureTestSSL(legalName: CordaX500Name = MEGA_CORP.name): SSLConfiguratio
fun getTestPartyAndCertificate(party: Party, trustRoot: CertificateAndKeyPair = DUMMY_CA): PartyAndCertificate {
val certFactory = CertificateFactory.getInstance("X509")
val certHolder = X509Utilities.createCertificate(CertificateType.IDENTITY, trustRoot.certificate, trustRoot.keyPair, party.name.x500Name, party.owningKey)
val certHolder = X509Utilities.createCertificate(CertificateType.IDENTITY, trustRoot.certificate, trustRoot.keyPair, party.name, party.owningKey)
val certPath = certFactory.generateCertPath(listOf(certHolder.cert, trustRoot.certificate.cert))
return PartyAndCertificate(certPath)
}

View File

@ -65,7 +65,7 @@ val DUMMY_REGULATOR: Party get() = Party(CordaX500Name(organisation = "Regulator
val DUMMY_CA_KEY: KeyPair by lazy { entropyToKeyPair(BigInteger.valueOf(110)) }
val DUMMY_CA: CertificateAndKeyPair by lazy {
// TODO: Should be identity scheme
val cert = X509Utilities.createSelfSignedCACertificate(getX500Name(CN = "Dummy CA", OU = "Corda", O = "R3 Ltd", L = "London", C = "GB"), DUMMY_CA_KEY)
val cert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Dummy CA", organisationUnit = "Corda", organisation = "R3 Ltd", locality = "London", state = null, country = "GB"), DUMMY_CA_KEY)
CertificateAndKeyPair(cert, DUMMY_CA_KEY)
}