mirror of
https://github.com/corda/corda.git
synced 2025-06-20 08:03:53 +00:00
Expand X509Utilities
Mark several functions in X509Utilities as @JvmStatic so they're readily accessible from Java. Add functions for modifying common name of an X.500 name, either direct replacement or adding a postfix.
This commit is contained in:
@ -1,6 +1,7 @@
|
|||||||
package net.corda.core.crypto
|
package net.corda.core.crypto
|
||||||
|
|
||||||
import net.corda.core.crypto.Crypto.generateKeyPair
|
import net.corda.core.crypto.Crypto.generateKeyPair
|
||||||
|
import org.bouncycastle.asn1.ASN1Encodable
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
import org.bouncycastle.asn1.x500.X500NameBuilder
|
import org.bouncycastle.asn1.x500.X500NameBuilder
|
||||||
import org.bouncycastle.asn1.x500.style.BCStyle
|
import org.bouncycastle.asn1.x500.style.BCStyle
|
||||||
@ -67,16 +68,33 @@ object X509Utilities {
|
|||||||
*/
|
*/
|
||||||
@Deprecated("Full legal names should be specified in all configurations")
|
@Deprecated("Full legal names should be specified in all configurations")
|
||||||
fun getDevX509Name(commonName: String): X500Name {
|
fun getDevX509Name(commonName: String): X500Name {
|
||||||
return X500NameBuilder(BCStyle.INSTANCE)
|
val nameBuilder = X500NameBuilder(BCStyle.INSTANCE)
|
||||||
.addRDN(BCStyle.CN, commonName)
|
nameBuilder.addRDN(BCStyle.CN, commonName)
|
||||||
.addRDN(BCStyle.O, "R3")
|
nameBuilder.addRDN(BCStyle.O, "R3")
|
||||||
.addRDN(BCStyle.OU, "corda")
|
nameBuilder.addRDN(BCStyle.OU, "corda")
|
||||||
.addRDN(BCStyle.L, "London")
|
nameBuilder.addRDN(BCStyle.L, "London")
|
||||||
.addRDN(BCStyle.C, "UK")
|
nameBuilder.addRDN(BCStyle.C, "UK")
|
||||||
.build()
|
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 and [KeyPair].
|
* Create a de novo root self-signed X509 v3 CA cert and [KeyPair].
|
||||||
* @param subject the cert Subject will be populated with the domain string
|
* @param subject the cert Subject will be populated with the domain string
|
||||||
* @param signatureScheme The signature scheme which will be used to generate keys and certificate. Default to [DEFAULT_TLS_SIGNATURE_SCHEME] if not provided.
|
* @param signatureScheme The signature scheme which will be used to generate keys and certificate. Default to [DEFAULT_TLS_SIGNATURE_SCHEME] if not provided.
|
||||||
@ -84,6 +102,7 @@ object X509Utilities {
|
|||||||
* @return A data class is returned containing the new root CA Cert and its [KeyPair] for signing downstream certificates.
|
* @return A data class is returned containing the new root CA Cert and its [KeyPair] for signing downstream certificates.
|
||||||
* Note the generated certificate tree is capped at max depth of 2 to be in line with commercially available certificates
|
* Note the generated certificate tree is capped at max depth of 2 to be in line with commercially available certificates
|
||||||
*/
|
*/
|
||||||
|
@JvmStatic
|
||||||
fun createSelfSignedCACert(subject: X500Name, signatureScheme: SignatureScheme = DEFAULT_TLS_SIGNATURE_SCHEME, validityWindow: Pair<Int, Int> = DEFAULT_VALIDITY_WINDOW): CertificateAndKey {
|
fun createSelfSignedCACert(subject: X500Name, signatureScheme: SignatureScheme = DEFAULT_TLS_SIGNATURE_SCHEME, validityWindow: Pair<Int, Int> = DEFAULT_VALIDITY_WINDOW): CertificateAndKey {
|
||||||
val keyPair = generateKeyPair(signatureScheme)
|
val keyPair = generateKeyPair(signatureScheme)
|
||||||
val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second)
|
val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second)
|
||||||
@ -100,6 +119,7 @@ object X509Utilities {
|
|||||||
* @return A data class is returned containing the new intermediate CA Cert and its KeyPair for signing downstream certificates.
|
* @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
|
* Note the generated certificate tree is capped at max depth of 1 below this to be in line with commercially available certificates
|
||||||
*/
|
*/
|
||||||
|
@JvmStatic
|
||||||
fun createIntermediateCert(subject: X500Name, ca: CertificateAndKey, signatureScheme: SignatureScheme = DEFAULT_TLS_SIGNATURE_SCHEME, validityWindow: Pair<Int, Int> = DEFAULT_VALIDITY_WINDOW): CertificateAndKey {
|
fun createIntermediateCert(subject: X500Name, ca: CertificateAndKey, signatureScheme: SignatureScheme = DEFAULT_TLS_SIGNATURE_SCHEME, validityWindow: Pair<Int, Int> = DEFAULT_VALIDITY_WINDOW): CertificateAndKey {
|
||||||
val keyPair = generateKeyPair(signatureScheme)
|
val keyPair = generateKeyPair(signatureScheme)
|
||||||
val issuer = X509CertificateHolder(ca.certificate.encoded).subject
|
val issuer = X509CertificateHolder(ca.certificate.encoded).subject
|
||||||
@ -120,6 +140,7 @@ object X509Utilities {
|
|||||||
* @return The generated X509Certificate suitable for use as a Server/Client certificate in TLS.
|
* @return The generated X509Certificate suitable for use as a Server/Client certificate in TLS.
|
||||||
* This certificate is not marked as a CA cert to be similar in nature to commercial certificates.
|
* This certificate is not marked as a CA cert to be similar in nature to commercial certificates.
|
||||||
*/
|
*/
|
||||||
|
@JvmStatic
|
||||||
fun createServerCert(subject: X500Name, publicKey: PublicKey,
|
fun createServerCert(subject: X500Name, publicKey: PublicKey,
|
||||||
ca: CertificateAndKey,
|
ca: CertificateAndKey,
|
||||||
subjectAlternativeNameDomains: List<String>,
|
subjectAlternativeNameDomains: List<String>,
|
||||||
@ -141,6 +162,7 @@ object X509Utilities {
|
|||||||
* @param x509Certificate certificate to save
|
* @param x509Certificate certificate to save
|
||||||
* @param filename Target filename
|
* @param filename Target filename
|
||||||
*/
|
*/
|
||||||
|
@JvmStatic
|
||||||
fun saveCertificateAsPEMFile(x509Certificate: X509Certificate, filename: Path) {
|
fun saveCertificateAsPEMFile(x509Certificate: X509Certificate, filename: Path) {
|
||||||
FileWriter(filename.toFile()).use {
|
FileWriter(filename.toFile()).use {
|
||||||
JcaPEMWriter(it).use {
|
JcaPEMWriter(it).use {
|
||||||
@ -154,6 +176,7 @@ object X509Utilities {
|
|||||||
* @param filename Source filename
|
* @param filename Source filename
|
||||||
* @return The X509Certificate that was encoded in the file
|
* @return The X509Certificate that was encoded in the file
|
||||||
*/
|
*/
|
||||||
|
@JvmStatic
|
||||||
fun loadCertificateFromPEMFile(filename: Path): X509Certificate {
|
fun loadCertificateFromPEMFile(filename: Path): X509Certificate {
|
||||||
val reader = PemReader(FileReader(filename.toFile()))
|
val reader = PemReader(FileReader(filename.toFile()))
|
||||||
val pemObject = reader.readPemObject()
|
val pemObject = reader.readPemObject()
|
||||||
@ -203,6 +226,46 @@ object X509Utilities {
|
|||||||
fun createCertificateSigningRequest(subject: X500Name, keyPair: KeyPair, signatureScheme: SignatureScheme = DEFAULT_TLS_SIGNATURE_SCHEME) = Crypto.createCertificateSigningRequest(subject, keyPair, signatureScheme)
|
fun createCertificateSigningRequest(subject: X500Name, keyPair: KeyPair, signatureScheme: SignatureScheme = DEFAULT_TLS_SIGNATURE_SCHEME) = Crypto.createCertificateSigningRequest(subject, keyPair, signatureScheme)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rebuild the distinguished name, adding a postfix to the common name. If no common name is present, this throws an
|
||||||
|
* exception
|
||||||
|
*/
|
||||||
|
@Throws(IllegalArgumentException::class)
|
||||||
|
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::class)
|
||||||
|
fun X500Name.replaceCommonName(commonName: String): X500Name = mutateCommonName { attr -> 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::class)
|
||||||
|
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.commonName: String get() = getRDNs(BCStyle.CN).first().first.value.toString()
|
||||||
val X500Name.location: String get() = getRDNs(BCStyle.L).first().first.value.toString()
|
val X500Name.location: String get() = getRDNs(BCStyle.L).first().first.value.toString()
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import net.corda.core.contracts.StateAndRef
|
|||||||
import net.corda.core.contracts.StateRef
|
import net.corda.core.contracts.StateRef
|
||||||
import net.corda.core.contracts.TransactionType
|
import net.corda.core.contracts.TransactionType
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
|
import net.corda.core.crypto.appendToCommonName
|
||||||
import net.corda.core.crypto.commonName
|
import net.corda.core.crypto.commonName
|
||||||
import net.corda.core.div
|
import net.corda.core.div
|
||||||
import net.corda.core.getOrThrow
|
import net.corda.core.getOrThrow
|
||||||
@ -21,8 +22,6 @@ import net.corda.node.utilities.ServiceIdentityGenerator
|
|||||||
import net.corda.node.utilities.transaction
|
import net.corda.node.utilities.transaction
|
||||||
import net.corda.testing.node.NodeBasedTest
|
import net.corda.testing.node.NodeBasedTest
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
import org.bouncycastle.asn1.x500.X500NameBuilder
|
|
||||||
import org.bouncycastle.asn1.x500.style.BCStyle
|
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -34,15 +33,7 @@ class BFTNotaryServiceTests : NodeBasedTest() {
|
|||||||
val notaryCommonName = X500Name("CN=BFT Notary Server,O=R3,OU=corda,L=Zurich,C=CH")
|
val notaryCommonName = X500Name("CN=BFT Notary Server,O=R3,OU=corda,L=Zurich,C=CH")
|
||||||
|
|
||||||
fun buildNodeName(it: Int, notaryName: X500Name): X500Name {
|
fun buildNodeName(it: Int, notaryName: X500Name): X500Name {
|
||||||
val builder = X500NameBuilder()
|
return notaryName.appendToCommonName("-$it")
|
||||||
notaryName.rdNs.map { it.first }.forEach { attr ->
|
|
||||||
if (attr.type == BCStyle.CN) {
|
|
||||||
builder.addRDN(BCStyle.CN, "${attr.value}-$it")
|
|
||||||
} else {
|
|
||||||
builder.addRDN(attr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return builder.build()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import net.corda.client.rpc.CordaRPCClient
|
|||||||
import net.corda.core.*
|
import net.corda.core.*
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
import net.corda.core.crypto.X509Utilities
|
import net.corda.core.crypto.X509Utilities
|
||||||
|
import net.corda.core.crypto.appendToCommonName
|
||||||
import net.corda.core.crypto.commonName
|
import net.corda.core.crypto.commonName
|
||||||
import net.corda.core.messaging.CordaRPCOps
|
import net.corda.core.messaging.CordaRPCOps
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
@ -30,8 +31,6 @@ import net.corda.nodeapi.config.parseAs
|
|||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
import org.bouncycastle.asn1.x500.X500NameBuilder
|
|
||||||
import org.bouncycastle.asn1.x500.style.BCStyle
|
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.*
|
import java.net.*
|
||||||
@ -503,16 +502,7 @@ class DriverDSL(
|
|||||||
verifierType: VerifierType,
|
verifierType: VerifierType,
|
||||||
rpcUsers: List<User>
|
rpcUsers: List<User>
|
||||||
): ListenableFuture<Pair<Party, List<NodeHandle>>> {
|
): ListenableFuture<Pair<Party, List<NodeHandle>>> {
|
||||||
val nodeNames = (1..clusterSize).map {
|
val nodeNames = (1..clusterSize).map { DUMMY_NOTARY.name.appendToCommonName(it.toString()) }
|
||||||
val nameBuilder = X500NameBuilder(BCStyle.INSTANCE)
|
|
||||||
nameBuilder.addRDN(BCStyle.CN, "${DUMMY_NOTARY.name.commonName} $it")
|
|
||||||
DUMMY_NOTARY.name.rdNs.forEach { rdn ->
|
|
||||||
if (rdn.first.type != BCStyle.CN) {
|
|
||||||
nameBuilder.addRDN(rdn.first)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
nameBuilder.build()
|
|
||||||
}
|
|
||||||
val paths = nodeNames.map { driverDirectory / it.commonName }
|
val paths = nodeNames.map { driverDirectory / it.commonName }
|
||||||
ServiceIdentityGenerator.generateToDisk(paths, type.id, notaryName)
|
ServiceIdentityGenerator.generateToDisk(paths, type.id, notaryName)
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ import net.corda.core.contracts.PartyAndReference
|
|||||||
import net.corda.core.crypto.KeyStoreUtilities
|
import net.corda.core.crypto.KeyStoreUtilities
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
import net.corda.core.crypto.X509Utilities
|
import net.corda.core.crypto.X509Utilities
|
||||||
|
import net.corda.core.crypto.replaceCommonName
|
||||||
import net.corda.core.flows.FlowInitiator
|
import net.corda.core.flows.FlowInitiator
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.flows.FlowVersion
|
import net.corda.core.flows.FlowVersion
|
||||||
@ -339,7 +340,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
|||||||
protected open fun makeServiceEntries(): List<ServiceEntry> {
|
protected open fun makeServiceEntries(): List<ServiceEntry> {
|
||||||
return advertisedServices.map {
|
return advertisedServices.map {
|
||||||
val serviceId = it.type.id
|
val serviceId = it.type.id
|
||||||
val serviceName = it.name ?: X500Name("CN=$serviceId,${configuration.myLegalName}")
|
val serviceName = it.name ?: configuration.myLegalName.replaceCommonName(serviceId)
|
||||||
val identity = obtainKeyPair(configuration.baseDirectory, serviceId + "-private-key", serviceId + "-public", serviceName).first
|
val identity = obtainKeyPair(configuration.baseDirectory, serviceId + "-private-key", serviceId + "-public", serviceName).first
|
||||||
ServiceEntry(it, identity)
|
ServiceEntry(it, identity)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user