diff --git a/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt b/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt index 80ce7842ae..6db86fb563 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt @@ -14,19 +14,19 @@ import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec -import org.bouncycastle.asn1.* +import org.bouncycastle.asn1.ASN1Integer +import org.bouncycastle.asn1.ASN1ObjectIdentifier +import org.bouncycastle.asn1.DERNull +import org.bouncycastle.asn1.DLSequence import org.bouncycastle.asn1.bc.BCObjectIdentifiers import org.bouncycastle.asn1.nist.NISTObjectIdentifiers import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers import org.bouncycastle.asn1.pkcs.PrivateKeyInfo import org.bouncycastle.asn1.sec.SECObjectIdentifiers -import org.bouncycastle.asn1.x500.X500Name -import org.bouncycastle.asn1.x509.* +import org.bouncycastle.asn1.x509.AlgorithmIdentifier +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo import org.bouncycastle.asn1.x9.X9ObjectIdentifiers import org.bouncycastle.cert.X509CertificateHolder -import org.bouncycastle.cert.X509v3CertificateBuilder -import org.bouncycastle.cert.bc.BcX509ExtensionUtils -import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPrivateKey @@ -40,10 +40,6 @@ import org.bouncycastle.jce.spec.ECPublicKeySpec import org.bouncycastle.math.ec.ECConstants import org.bouncycastle.math.ec.FixedPointCombMultiplier import org.bouncycastle.math.ec.WNafUtil -import org.bouncycastle.operator.ContentSigner -import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder -import org.bouncycastle.pkcs.PKCS10CertificationRequest -import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PrivateKey import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PublicKey @@ -53,7 +49,6 @@ import java.security.* import java.security.spec.InvalidKeySpecException import java.security.spec.PKCS8EncodedKeySpec import java.security.spec.X509EncodedKeySpec -import java.util.* import javax.crypto.Mac import javax.crypto.spec.SecretKeySpec @@ -196,7 +191,7 @@ object Crypto { // that could cause unexpected and suspicious behaviour. // i.e. if someone removes a Provider and then he/she adds a new one with the same name. // The val is private to avoid any harmful state changes. - private val providerMap: Map = mapOf( + val providerMap: Map = mapOf( BouncyCastleProvider.PROVIDER_NAME to getBouncyCastleProvider(), CordaSecurityProvider.PROVIDER_NAME to CordaSecurityProvider(), "BCPQC" to BouncyCastlePQCProvider()) // unfortunately, provider's name is not final in BouncyCastlePQCProvider, so we explicitly set it. @@ -770,90 +765,6 @@ object Crypto { return mac.doFinal(seed) } - /** - * 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. - */ - fun createCertificate(certificateType: CertificateType, issuer: X500Name, - subject: X500Name, 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 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) - .addExtension(Extension.extendedKeyUsage, false, keyPurposes) - - if (nameConstraints != null) { - builder.addExtension(Extension.nameConstraints, true, nameConstraints) - } - return builder - } - - /** - * Build and sign an X.509 certificate with the given signer. - * - * @param issuer name of the issuing entity. - * @param issuerSigner content signer to sign the certificate with. - * @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. - */ - fun createCertificate(certificateType: CertificateType, issuer: X500Name, issuerSigner: ContentSigner, - subject: X500Name, subjectPublicKey: PublicKey, - validityWindow: Pair, - nameConstraints: NameConstraints? = null): X509CertificateHolder { - val builder = createCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints) - return builder.build(issuerSigner).apply { - require(isValidOn(Date())) - } - } - - /** - * Build and sign an X.509 certificate with CA cert private key. - * - * @param issuer name of the issuing entity. - * @param issuerKeyPair the public & private key to sign the certificate with. - * @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. - */ - fun createCertificate(certificateType: CertificateType, issuer: X500Name, issuerKeyPair: KeyPair, - subject: X500Name, subjectPublicKey: PublicKey, - validityWindow: Pair, - nameConstraints: NameConstraints? = null): X509CertificateHolder { - - val signatureScheme = findSignatureScheme(issuerKeyPair.private) - val provider = providerMap[signatureScheme.providerName] - val builder = createCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints) - - val signer = ContentSignerBuilder.build(signatureScheme, issuerKeyPair.private, provider) - return builder.build(signer).apply { - require(isValidOn(Date())) - require(isSignatureValid(JcaContentVerifierProviderBuilder().build(issuerKeyPair.public))) - } - } - - /** - * Create certificate signing request using provided information. - */ - fun createCertificateSigningRequest(subject: X500Name, keyPair: KeyPair, signatureScheme: SignatureScheme): PKCS10CertificationRequest { - val signer = ContentSignerBuilder.build(signatureScheme, keyPair.private, providerMap[signatureScheme.providerName]) - return JcaPKCS10CertificationRequestBuilder(subject, keyPair.public).build(signer) - } - private class KeyInfoConverter(val signatureScheme: SignatureScheme) : AsymmetricKeyInfoConverter { override fun generatePublic(keyInfo: SubjectPublicKeyInfo?): PublicKey? = keyInfo?.let { decodePublicKey(signatureScheme, it.encoded) } override fun generatePrivate(keyInfo: PrivateKeyInfo?): PrivateKey? = keyInfo?.let { decodePrivateKey(signatureScheme, it.encoded) } diff --git a/core/src/main/kotlin/net/corda/core/crypto/X500NameUtils.kt b/core/src/main/kotlin/net/corda/core/crypto/X500NameUtils.kt new file mode 100644 index 0000000000..0043988ba4 --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/crypto/X500NameUtils.kt @@ -0,0 +1,77 @@ +@file:JvmName("X500NameUtils") +package net.corda.core.crypto + +import org.bouncycastle.asn1.ASN1Encodable +import org.bouncycastle.asn1.x500.X500Name +import org.bouncycastle.asn1.x500.X500NameBuilder +import org.bouncycastle.asn1.x500.style.BCStyle +import org.bouncycastle.cert.X509CertificateHolder +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter +import java.security.KeyPair +import java.security.cert.X509Certificate + +/** + * Rebuild the distinguished name, adding a postfix to the common name. If no common name is present. + * @throws IllegalArgumentException if the distinguished name does not contain a common name element. + */ +fun X500Name.appendToCommonName(commonName: String): X500Name = mutateCommonName { attr -> attr.toString() + commonName } + +/** + * Rebuild the distinguished name, replacing the common name with the given value. If no common name is present, this + * adds one. + * @throws IllegalArgumentException if the distinguished name does not contain a common name element. + */ +fun X500Name.replaceCommonName(commonName: String): X500Name = mutateCommonName { _ -> commonName } + +/** + * Rebuild the distinguished name, replacing the common name with a value generated from the provided function. + * + * @param mutator a function to generate the new value from the previous one. + * @throws IllegalArgumentException if the distinguished name does not contain a common name element. + */ +private fun X500Name.mutateCommonName(mutator: (ASN1Encodable) -> String): X500Name { + val builder = X500NameBuilder(BCStyle.INSTANCE) + var matched = false + this.rdNs.forEach { rdn -> + rdn.typesAndValues.forEach { typeAndValue -> + when (typeAndValue.type) { + BCStyle.CN -> { + matched = true + builder.addRDN(typeAndValue.type, mutator(typeAndValue.value)) + } + else -> { + builder.addRDN(typeAndValue) + } + } + } + } + require(matched) { "Input X.500 name must include a common name (CN) attribute: ${this}" } + return builder.build() +} + +val X500Name.commonName: String get() = getRDNs(BCStyle.CN).first().first.value.toString() +val X500Name.orgName: String? get() = getRDNs(BCStyle.O).firstOrNull()?.first?.value?.toString() +val X500Name.location: String get() = getRDNs(BCStyle.L).first().first.value.toString() +val X500Name.locationOrNull: String? get() = try { + location +} catch (e: Exception) { + null +} +val X509Certificate.subject: X500Name get() = X509CertificateHolder(encoded).subject +val X509CertificateHolder.cert: X509Certificate get() = JcaX509CertificateConverter().getCertificate(this) + +/** + * Generate a distinguished name from the provided values. + */ +@JvmOverloads +fun getX509Name(myLegalName: String, nearestCity: String, email: String, country: String? = null): X500Name { + return X500NameBuilder(BCStyle.INSTANCE).let { builder -> + builder.addRDN(BCStyle.CN, myLegalName) + builder.addRDN(BCStyle.L, nearestCity) + country?.let { builder.addRDN(BCStyle.C, it) } + builder.addRDN(BCStyle.E, email) + builder.build() + } +} + +data class CertificateAndKeyPair(val certificate: X509CertificateHolder, val keyPair: KeyPair) 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 f02f028fb4..b34d2cc976 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt @@ -8,9 +8,7 @@ import net.corda.core.internal.declaredField import net.corda.core.internal.div import net.corda.core.serialization.serialize import net.corda.core.utilities.OpaqueBytes -import net.corda.node.utilities.loadKeyStore -import net.corda.node.utilities.loadOrCreateKeyStore -import net.corda.node.utilities.save +import net.corda.node.utilities.* import net.corda.testing.TestDependencyInjectionBase import org.bouncycastle.asn1.x500.X500Name import org.junit.Rule 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 ef6f879409..8d2589119c 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/X509NameConstraintsTest.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/X509NameConstraintsTest.kt @@ -1,9 +1,7 @@ package net.corda.core.crypto import net.corda.core.internal.toTypedArray -import net.corda.node.utilities.KEYSTORE_TYPE -import net.corda.node.utilities.addOrReplaceCertificate -import net.corda.node.utilities.addOrReplaceKey +import net.corda.node.utilities.* import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x509.GeneralName import org.bouncycastle.asn1.x509.GeneralSubtree @@ -20,13 +18,13 @@ class X509NameConstraintsTest { private fun makeKeyStores(subjectName: X500Name, nameConstraints: NameConstraints): Pair { val rootKeys = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val rootCACert = X509Utilities.createSelfSignedCACertificate(X509Utilities.getX509Name("Corda Root CA","London","demo@r3.com",null), rootKeys) + val rootCACert = X509Utilities.createSelfSignedCACertificate(getX509Name("Corda Root CA", "London", "demo@r3.com", null), rootKeys) val intermediateCAKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootKeys, X509Utilities.getX509Name("Corda Intermediate CA","London","demo@r3.com",null), intermediateCAKeyPair.public) + val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootKeys, getX509Name("Corda Intermediate CA", "London", "demo@r3.com", null), intermediateCAKeyPair.public) val clientCAKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val clientCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, intermediateCACert, intermediateCAKeyPair, X509Utilities.getX509Name("Corda Client CA","London","demo@r3.com",null), clientCAKeyPair.public, nameConstraints = nameConstraints) + val clientCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, intermediateCACert, intermediateCAKeyPair, getX509Name("Corda Client CA", "London", "demo@r3.com", null), clientCAKeyPair.public, nameConstraints = nameConstraints) val keyPass = "password" val trustStore = KeyStore.getInstance(KEYSTORE_TYPE) diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/KryoTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/KryoTests.kt index 41c9dd0466..702c66b9b7 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/KryoTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/KryoTests.kt @@ -11,20 +11,14 @@ import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.sequence import net.corda.node.serialization.KryoServerSerializationScheme import net.corda.node.services.persistence.NodeAttachmentService -import net.corda.testing.ALICE import net.corda.testing.ALICE_PUBKEY -import net.corda.testing.BOB -import net.corda.testing.BOB_PUBKEY import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy -import org.bouncycastle.cert.X509CertificateHolder import org.junit.Before import org.junit.Test import org.slf4j.LoggerFactory import java.io.ByteArrayInputStream import java.io.InputStream -import java.security.cert.CertPath -import java.security.cert.CertificateFactory import java.time.Instant import kotlin.test.assertEquals import kotlin.test.assertTrue @@ -150,26 +144,6 @@ class KryoTests { assertEquals(-1, readRubbishStream.read()) } - @Test - fun `serialize - deserialize X509CertififcateHolder`() { - val expected: X509CertificateHolder = X509Utilities.createSelfSignedCACertificate(ALICE.name, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) - val serialized = expected.serialize(factory, context).bytes - val actual: X509CertificateHolder = serialized.deserialize(factory, context) - assertEquals(expected, actual) - } - - @Test - fun `serialize - deserialize X509CertPath`() { - val certFactory = CertificateFactory.getInstance("X509") - 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, BOB_PUBKEY) - val expected = certFactory.generateCertPath(listOf(certificate.cert, rootCACert.cert)) - val serialized = expected.serialize(factory, context).bytes - val actual: CertPath = serialized.deserialize(factory, context) - assertEquals(expected, actual) - } - @CordaSerializable private data class Person(val name: String, val birthday: Instant?) 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 59649518bd..d053552c1c 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 @@ -1,10 +1,8 @@ package net.corda.services.messaging +import net.corda.core.crypto.Crypto import net.corda.core.internal.copyTo import net.corda.core.internal.createDirectories -import net.corda.core.crypto.CertificateType -import net.corda.core.crypto.Crypto -import net.corda.core.crypto.X509Utilities import net.corda.core.internal.exists import net.corda.node.utilities.* import net.corda.nodeapi.ArtemisMessagingComponent.Companion.NODE_USER 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 76d7ec5792..f66e53a7be 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 @@ -4,9 +4,10 @@ import com.typesafe.config.Config import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigParseOptions import com.typesafe.config.ConfigRenderOptions +import net.corda.core.crypto.Crypto +import net.corda.core.crypto.SignatureScheme import net.corda.core.internal.copyTo import net.corda.core.internal.createDirectories -import net.corda.core.crypto.* import net.corda.core.internal.div import net.corda.core.internal.exists import net.corda.core.utilities.loggerFor 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 9b50ac1572..88ae5560bf 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 @@ -1,10 +1,14 @@ package net.corda.node.services.keys -import net.corda.core.crypto.* +import net.corda.core.crypto.ContentSignerBuilder +import net.corda.core.crypto.Crypto +import net.corda.core.crypto.cert import net.corda.core.identity.AnonymousPartyAndPath import net.corda.core.identity.PartyAndCertificate import net.corda.core.node.services.IdentityService import net.corda.core.utilities.days +import net.corda.node.utilities.CertificateType +import net.corda.node.utilities.X509Utilities import org.bouncycastle.operator.ContentSigner import java.security.KeyPair import java.security.PublicKey @@ -31,7 +35,7 @@ fun freshCertificate(identityService: IdentityService, revocationEnabled: Boolean = false): AnonymousPartyAndPath { val issuerCertificate = issuer.certificate val window = X509Utilities.getCertificateValidityWindow(Duration.ZERO, 3650.days, issuerCertificate) - val ourCertificate = Crypto.createCertificate(CertificateType.IDENTITY, issuerCertificate.subject, issuerSigner, issuer.name, subjectPublicKey, window) + val ourCertificate = X509Utilities.createCertificate(CertificateType.IDENTITY, issuerCertificate.subject, issuerSigner, issuer.name, subjectPublicKey, window) val certFactory = CertificateFactory.getInstance("X509") val ourCertPath = certFactory.generateCertPath(listOf(ourCertificate.cert) + issuer.certPath.certificates) val anonymisedIdentity = AnonymousPartyAndPath(subjectPublicKey, ourCertPath) diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt index 5ef9d5f906..919a744191 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt @@ -2,13 +2,13 @@ package net.corda.node.services.messaging import com.google.common.util.concurrent.ListenableFuture import io.netty.handler.ssl.SslHandler -import net.corda.core.* import net.corda.core.concurrent.CordaFuture -import net.corda.core.crypto.* -import net.corda.core.crypto.X509Utilities.CORDA_CLIENT_TLS -import net.corda.core.crypto.X509Utilities.CORDA_ROOT_CA -import net.corda.core.internal.concurrent.openFuture +import net.corda.core.crypto.AddressFormatException +import net.corda.core.crypto.newSecureRandom +import net.corda.core.crypto.parsePublicKeyBase58 +import net.corda.core.crypto.random63BitValue import net.corda.core.internal.ThreadBox +import net.corda.core.internal.concurrent.openFuture import net.corda.core.internal.div import net.corda.core.internal.noneOrSingle import net.corda.core.node.NodeInfo @@ -22,6 +22,9 @@ import net.corda.node.services.messaging.NodeLoginModule.Companion.NODE_ROLE import net.corda.node.services.messaging.NodeLoginModule.Companion.PEER_ROLE import net.corda.node.services.messaging.NodeLoginModule.Companion.RPC_ROLE import net.corda.node.services.messaging.NodeLoginModule.Companion.VERIFIER_ROLE +import net.corda.node.utilities.X509Utilities +import net.corda.node.utilities.X509Utilities.CORDA_CLIENT_TLS +import net.corda.node.utilities.X509Utilities.CORDA_ROOT_CA import net.corda.node.utilities.getX509Certificate import net.corda.node.utilities.loadKeyStore import net.corda.nodeapi.* diff --git a/core/src/main/kotlin/net/corda/core/crypto/X509Utilities.kt b/node/src/main/kotlin/net/corda/node/utilities/X509Utilities.kt similarity index 55% rename from core/src/main/kotlin/net/corda/core/crypto/X509Utilities.kt rename to node/src/main/kotlin/net/corda/node/utilities/X509Utilities.kt index db7717aa33..53ed5580eb 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/X509Utilities.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/X509Utilities.kt @@ -1,25 +1,33 @@ -package net.corda.core.crypto +package net.corda.node.utilities +import net.corda.core.crypto.* import net.corda.core.utilities.days import net.corda.core.utilities.millis -import org.bouncycastle.asn1.ASN1Encodable +import org.bouncycastle.asn1.ASN1EncodableVector +import org.bouncycastle.asn1.ASN1Sequence +import org.bouncycastle.asn1.DERSequence import org.bouncycastle.asn1.x500.X500Name -import org.bouncycastle.asn1.x500.X500NameBuilder -import org.bouncycastle.asn1.x500.style.BCStyle -import org.bouncycastle.asn1.x509.KeyPurposeId -import org.bouncycastle.asn1.x509.KeyUsage -import org.bouncycastle.asn1.x509.NameConstraints +import org.bouncycastle.asn1.x509.* +import org.bouncycastle.asn1.x509.Extension import org.bouncycastle.cert.X509CertificateHolder -import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter +import org.bouncycastle.cert.X509v3CertificateBuilder +import org.bouncycastle.cert.bc.BcX509ExtensionUtils +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder import org.bouncycastle.openssl.jcajce.JcaPEMWriter +import org.bouncycastle.operator.ContentSigner +import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder +import org.bouncycastle.pkcs.PKCS10CertificationRequest +import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder import org.bouncycastle.util.io.pem.PemReader import java.io.FileReader import java.io.FileWriter import java.io.InputStream +import java.math.BigInteger import java.nio.file.Path import java.security.KeyPair import java.security.PublicKey import java.security.cert.* +import java.security.cert.Certificate import java.time.Duration import java.time.Instant import java.time.temporal.ChronoUnit @@ -69,44 +77,13 @@ object X509Utilities { return Pair(notBefore, notAfter) } - /** - * Return a bogus X509 for dev purposes. Use [getX509Name] for something more real. - */ - @Deprecated("Full legal names should be specified in all configurations") - fun getDevX509Name(commonName: String): X500Name { - val nameBuilder = X500NameBuilder(BCStyle.INSTANCE) - nameBuilder.addRDN(BCStyle.CN, commonName) - nameBuilder.addRDN(BCStyle.O, "R3") - nameBuilder.addRDN(BCStyle.OU, "corda") - nameBuilder.addRDN(BCStyle.L, "London") - nameBuilder.addRDN(BCStyle.C, "GB") - return nameBuilder.build() - } - - /** - * Generate a distinguished name from the provided values. - * - * @see [CoreTestUtils.getTestX509Name] for generating distinguished names for test cases. - */ - @JvmOverloads - @JvmStatic - fun getX509Name(myLegalName: String, nearestCity: String, email: String, country: String? = null): X500Name { - return X500NameBuilder(BCStyle.INSTANCE).let { builder -> - builder.addRDN(BCStyle.CN, myLegalName) - builder.addRDN(BCStyle.L, nearestCity) - country?.let { builder.addRDN(BCStyle.C, it) } - builder.addRDN(BCStyle.E, email) - builder.build() - } - } - /* * Create a de novo root self-signed X509 v3 CA cert. */ @JvmStatic fun createSelfSignedCACertificate(subject: X500Name, keyPair: KeyPair, validityWindow: Pair = DEFAULT_VALIDITY_WINDOW): X509CertificateHolder { val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second) - return Crypto.createCertificate(CertificateType.ROOT_CA, subject, keyPair, subject, keyPair.public, window) + return createCertificate(CertificateType.ROOT_CA, subject, keyPair, subject, keyPair.public, window) } /** @@ -126,7 +103,7 @@ object X509Utilities { validityWindow: Pair = DEFAULT_VALIDITY_WINDOW, nameConstraints: NameConstraints? = null): X509CertificateHolder { val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second, issuerCertificate) - return Crypto.createCertificate(certificateType, issuerCertificate.subject, issuerKeyPair, subject, subjectPublicKey, window, nameConstraints) + return createCertificate(certificateType, issuerCertificate.subject, issuerKeyPair, subject, subjectPublicKey, window, nameConstraints) } fun validateCertificateChain(trustedRoot: X509CertificateHolder, vararg certificates: Certificate) { @@ -168,54 +145,93 @@ object X509Utilities { } } - fun createCertificateSigningRequest(subject: X500Name, keyPair: KeyPair, signatureScheme: SignatureScheme = DEFAULT_TLS_SIGNATURE_SCHEME) = Crypto.createCertificateSigningRequest(subject, keyPair, signatureScheme) -} + /** + * 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. + */ + fun createCertificate(certificateType: CertificateType, issuer: X500Name, + subject: X500Name, subjectPublicKey: PublicKey, + validityWindow: Pair, + nameConstraints: NameConstraints? = null): X509v3CertificateBuilder { -/** - * Rebuild the distinguished name, adding a postfix to the common name. If no common name is present. - * @throws IllegalArgumentException if the distinguished name does not contain a common name element. - */ -fun X500Name.appendToCommonName(commonName: String): X500Name = mutateCommonName { attr -> attr.toString() + commonName } + val serial = BigInteger.valueOf(random63BitValue()) + val keyPurposes = DERSequence(ASN1EncodableVector().apply { certificateType.purposes.forEach { add(it) } }) + val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(subjectPublicKey.encoded)) -/** - * Rebuild the distinguished name, replacing the common name with the given value. If no common name is present, this - * adds one. - * @throws IllegalArgumentException if the distinguished name does not contain a common name element. - */ -fun X500Name.replaceCommonName(commonName: String): X500Name = mutateCommonName { _ -> commonName } + 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) + .addExtension(Extension.extendedKeyUsage, false, keyPurposes) -/** - * Rebuild the distinguished name, replacing the common name with a value generated from the provided function. - * - * @param mutator a function to generate the new value from the previous one. - * @throws IllegalArgumentException if the distinguished name does not contain a common name element. - */ -private fun X500Name.mutateCommonName(mutator: (ASN1Encodable) -> String): X500Name { - val builder = X500NameBuilder(BCStyle.INSTANCE) - var matched = false - this.rdNs.forEach { rdn -> - rdn.typesAndValues.forEach { typeAndValue -> - when (typeAndValue.type) { - BCStyle.CN -> { - matched = true - builder.addRDN(typeAndValue.type, mutator(typeAndValue.value)) - } - else -> { - builder.addRDN(typeAndValue) - } - } + if (nameConstraints != null) { + builder.addExtension(Extension.nameConstraints, true, nameConstraints) + } + return builder + } + + /** + * Build and sign an X.509 certificate with the given signer. + * + * @param issuer name of the issuing entity. + * @param issuerSigner content signer to sign the certificate with. + * @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. + */ + fun createCertificate(certificateType: CertificateType, issuer: X500Name, issuerSigner: ContentSigner, + subject: X500Name, subjectPublicKey: PublicKey, + validityWindow: Pair, + nameConstraints: NameConstraints? = null): X509CertificateHolder { + val builder = createCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints) + return builder.build(issuerSigner).apply { + require(isValidOn(Date())) } } - require(matched) { "Input X.500 name must include a common name (CN) attribute: ${this}" } - return builder.build() + + /** + * Build and sign an X.509 certificate with CA cert private key. + * + * @param issuer name of the issuing entity. + * @param issuerKeyPair the public & private key to sign the certificate with. + * @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. + */ + fun createCertificate(certificateType: CertificateType, issuer: X500Name, issuerKeyPair: KeyPair, + subject: X500Name, subjectPublicKey: PublicKey, + validityWindow: Pair, + nameConstraints: NameConstraints? = null): X509CertificateHolder { + + val signatureScheme = Crypto.findSignatureScheme(issuerKeyPair.private) + val provider = Crypto.providerMap[signatureScheme.providerName] + val builder = createCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints) + + val signer = ContentSignerBuilder.build(signatureScheme, issuerKeyPair.private, provider) + return builder.build(signer).apply { + require(isValidOn(Date())) + require(isSignatureValid(JcaContentVerifierProviderBuilder().build(issuerKeyPair.public))) + } + } + + /** + * Create certificate signing request using provided information. + */ + fun createCertificateSigningRequest(subject: X500Name, keyPair: KeyPair, signatureScheme: SignatureScheme): PKCS10CertificationRequest { + val signer = ContentSignerBuilder.build(signatureScheme, keyPair.private, Crypto.providerMap[signatureScheme.providerName]) + return JcaPKCS10CertificationRequestBuilder(subject, keyPair.public).build(signer) + } + + fun createCertificateSigningRequest(subject: X500Name, keyPair: KeyPair) = createCertificateSigningRequest(subject, keyPair, DEFAULT_TLS_SIGNATURE_SCHEME) } -val X500Name.commonName: String get() = getRDNs(BCStyle.CN).first().first.value.toString() -val X500Name.orgName: String? get() = getRDNs(BCStyle.O).firstOrNull()?.first?.value?.toString() -val X500Name.location: String get() = getRDNs(BCStyle.L).first().first.value.toString() -val X500Name.locationOrNull: String? get() = try { location } catch (e: Exception) { null } -val X509Certificate.subject: X500Name get() = X509CertificateHolder(encoded).subject -val X509CertificateHolder.cert: X509Certificate get() = JcaX509CertificateConverter().getCertificate(this) class CertificateStream(val input: InputStream) { private val certificateFactory = CertificateFactory.getInstance("X.509") @@ -223,8 +239,6 @@ class CertificateStream(val input: InputStream) { fun nextCertificate(): X509Certificate = certificateFactory.generateCertificate(input) as X509Certificate } -data class CertificateAndKeyPair(val certificate: X509CertificateHolder, val keyPair: KeyPair) - enum class CertificateType(val keyUsage: KeyUsage, vararg val purposes: KeyPurposeId, val isCA: Boolean) { ROOT_CA(KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyCertSign or KeyUsage.cRLSign), KeyPurposeId.id_kp_serverAuth, KeyPurposeId.id_kp_clientAuth, KeyPurposeId.anyExtendedKeyUsage, isCA = true), INTERMEDIATE_CA(KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyCertSign or KeyUsage.cRLSign), KeyPurposeId.id_kp_serverAuth, KeyPurposeId.id_kp_clientAuth, KeyPurposeId.anyExtendedKeyUsage, isCA = true), diff --git a/node/src/main/kotlin/net/corda/node/utilities/registration/HTTPNetworkRegistrationService.kt b/node/src/main/kotlin/net/corda/node/utilities/registration/HTTPNetworkRegistrationService.kt index 7d5ccae2b5..6904f91258 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/registration/HTTPNetworkRegistrationService.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/registration/HTTPNetworkRegistrationService.kt @@ -1,7 +1,7 @@ package net.corda.node.utilities.registration import com.google.common.net.MediaType -import net.corda.core.crypto.CertificateStream +import net.corda.node.utilities.CertificateStream import org.apache.commons.io.IOUtils import org.bouncycastle.pkcs.PKCS10CertificationRequest import java.io.IOException 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 863bee702c..268b89fa53 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 @@ -1,17 +1,15 @@ package net.corda.node.utilities.registration -import net.corda.core.crypto.CertificateType import net.corda.core.crypto.Crypto -import net.corda.core.crypto.X509Utilities -import net.corda.core.crypto.X509Utilities.CORDA_CLIENT_CA -import net.corda.core.crypto.X509Utilities.CORDA_CLIENT_TLS -import net.corda.core.crypto.X509Utilities.CORDA_ROOT_CA import net.corda.core.crypto.cert import net.corda.core.internal.* import net.corda.core.utilities.seconds import net.corda.core.utilities.validateX500Name import net.corda.node.services.config.NodeConfiguration import net.corda.node.utilities.* +import net.corda.node.utilities.X509Utilities.CORDA_CLIENT_CA +import net.corda.node.utilities.X509Utilities.CORDA_CLIENT_TLS +import net.corda.node.utilities.X509Utilities.CORDA_ROOT_CA import org.bouncycastle.openssl.jcajce.JcaPEMWriter import org.bouncycastle.util.io.pem.PemObject import java.io.StringWriter diff --git a/node/src/test/kotlin/net/corda/node/services/network/InMemoryIdentityServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/network/InMemoryIdentityServiceTests.kt index 9fa1d0e0e5..b425fdcc8e 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/InMemoryIdentityServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/InMemoryIdentityServiceTests.kt @@ -1,12 +1,17 @@ package net.corda.node.services.network -import net.corda.core.crypto.* -import net.corda.core.identity.AnonymousPartyAndPath +import net.corda.core.crypto.CertificateAndKeyPair +import net.corda.core.crypto.Crypto +import net.corda.core.crypto.cert +import net.corda.core.crypto.generateKeyPair import net.corda.core.identity.AnonymousParty +import net.corda.core.identity.AnonymousPartyAndPath import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate import net.corda.core.node.services.IdentityService import net.corda.node.services.identity.InMemoryIdentityService +import net.corda.node.utilities.CertificateType +import net.corda.node.utilities.X509Utilities import net.corda.testing.* import org.bouncycastle.asn1.x500.X500Name import org.junit.Test diff --git a/core/src/test/kotlin/net/corda/core/crypto/X509UtilitiesTest.kt b/node/src/test/kotlin/net/corda/node/utilities/X509UtilitiesTest.kt similarity index 79% rename from core/src/test/kotlin/net/corda/core/crypto/X509UtilitiesTest.kt rename to node/src/test/kotlin/net/corda/node/utilities/X509UtilitiesTest.kt index d1da2f6a27..2c528cbfd5 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/X509UtilitiesTest.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/X509UtilitiesTest.kt @@ -1,19 +1,28 @@ -package net.corda.core.crypto +package net.corda.node.utilities +import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto.EDDSA_ED25519_SHA512 import net.corda.core.crypto.Crypto.generateKeyPair -import net.corda.core.crypto.X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME -import net.corda.core.crypto.X509Utilities.createSelfSignedCACertificate +import net.corda.core.crypto.cert +import net.corda.core.crypto.commonName +import net.corda.core.crypto.getX509Name import net.corda.core.internal.div import net.corda.core.internal.toTypedArray +import net.corda.core.serialization.SerializationContext +import net.corda.core.serialization.deserialize +import net.corda.core.serialization.serialize +import net.corda.node.serialization.KryoServerSerializationScheme import net.corda.node.services.config.createKeystoreForCordaNode -import net.corda.node.utilities.* -import net.corda.testing.MEGA_CORP -import net.corda.testing.getTestX509Name +import net.corda.nodeapi.internal.serialization.AllWhitelist +import net.corda.nodeapi.internal.serialization.KryoHeaderV0_1 +import net.corda.nodeapi.internal.serialization.SerializationContextImpl +import net.corda.nodeapi.internal.serialization.SerializationFactoryImpl +import net.corda.testing.* import 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 @@ -27,7 +36,9 @@ import java.nio.file.Path import java.security.KeyStore import java.security.PrivateKey import java.security.SecureRandom +import java.security.cert.CertPath import java.security.cert.Certificate +import java.security.cert.CertificateFactory import java.security.cert.X509Certificate import java.util.* import java.util.stream.Stream @@ -42,8 +53,8 @@ class X509UtilitiesTest { @Test fun `create valid self-signed CA certificate`() { - val caKey = generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME) - val caCert = createSelfSignedCACertificate(getTestX509Name("Test Cert"), caKey) + val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) + val caCert = X509Utilities.createSelfSignedCACertificate(getTestX509Name("Test Cert"), caKey) assertTrue { caCert.subject.commonName == "Test Cert" } // using our subject common name assertEquals(caCert.issuer, caCert.subject) //self-signed caCert.isValidOn(Date()) // throws on verification problems @@ -57,8 +68,8 @@ class X509UtilitiesTest { @Test fun `load and save a PEM file certificate`() { val tmpCertificateFile = tempFile("cacert.pem") - val caKey = generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME) - val caCert = createSelfSignedCACertificate(getTestX509Name("Test Cert"), caKey) + val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) + val caCert = X509Utilities.createSelfSignedCACertificate(getTestX509Name("Test Cert"), caKey) X509Utilities.saveCertificateAsPEMFile(caCert, tmpCertificateFile) val readCertificate = X509Utilities.loadCertificateFromPEMFile(tmpCertificateFile) assertEquals(caCert, readCertificate) @@ -66,10 +77,10 @@ class X509UtilitiesTest { @Test fun `create valid server certificate chain`() { - val caKey = generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME) - val caCert = createSelfSignedCACertificate(getTestX509Name("Test CA Cert"), caKey) + val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) + val caCert = X509Utilities.createSelfSignedCACertificate(getTestX509Name("Test CA Cert"), caKey) val subject = getTestX509Name("Server Cert") - val keyPair = generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME) + val keyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val serverCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, caKey, subject, keyPair.public) assertTrue { serverCert.subject.toString().contains("CN=Server Cert") } // using our subject common name assertEquals(caCert.issuer, serverCert.issuer) // Issued by our CA cert @@ -86,7 +97,7 @@ class X509UtilitiesTest { val tmpKeyStore = tempFile("keystore.jks") val keyPair = generateKeyPair(EDDSA_ED25519_SHA512) - val selfSignCert = createSelfSignedCACertificate(X500Name("CN=Test"), keyPair) + val selfSignCert = X509Utilities.createSelfSignedCACertificate(X500Name("CN=Test"), keyPair) assertTrue(Arrays.equals(selfSignCert.subjectPublicKeyInfo.encoded, keyPair.public.encoded)) @@ -111,7 +122,7 @@ class X509UtilitiesTest { fun `signing EdDSA key with EcDSA certificate`() { val tmpKeyStore = tempFile("keystore.jks") val ecDSAKey = generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256) - val ecDSACert = createSelfSignedCACertificate(X500Name("CN=Test"), ecDSAKey) + val ecDSACert = X509Utilities.createSelfSignedCACertificate(X500Name("CN=Test"), ecDSAKey) val edDSAKeypair = generateKeyPair(EDDSA_ED25519_SHA512) val edDSACert = X509Utilities.createCertificate(CertificateType.TLS, ecDSACert, ecDSAKey, X500Name("CN=TestEdDSA"), edDSAKeypair.public) @@ -155,8 +166,8 @@ class X509UtilitiesTest { // Now sign something with private key and verify against certificate public key val testData = "12345".toByteArray() - val caSignature = Crypto.doSign(DEFAULT_TLS_SIGNATURE_SCHEME, rootCaPrivateKey, testData) - assertTrue { Crypto.isValid(DEFAULT_TLS_SIGNATURE_SCHEME, rootCaCert.publicKey, caSignature, testData) } + val caSignature = Crypto.doSign(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME, rootCaPrivateKey, testData) + assertTrue { Crypto.isValid(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME, rootCaCert.publicKey, caSignature, testData) } // Load back generated intermediate CA Cert and private key val intermediateCaCert = keyStore.getCertificate(X509Utilities.CORDA_INTERMEDIATE_CA) as X509Certificate @@ -165,8 +176,8 @@ class X509UtilitiesTest { intermediateCaCert.verify(rootCaCert.publicKey) // Now sign something with private key and verify against certificate public key - val intermediateSignature = Crypto.doSign(DEFAULT_TLS_SIGNATURE_SCHEME, intermediateCaCertPrivateKey, testData) - assertTrue { Crypto.isValid(DEFAULT_TLS_SIGNATURE_SCHEME, intermediateCaCert.publicKey, intermediateSignature, testData) } + val intermediateSignature = Crypto.doSign(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME, intermediateCaCertPrivateKey, testData) + assertTrue { Crypto.isValid(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME, intermediateCaCert.publicKey, intermediateSignature, testData) } } @Test @@ -209,9 +220,9 @@ class X509UtilitiesTest { assertTrue { sslCertAndKey.certificate.subject.toString().contains(MEGA_CORP.name.commonName) } // Now sign something with private key and verify against certificate public key val testData = "123456".toByteArray() - val signature = Crypto.doSign(DEFAULT_TLS_SIGNATURE_SCHEME, serverCertAndKey.keyPair.private, testData) + val signature = Crypto.doSign(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME, serverCertAndKey.keyPair.private, testData) val publicKey = Crypto.toSupportedPublicKey(serverCertAndKey.certificate.subjectPublicKeyInfo) - assertTrue { Crypto.isValid(DEFAULT_TLS_SIGNATURE_SCHEME, publicKey, signature, testData) } + assertTrue { Crypto.isValid(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME, publicKey, signature, testData) } } @Test @@ -343,11 +354,11 @@ class X509UtilitiesTest { trustStoreFilePath: Path, trustStorePassword: String ): KeyStore { - val rootCAKey = generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME) - val rootCACert = createSelfSignedCACertificate(X509Utilities.getX509Name("Corda Node Root CA","London","demo@r3.com",null), rootCAKey) + val rootCAKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) + val rootCACert = X509Utilities.createSelfSignedCACertificate(getX509Name("Corda Node Root CA", "London", "demo@r3.com", null), rootCAKey) val intermediateCAKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, X509Utilities.getX509Name("Corda Node Intermediate CA","London","demo@r3.com",null), intermediateCAKeyPair.public) + val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, getX509Name("Corda Node Intermediate CA", "London", "demo@r3.com", null), intermediateCAKeyPair.public) val keyPass = keyPassword.toCharArray() val keyStore = loadOrCreateKeyStore(keyStoreFilePath, storePassword) @@ -374,7 +385,7 @@ class X509UtilitiesTest { @Test fun `Get correct private key type from Keystore`() { val keyPair = generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256) - val selfSignCert = createSelfSignedCACertificate(X500Name("CN=Test"), keyPair) + val selfSignCert = X509Utilities.createSelfSignedCACertificate(X500Name("CN=Test"), keyPair) val keyStore = loadOrCreateKeyStore(tempFile("testKeystore.jks"), "keystorepassword") keyStore.setKeyEntry("Key", keyPair.private, "keypassword".toCharArray(), arrayOf(selfSignCert.cert)) @@ -384,4 +395,38 @@ class X509UtilitiesTest { assertTrue(keyFromKeystore is java.security.interfaces.ECPrivateKey) // by default JKS returns SUN EC key assertTrue(keyFromKeystoreCasted is org.bouncycastle.jce.interfaces.ECPrivateKey) } + + @Test + fun `serialize - deserialize X509CertififcateHolder`() { + val factory = SerializationFactoryImpl().apply { registerScheme(KryoServerSerializationScheme()) } + val context = SerializationContextImpl(KryoHeaderV0_1, + javaClass.classLoader, + AllWhitelist, + emptyMap(), + true, + SerializationContext.UseCase.P2P) + val expected: X509CertificateHolder = X509Utilities.createSelfSignedCACertificate(ALICE.name, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) + val serialized = expected.serialize(factory, context).bytes + val actual: X509CertificateHolder = serialized.deserialize(factory, context) + assertEquals(expected, actual) + } + + @Test + fun `serialize - deserialize X509CertPath`() { + val factory = SerializationFactoryImpl().apply { registerScheme(KryoServerSerializationScheme()) } + val context = SerializationContextImpl(KryoHeaderV0_1, + javaClass.classLoader, + AllWhitelist, + emptyMap(), + true, + SerializationContext.UseCase.P2P) + val certFactory = CertificateFactory.getInstance("X509") + 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, BOB_PUBKEY) + val expected = certFactory.generateCertPath(listOf(certificate.cert, rootCACert.cert)) + val serialized = expected.serialize(factory, context).bytes + val actual: CertPath = serialized.deserialize(factory, context) + assertEquals(expected, actual) + } } diff --git a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkisRegistrationHelperTest.kt b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkisRegistrationHelperTest.kt index 1eeae40142..51de115d1f 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkisRegistrationHelperTest.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkisRegistrationHelperTest.kt @@ -3,9 +3,13 @@ package net.corda.node.utilities.registration import com.nhaarman.mockito_kotlin.any import com.nhaarman.mockito_kotlin.eq import com.nhaarman.mockito_kotlin.mock -import net.corda.core.crypto.* +import net.corda.core.crypto.Crypto +import net.corda.core.crypto.SecureHash +import net.corda.core.crypto.cert +import net.corda.core.crypto.commonName import net.corda.core.internal.exists import net.corda.core.internal.toTypedArray +import net.corda.node.utilities.X509Utilities import net.corda.node.utilities.loadKeyStore import net.corda.testing.ALICE import net.corda.testing.getTestX509Name diff --git a/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt b/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt index 5b93da6c1c..5a573b90e3 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt @@ -11,13 +11,15 @@ import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate import net.corda.core.node.ServiceHub import net.corda.core.node.services.IdentityService -import net.corda.core.utilities.OpaqueBytes import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.NetworkHostAndPort +import net.corda.core.utilities.OpaqueBytes import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.VerifierType import net.corda.node.services.config.configureDevKeyAndTrustStores import net.corda.node.services.identity.InMemoryIdentityService +import net.corda.node.utilities.CertificateType +import net.corda.node.utilities.X509Utilities import net.corda.nodeapi.config.SSLConfiguration import net.corda.testing.node.MockServices import net.corda.testing.node.makeTestDataSourceProperties @@ -67,9 +69,9 @@ val ALICE_PUBKEY: PublicKey get() = ALICE_KEY.public val BOB_PUBKEY: PublicKey get() = BOB_KEY.public val CHARLIE_PUBKEY: PublicKey get() = CHARLIE_KEY.public -val MEGA_CORP_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(X509Utilities.getX509Name("MegaCorp","London","demo@r3.com",null), MEGA_CORP_PUBKEY) +val MEGA_CORP_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(getX509Name("MegaCorp", "London", "demo@r3.com", null), MEGA_CORP_PUBKEY) val MEGA_CORP: Party get() = MEGA_CORP_IDENTITY.party -val MINI_CORP_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(X509Utilities.getX509Name("MiniCorp","London","demo@r3.com",null), MINI_CORP_PUBKEY) +val MINI_CORP_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(getX509Name("MiniCorp", "London", "demo@r3.com", null), MINI_CORP_PUBKEY) val MINI_CORP: Party get() = MINI_CORP_IDENTITY.party val BOC_KEY: KeyPair by lazy { generateKeyPair() } @@ -80,7 +82,7 @@ val BOC_PARTY_REF = BOC.ref(OpaqueBytes.of(1)).reference val BIG_CORP_KEY: KeyPair by lazy { generateKeyPair() } val BIG_CORP_PUBKEY: PublicKey get() = BIG_CORP_KEY.public -val BIG_CORP_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(X509Utilities.getX509Name("BigCorporation","London","demo@r3.com",null), BIG_CORP_PUBKEY) +val BIG_CORP_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(getX509Name("BigCorporation", "London", "demo@r3.com", null), BIG_CORP_PUBKEY) val BIG_CORP: Party get() = BIG_CORP_IDENTITY.party val BIG_CORP_PARTY_REF = BIG_CORP.ref(OpaqueBytes.of(1)).reference diff --git a/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt b/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt index f6c236b330..2975a84ada 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt @@ -4,7 +4,9 @@ package net.corda.testing import net.corda.core.contracts.Command import net.corda.core.contracts.TypeOnlyCommandData -import net.corda.core.crypto.* +import net.corda.core.crypto.CertificateAndKeyPair +import net.corda.core.crypto.entropyToKeyPair +import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.testing.DummyPublicKey import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate @@ -12,6 +14,7 @@ import net.corda.core.internal.concurrent.transpose import net.corda.core.messaging.CordaRPCOps import net.corda.core.node.services.ServiceInfo import net.corda.node.services.transactions.ValidatingNotaryService +import net.corda.node.utilities.X509Utilities import net.corda.nodeapi.User import net.corda.testing.driver.DriverDSLExposedInterface import org.bouncycastle.asn1.x500.X500Name diff --git a/test-utils/src/main/kotlin/net/corda/testing/driver/Driver.kt b/test-utils/src/main/kotlin/net/corda/testing/driver/Driver.kt index ab21d10c69..3a14d967e0 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/driver/Driver.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/driver/Driver.kt @@ -11,12 +11,12 @@ import net.corda.cordform.CordformNode import net.corda.cordform.NodeDefinition import net.corda.core.concurrent.CordaFuture import net.corda.core.concurrent.firstOf -import net.corda.core.crypto.X509Utilities import net.corda.core.crypto.appendToCommonName import net.corda.core.crypto.commonName +import net.corda.core.crypto.getX509Name import net.corda.core.identity.Party -import net.corda.core.internal.concurrent.* import net.corda.core.internal.ThreadBox +import net.corda.core.internal.concurrent.* import net.corda.core.internal.div import net.corda.core.internal.times import net.corda.core.messaging.CordaRPCOps @@ -53,9 +53,12 @@ import java.time.Instant import java.time.ZoneOffset.UTC import java.time.format.DateTimeFormatter import java.util.* -import java.util.concurrent.* +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors +import java.util.concurrent.ScheduledExecutorService import java.util.concurrent.TimeUnit.MILLISECONDS import java.util.concurrent.TimeUnit.SECONDS +import java.util.concurrent.TimeoutException import java.util.concurrent.atomic.AtomicInteger import kotlin.concurrent.thread @@ -566,7 +569,7 @@ class DriverDSL( val rpcAddress = portAllocation.nextHostAndPort() val webAddress = portAllocation.nextHostAndPort() // TODO: Derive name from the full picked name, don't just wrap the common name - val name = providedName ?: X509Utilities.getX509Name("${oneOf(names).commonName}-${p2pAddress.port}","London","demo@r3.com",null) + val name = providedName ?: getX509Name("${oneOf(names).commonName}-${p2pAddress.port}", "London", "demo@r3.com", null) val networkMapServiceConfigLookup = networkMapServiceConfigLookup(listOf(object : NodeDefinition { override fun getName() = name.toString() override fun getConfig() = configOf("p2pAddress" to p2pAddress.toString()) diff --git a/test-utils/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt b/test-utils/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt index c85f87f97d..440b4da95a 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt @@ -3,8 +3,8 @@ package net.corda.testing.node import com.google.common.util.concurrent.Futures import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.SettableFuture +import net.corda.core.crypto.getX509Name import net.corda.core.internal.ThreadBox -import net.corda.core.crypto.X509Utilities import net.corda.core.messaging.AllPossibleRecipients import net.corda.core.messaging.MessageRecipientGroup import net.corda.core.messaging.MessageRecipients @@ -128,7 +128,7 @@ class InMemoryMessagingNetwork( id: Int, executor: AffinityExecutor, advertisedServices: List, - description: X500Name = X509Utilities.getX509Name("In memory node $id","London","demo@r3.com",null), + description: X500Name = getX509Name("In memory node $id", "London", "demo@r3.com", null), database: CordaPersistence) : MessagingServiceBuilder { val peerHandle = PeerHandle(id, description) diff --git a/test-utils/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt b/test-utils/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt index e9ba532ce4..7e8d3bedd1 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt @@ -1,10 +1,13 @@ package net.corda.testing.node import net.corda.core.concurrent.CordaFuture -import net.corda.core.crypto.X509Utilities import net.corda.core.crypto.appendToCommonName import net.corda.core.crypto.commonName -import net.corda.core.internal.concurrent.* +import net.corda.core.crypto.getX509Name +import net.corda.core.internal.concurrent.flatMap +import net.corda.core.internal.concurrent.fork +import net.corda.core.internal.concurrent.map +import net.corda.core.internal.concurrent.transpose import net.corda.core.internal.createDirectories import net.corda.core.internal.div import net.corda.core.node.services.ServiceInfo @@ -120,13 +123,13 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() { val nodeAddresses = getFreeLocalPorts("localhost", clusterSize).map { it.toString() } val masterNodeFuture = startNode( - X509Utilities.getX509Name("${notaryName.commonName}-0","London","demo@r3.com",null), + getX509Name("${notaryName.commonName}-0", "London", "demo@r3.com", null), advertisedServices = setOf(serviceInfo), configOverrides = mapOf("notaryNodeAddress" to nodeAddresses[0])) val remainingNodesFutures = (1 until clusterSize).map { startNode( - X509Utilities.getX509Name("${notaryName.commonName}-$it","London","demo@r3.com",null), + getX509Name("${notaryName.commonName}-$it", "London", "demo@r3.com", null), advertisedServices = setOf(serviceInfo), configOverrides = mapOf( "notaryNodeAddress" to nodeAddresses[it], diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt index b593bc32ce..0b484aacda 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt @@ -1,6 +1,6 @@ package net.corda.demobench.model -import net.corda.core.crypto.X509Utilities.getX509Name +import net.corda.core.crypto.getX509Name import net.corda.demobench.plugin.PluginController import net.corda.demobench.pty.R3Pty import tornadofx.* diff --git a/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeControllerTest.kt b/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeControllerTest.kt index 6c77fa3b05..d476ccc93a 100644 --- a/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeControllerTest.kt +++ b/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeControllerTest.kt @@ -1,8 +1,8 @@ package net.corda.demobench.model -import net.corda.core.crypto.X509Utilities.getX509Name -import net.corda.testing.DUMMY_NOTARY +import net.corda.core.crypto.getX509Name import net.corda.nodeapi.User +import net.corda.testing.DUMMY_NOTARY import org.junit.Test import java.nio.file.Path import java.nio.file.Paths diff --git a/verifier/src/integration-test/kotlin/net/corda/verifier/GeneratedLedger.kt b/verifier/src/integration-test/kotlin/net/corda/verifier/GeneratedLedger.kt index a273485ea3..e178b25e71 100644 --- a/verifier/src/integration-test/kotlin/net/corda/verifier/GeneratedLedger.kt +++ b/verifier/src/integration-test/kotlin/net/corda/verifier/GeneratedLedger.kt @@ -3,7 +3,6 @@ package net.corda.verifier import net.corda.client.mock.* import net.corda.core.contracts.* import net.corda.core.crypto.SecureHash -import net.corda.core.crypto.X509Utilities import net.corda.core.crypto.entropyToKeyPair import net.corda.core.crypto.sha256 import net.corda.core.identity.AbstractParty @@ -12,6 +11,7 @@ import net.corda.core.identity.Party import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.WireTransaction import net.corda.testing.contracts.DummyContract +import net.corda.testing.getTestX509Name import java.io.ByteArrayInputStream import java.math.BigInteger import java.security.PublicKey @@ -179,7 +179,7 @@ fun commandGenerator(partiesToPickFrom: Collection): Generator = Generator.int().combine(publicKeyGenerator) { n, key -> - Party(X509Utilities.getDevX509Name("Party$n"), key) + Party(getTestX509Name("Party$n"), key) } fun pickOneOrMaybeNew(from: Collection, generator: Generator): Generator { diff --git a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt index b2432f7e4d..b97f5845dc 100644 --- a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt +++ b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt @@ -2,15 +2,13 @@ package net.corda.verifier import com.typesafe.config.Config import com.typesafe.config.ConfigFactory -import net.corda.core.concurrent.* -import net.corda.core.crypto.X509Utilities +import net.corda.core.concurrent.CordaFuture import net.corda.core.crypto.commonName -import net.corda.core.internal.div import net.corda.core.crypto.random63BitValue import net.corda.core.internal.concurrent.* +import net.corda.core.internal.div import net.corda.core.transactions.LedgerTransaction import net.corda.core.utilities.NetworkHostAndPort -import net.corda.testing.driver.ProcessUtilities import net.corda.core.utilities.loggerFor import net.corda.node.services.config.configureDevKeyAndTrustStores import net.corda.nodeapi.ArtemisMessagingComponent.Companion.NODE_USER @@ -20,6 +18,7 @@ import net.corda.nodeapi.VerifierApi import net.corda.nodeapi.config.NodeSSLConfiguration import net.corda.nodeapi.config.SSLConfiguration import net.corda.testing.driver.* +import net.corda.testing.getTestX509Name import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.api.core.client.ActiveMQClient import org.apache.activemq.artemis.api.core.client.ClientProducer @@ -250,7 +249,7 @@ data class VerifierDriverDSL( val id = verifierCount.andIncrement val jdwpPort = if (driverDSL.isDebug) driverDSL.debugPortAllocation.nextPort() else null val processFuture = driverDSL.executorService.fork { - val verifierName = X509Utilities.getDevX509Name("verifier$id") + val verifierName = getTestX509Name("verifier$id") val baseDirectory = driverDSL.driverDirectory / verifierName.commonName val config = createConfiguration(baseDirectory, address) val configFilename = "verifier.conf"