diff --git a/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesFlow.kt b/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesFlow.kt index 575c1f8d52..f98970c322 100644 --- a/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesFlow.kt +++ b/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesFlow.kt @@ -10,7 +10,6 @@ import net.corda.core.identity.AnonymousParty import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate -import net.corda.core.internal.toX509CertHolder import net.corda.core.node.services.IdentityService import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.SerializedBytes @@ -18,14 +17,8 @@ import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.unwrap -import org.bouncycastle.asn1.DERSet -import org.bouncycastle.asn1.pkcs.CertificationRequestInfo -import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo -import java.io.ByteArrayOutputStream -import java.nio.charset.Charset import java.security.PublicKey import java.security.SignatureException -import java.security.cert.CertPath import java.util.* /** diff --git a/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt b/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt index ad315c065e..abe3d21fd4 100644 --- a/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt +++ b/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt @@ -80,8 +80,10 @@ data class CordaX500Name(val commonName: String?, const val MAX_LENGTH_STATE = 64 const val MAX_LENGTH_ORGANISATION_UNIT = 64 const val MAX_LENGTH_COMMON_NAME = 64 + private val supportedAttributes = setOf(BCStyle.O, BCStyle.C, BCStyle.L, BCStyle.CN, BCStyle.ST, BCStyle.OU) private val countryCodes: Set = ImmutableSet.copyOf(Locale.getISOCountries() + unspecifiedCountry) + @JvmStatic fun build(principal: X500Principal): CordaX500Name { val x500Name = X500Name.getInstance(principal.encoded) @@ -115,20 +117,12 @@ data class CordaX500Name(val commonName: String?, } @Transient - private var x500Cache: X500Name? = null + private var _x500Principal: X500Principal? = null - val x500Principal: X500Principal - get() { - if (x500Cache == null) { - x500Cache = this.x500Name - } - return X500Principal(x500Cache!!.encoded) - } - - override fun toString(): String { - if (x500Cache == null) { - x500Cache = this.x500Name - } - return x500Cache.toString() + /** Return the [X500Principal] equivalent of this name. */ + val x500Principal: X500Principal get() { + return _x500Principal ?: X500Principal(this.x500Name.encoded).also { _x500Principal = it } } + + override fun toString(): String = x500Principal.toString() } \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt index 42145a2f26..1ad9e16630 100644 --- a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt @@ -10,8 +10,9 @@ import net.corda.core.node.ServicesForResolution import net.corda.core.serialization.SerializationContext import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.WireTransaction -import org.bouncycastle.cert.X509CertificateHolder -import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter +import org.bouncycastle.asn1.x500.X500Name +import org.bouncycastle.asn1.x500.X500NameBuilder +import org.bouncycastle.asn1.x500.style.BCStyle import org.slf4j.Logger import rx.Observable import rx.Observer @@ -26,8 +27,6 @@ import java.nio.charset.Charset import java.nio.charset.StandardCharsets.UTF_8 import java.nio.file.* import java.nio.file.attribute.FileAttribute -import java.security.cert.Certificate -import java.security.cert.X509Certificate import java.time.Duration import java.time.temporal.Temporal import java.util.* @@ -186,9 +185,6 @@ fun logElapsedTime(label: String, logger: Logger? = null, body: () -> T): T } } -fun Certificate.toX509CertHolder() = X509CertificateHolder(encoded) -val X509CertificateHolder.cert: X509Certificate get() = JcaX509CertificateConverter().getCertificate(this) - /** Convert a [ByteArrayOutputStream] to [InputStreamAndHash]. */ fun ByteArrayOutputStream.toInputStreamAndHash(): InputStreamAndHash { val bytes = toByteArray() @@ -320,6 +316,22 @@ fun ExecutorService.join() { } } +/** + * 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() + } + @Suppress("unused") @VisibleForTesting val CordaX500Name.Companion.unspecifiedCountry diff --git a/core/src/main/kotlin/net/corda/core/internal/X500NameUtils.kt b/core/src/main/kotlin/net/corda/core/internal/X500NameUtils.kt deleted file mode 100644 index 7d57d7d522..0000000000 --- a/core/src/main/kotlin/net/corda/core/internal/X500NameUtils.kt +++ /dev/null @@ -1,34 +0,0 @@ -@file:JvmName("X500NameUtils") - -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) -val X500Name.state: String? get() = getRDNValueString(BCStyle.ST) -val X500Name.organisation: String get() = getRDNValueString(BCStyle.O) ?: throw IllegalArgumentException("Malformed X500 name, organisation attribute (O) cannot be empty.") -val X500Name.locality: String get() = getRDNValueString(BCStyle.L) ?: throw IllegalArgumentException("Malformed X500 name, locality attribute (L) cannot be empty.") -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() - } \ No newline at end of file diff --git a/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt b/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt index 751037b1ee..61d0c3d2f6 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt @@ -1,8 +1,6 @@ package net.corda.core.crypto import net.corda.core.crypto.CompositeKey.NodeAndWeight -import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.cert import net.corda.core.internal.declaredField import net.corda.core.internal.div import net.corda.core.serialization.serialize @@ -15,6 +13,7 @@ import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder import java.security.PublicKey +import javax.security.auth.x500.X500Principal import kotlin.test.assertEquals import kotlin.test.assertFailsWith import kotlin.test.assertFalse @@ -334,7 +333,7 @@ class CompositeKeyTests { // Create self sign CA. val caKeyPair = Crypto.generateKeyPair() - val caName = CordaX500Name(commonName = "Test CA", organisation = "R3 Ltd", locality = "London", country = "GB") + val caName = X500Principal("CN=Test CA,O=R3 Ltd,L=London,C=GB") val ca = X509Utilities.createSelfSignedCACertificate(caName, caKeyPair) // Sign the composite key with the self sign CA. @@ -343,7 +342,7 @@ class CompositeKeyTests { // Store certificate to keystore. val keystorePath = tempFolder.root.toPath() / "keystore.jks" val keystore = loadOrCreateKeyStore(keystorePath, "password") - keystore.setCertificateEntry("CompositeKey", compositeKeyCert.cert) + keystore.setCertificateEntry("CompositeKey", compositeKeyCert) keystore.save(keystorePath, "password") // Load keystore from disk. diff --git a/core/src/test/kotlin/net/corda/core/crypto/X509NameConstraintsTest.kt b/core/src/test/kotlin/net/corda/core/crypto/X509NameConstraintsTest.kt index 707a32bba4..3e51ad7b9f 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/X509NameConstraintsTest.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/X509NameConstraintsTest.kt @@ -1,7 +1,6 @@ package net.corda.core.crypto import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.cert import net.corda.nodeapi.internal.crypto.* import net.corda.testing.internal.createDevIntermediateCaCertPath import org.bouncycastle.asn1.x500.X500Name @@ -14,6 +13,7 @@ import java.security.KeyStore import java.security.cert.CertPathValidator import java.security.cert.CertPathValidatorException import java.security.cert.PKIXParameters +import javax.security.auth.x500.X500Principal import kotlin.test.assertFailsWith import kotlin.test.assertTrue @@ -26,17 +26,22 @@ class X509NameConstraintsTest { CertificateType.NODE_CA, intermediateCa.certificate, intermediateCa.keyPair, - CordaX500Name("Corda Client CA", "R3 Ltd", "London", "GB"), + CordaX500Name("Corda Client CA", "R3 Ltd", "London", "GB").x500Principal, nodeCaKeyPair.public, nameConstraints = nameConstraints) val keyPass = "password" val trustStore = KeyStore.getInstance(KEYSTORE_TYPE) trustStore.load(null, keyPass.toCharArray()) - trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCa.certificate.cert) + trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCa.certificate) val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, subjectName, tlsKeyPair.public) + val tlsCert = X509Utilities.createCertificate( + CertificateType.TLS, + nodeCaCert, + nodeCaKeyPair, + X500Principal(subjectName.encoded), + tlsKeyPair.public) val keyStore = KeyStore.getInstance(KEYSTORE_TYPE) keyStore.load(null, keyPass.toCharArray()) diff --git a/core/src/test/kotlin/net/corda/core/identity/CordaX500NameTest.kt b/core/src/test/kotlin/net/corda/core/identity/CordaX500NameTest.kt index 766fa202a4..3cfc7c089b 100644 --- a/core/src/test/kotlin/net/corda/core/identity/CordaX500NameTest.kt +++ b/core/src/test/kotlin/net/corda/core/identity/CordaX500NameTest.kt @@ -7,30 +7,36 @@ import kotlin.test.assertNull class CordaX500NameTest { @Test - fun `parse service name with organisational unit`() { + fun `service name with organisational unit`() { val name = CordaX500Name.parse("O=Bank A, L=New York, C=US, OU=Org Unit, CN=Service Name") assertEquals("Service Name", name.commonName) assertEquals("Org Unit", name.organisationUnit) assertEquals("Bank A", name.organisation) assertEquals("New York", name.locality) + assertEquals(CordaX500Name.parse(name.toString()), name) + assertEquals(CordaX500Name.build(name.x500Principal), name) } @Test - fun `parse service name`() { + fun `service name`() { val name = CordaX500Name.parse("O=Bank A, L=New York, C=US, CN=Service Name") assertEquals("Service Name", name.commonName) assertNull(name.organisationUnit) assertEquals("Bank A", name.organisation) assertEquals("New York", name.locality) + assertEquals(CordaX500Name.parse(name.toString()), name) + assertEquals(CordaX500Name.build(name.x500Principal), name) } @Test - fun `parse legal entity name`() { + fun `legal entity name`() { val name = CordaX500Name.parse("O=Bank A, L=New York, C=US") assertNull(name.commonName) assertNull(name.organisationUnit) assertEquals("Bank A", name.organisation) assertEquals("New York", name.locality) + assertEquals(CordaX500Name.parse(name.toString()), name) + assertEquals(CordaX500Name.build(name.x500Principal), name) } @Test diff --git a/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt b/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt index f64ac5440a..ff8207db8d 100644 --- a/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt +++ b/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt @@ -1,14 +1,13 @@ package net.corda.core.identity import net.corda.core.crypto.entropyToKeyPair -import net.corda.core.internal.cert import net.corda.core.internal.read import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.nodeapi.internal.crypto.KEYSTORE_TYPE import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.save -import net.corda.testing.DEV_CA +import net.corda.testing.DEV_ROOT_CA import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.getTestPartyAndCertificate import org.assertj.core.api.Assertions.assertThat @@ -25,8 +24,8 @@ class PartyAndCertificateTest { val testSerialization = SerializationEnvironmentRule() @Test - fun `should reject a path with no roles`() { - val path = X509CertificateFactory().generateCertPath(DEV_CA.certificate.cert) + fun `reject a path with no roles`() { + val path = X509CertificateFactory().generateCertPath(DEV_ROOT_CA.certificate) assertFailsWith { PartyAndCertificate(path) } } diff --git a/docs/source/serialization.rst b/docs/source/serialization.rst index 70dcb44dee..b4f87b912a 100644 --- a/docs/source/serialization.rst +++ b/docs/source/serialization.rst @@ -192,9 +192,6 @@ The following 3rd party types are supported. org.apache.activemq.artemis.api.core.SimpleString - org.bouncycastle.asn1.x500.X500Name - org.bouncycastle.cert.X509CertificateHolder - Corda Types ``````````` diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/DevIdentityGenerator.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/DevIdentityGenerator.kt index ef58a930b2..0cb8472f89 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/DevIdentityGenerator.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/DevIdentityGenerator.kt @@ -5,10 +5,8 @@ import net.corda.core.crypto.Crypto import net.corda.core.crypto.generateKeyPair import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party -import net.corda.core.internal.cert import net.corda.core.internal.createDirectories import net.corda.core.internal.div -import net.corda.core.internal.toX509CertHolder import net.corda.core.utilities.trace import net.corda.nodeapi.internal.config.NodeSSLConfiguration import net.corda.nodeapi.internal.crypto.* @@ -39,10 +37,10 @@ object DevIdentityGenerator { // TODO The passwords for the dev key stores are spread everywhere and should be constants in a single location val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass") val intermediateCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass") - val rootCert = caKeyStore.getCertificate(X509Utilities.CORDA_ROOT_CA) + val rootCert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA) nodeSslConfig.certificatesDirectory.createDirectories() - nodeSslConfig.createDevKeyStores(rootCert.toX509CertHolder(), intermediateCa, legalName) + nodeSslConfig.createDevKeyStores(rootCert, intermediateCa, legalName) val keyStoreWrapper = KeyStoreWrapper(nodeSslConfig.nodeKeystore, nodeSslConfig.keyStorePassword) val identity = keyStoreWrapper.storeLegalIdentity(legalName, "$NODE_IDENTITY_ALIAS_PREFIX-private-key", Crypto.generateKeyPair()) @@ -62,16 +60,21 @@ object DevIdentityGenerator { keyPairs.zip(dirs) { keyPair, nodeDir -> val (serviceKeyCert, compositeKeyCert) = listOf(keyPair.public, compositeKey).map { publicKey -> - X509Utilities.createCertificate(CertificateType.SERVICE_IDENTITY, intermediateCa.certificate, intermediateCa.keyPair, notaryName, publicKey) + X509Utilities.createCertificate( + CertificateType.SERVICE_IDENTITY, + intermediateCa.certificate, + intermediateCa.keyPair, + notaryName.x500Principal, + publicKey) } val distServKeyStoreFile = (nodeDir / "certificates").createDirectories() / "distributedService.jks" val keystore = loadOrCreateKeyStore(distServKeyStoreFile, "cordacadevpass") - keystore.setCertificateEntry("$DISTRIBUTED_NOTARY_ALIAS_PREFIX-composite-key", compositeKeyCert.cert) + keystore.setCertificateEntry("$DISTRIBUTED_NOTARY_ALIAS_PREFIX-composite-key", compositeKeyCert) keystore.setKeyEntry( "$DISTRIBUTED_NOTARY_ALIAS_PREFIX-private-key", keyPair.private, "cordacadevkeypass".toCharArray(), - arrayOf(serviceKeyCert.cert, intermediateCa.certificate.cert, rootCert)) + arrayOf(serviceKeyCert, intermediateCa.certificate, rootCert)) keystore.save(distServKeyStoreFile, "cordacadevpass") } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt index 03bd39e635..374a4cdf24 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt @@ -8,13 +8,13 @@ import net.corda.nodeapi.internal.crypto.* import org.bouncycastle.asn1.x509.GeneralName import org.bouncycastle.asn1.x509.GeneralSubtree import org.bouncycastle.asn1.x509.NameConstraints -import org.bouncycastle.cert.X509CertificateHolder +import java.security.cert.X509Certificate /** * Create the node and SSL key stores needed by a node. The node key store will be populated with a node CA cert (using * the given legal name), and the SSL key store will store the TLS cert which is a sub-cert of the node CA. */ -fun SSLConfiguration.createDevKeyStores(rootCert: X509CertificateHolder, intermediateCa: CertificateAndKeyPair, legalName: CordaX500Name) { +fun SSLConfiguration.createDevKeyStores(rootCert: X509Certificate, intermediateCa: CertificateAndKeyPair, legalName: CordaX500Name) { val (nodeCaCert, nodeCaKeyPair) = createDevNodeCa(intermediateCa, legalName) loadOrCreateKeyStore(nodeKeystore, keyStorePassword).apply { @@ -27,7 +27,7 @@ fun SSLConfiguration.createDevKeyStores(rootCert: X509CertificateHolder, interme } val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, legalName, tlsKeyPair.public) + val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, legalName.x500Principal, tlsKeyPair.public) loadOrCreateKeyStore(sslKeystore, keyStorePassword).apply { addOrReplaceKey( @@ -50,7 +50,7 @@ fun createDevNodeCa(intermediateCa: CertificateAndKeyPair, legalName: CordaX500N CertificateType.NODE_CA, intermediateCa.certificate, intermediateCa.keyPair, - legalName, + legalName.x500Principal, keyPair.public, nameConstraints = nameConstraints) return CertificateAndKeyPair(cert, keyPair) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreUtilities.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreUtilities.kt index b49493ded2..d060c756b0 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreUtilities.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreUtilities.kt @@ -3,8 +3,9 @@ package net.corda.nodeapi.internal.crypto import net.corda.core.crypto.Crypto -import net.corda.core.internal.* -import org.bouncycastle.cert.X509CertificateHolder +import net.corda.core.internal.exists +import net.corda.core.internal.read +import net.corda.core.internal.write import java.io.IOException import java.io.InputStream import java.io.OutputStream @@ -67,18 +68,6 @@ fun loadKeyStore(input: InputStream, storePassword: String): KeyStore { return keyStore } -/** - * Helper extension method to add, or overwrite any key data in store. - * @param alias name to record the private key and certificate chain under. - * @param key cryptographic key to store. - * @param password password for unlocking the key entry in the future. This does not have to be the same password as any keys stored, - * but for SSL purposes this is recommended. - * @param chain the sequence of certificates starting with the public key certificate for this key and extending to the root CA cert. - */ -fun KeyStore.addOrReplaceKey(alias: String, key: Key, password: CharArray, chain: Array) { - addOrReplaceKey(alias, key, password, chain.map { it.cert }.toTypedArray()) -} - /** * Helper extension method to add, or overwrite any key data in store. * @param alias name to record the private key and certificate chain under. @@ -132,9 +121,9 @@ 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).toX509CertHolder() - val publicKey = Crypto.toSupportedPublicKey(cert.subjectPublicKeyInfo) - return CertificateAndKeyPair(cert, KeyPair(publicKey, getSupportedKey(alias, keyPassword))) + val certificate = getX509Certificate(alias) + val publicKey = Crypto.toSupportedPublicKey(certificate.publicKey) + return CertificateAndKeyPair(certificate, KeyPair(publicKey, getSupportedKey(alias, keyPassword))) } /** diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreWrapper.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreWrapper.kt index 2504ce221b..f7cae5b441 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreWrapper.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreWrapper.kt @@ -2,7 +2,6 @@ package net.corda.nodeapi.internal.crypto import net.corda.core.identity.CordaX500Name import net.corda.core.identity.PartyAndCertificate -import net.corda.core.internal.cert import net.corda.core.internal.read import java.nio.file.Path import java.security.KeyPair @@ -15,8 +14,13 @@ class KeyStoreWrapper(private val storePath: Path, private val storePassword: St fun storeLegalIdentity(legalName: CordaX500Name, alias: String, keyPair: KeyPair): PartyAndCertificate { val nodeCaCertChain = keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA) val nodeCa = getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA) - val identityCert = X509Utilities.createCertificate(CertificateType.LEGAL_IDENTITY, nodeCa.certificate, nodeCa.keyPair, legalName, keyPair.public) - val identityCertPath = X509CertificateFactory().generateCertPath(identityCert.cert, *nodeCaCertChain) + val identityCert = X509Utilities.createCertificate( + CertificateType.LEGAL_IDENTITY, + nodeCa.certificate, + nodeCa.keyPair, + legalName.x500Principal, + keyPair.public) + val identityCertPath = X509CertificateFactory().generateCertPath(identityCert, *nodeCaCertChain) // Assume key password = store password. keyStore.addOrReplaceKey(alias, keyPair.private, storePassword.toCharArray(), identityCertPath.certificates.toTypedArray()) keyStore.save(storePath, storePassword) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt index 78b44a8786..fb2e512d38 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt @@ -4,12 +4,14 @@ import net.corda.core.CordaOID import net.corda.core.crypto.Crypto import net.corda.core.crypto.SignatureScheme import net.corda.core.crypto.random63BitValue +import net.corda.core.internal.CertRole +import net.corda.core.internal.reader +import net.corda.core.internal.writer import net.corda.core.identity.CordaX500Name import net.corda.core.internal.* import net.corda.core.utilities.days import net.corda.core.utilities.millis import org.bouncycastle.asn1.* -import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.style.BCStyle import org.bouncycastle.asn1.x509.* import org.bouncycastle.asn1.x509.Extension @@ -34,6 +36,7 @@ import java.time.Duration import java.time.Instant import java.time.temporal.ChronoUnit import java.util.* +import javax.security.auth.x500.X500Principal object X509Utilities { val DEFAULT_IDENTITY_SIGNATURE_SCHEME = Crypto.EDDSA_ED25519_SHA512 @@ -74,7 +77,7 @@ object X509Utilities { * @param after duration to roll forward returned end date relative to current date. * @param parent if provided certificate whose validity should bound the date interval returned. */ - fun getCertificateValidityWindow(before: Duration, after: Duration, parent: X509CertificateHolder? = null): Pair { + fun getCertificateValidityWindow(before: Duration, after: Duration, parent: X509Certificate? = null): Pair { val startOfDayUTC = Instant.now().truncatedTo(ChronoUnit.DAYS) val notBefore = max(startOfDayUTC - before, parent?.notBefore) val notAfter = min(startOfDayUTC + after, parent?.notAfter) @@ -85,60 +88,11 @@ object X509Utilities { * Create a de novo root self-signed X509 v3 CA cert. */ @JvmStatic - fun createSelfSignedCACertificate(subject: CordaX500Name, + fun createSelfSignedCACertificate(subject: X500Principal, keyPair: KeyPair, - validityWindow: Pair = DEFAULT_VALIDITY_WINDOW): X509CertificateHolder { + validityWindow: Pair = DEFAULT_VALIDITY_WINDOW): X509Certificate { val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second) - return createCertificate(CertificateType.ROOT_CA, subject.x500Name, keyPair, subject.x500Name, keyPair.public, window) - } - - /** - * 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 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, - validityWindow: Pair = DEFAULT_VALIDITY_WINDOW, - nameConstraints: NameConstraints? = null): X509CertificateHolder { - return createCertificate(certificateType, issuerCertificate, issuerKeyPair, subject.x500Name, subjectPublicKey, validityWindow, nameConstraints) - } - - /** - * 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 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: X500Name, - subjectPublicKey: PublicKey, - validityWindow: Pair = DEFAULT_VALIDITY_WINDOW, - nameConstraints: NameConstraints? = null): X509CertificateHolder { - val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second, issuerCertificate) - return createCertificate(certificateType, issuerCertificate.subject, issuerKeyPair, subject, subjectPublicKey, window, nameConstraints) + return createCertificate(CertificateType.ROOT_CA, subject, keyPair, subject, keyPair.public, window) } @Throws(CertPathValidatorException::class) @@ -153,13 +107,13 @@ object X509Utilities { /** * Helper method to store a .pem/.cer format file copy of a certificate if required for import into a PC/Mac, or for inspection. - * @param x509Certificate certificate to save. + * @param certificate certificate to save. * @param file Target file. */ @JvmStatic - fun saveCertificateAsPEMFile(x509Certificate: X509Certificate, file: Path) { + fun saveCertificateAsPEMFile(certificate: X509Certificate, file: Path) { JcaPEMWriter(file.writer()).use { - it.writeObject(x509Certificate) + it.writeObject(certificate) } } @@ -172,9 +126,10 @@ object X509Utilities { fun loadCertificateFromPEMFile(file: Path): X509Certificate { return file.reader().use { val pemObject = PemReader(it).readPemObject() - val certHolder = X509CertificateHolder(pemObject.content) - certHolder.isValidOn(Date()) - certHolder.cert + X509CertificateHolder(pemObject.content).run { + isValidOn(Date()) + toJca() + } } } @@ -187,38 +142,18 @@ 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: CordaX500Name, - subject: CordaX500Name, - subjectPublicKey: PublicKey, - validityWindow: Pair, - 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, - nameConstraints: NameConstraints? = null): X509v3CertificateBuilder { - + fun createPartialCertificate(certificateType: CertificateType, + issuer: X500Principal, + subject: X500Principal, + subjectPublicKey: PublicKey, + validityWindow: Pair, + 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 role = certificateType.role - 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) @@ -234,6 +169,37 @@ object X509Utilities { return builder } + /** + * Create a X509 v3 certificate using the given issuer certificate and key pair. + * + * @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 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: X509Certificate, + issuerKeyPair: KeyPair, + subject: X500Principal, + subjectPublicKey: PublicKey, + validityWindow: Pair = DEFAULT_VALIDITY_WINDOW, + nameConstraints: NameConstraints? = null): X509Certificate { + val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second, issuerCertificate) + return createCertificate( + certificateType, + issuerCertificate.subjectX500Principal, + issuerKeyPair, + subject, + subjectPublicKey, + window, + nameConstraints + ) + } + /** * Build and sign an X.509 certificate with the given signer. * @@ -245,15 +211,16 @@ object X509Utilities { * @param nameConstraints any name constraints to impose on certificates signed by the generated certificate. */ fun createCertificate(certificateType: CertificateType, - issuer: X500Name, + issuer: X500Principal, issuerSigner: ContentSigner, - subject: CordaX500Name, + subject: X500Principal, subjectPublicKey: PublicKey, validityWindow: Pair, - nameConstraints: NameConstraints? = null): X509CertificateHolder { - val builder = createCertificate(certificateType, issuer, subject.x500Name, subjectPublicKey, validityWindow, nameConstraints) - return builder.build(issuerSigner).apply { + nameConstraints: NameConstraints? = null): X509Certificate { + val builder = createPartialCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints) + return builder.build(issuerSigner).run { require(isValidOn(Date())) + toJca() } } @@ -268,39 +235,47 @@ object X509Utilities { * @param nameConstraints any name constraints to impose on certificates signed by the generated certificate. */ fun createCertificate(certificateType: CertificateType, - issuer: X500Name, + issuer: X500Principal, issuerKeyPair: KeyPair, - subject: X500Name, + subject: X500Principal, subjectPublicKey: PublicKey, validityWindow: Pair, - nameConstraints: NameConstraints? = null): X509CertificateHolder { + nameConstraints: NameConstraints? = null): X509Certificate { val signatureScheme = Crypto.findSignatureScheme(issuerKeyPair.private) val provider = Crypto.findProvider(signatureScheme.providerName) - val builder = createCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints) - val signer = ContentSignerBuilder.build(signatureScheme, issuerKeyPair.private, provider) - return builder.build(signer).apply { + val builder = createPartialCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints) + return builder.build(signer).run { require(isValidOn(Date())) require(isSignatureValid(JcaContentVerifierProviderBuilder().build(issuerKeyPair.public))) + toJca() } } /** * Create certificate signing request using provided information. */ - private fun createCertificateSigningRequest(subject: CordaX500Name, + private fun createCertificateSigningRequest(subject: X500Principal, email: String, keyPair: KeyPair, signatureScheme: SignatureScheme): PKCS10CertificationRequest { val signer = ContentSignerBuilder.build(signatureScheme, keyPair.private, Crypto.findProvider(signatureScheme.providerName)) - return JcaPKCS10CertificationRequestBuilder(subject.x500Name, keyPair.public).addAttribute(BCStyle.E, DERUTF8String(email)).build(signer) + return JcaPKCS10CertificationRequestBuilder(subject, keyPair.public).addAttribute(BCStyle.E, DERUTF8String(email)).build(signer) } - fun createCertificateSigningRequest(subject: CordaX500Name, email: String, keyPair: KeyPair): PKCS10CertificationRequest { + fun createCertificateSigningRequest(subject: X500Principal, email: String, keyPair: KeyPair): PKCS10CertificationRequest { return createCertificateSigningRequest(subject, email, keyPair, DEFAULT_TLS_SIGNATURE_SCHEME) } } +/** + * Convert a [X509Certificate] into Bouncycastle's [X509CertificateHolder]. + * + * NOTE: To avoid unnecessary copying use [X509Certificate] where possible. + */ +fun X509Certificate.toBc() = X509CertificateHolder(encoded) +fun X509CertificateHolder.toJca(): X509Certificate = X509CertificateFactory().generateCertificate(encoded.inputStream()) + /** * Wraps a [CertificateFactory] to remove boilerplate. It's unclear whether [CertificateFactory] is threadsafe so best * so assume this class is not. @@ -396,4 +371,4 @@ enum class CertificateType(val keyUsage: KeyUsage, vararg val purposes: KeyPurpo ) } -data class CertificateAndKeyPair(val certificate: X509CertificateHolder, val keyPair: KeyPair) +data class CertificateAndKeyPair(val certificate: X509Certificate, val keyPair: KeyPair) diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt index 47c2c8b9ef..af38597bfe 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt @@ -4,10 +4,7 @@ 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.cert 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 @@ -23,12 +20,9 @@ import net.corda.testing.BOB_NAME import net.corda.testing.TestIdentity import net.corda.testing.internal.createDevIntermediateCaCertPath import org.assertj.core.api.Assertions.assertThat -import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x509.BasicConstraints import org.bouncycastle.asn1.x509.Extension import org.bouncycastle.asn1.x509.KeyUsage -import org.bouncycastle.cert.X509CertificateHolder -import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder @@ -42,18 +36,16 @@ import java.security.SecureRandom import java.security.cert.CertPath import java.security.cert.X509Certificate import java.util.* -import java.util.stream.Stream import javax.net.ssl.* +import javax.security.auth.x500.X500Principal import kotlin.concurrent.thread import kotlin.test.* class X509UtilitiesTest { private companion object { val ALICE = TestIdentity(ALICE_NAME, 70).party - val bob = TestIdentity(BOB_NAME, 80) + val BOB = TestIdentity(BOB_NAME, 80) val MEGA_CORP = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).party - val BOB get() = bob.party - val BOB_PUBKEY get() = bob.publicKey val CIPHER_SUITES = arrayOf( "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", @@ -63,27 +55,30 @@ class X509UtilitiesTest { @Rule @JvmField - val tempFolder: TemporaryFolder = TemporaryFolder() + val tempFolder = TemporaryFolder() @Test fun `create valid self-signed CA certificate`() { val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - 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 - val basicConstraints = BasicConstraints.getInstance(caCert.getExtension(Extension.basicConstraints).parsedValue) - val keyUsage = KeyUsage.getInstance(caCert.getExtension(Extension.keyUsage).parsedValue) - assertFalse { keyUsage.hasUsages(5) } // Bit 5 == keyCertSign according to ASN.1 spec (see full comment on KeyUsage property) - assertNull(basicConstraints.pathLenConstraint) // No length constraint specified on this CA certificate + val subject = X500Principal("CN=Test Cert,O=R3 Ltd,L=London,C=GB") + val caCert = X509Utilities.createSelfSignedCACertificate(subject, caKey) + assertEquals(subject, caCert.subjectX500Principal) // using our subject common name + assertEquals(caCert.issuerX500Principal, caCert.subjectX500Principal) //self-signed + caCert.checkValidity(Date()) // throws on verification problems + caCert.verify(caKey.public) // throws on verification problems + caCert.toBc().run { + val basicConstraints = BasicConstraints.getInstance(getExtension(Extension.basicConstraints).parsedValue) + val keyUsage = KeyUsage.getInstance(getExtension(Extension.keyUsage).parsedValue) + assertFalse { keyUsage.hasUsages(5) } // Bit 5 == keyCertSign according to ASN.1 spec (see full comment on KeyUsage property) + assertNull(basicConstraints.pathLenConstraint) // No length constraint specified on this CA certificate + } } @Test 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(CordaX500Name(commonName = "Test Cert", organisation = "R3 Ltd", locality = "London", country = "GB"), caKey).cert + val caCert = X509Utilities.createSelfSignedCACertificate(X500Principal("CN=Test Cert,O=R3 Ltd,L=London,C=GB"), caKey) X509Utilities.saveCertificateAsPEMFile(caCert, tmpCertificateFile) val readCertificate = X509Utilities.loadCertificateFromPEMFile(tmpCertificateFile) assertEquals(caCert, readCertificate) @@ -92,18 +87,20 @@ class X509UtilitiesTest { @Test fun `create valid server certificate chain`() { val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - 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 caCert = X509Utilities.createSelfSignedCACertificate(X500Principal("CN=Test CA Cert,O=R3 Ltd,L=London,C=GB"), caKey) + val subject = X500Principal("CN=Server Cert,O=R3 Ltd,L=London,C=GB") val keyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val serverCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, caKey, subject, keyPair.public) - 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 - val basicConstraints = BasicConstraints.getInstance(serverCert.getExtension(Extension.basicConstraints).parsedValue) - val keyUsage = KeyUsage.getInstance(serverCert.getExtension(Extension.keyUsage).parsedValue) - assertFalse { keyUsage.hasUsages(5) } // Bit 5 == keyCertSign according to ASN.1 spec (see full comment on KeyUsage property) - assertNull(basicConstraints.pathLenConstraint) // Non-CA certificate + assertEquals(subject, serverCert.subjectX500Principal) // using our subject common name + assertEquals(caCert.issuerX500Principal, serverCert.issuerX500Principal) // Issued by our CA cert + serverCert.checkValidity(Date()) // throws on verification problems + serverCert.verify(caKey.public) // throws on verification problems + serverCert.toBc().run { + val basicConstraints = BasicConstraints.getInstance(getExtension(Extension.basicConstraints).parsedValue) + val keyUsage = KeyUsage.getInstance(getExtension(Extension.keyUsage).parsedValue) + assertFalse { keyUsage.hasUsages(5) } // Bit 5 == keyCertSign according to ASN.1 spec (see full comment on KeyUsage property) + assertNull(basicConstraints.pathLenConstraint) // Non-CA certificate + } } @Test @@ -111,15 +108,14 @@ class X509UtilitiesTest { val tmpKeyStore = tempFile("keystore.jks") val keyPair = generateKeyPair(EDDSA_ED25519_SHA512) - val testName = CordaX500Name(commonName = "Test", organisation = "R3 Ltd", locality = "London", country = "GB") + val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB") val selfSignCert = X509Utilities.createSelfSignedCACertificate(testName, keyPair) - assertTrue(Arrays.equals(selfSignCert.subjectPublicKeyInfo.encoded, keyPair.public.encoded)) + assertTrue(Arrays.equals(selfSignCert.publicKey.encoded, keyPair.public.encoded)) // Save the EdDSA private key with self sign cert in the keystore. val keyStore = loadOrCreateKeyStore(tmpKeyStore, "keystorepass") - keyStore.setKeyEntry("Key", keyPair.private, "password".toCharArray(), - Stream.of(selfSignCert).map { it.cert }.toTypedArray()) + keyStore.setKeyEntry("Key", keyPair.private, "password".toCharArray(), arrayOf(selfSignCert)) keyStore.save(tmpKeyStore, "keystorepass") // Load the keystore from file and make sure keys are intact. @@ -137,15 +133,14 @@ class X509UtilitiesTest { fun `signing EdDSA key with EcDSA certificate`() { val tmpKeyStore = tempFile("keystore.jks") val ecDSAKey = generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256) - val testName = CordaX500Name(commonName = "Test", organisation = "R3 Ltd", locality = "London", country = "GB") + val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=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) + val edDSACert = X509Utilities.createCertificate(CertificateType.TLS, ecDSACert, ecDSAKey, BOB.name.x500Principal, edDSAKeypair.public) // Save the EdDSA private key with cert chains. val keyStore = loadOrCreateKeyStore(tmpKeyStore, "keystorepass") - keyStore.setKeyEntry("Key", edDSAKeypair.private, "password".toCharArray(), - Stream.of(ecDSACert, edDSACert).map { it.cert }.toTypedArray()) + keyStore.setKeyEntry("Key", edDSAKeypair.private, "password".toCharArray(), arrayOf(ecDSACert, edDSACert)) keyStore.save(tmpKeyStore, "keystorepass") // Load the keystore from file and make sure keys are intact. @@ -179,23 +174,22 @@ class X509UtilitiesTest { val serverKeyStore = loadKeyStore(sslConfig.nodeKeystore, sslConfig.keyStorePassword) val (serverCert, serverKeyPair) = serverKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, sslConfig.keyStorePassword) - serverCert.cert.checkValidity() - serverCert.cert.verify(intermediateCa.certificate.cert.publicKey) - assertThat(CordaX500Name.parse(serverCert.subject.toString())).isEqualTo(MEGA_CORP.name) + serverCert.checkValidity() + serverCert.verify(intermediateCa.certificate.publicKey) + assertThat(CordaX500Name.build(serverCert.subjectX500Principal)).isEqualTo(MEGA_CORP.name) // Load back SSL certificate val sslKeyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword) val (sslCert) = sslKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_TLS, sslConfig.keyStorePassword) - sslCert.cert.checkValidity() - sslCert.cert.verify(serverCert.cert.publicKey) - assertThat(CordaX500Name.parse(sslCert.subject.toString())).isEqualTo(MEGA_CORP.name) + sslCert.checkValidity() + sslCert.verify(serverCert.publicKey) + assertThat(CordaX500Name.build(sslCert.subjectX500Principal)).isEqualTo(MEGA_CORP.name) // Now sign something with private key and verify against certificate public key val testData = "123456".toByteArray() val signature = Crypto.doSign(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME, serverKeyPair.private, testData) - val publicKey = Crypto.toSupportedPublicKey(serverCert.subjectPublicKeyInfo) - assertTrue { Crypto.isValid(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME, publicKey, signature, testData) } + assertTrue { Crypto.isValid(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME, serverCert.publicKey, signature, testData) } } @Test @@ -210,7 +204,7 @@ class X509UtilitiesTest { // Generate server cert and private key and populate another keystore suitable for SSL sslConfig.createDevKeyStores(rootCa.certificate, intermediateCa, MEGA_CORP.name) - sslConfig.createTrustStore(rootCa.certificate.cert) + sslConfig.createTrustStore(rootCa.certificate) val keyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword) val trustStore = loadKeyStore(sslConfig.trustStoreFile, sslConfig.trustStorePassword) @@ -303,10 +297,10 @@ class X509UtilitiesTest { @Test fun `get correct private key type from Keystore`() { val keyPair = generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256) - val testName = CordaX500Name(commonName = "Test", organisation = "R3 Ltd", locality = "London", country = "GB") + val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB") val selfSignCert = X509Utilities.createSelfSignedCACertificate(testName, keyPair) val keyStore = loadOrCreateKeyStore(tempFile("testKeystore.jks"), "keystorepassword") - keyStore.setKeyEntry("Key", keyPair.private, "keypassword".toCharArray(), arrayOf(selfSignCert.cert)) + keyStore.setKeyEntry("Key", keyPair.private, "keypassword".toCharArray(), arrayOf(selfSignCert)) val keyFromKeystore = keyStore.getKey("Key", "keypassword".toCharArray()) val keyFromKeystoreCasted = keyStore.getSupportedKey("Key", "keypassword") @@ -316,7 +310,7 @@ class X509UtilitiesTest { } @Test - fun `serialize - deserialize X509CertififcateHolder`() { + fun `serialize - deserialize X509Certififcate`() { val factory = SerializationFactoryImpl().apply { registerScheme(KryoServerSerializationScheme()) } val context = SerializationContextImpl(KryoHeaderV0_1, javaClass.classLoader, @@ -324,9 +318,9 @@ class X509UtilitiesTest { emptyMap(), true, SerializationContext.UseCase.P2P) - val expected: X509CertificateHolder = X509Utilities.createSelfSignedCACertificate(ALICE.name, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) + val expected = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Principal, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) val serialized = expected.serialize(factory, context).bytes - val actual: X509CertificateHolder = serialized.deserialize(factory, context) + val actual = serialized.deserialize(factory, context) assertEquals(expected, actual) } @@ -340,9 +334,9 @@ class X509UtilitiesTest { true, SerializationContext.UseCase.P2P) val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val rootCACert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootCAKey) - val certificate = X509Utilities.createCertificate(CertificateType.TLS, rootCACert, rootCAKey, BOB.name.x500Name, BOB_PUBKEY) - val expected = X509CertificateFactory().generateCertPath(certificate.cert, rootCACert.cert) + val rootCACert = X509Utilities.createSelfSignedCACertificate(ALICE_NAME.x500Principal, rootCAKey) + val certificate = X509Utilities.createCertificate(CertificateType.TLS, rootCACert, rootCAKey, BOB_NAME.x500Principal, BOB.publicKey) + val expected = X509CertificateFactory().generateCertPath(certificate, rootCACert) val serialized = expected.serialize(factory, context).bytes val actual: CertPath = serialized.deserialize(factory, context) assertEquals(expected, actual) diff --git a/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt b/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt index 17cad81d22..4a3992213e 100644 --- a/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt @@ -1,8 +1,6 @@ package net.corda.node import net.corda.core.crypto.Crypto -import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.cert import net.corda.core.internal.div import net.corda.core.utilities.getOrThrow import net.corda.node.services.config.configureDevKeyAndTrustStores @@ -13,6 +11,10 @@ import net.corda.testing.driver.driver import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Test import java.nio.file.Path +import javax.security.auth.x500.X500Principal +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertTrue class NodeKeystoreCheckTest { @Test @@ -50,10 +52,10 @@ class NodeKeystoreCheckTest { // Self signed root val badRootKeyPair = Crypto.generateKeyPair() - val badRoot = X509Utilities.createSelfSignedCACertificate(CordaX500Name("Bad Root", "Lodnon", "GB"), badRootKeyPair) + val badRoot = X509Utilities.createSelfSignedCACertificate(X500Principal("O=Bad Root,L=Lodnon,C=GB"), badRootKeyPair) val nodeCA = keystore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, config.keyStorePassword) - val badNodeCACert = X509Utilities.createCertificate(CertificateType.NODE_CA, badRoot, badRootKeyPair, ALICE_NAME, nodeCA.keyPair.public) - keystore.setKeyEntry(X509Utilities.CORDA_CLIENT_CA, nodeCA.keyPair.private, config.keyStorePassword.toCharArray(), arrayOf(badNodeCACert.cert, badRoot.cert)) + val badNodeCACert = X509Utilities.createCertificate(CertificateType.NODE_CA, badRoot, badRootKeyPair, ALICE_NAME.x500Principal, nodeCA.keyPair.public) + keystore.setKeyEntry(X509Utilities.CORDA_CLIENT_CA, nodeCA.keyPair.private, config.keyStorePassword.toCharArray(), arrayOf(badNodeCACert, badRoot)) keystore.save(config.nodeKeystore, config.keyStorePassword) assertThatThrownBy { diff --git a/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt b/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt index 871c7de400..88c299dd41 100644 --- a/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt @@ -59,10 +59,10 @@ class ProtonWrapperTests { amqpClient.start() val serverConnect = serverConnected.get() assertEquals(true, serverConnect.connected) - assertEquals(BOB_NAME, CordaX500Name.parse(serverConnect.remoteCert!!.subject.toString())) + assertEquals(BOB_NAME, CordaX500Name.build(serverConnect.remoteCert!!.subjectX500Principal)) val clientConnect = clientConnected.get() assertEquals(true, clientConnect.connected) - assertEquals(ALICE_NAME, CordaX500Name.parse(clientConnect.remoteCert!!.subject.toString())) + assertEquals(ALICE_NAME, CordaX500Name.build(clientConnect.remoteCert!!.subjectX500Principal)) val msg = amqpClient.createMessage("Test".toByteArray(), "p2p.inbound", ALICE_NAME.toString(), @@ -102,10 +102,10 @@ class ProtonWrapperTests { amqpClient.start() val serverConn1 = serverConnected.get() assertEquals(true, serverConn1.connected) - assertEquals(BOB_NAME, CordaX500Name.parse(serverConn1.remoteCert!!.subject.toString())) + assertEquals(BOB_NAME, CordaX500Name.build(serverConn1.remoteCert!!.subjectX500Principal)) val connState1 = clientConnected.next() assertEquals(true, connState1.connected) - assertEquals(ALICE_NAME, CordaX500Name.parse(connState1.remoteCert!!.subject.toString())) + assertEquals(ALICE_NAME, CordaX500Name.build(connState1.remoteCert!!.subjectX500Principal)) assertEquals(serverPort, connState1.remoteAddress.port) // Fail over @@ -116,10 +116,10 @@ class ProtonWrapperTests { assertEquals(serverPort, connState2.remoteAddress.port) val serverConn2 = serverConnected2.get() assertEquals(true, serverConn2.connected) - assertEquals(BOB_NAME, CordaX500Name.parse(serverConn2.remoteCert!!.subject.toString())) + assertEquals(BOB_NAME, CordaX500Name.build(serverConn2.remoteCert!!.subjectX500Principal)) val connState3 = clientConnected.next() assertEquals(true, connState3.connected) - assertEquals(ALICE_NAME, CordaX500Name.parse(connState3.remoteCert!!.subject.toString())) + assertEquals(ALICE_NAME, CordaX500Name.build(connState3.remoteCert!!.subjectX500Principal)) assertEquals(serverPort2, connState3.remoteAddress.port) // Fail back @@ -130,10 +130,10 @@ class ProtonWrapperTests { assertEquals(serverPort2, connState4.remoteAddress.port) val serverConn3 = serverConnected.get() assertEquals(true, serverConn3.connected) - assertEquals(BOB_NAME, CordaX500Name.parse(serverConn3.remoteCert!!.subject.toString())) + assertEquals(BOB_NAME, CordaX500Name.build(serverConn3.remoteCert!!.subjectX500Principal)) val connState5 = clientConnected.next() assertEquals(true, connState5.connected) - assertEquals(ALICE_NAME, CordaX500Name.parse(connState5.remoteCert!!.subject.toString())) + assertEquals(ALICE_NAME, CordaX500Name.build(connState5.remoteCert!!.subjectX500Principal)) assertEquals(serverPort, connState5.remoteAddress.port) } finally { amqpClient.close() @@ -149,7 +149,7 @@ class ProtonWrapperTests { val clientConnected = amqpClient.onConnection.toFuture() amqpClient.start() assertEquals(true, clientConnected.get().connected) - assertEquals(CHARLIE_NAME, CordaX500Name.parse(clientConnected.get().remoteCert!!.subject.toString())) + assertEquals(CHARLIE_NAME, CordaX500Name.build(clientConnected.get().remoteCert!!.subjectX500Principal)) val artemis = artemisClient.started!! val sendAddress = "p2p.inbound" artemis.session.createQueue(sendAddress, RoutingType.MULTICAST, "queue", true) @@ -180,13 +180,13 @@ class ProtonWrapperTests { amqpClient1.start() val connection1 = connectionEvents.next() assertEquals(true, connection1.connected) - val connection1ID = CordaX500Name.parse(connection1.remoteCert!!.subject.toString()) + val connection1ID = CordaX500Name.build(connection1.remoteCert!!.subjectX500Principal) assertEquals("client 0", connection1ID.organisationUnit) val source1 = connection1.remoteAddress amqpClient2.start() val connection2 = connectionEvents.next() assertEquals(true, connection2.connected) - val connection2ID = CordaX500Name.parse(connection2.remoteCert!!.subject.toString()) + val connection2ID = CordaX500Name.build(connection2.remoteCert!!.subjectX500Principal) assertEquals("client 1", connection2ID.organisationUnit) val source2 = connection2.remoteAddress // Stopping one shouldn't disconnect the other @@ -207,7 +207,7 @@ class ProtonWrapperTests { amqpClient1.start() val connection5 = connectionEvents.next() assertEquals(true, connection5.connected) - val connection5ID = CordaX500Name.parse(connection5.remoteCert!!.subject.toString()) + val connection5ID = CordaX500Name.build(connection5.remoteCert!!.subjectX500Principal) assertEquals("client 0", connection5ID.organisationUnit) assertEquals(true, amqpClient1.connected) assertEquals(false, amqpClient2.connected) @@ -252,7 +252,8 @@ class ProtonWrapperTests { val clientTruststore = loadKeyStore(clientConfig.trustStoreFile, clientConfig.trustStorePassword) val clientKeystore = loadKeyStore(clientConfig.sslKeystore, clientConfig.keyStorePassword) - val amqpClient = AMQPClient(listOf(NetworkHostAndPort("localhost", serverPort), + return AMQPClient( + listOf(NetworkHostAndPort("localhost", serverPort), NetworkHostAndPort("localhost", serverPort2), NetworkHostAndPort("localhost", artemisPort)), setOf(ALICE_NAME, CHARLIE_NAME), @@ -261,7 +262,6 @@ class ProtonWrapperTests { clientKeystore, clientConfig.keyStorePassword, clientTruststore, true) - return amqpClient } private fun createSharedThreadsClient(sharedEventGroup: EventLoopGroup, id: Int): AMQPClient { @@ -275,14 +275,14 @@ class ProtonWrapperTests { val clientTruststore = loadKeyStore(clientConfig.trustStoreFile, clientConfig.trustStorePassword) val clientKeystore = loadKeyStore(clientConfig.sslKeystore, clientConfig.keyStorePassword) - val amqpClient = AMQPClient(listOf(NetworkHostAndPort("localhost", serverPort)), + return AMQPClient( + listOf(NetworkHostAndPort("localhost", serverPort)), setOf(ALICE_NAME), PEER_USER, PEER_USER, clientKeystore, clientConfig.keyStorePassword, clientTruststore, true, sharedEventGroup) - return amqpClient } private fun createServer(port: Int, name: CordaX500Name = ALICE_NAME): AMQPServer { @@ -296,14 +296,13 @@ class ProtonWrapperTests { val serverTruststore = loadKeyStore(serverConfig.trustStoreFile, serverConfig.trustStorePassword) val serverKeystore = loadKeyStore(serverConfig.sslKeystore, serverConfig.keyStorePassword) - val amqpServer = AMQPServer("0.0.0.0", + return AMQPServer( + "0.0.0.0", port, PEER_USER, PEER_USER, serverKeystore, serverConfig.keyStorePassword, serverTruststore) - return amqpServer } - -} \ No newline at end of file +} diff --git a/node/src/integration-test/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt b/node/src/integration-test/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt index 2fe371202a..17544c8bd0 100644 --- a/node/src/integration-test/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt @@ -2,9 +2,7 @@ package net.corda.node.utilities.registration import net.corda.core.crypto.Crypto import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.cert import net.corda.core.internal.concurrent.transpose -import net.corda.core.internal.toX509CertHolder import net.corda.core.messaging.startFlow import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.OpaqueBytes @@ -19,7 +17,7 @@ import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_CA import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_INTERMEDIATE_CA import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA -import net.corda.testing.ROOT_CA +import net.corda.testing.DEV_ROOT_CA import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.driver.PortAllocation @@ -40,8 +38,10 @@ import java.security.KeyPair import java.security.cert.CertPath import java.security.cert.CertPathValidatorException import java.security.cert.Certificate +import java.security.cert.X509Certificate import java.util.zip.ZipEntry import java.util.zip.ZipOutputStream +import javax.security.auth.x500.X500Principal import javax.ws.rs.* import javax.ws.rs.core.MediaType import javax.ws.rs.core.Response @@ -58,14 +58,13 @@ class NodeRegistrationTest { val testSerialization = SerializationEnvironmentRule(true) private val portAllocation = PortAllocation.Incremental(13000) - private val registrationHandler = RegistrationHandler(ROOT_CA) - + private val registrationHandler = RegistrationHandler(DEV_ROOT_CA) private lateinit var server: NetworkMapServer private lateinit var serverHostAndPort: NetworkHostAndPort @Before fun startServer() { - server = NetworkMapServer(1.minutes, portAllocation.nextHostAndPort(), ROOT_CA, "localhost", registrationHandler) + server = NetworkMapServer(1.minutes, portAllocation.nextHostAndPort(), DEV_ROOT_CA, "localhost", registrationHandler) serverHostAndPort = server.start() } @@ -79,7 +78,7 @@ class NodeRegistrationTest { val compatibilityZone = CompatibilityZoneParams( URL("http://$serverHostAndPort"), publishNotaries = { server.networkParameters = testNetworkParameters(it) }, - rootCert = ROOT_CA.certificate.cert) + rootCert = DEV_ROOT_CA.certificate) internalDriver( portAllocation = portAllocation, compatibilityZone = compatibilityZone, @@ -115,12 +114,12 @@ class NodeRegistrationTest { @Test fun `node registration wrong root cert`() { val someRootCert = X509Utilities.createSelfSignedCACertificate( - CordaX500Name("Integration Test Corda Node Root CA", "R3 Ltd", "London", "GB"), + X500Principal("CN=Integration Test Corda Node Root CA,O=R3 Ltd,L=London,C=GB"), Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) val compatibilityZone = CompatibilityZoneParams( URL("http://$serverHostAndPort"), publishNotaries = { server.networkParameters = testNetworkParameters(it) }, - rootCert = someRootCert.cert) + rootCert = someRootCert) internalDriver( portAllocation = portAllocation, compatibilityZone = compatibilityZone, @@ -148,7 +147,7 @@ class RegistrationHandler(private val rootCertAndKeyPair: CertificateAndKeyPair) val (certPath, name) = createSignedClientCertificate( certificationRequest, rootCertAndKeyPair.keyPair, - arrayOf(rootCertAndKeyPair.certificate.cert)) + arrayOf(rootCertAndKeyPair.certificate)) require(!name.organisation.contains("\\s".toRegex())) { "Whitespace in the organisation name not supported" } certPaths[name.organisation] = certPath return Response.ok(name.organisation).build() @@ -182,12 +181,12 @@ class RegistrationHandler(private val rootCertAndKeyPair: CertificateAndKeyPair) val name = CordaX500Name.parse(request.subject.toString()) val nodeCaCert = X509Utilities.createCertificate( CertificateType.NODE_CA, - caCertPath.first().toX509CertHolder(), + caCertPath[0] as X509Certificate , caKeyPair, - name, + name.x500Principal, request.publicKey, nameConstraints = null) - val certPath = X509CertificateFactory().generateCertPath(nodeCaCert.cert, *caCertPath) + val certPath = X509CertificateFactory().generateCertPath(nodeCaCert, *caCertPath) return Pair(certPath, name) } } diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt index f8da5302a4..8252173ca0 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt @@ -94,22 +94,34 @@ class MQSecurityAsNodeTest : MQSecurityTest() { javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass") - val rootCACert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA).toX509CertHolder() + val rootCACert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA) val intermediateCA = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass") - val clientKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) + val clientKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) // Set name constrain to the legal name. val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.x500Name))), arrayOf()) - val clientCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, intermediateCA.certificate, - intermediateCA.keyPair, legalName, clientKey.public, nameConstraints = nameConstraints) - val tlsKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) + val clientCACert = X509Utilities.createCertificate( + CertificateType.INTERMEDIATE_CA, + intermediateCA.certificate, + intermediateCA.keyPair, + legalName.x500Principal, + clientKeyPair.public, + nameConstraints = nameConstraints) + + val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) // Using different x500 name in the TLS cert which is not allowed in the name constraints. - val clientTLSCert = X509Utilities.createCertificate(CertificateType.TLS, clientCACert, clientKey, CordaX500Name("MiniCorp", "London", "GB"), tlsKey.public) + val clientTLSCert = X509Utilities.createCertificate( + CertificateType.TLS, + clientCACert, + clientKeyPair, + CordaX500Name("MiniCorp", "London", "GB").x500Principal, + tlsKeyPair.public) + val keyPass = keyStorePassword.toCharArray() val clientCAKeystore = loadOrCreateKeyStore(nodeKeystore, keyStorePassword) clientCAKeystore.addOrReplaceKey( X509Utilities.CORDA_CLIENT_CA, - clientKey.private, + clientKeyPair.private, keyPass, arrayOf(clientCACert, intermediateCA.certificate, rootCACert)) clientCAKeystore.save(nodeKeystore, keyStorePassword) @@ -117,7 +129,7 @@ class MQSecurityAsNodeTest : MQSecurityTest() { val tlsKeystore = loadOrCreateKeyStore(sslKeystore, keyStorePassword) tlsKeystore.addOrReplaceKey( X509Utilities.CORDA_CLIENT_TLS, - tlsKey.private, + tlsKeyPair.private, keyPass, arrayOf(clientTLSCert, clientCACert, intermediateCA.certificate, rootCACert)) tlsKeystore.save(sslKeystore, keyStorePassword) diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index c924c42a91..0edc5a1624 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -698,7 +698,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, val caKeyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword) val trustRoot = trustStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA) val clientCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA) - val caCertificates = arrayOf(identityCert, clientCa.certificate.cert) + val caCertificates = arrayOf(identityCert, clientCa.certificate) return PersistentIdentityService(trustRoot, *caCertificates) } @@ -756,7 +756,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, listOf(certificate) + keyStore.getCertificateChain(privateKeyAlias).drop(1) } else { keyStore.getCertificateChain(privateKeyAlias).let { - check(it[0].toX509CertHolder() == x509Cert) { "Certificates from key store do not line up!" } + check(it[0] == x509Cert) { "Certificates from key store do not line up!" } it.asList() } } diff --git a/node/src/main/kotlin/net/corda/node/internal/protonwrapper/netty/AMQPChannelHandler.kt b/node/src/main/kotlin/net/corda/node/internal/protonwrapper/netty/AMQPChannelHandler.kt index 0f9e6cd607..f31f9fbe88 100644 --- a/node/src/main/kotlin/net/corda/node/internal/protonwrapper/netty/AMQPChannelHandler.kt +++ b/node/src/main/kotlin/net/corda/node/internal/protonwrapper/netty/AMQPChannelHandler.kt @@ -9,7 +9,6 @@ import io.netty.handler.ssl.SslHandler import io.netty.handler.ssl.SslHandshakeCompletionEvent import io.netty.util.ReferenceCountUtil import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.toX509CertHolder import net.corda.core.utilities.debug import net.corda.node.internal.protonwrapper.engine.EventProcessor import net.corda.node.internal.protonwrapper.messages.ReceivedMessage @@ -19,9 +18,9 @@ import org.apache.qpid.proton.engine.ProtonJTransport import org.apache.qpid.proton.engine.Transport import org.apache.qpid.proton.engine.impl.ProtocolTracer import org.apache.qpid.proton.framing.TransportFrame -import org.bouncycastle.cert.X509CertificateHolder import org.slf4j.LoggerFactory import java.net.InetSocketAddress +import java.security.cert.X509Certificate /** * An instance of AMQPChannelHandler sits inside the netty pipeline and controls the socket level lifecycle. @@ -38,20 +37,20 @@ internal class AMQPChannelHandler(private val serverMode: Boolean, private val onReceive: (ReceivedMessage) -> Unit) : ChannelDuplexHandler() { private val log = LoggerFactory.getLogger(allowedRemoteLegalNames?.firstOrNull()?.toString() ?: "AMQPChannelHandler") private lateinit var remoteAddress: InetSocketAddress - private lateinit var localCert: X509CertificateHolder - private lateinit var remoteCert: X509CertificateHolder + private lateinit var localCert: X509Certificate + private lateinit var remoteCert: X509Certificate private var eventProcessor: EventProcessor? = null override fun channelActive(ctx: ChannelHandlerContext) { val ch = ctx.channel() remoteAddress = ch.remoteAddress() as InetSocketAddress val localAddress = ch.localAddress() as InetSocketAddress - log.info("New client connection ${ch.id()} from ${remoteAddress} to ${localAddress}") + log.info("New client connection ${ch.id()} from $remoteAddress to $localAddress") } private fun createAMQPEngine(ctx: ChannelHandlerContext) { val ch = ctx.channel() - eventProcessor = EventProcessor(ch, serverMode, localCert.subject.toString(), remoteCert.subject.toString(), userName, password) + eventProcessor = EventProcessor(ch, serverMode, localCert.subjectX500Principal.toString(), remoteCert.subjectX500Principal.toString(), userName, password) val connection = eventProcessor!!.connection val transport = connection.transport as ProtonJTransport if (trace) { @@ -71,7 +70,7 @@ internal class AMQPChannelHandler(private val serverMode: Boolean, override fun channelInactive(ctx: ChannelHandlerContext) { val ch = ctx.channel() - log.info("Closed client connection ${ch.id()} from ${remoteAddress} to ${ch.localAddress()}") + log.info("Closed client connection ${ch.id()} from $remoteAddress to ${ch.localAddress()}") onClose(Pair(ch as SocketChannel, ConnectionChange(remoteAddress, null, false))) eventProcessor?.close() ctx.fireChannelInactive() @@ -81,12 +80,12 @@ internal class AMQPChannelHandler(private val serverMode: Boolean, if (evt is SslHandshakeCompletionEvent) { if (evt.isSuccess) { val sslHandler = ctx.pipeline().get(SslHandler::class.java) - localCert = sslHandler.engine().session.localCertificates.first().toX509CertHolder() - remoteCert = sslHandler.engine().session.peerCertificates.first().toX509CertHolder() + localCert = sslHandler.engine().session.localCertificates[0] as X509Certificate + remoteCert = sslHandler.engine().session.peerCertificates[0] as X509Certificate try { - val remoteX500Name = CordaX500Name.parse(remoteCert.subject.toString()) + val remoteX500Name = CordaX500Name.build(remoteCert.subjectX500Principal) require(allowedRemoteLegalNames == null || remoteX500Name in allowedRemoteLegalNames) - log.info("handshake completed subject: ${remoteX500Name}") + log.info("handshake completed subject: $remoteX500Name") } catch (ex: IllegalArgumentException) { log.error("Invalid certificate subject", ex) ctx.close() @@ -124,7 +123,7 @@ internal class AMQPChannelHandler(private val serverMode: Boolean, require(inetAddress == remoteAddress) { "Message for incorrect endpoint" } - require(CordaX500Name.parse(msg.destinationLegalName) == CordaX500Name.parse(remoteCert.subject.toString())) { + require(CordaX500Name.parse(msg.destinationLegalName) == CordaX500Name.build(remoteCert.subjectX500Principal)) { "Message for incorrect legal identity" } log.debug { "channel write ${msg.applicationProperties["_AMQ_DUPL_ID"]}" } diff --git a/node/src/main/kotlin/net/corda/node/internal/protonwrapper/netty/ConnectionChange.kt b/node/src/main/kotlin/net/corda/node/internal/protonwrapper/netty/ConnectionChange.kt index a576a25d2b..f0d83a8cf5 100644 --- a/node/src/main/kotlin/net/corda/node/internal/protonwrapper/netty/ConnectionChange.kt +++ b/node/src/main/kotlin/net/corda/node/internal/protonwrapper/netty/ConnectionChange.kt @@ -1,6 +1,6 @@ package net.corda.node.internal.protonwrapper.netty -import org.bouncycastle.cert.X509CertificateHolder import java.net.InetSocketAddress +import java.security.cert.X509Certificate -data class ConnectionChange(val remoteAddress: InetSocketAddress, val remoteCert: X509CertificateHolder?, val connected: Boolean) \ No newline at end of file +data class ConnectionChange(val remoteAddress: InetSocketAddress, val remoteCert: X509Certificate?, val connected: Boolean) \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt index bb1e3da993..f48e8d4022 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt @@ -8,7 +8,6 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.internal.createDirectories import net.corda.core.internal.div import net.corda.core.internal.exists -import net.corda.core.internal.toX509CertHolder import net.corda.nodeapi.internal.config.SSLConfiguration import net.corda.nodeapi.internal.createDevKeyStores import net.corda.nodeapi.internal.crypto.* @@ -54,7 +53,7 @@ fun SSLConfiguration.configureDevKeyAndTrustStores(myLegalName: CordaX500Name) { } if (!sslKeystore.exists() || !nodeKeystore.exists()) { val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass") - val rootCert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA).toX509CertHolder() + val rootCert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA) val intermediateCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass") createDevKeyStores(rootCert, intermediateCa, myLegalName) diff --git a/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt b/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt index 4654473ef1..4876533003 100644 --- a/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt +++ b/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt @@ -4,15 +4,12 @@ import net.corda.core.contracts.PartyAndReference import net.corda.core.crypto.toStringShort import net.corda.core.identity.* import net.corda.core.internal.CertRole -import net.corda.core.internal.cert -import net.corda.core.internal.toX509CertHolder import net.corda.core.node.services.UnknownAnonymousPartyException import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.utilities.contextLogger import net.corda.core.utilities.trace import net.corda.node.services.api.IdentityServiceInternal import net.corda.nodeapi.internal.crypto.X509CertificateFactory -import org.bouncycastle.cert.X509CertificateHolder import java.security.InvalidAlgorithmParameterException import java.security.PublicKey import java.security.cert.* @@ -27,7 +24,7 @@ import javax.annotation.concurrent.ThreadSafe // TODO There is duplicated logic between this and PersistentIdentityService @ThreadSafe class InMemoryIdentityService(identities: Array, - trustRoot: X509CertificateHolder) : SingletonSerializeAsToken(), IdentityServiceInternal { + override val trustRoot: X509Certificate) : SingletonSerializeAsToken(), IdentityServiceInternal { companion object { private val log = contextLogger() } @@ -35,14 +32,12 @@ class InMemoryIdentityService(identities: Array, /** * Certificate store for certificate authority and intermediary certificates. */ - override val caCertStore: CertStore - override val trustRoot = trustRoot.cert - override val trustAnchor: TrustAnchor = TrustAnchor(this.trustRoot, null) + override val caCertStore: CertStore = CertStore.getInstance("Collection", CollectionCertStoreParameters(setOf(trustRoot))) + override val trustAnchor: TrustAnchor = TrustAnchor(trustRoot, null) private val keyToParties = ConcurrentHashMap() private val principalToParties = ConcurrentHashMap() init { - caCertStore = CertStore.getInstance("Collection", CollectionCertStoreParameters(setOf(this.trustRoot))) keyToParties.putAll(identities.associateBy { it.owningKey }) principalToParties.putAll(identities.associateBy { it.name }) } @@ -57,7 +52,7 @@ class InMemoryIdentityService(identities: Array, log.warn("Certificate path :") identity.certPath.certificates.reversed().forEachIndexed { index, certificate -> val space = (0 until index).joinToString("") { " " } - log.warn("$space${certificate.toX509CertHolder().subject}") + log.warn("$space${(certificate as X509Certificate).subjectX500Principal}") } throw e } diff --git a/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt b/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt index 0a07bc40c6..c21f7e9c91 100644 --- a/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt +++ b/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt @@ -5,8 +5,6 @@ import net.corda.core.crypto.SecureHash import net.corda.core.crypto.toStringShort import net.corda.core.identity.* import net.corda.core.internal.CertRole -import net.corda.core.internal.cert -import net.corda.core.internal.toX509CertHolder import net.corda.core.node.services.UnknownAnonymousPartyException import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.utilities.MAX_HASH_HEX_SIZE @@ -16,7 +14,6 @@ import net.corda.node.services.api.IdentityServiceInternal import net.corda.node.utilities.AppendOnlyPersistentMap import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX -import org.bouncycastle.cert.X509CertificateHolder import java.security.InvalidAlgorithmParameterException import java.security.PublicKey import java.security.cert.* @@ -30,7 +27,6 @@ import javax.persistence.Lob @ThreadSafe class PersistentIdentityService(override val trustRoot: X509Certificate, vararg caCertificates: X509Certificate) : SingletonSerializeAsToken(), IdentityServiceInternal { - constructor(trustRoot: X509CertificateHolder) : this(trustRoot.cert) companion object { private val log = contextLogger() @@ -121,7 +117,7 @@ class PersistentIdentityService(override val trustRoot: X509Certificate, log.warn(e.localizedMessage) log.warn("Path = ") identity.certPath.certificates.reversed().forEach { - log.warn(it.toX509CertHolder().subject.toString()) + log.warn((it as X509Certificate).subjectX500Principal.toString()) } throw e } diff --git a/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt b/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt index df99435dd3..dad6a7b2a7 100644 --- a/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt +++ b/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt @@ -3,8 +3,6 @@ package net.corda.node.services.keys import net.corda.core.crypto.Crypto import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.CertRole -import net.corda.core.internal.cert -import net.corda.core.internal.toX509CertHolder import net.corda.core.utilities.days import net.corda.node.services.api.IdentityServiceInternal import net.corda.nodeapi.internal.crypto.CertificateType @@ -36,11 +34,16 @@ fun freshCertificate(identityService: IdentityServiceInternal, revocationEnabled: Boolean = false): PartyAndCertificate { val issuerRole = CertRole.extract(issuer.certificate) require(issuerRole == CertRole.LEGAL_IDENTITY) { "Confidential identities can only be issued from well known identities, provided issuer ${issuer.name} has role $issuerRole" } - val issuerCert = issuer.certificate.toX509CertHolder() + val issuerCert = issuer.certificate val window = X509Utilities.getCertificateValidityWindow(Duration.ZERO, 3650.days, issuerCert) - val ourCertificate = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuerCert.subject, - issuerSigner, issuer.name, subjectPublicKey, window) - val ourCertPath = X509CertificateFactory().generateCertPath(listOf(ourCertificate.cert) + issuer.certPath.certificates) + val ourCertificate = X509Utilities.createCertificate( + CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, + issuerCert.subjectX500Principal, + issuerSigner, + issuer.name.x500Principal, + subjectPublicKey, + window) + val ourCertPath = X509CertificateFactory().generateCertPath(listOf(ourCertificate) + issuer.certPath.certificates) val anonymisedIdentity = PartyAndCertificate(ourCertPath) identityService.justVerifyAndRegisterIdentity(anonymisedIdentity) return anonymisedIdentity diff --git a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt index 09a46121e8..090eef335c 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt @@ -72,7 +72,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 (!nodeKeyStore.containsAlias(SELF_SIGNED_PRIVATE_KEY)) { val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val selfSignCert = X509Utilities.createSelfSignedCACertificate(config.myLegalName, keyPair) + val selfSignCert = X509Utilities.createSelfSignedCACertificate(config.myLegalName.x500Principal, keyPair) // Save to the key store. nodeKeyStore.addOrReplaceKey(SELF_SIGNED_PRIVATE_KEY, keyPair.private, privateKeyPassword.toCharArray(), arrayOf(selfSignCert)) @@ -112,8 +112,8 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v throw CertificateRequestException("Received node CA cert has invalid role: $nodeCaCertRole") } - println("Checking root of the certificate path is what we expect.") - X509Utilities.validateCertificateChain (rootCert , * certificates) + println("Checking root of the certificate path is what we expect.") + X509Utilities.validateCertificateChain(rootCert, *certificates) println("Certificate signing request approved, storing private key with the certificate chain.") // Save private key and certificate chain to the key store. @@ -126,12 +126,12 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v val sslKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val sslCert = X509Utilities.createCertificate( CertificateType.TLS, - nodeCaCert.toX509CertHolder(), + nodeCaCert, keyPair, - config.myLegalName, + config.myLegalName.x500Principal, sslKeyPair.public) val sslKeyStore = loadOrCreateKeyStore(config.sslKeystore, keystorePassword) - sslKeyStore.addOrReplaceKey(CORDA_CLIENT_TLS, sslKeyPair.private, privateKeyPassword.toCharArray(), arrayOf(sslCert.cert, *certificates)) + sslKeyStore.addOrReplaceKey(CORDA_CLIENT_TLS, sslKeyPair.private, privateKeyPassword.toCharArray(), arrayOf(sslCert, *certificates)) sslKeyStore.save(config.sslKeystore, config.keyStorePassword) println("SSL private key and certificate stored in ${config.sslKeystore}.") @@ -165,7 +165,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, config.emailAddress, keyPair) + val request = X509Utilities.createCertificateSigningRequest(config.myLegalName.x500Principal, config.emailAddress, keyPair) val writer = StringWriter() JcaPEMWriter(writer).use { it.writeObject(PemObject("CERTIFICATE REQUEST", request.encoded)) diff --git a/node/src/test/kotlin/net/corda/node/services/identity/InMemoryIdentityServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/identity/InMemoryIdentityServiceTests.kt index 30717cdad4..9131143e9f 100644 --- a/node/src/test/kotlin/net/corda/node/services/identity/InMemoryIdentityServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/identity/InMemoryIdentityServiceTests.kt @@ -6,8 +6,6 @@ import net.corda.core.identity.AnonymousParty import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate -import net.corda.core.internal.cert -import net.corda.core.internal.toX509CertHolder import net.corda.core.node.services.UnknownAnonymousPartyException import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509CertificateFactory @@ -32,7 +30,7 @@ class InMemoryIdentityServiceTests { val BOB get() = bob.party val BOB_IDENTITY get() = bob.identity val BOB_PUBKEY get() = bob.publicKey - fun createService(vararg identities: PartyAndCertificate) = InMemoryIdentityService(identities, DEV_TRUST_ROOT) + fun createService(vararg identities: PartyAndCertificate) = InMemoryIdentityService(identities, DEV_ROOT_CA.certificate) } @Rule @@ -100,11 +98,11 @@ class InMemoryIdentityServiceTests { @Test fun `assert unknown anonymous key is unrecognised`() { val rootKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootKey) + val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Principal, rootKey) val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val service = createService() // TODO: Generate certificate with an EdDSA key rather than ECDSA - val identity = Party(rootCert.cert) + val identity = Party(rootCert) val txIdentity = AnonymousParty(txKey.public) assertFailsWith { @@ -159,8 +157,8 @@ class InMemoryIdentityServiceTests { } assertFailsWith { - val owningKey = Crypto.decodePublicKey(DEV_CA.certificate.subjectPublicKeyInfo.encoded) - val subject = CordaX500Name.build(DEV_CA.certificate.cert.subjectX500Principal) + val owningKey = DEV_INTERMEDIATE_CA.certificate.publicKey + val subject = CordaX500Name.build(DEV_INTERMEDIATE_CA.certificate.subjectX500Principal) service.assertOwnership(Party(subject, owningKey), anonymousAlice.party.anonymise()) } } @@ -168,9 +166,14 @@ class InMemoryIdentityServiceTests { private fun createConfidentialIdentity(x500Name: CordaX500Name): Pair { val issuerKeyPair = generateKeyPair() val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public) - val txKey = Crypto.generateKeyPair() - val txCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuer.certificate.toX509CertHolder(), issuerKeyPair, x500Name, txKey.public) - val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates) + val txKeyPair = Crypto.generateKeyPair() + val txCert = X509Utilities.createCertificate( + CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, + issuer.certificate, + issuerKeyPair, + x500Name.x500Principal, + txKeyPair.public) + val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert) + issuer.certPath.certificates) return Pair(issuer, PartyAndCertificate(txCertPath)) } diff --git a/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt index 2553d30ef5..5d6cc41ce1 100644 --- a/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt @@ -6,8 +6,6 @@ import net.corda.core.identity.AnonymousParty import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate -import net.corda.core.internal.cert -import net.corda.core.internal.toX509CertHolder import net.corda.core.node.services.IdentityService import net.corda.core.node.services.UnknownAnonymousPartyException import net.corda.node.internal.configureDatabase @@ -50,7 +48,7 @@ class PersistentIdentityServiceTests { @Before fun setup() { - identityService = PersistentIdentityService(DEV_TRUST_ROOT) + identityService = PersistentIdentityService(DEV_ROOT_CA.certificate) database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), identityService) } @@ -143,9 +141,9 @@ class PersistentIdentityServiceTests { @Test fun `assert unknown anonymous key is unrecognised`() { val rootKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootKey) + val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Principal, rootKey) val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_IDENTITY_SIGNATURE_SCHEME) - val identity = Party(rootCert.cert) + val identity = Party(rootCert) val txIdentity = AnonymousParty(txKey.public) assertFailsWith { @@ -219,9 +217,9 @@ class PersistentIdentityServiceTests { } assertFailsWith { - val owningKey = Crypto.decodePublicKey(DEV_CA.certificate.subjectPublicKeyInfo.encoded) + val owningKey = DEV_INTERMEDIATE_CA.certificate.publicKey database.transaction { - val subject = CordaX500Name.build(DEV_CA.certificate.cert.subjectX500Principal) + val subject = CordaX500Name.build(DEV_INTERMEDIATE_CA.certificate.subjectX500Principal) identityService.assertOwnership(Party(subject, owningKey), anonymousAlice.party.anonymise()) } } @@ -243,7 +241,7 @@ class PersistentIdentityServiceTests { // Create new identity service mounted onto same DB val newPersistentIdentityService = database.transaction { - PersistentIdentityService(DEV_TRUST_ROOT) + PersistentIdentityService(DEV_ROOT_CA.certificate) } database.transaction { @@ -266,8 +264,8 @@ class PersistentIdentityServiceTests { val issuerKeyPair = generateKeyPair() val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public) val txKey = Crypto.generateKeyPair() - val txCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuer.certificate.toX509CertHolder(), issuerKeyPair, x500Name, txKey.public) - val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates) + val txCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuer.certificate, issuerKeyPair, x500Name.x500Principal, txKey.public) + val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert) + issuer.certPath.certificates) return Pair(issuer, PartyAndCertificate(txCertPath)) } diff --git a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapClientTest.kt b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapClientTest.kt index c609d59f2c..9f342b2e0b 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapClientTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapClientTest.kt @@ -2,12 +2,11 @@ package net.corda.node.services.network import net.corda.core.crypto.SecureHash import net.corda.core.crypto.sha256 -import net.corda.core.internal.cert import net.corda.core.serialization.serialize import net.corda.core.utilities.seconds import net.corda.testing.ALICE_NAME import net.corda.testing.BOB_NAME -import net.corda.testing.DEV_TRUST_ROOT +import net.corda.testing.DEV_ROOT_CA import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.driver.PortAllocation import net.corda.testing.internal.createNodeInfoAndSigned @@ -35,7 +34,7 @@ class NetworkMapClientTest { fun setUp() { server = NetworkMapServer(cacheTimeout, PortAllocation.Incremental(10000).nextHostAndPort()) val hostAndPort = server.start() - networkMapClient = NetworkMapClient(URL("http://${hostAndPort.host}:${hostAndPort.port}"), DEV_TRUST_ROOT.cert) + networkMapClient = NetworkMapClient(URL("http://${hostAndPort.host}:${hostAndPort.port}"), DEV_ROOT_CA.certificate) } @After diff --git a/node/src/test/kotlin/net/corda/node/utilities/TLSAuthenticationTests.kt b/node/src/test/kotlin/net/corda/node/utilities/TLSAuthenticationTests.kt index 98ff559fe8..ef933d5ccc 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/TLSAuthenticationTests.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/TLSAuthenticationTests.kt @@ -18,6 +18,7 @@ import java.net.ServerSocket import java.nio.file.Path import java.security.KeyStore import javax.net.ssl.* +import javax.security.auth.x500.X500Principal import kotlin.concurrent.thread import kotlin.test.* @@ -51,9 +52,9 @@ class TLSAuthenticationTests { val tempFolder: TemporaryFolder = TemporaryFolder() // Root CA. - private val ROOT_X500 = CordaX500Name(commonName = "Root_CA_1", organisation = "R3CEV", locality = "London", country = "GB") + private val ROOT_X500 = X500Principal("CN=Root_CA_1,O=R3CEV,L=London,C=GB") // Intermediate CA. - private val INTERMEDIATE_X500 = CordaX500Name(commonName = "Intermediate_CA_1", organisation = "R3CEV", locality = "London", country = "GB") + private val INTERMEDIATE_X500 = X500Principal("CN=Intermediate_CA_1,O=R3CEV,L=London,C=GB") // TLS server (client1). private val CLIENT_1_X500 = CordaX500Name(commonName = "Client_1", organisation = "R3CEV", locality = "London", country = "GB") // TLS client (client2). @@ -274,7 +275,7 @@ class TLSAuthenticationTests { CertificateType.NODE_CA, intermediateCACert, intermediateCAKeyPair, - CLIENT_1_X500, + CLIENT_1_X500.x500Principal, client1CAKeyPair.public ) @@ -283,7 +284,7 @@ class TLSAuthenticationTests { CertificateType.TLS, client1CACert, client1CAKeyPair, - CLIENT_1_X500, + CLIENT_1_X500.x500Principal, client1TLSKeyPair.public ) @@ -301,7 +302,7 @@ class TLSAuthenticationTests { CertificateType.NODE_CA, intermediateCACert, intermediateCAKeyPair, - CLIENT_2_X500, + CLIENT_2_X500.x500Principal, client2CAKeyPair.public ) @@ -310,7 +311,7 @@ class TLSAuthenticationTests { CertificateType.TLS, client2CACert, client2CAKeyPair, - CLIENT_2_X500, + CLIENT_2_X500.x500Principal, client2TLSKeyPair.public ) @@ -323,8 +324,8 @@ class TLSAuthenticationTests { // client2TLSKeyStore.save(client2TLSKeyStorePath, PASSWORD) val trustStore = loadOrCreateKeyStore(trustStorePath, PASSWORD) - trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCACert.cert) - trustStore.addOrReplaceCertificate(X509Utilities.CORDA_INTERMEDIATE_CA, intermediateCACert.cert) + trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCACert) + trustStore.addOrReplaceCertificate(X509Utilities.CORDA_INTERMEDIATE_CA, intermediateCACert) // trustStore.save(trustStorePath, PASSWORD) val client1SSLContext = sslContext(client1TLSKeyStore, PASSWORD, trustStore) diff --git a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt index 5b8dab7282..f56c82a6cf 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt @@ -9,7 +9,6 @@ import com.nhaarman.mockito_kotlin.whenever import net.corda.core.crypto.Crypto import net.corda.core.crypto.SecureHash import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.cert import net.corda.core.internal.createDirectories import net.corda.core.internal.x500Name import net.corda.node.services.config.NodeConfiguration @@ -26,6 +25,7 @@ import org.junit.Before import org.junit.Test import java.security.cert.CertPathValidatorException import java.security.cert.X509Certificate +import javax.security.auth.x500.X500Principal import kotlin.test.assertFalse import kotlin.test.assertTrue @@ -129,9 +129,9 @@ class NetworkRegistrationHelperTest { @Test fun `wrong root cert in truststore`() { val wrongRootCert = X509Utilities.createSelfSignedCACertificate( - CordaX500Name("Foo", "MU", "GB"), + X500Principal("O=Foo,L=MU,C=GB"), Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) - saveTrustStoreWithRootCa(wrongRootCert.cert) + saveTrustStoreWithRootCa(wrongRootCert) val registrationHelper = createRegistrationHelper(createNodeCaCertPath()) assertThatThrownBy { registrationHelper.buildKeystore() @@ -147,10 +147,10 @@ class NetworkRegistrationHelperTest { type, intermediateCa.certificate, intermediateCa.keyPair, - legalName, + legalName.x500Principal, keyPair.public, nameConstraints = nameConstraints) - return arrayOf(nodeCaCert.cert, intermediateCa.certificate.cert, rootCa.certificate.cert) + return arrayOf(nodeCaCert, intermediateCa.certificate, rootCa.certificate) } private fun createRegistrationHelper(response: Array): NetworkRegistrationHelper { diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt index bc3add840c..25403c008f 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt @@ -15,7 +15,9 @@ import net.corda.core.serialization.SerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.transactions.SignedTransaction import net.corda.node.VersionInfo +import net.corda.node.internal.configureDatabase import net.corda.node.internal.cordapp.CordappLoader +import net.corda.node.services.api.IdentityServiceInternal import net.corda.node.services.api.SchemaService import net.corda.node.services.api.VaultServiceInternal import net.corda.node.services.api.WritableTransactionStorage @@ -26,12 +28,11 @@ import net.corda.node.services.schema.HibernateObserver import net.corda.node.services.schema.NodeSchemaService import net.corda.node.services.transactions.InMemoryTransactionVerifierService import net.corda.node.services.vault.NodeVaultService -import net.corda.node.internal.configureDatabase -import net.corda.node.services.api.IdentityServiceInternal import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.nodeapi.internal.persistence.HibernateConfiguration -import net.corda.testing.* +import net.corda.testing.DEV_ROOT_CA +import net.corda.testing.TestIdentity import net.corda.testing.services.MockAttachmentStorage import net.corda.testing.services.MockCordappProvider import org.bouncycastle.operator.ContentSigner @@ -44,7 +45,7 @@ import java.sql.Connection import java.time.Clock import java.util.* -fun makeTestIdentityService(vararg identities: PartyAndCertificate) = InMemoryIdentityService(identities, DEV_TRUST_ROOT) +fun makeTestIdentityService(vararg identities: PartyAndCertificate) = InMemoryIdentityService(identities, DEV_ROOT_CA.certificate) /** * A singleton utility that only provides a mock identity, key and storage service. However, this is sufficient for * building chains of transactions and verifying them. It isn't sufficient for testing flows however. @@ -164,7 +165,7 @@ class MockKeyManagementService(val identityService: IdentityServiceInternal, override val keys: Set get() = keyStore.keys - val nextKeys = LinkedList() + private val nextKeys = LinkedList() override fun freshKey(): PublicKey { val k = nextKeys.poll() ?: generateKeyPair() @@ -222,11 +223,10 @@ open class MockTransactionStorage : WritableTransactionStorage, SingletonSeriali } fun createMockCordaService(serviceHub: MockServices, serviceConstructor: (AppServiceHub) -> T): T { - class MockAppServiceHubImpl(val serviceHub: MockServices, serviceConstructor: (AppServiceHub) -> T) : AppServiceHub, ServiceHub by serviceHub { - val serviceInstance: T + class MockAppServiceHubImpl(val serviceHub: MockServices, serviceConstructor: (AppServiceHub) -> T) : AppServiceHub, ServiceHub by serviceHub { + val serviceInstance: T = serviceConstructor(this) init { - serviceInstance = serviceConstructor(this) serviceHub.cordappServices.putInstance(serviceInstance.javaClass, serviceInstance) } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/network/NetworkMapServer.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/network/NetworkMapServer.kt index 345eaf84b8..be1a11c293 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/network/NetworkMapServer.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/network/NetworkMapServer.kt @@ -2,8 +2,6 @@ package net.corda.testing.node.internal.network import net.corda.core.crypto.* import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.cert -import net.corda.core.internal.toX509CertHolder import net.corda.core.node.NodeInfo import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize @@ -16,7 +14,7 @@ import net.corda.nodeapi.internal.network.DigitalSignatureWithCert import net.corda.nodeapi.internal.network.NetworkMap import net.corda.nodeapi.internal.network.NetworkParameters import net.corda.nodeapi.internal.network.SignedNetworkMap -import net.corda.testing.ROOT_CA +import net.corda.testing.DEV_ROOT_CA import org.eclipse.jetty.server.Server import org.eclipse.jetty.server.ServerConnector import org.eclipse.jetty.server.handler.HandlerCollection @@ -29,6 +27,7 @@ import java.io.InputStream import java.net.InetSocketAddress import java.time.Duration import java.time.Instant +import javax.security.auth.x500.X500Principal import javax.ws.rs.* import javax.ws.rs.core.MediaType import javax.ws.rs.core.Response @@ -36,7 +35,7 @@ import javax.ws.rs.core.Response.ok class NetworkMapServer(cacheTimeout: Duration, hostAndPort: NetworkHostAndPort, - rootCa: CertificateAndKeyPair = ROOT_CA, // Default to ROOT_CA for testing. + rootCa: CertificateAndKeyPair = DEV_ROOT_CA, private val myHostNameValue: String = "test.host.name", vararg additionalServices: Any) : Closeable { companion object { @@ -48,12 +47,12 @@ class NetworkMapServer(cacheTimeout: Duration, CertificateType.NETWORK_MAP, rootCAKeyAndCert.certificate, rootCAKeyAndCert.keyPair, - CordaX500Name("Corda Network Map", "R3 Ltd", "London","GB"), - networkMapKey.public).cert + X500Principal("CN=Corda Network Map,O=R3 Ltd,L=London,C=GB"), + networkMapKey.public) // Check that the certificate validates. Nodes will perform this check upon receiving a network map, // it's better to fail here than there. - X509Utilities.validateCertificateChain(rootCAKeyAndCert.certificate.cert, networkMapCert) - return CertificateAndKeyPair(networkMapCert.toX509CertHolder(), networkMapKey) + X509Utilities.validateCertificateChain(rootCAKeyAndCert.certificate, networkMapCert) + return CertificateAndKeyPair(networkMapCert, networkMapKey) } } @@ -130,7 +129,7 @@ class NetworkMapServer(cacheTimeout: Duration, val networkMap = NetworkMap(nodeInfoMap.keys.toList(), parametersHash) val serializedNetworkMap = networkMap.serialize() val signature = Crypto.doSign(networkMapKeyAndCert.keyPair.private, serializedNetworkMap.bytes) - val signedNetworkMap = SignedNetworkMap(networkMap.serialize(), DigitalSignatureWithCert(networkMapKeyAndCert.certificate.cert, signature)) + val signedNetworkMap = SignedNetworkMap(networkMap.serialize(), DigitalSignatureWithCert(networkMapKeyAndCert.certificate, signature)) return Response.ok(signedNetworkMap.serialize().bytes).header("Cache-Control", "max-age=${cacheTimeout.seconds}").build() } diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt index 362672ce89..e24496c50c 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt @@ -12,7 +12,6 @@ import net.corda.core.crypto.toStringShort import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate -import net.corda.core.internal.cert import net.corda.core.internal.unspecifiedCountry import net.corda.core.node.NodeInfo import net.corda.core.utilities.NetworkHostAndPort @@ -21,10 +20,13 @@ import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509Utilities -import org.bouncycastle.cert.X509CertificateHolder +import org.bouncycastle.asn1.x509.GeneralName +import org.bouncycastle.asn1.x509.GeneralSubtree +import org.bouncycastle.asn1.x509.NameConstraints import java.math.BigInteger import java.security.KeyPair import java.security.PublicKey +import java.security.cert.X509Certificate import java.util.concurrent.atomic.AtomicInteger /** @@ -76,8 +78,8 @@ fun getFreeLocalPorts(hostName: String, numberToAlloc: Int): List(DummyCommandData, signers.toList()) diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt index 87a73491b5..ccf34284e7 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt @@ -15,6 +15,7 @@ import org.mockito.internal.stubbing.answers.ThrowsException import java.lang.reflect.Modifier import java.nio.file.Files import java.util.* +import javax.security.auth.x500.X500Principal @Suppress("unused") inline fun T.kryoSpecific(reason: String, function: () -> Unit) = if (!AMQP_ENABLED) { @@ -64,8 +65,8 @@ fun configureTestSSL(legalName: CordaX500Name): SSLConfiguration { } } -private val defaultRootCaName = CordaX500Name("Corda Root CA", "R3 Ltd", "London", "GB") -private val defaultIntermediateCaName = CordaX500Name("Corda Intermediate CA", "R3 Ltd", "London", "GB") +private val defaultRootCaName = X500Principal("CN=Corda Root CA,O=R3 Ltd,L=London,C=GB") +private val defaultIntermediateCaName = X500Principal("CN=Corda Intermediate CA,O=R3 Ltd,L=London,C=GB") /** * Returns a pair of [CertificateAndKeyPair]s, the first being the root CA and the second the intermediate CA. @@ -73,8 +74,8 @@ private val defaultIntermediateCaName = CordaX500Name("Corda Intermediate CA", " * @param intermediateCaName The subject name for the intermediate CA cert. */ fun createDevIntermediateCaCertPath( - rootCaName: CordaX500Name = defaultRootCaName, - intermediateCaName: CordaX500Name = defaultIntermediateCaName + rootCaName: X500Principal = defaultRootCaName, + intermediateCaName: X500Principal = defaultIntermediateCaName ): Pair { val rootKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val rootCert = X509Utilities.createSelfSignedCACertificate(rootCaName, rootKeyPair) @@ -87,7 +88,10 @@ fun createDevIntermediateCaCertPath( intermediateCaName, intermediateCaKeyPair.public) - return Pair(CertificateAndKeyPair(rootCert, rootKeyPair), CertificateAndKeyPair(intermediateCaCert, intermediateCaKeyPair)) + return Pair( + CertificateAndKeyPair(rootCert, rootKeyPair), + CertificateAndKeyPair(intermediateCaCert, intermediateCaKeyPair) + ) } /** @@ -97,8 +101,8 @@ fun createDevIntermediateCaCertPath( */ fun createDevNodeCaCertPath( legalName: CordaX500Name, - rootCaName: CordaX500Name = defaultRootCaName, - intermediateCaName: CordaX500Name = defaultIntermediateCaName + rootCaName: X500Principal = defaultRootCaName, + intermediateCaName: X500Principal = defaultIntermediateCaName ): Triple { val (rootCa, intermediateCa) = createDevIntermediateCaCertPath(rootCaName, intermediateCaName) val nodeCa = createDevNodeCa(intermediateCa, legalName)