mirror of
https://github.com/corda/corda.git
synced 2025-06-19 07:38:22 +00:00
Added various X509 utilities to remove some of the existing boilerplate. (#2416)
This commit is contained in:
@ -1,6 +1,7 @@
|
|||||||
package net.corda.core.identity
|
package net.corda.core.identity
|
||||||
|
|
||||||
import net.corda.core.internal.CertRole
|
import net.corda.core.internal.CertRole
|
||||||
|
import net.corda.core.internal.uncheckedCast
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.security.cert.*
|
import java.security.cert.*
|
||||||
@ -45,15 +46,16 @@ class PartyAndCertificate(val certPath: CertPath) {
|
|||||||
// Apply Corda-specific validity rules to the chain. This only applies to chains with any roles present, so
|
// Apply Corda-specific validity rules to the chain. This only applies to chains with any roles present, so
|
||||||
// an all-null chain is in theory valid.
|
// an all-null chain is in theory valid.
|
||||||
var parentRole: CertRole? = CertRole.extract(result.trustAnchor.trustedCert)
|
var parentRole: CertRole? = CertRole.extract(result.trustAnchor.trustedCert)
|
||||||
for (certIdx in (0 until certPath.certificates.size).reversed()) {
|
val certChain: List<X509Certificate> = uncheckedCast(certPath.certificates)
|
||||||
val certificate = certPath.certificates[certIdx]
|
for (certIdx in (0 until certChain.size).reversed()) {
|
||||||
|
val certificate = certChain[certIdx]
|
||||||
val role = CertRole.extract(certificate)
|
val role = CertRole.extract(certificate)
|
||||||
if (parentRole != null) {
|
if (parentRole != null) {
|
||||||
if (role == null) {
|
if (role == null) {
|
||||||
throw CertPathValidatorException("Child certificate whose issuer includes a Corda role, must also specify Corda role")
|
throw CertPathValidatorException("Child certificate whose issuer includes a Corda role, must also specify Corda role")
|
||||||
}
|
}
|
||||||
if (!role.isValidParent(parentRole)) {
|
if (!role.isValidParent(parentRole)) {
|
||||||
val certificateString = (certificate as? X509Certificate)?.subjectDN?.toString() ?: certificate.toString()
|
val certificateString = certificate.subjectDN.toString()
|
||||||
throw CertPathValidatorException("The issuing certificate for $certificateString has role $parentRole, expected one of ${role.validParents}")
|
throw CertPathValidatorException("The issuing certificate for $certificateString has role $parentRole, expected one of ${role.validParents}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import org.bouncycastle.asn1.ASN1Integer
|
|||||||
import org.bouncycastle.asn1.ASN1Primitive
|
import org.bouncycastle.asn1.ASN1Primitive
|
||||||
import org.bouncycastle.asn1.DEROctetString
|
import org.bouncycastle.asn1.DEROctetString
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.security.cert.Certificate
|
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -70,14 +69,6 @@ enum class CertRole(val validParents: NonEmptySet<CertRole?>, val isIdentity: Bo
|
|||||||
*/
|
*/
|
||||||
fun getInstance(data: ByteArray): CertRole = getInstance(ASN1Integer.getInstance(data))
|
fun getInstance(data: ByteArray): CertRole = getInstance(ASN1Integer.getInstance(data))
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a role from a certificate.
|
|
||||||
*
|
|
||||||
* @return the role if the extension is present, or null otherwise.
|
|
||||||
* @throws IllegalArgumentException if the extension is present but is invalid.
|
|
||||||
*/
|
|
||||||
fun extract(cert: Certificate): CertRole? = (cert as? X509Certificate)?.let { extract(it) }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a role from a certificate.
|
* Get a role from a certificate.
|
||||||
*
|
*
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package net.corda.core.crypto
|
package net.corda.core.crypto
|
||||||
|
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.nodeapi.internal.crypto.*
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
|
import net.corda.nodeapi.internal.crypto.X509KeyStore
|
||||||
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
import org.bouncycastle.asn1.x509.GeneralName
|
import org.bouncycastle.asn1.x509.GeneralName
|
||||||
@ -9,7 +11,6 @@ import org.bouncycastle.asn1.x509.GeneralSubtree
|
|||||||
import org.bouncycastle.asn1.x509.NameConstraints
|
import org.bouncycastle.asn1.x509.NameConstraints
|
||||||
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.security.KeyStore
|
|
||||||
import java.security.cert.CertPathValidator
|
import java.security.cert.CertPathValidator
|
||||||
import java.security.cert.CertPathValidatorException
|
import java.security.cert.CertPathValidatorException
|
||||||
import java.security.cert.PKIXParameters
|
import java.security.cert.PKIXParameters
|
||||||
@ -19,8 +20,14 @@ import kotlin.test.assertTrue
|
|||||||
|
|
||||||
class X509NameConstraintsTest {
|
class X509NameConstraintsTest {
|
||||||
|
|
||||||
private fun makeKeyStores(subjectName: X500Name, nameConstraints: NameConstraints): Pair<KeyStore, KeyStore> {
|
private fun makeKeyStores(subjectName: X500Name, nameConstraints: NameConstraints): Pair<X509KeyStore, X509KeyStore> {
|
||||||
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
|
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
|
||||||
|
|
||||||
|
val trustStore = X509KeyStore("password").apply {
|
||||||
|
setCertificate(X509Utilities.CORDA_ROOT_CA, rootCa.certificate)
|
||||||
|
}
|
||||||
|
|
||||||
|
val keyStore = X509KeyStore("password").apply {
|
||||||
val nodeCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val nodeCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val nodeCaCert = X509Utilities.createCertificate(
|
val nodeCaCert = X509Utilities.createCertificate(
|
||||||
CertificateType.NODE_CA,
|
CertificateType.NODE_CA,
|
||||||
@ -29,12 +36,6 @@ class X509NameConstraintsTest {
|
|||||||
CordaX500Name("Corda Client CA", "R3 Ltd", "London", "GB").x500Principal,
|
CordaX500Name("Corda Client CA", "R3 Ltd", "London", "GB").x500Principal,
|
||||||
nodeCaKeyPair.public,
|
nodeCaKeyPair.public,
|
||||||
nameConstraints = nameConstraints)
|
nameConstraints = nameConstraints)
|
||||||
|
|
||||||
val keyPass = "password"
|
|
||||||
val trustStore = KeyStore.getInstance(KEYSTORE_TYPE)
|
|
||||||
trustStore.load(null, keyPass.toCharArray())
|
|
||||||
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCa.certificate)
|
|
||||||
|
|
||||||
val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val tlsCert = X509Utilities.createCertificate(
|
val tlsCert = X509Utilities.createCertificate(
|
||||||
CertificateType.TLS,
|
CertificateType.TLS,
|
||||||
@ -42,14 +43,9 @@ class X509NameConstraintsTest {
|
|||||||
nodeCaKeyPair,
|
nodeCaKeyPair,
|
||||||
X500Principal(subjectName.encoded),
|
X500Principal(subjectName.encoded),
|
||||||
tlsKeyPair.public)
|
tlsKeyPair.public)
|
||||||
|
setPrivateKey(X509Utilities.CORDA_CLIENT_TLS, tlsKeyPair.private, listOf(tlsCert, nodeCaCert, intermediateCa.certificate, rootCa.certificate))
|
||||||
|
}
|
||||||
|
|
||||||
val keyStore = KeyStore.getInstance(KEYSTORE_TYPE)
|
|
||||||
keyStore.load(null, keyPass.toCharArray())
|
|
||||||
keyStore.addOrReplaceKey(
|
|
||||||
X509Utilities.CORDA_CLIENT_TLS,
|
|
||||||
tlsKeyPair.private,
|
|
||||||
keyPass.toCharArray(),
|
|
||||||
arrayOf(tlsCert, nodeCaCert, intermediateCa.certificate, rootCa.certificate))
|
|
||||||
return Pair(keyStore, trustStore)
|
return Pair(keyStore, trustStore)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,30 +56,29 @@ class X509NameConstraintsTest {
|
|||||||
|
|
||||||
val nameConstraints = NameConstraints(acceptableNames, arrayOf())
|
val nameConstraints = NameConstraints(acceptableNames, arrayOf())
|
||||||
val pathValidator = CertPathValidator.getInstance("PKIX")
|
val pathValidator = CertPathValidator.getInstance("PKIX")
|
||||||
val certFactory = X509CertificateFactory()
|
|
||||||
|
|
||||||
assertFailsWith(CertPathValidatorException::class) {
|
assertFailsWith(CertPathValidatorException::class) {
|
||||||
val (keystore, trustStore) = makeKeyStores(X500Name("CN=Bank B"), nameConstraints)
|
val (keystore, trustStore) = makeKeyStores(X500Name("CN=Bank B"), nameConstraints)
|
||||||
val params = PKIXParameters(trustStore)
|
val params = PKIXParameters(trustStore.internal)
|
||||||
params.isRevocationEnabled = false
|
params.isRevocationEnabled = false
|
||||||
val certPath = certFactory.generateCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).asList())
|
val certPath = X509Utilities.buildCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS))
|
||||||
pathValidator.validate(certPath, params)
|
pathValidator.validate(certPath, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue {
|
assertTrue {
|
||||||
val (keystore, trustStore) = makeKeyStores(X500Name("CN=Bank A TLS, O=Bank A"), nameConstraints)
|
val (keystore, trustStore) = makeKeyStores(X500Name("CN=Bank A TLS, O=Bank A"), nameConstraints)
|
||||||
val params = PKIXParameters(trustStore)
|
val params = PKIXParameters(trustStore.internal)
|
||||||
params.isRevocationEnabled = false
|
params.isRevocationEnabled = false
|
||||||
val certPath = certFactory.generateCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).asList())
|
val certPath = X509Utilities.buildCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS))
|
||||||
pathValidator.validate(certPath, params)
|
pathValidator.validate(certPath, params)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue {
|
assertTrue {
|
||||||
val (keystore, trustStore) = makeKeyStores(X500Name("CN=Bank A"), nameConstraints)
|
val (keystore, trustStore) = makeKeyStores(X500Name("CN=Bank A"), nameConstraints)
|
||||||
val params = PKIXParameters(trustStore)
|
val params = PKIXParameters(trustStore.internal)
|
||||||
params.isRevocationEnabled = false
|
params.isRevocationEnabled = false
|
||||||
val certPath = certFactory.generateCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).asList())
|
val certPath = X509Utilities.buildCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS))
|
||||||
pathValidator.validate(certPath, params)
|
pathValidator.validate(certPath, params)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@ -95,40 +90,39 @@ class X509NameConstraintsTest {
|
|||||||
.map { GeneralSubtree(GeneralName(X500Name(it))) }.toTypedArray()
|
.map { GeneralSubtree(GeneralName(X500Name(it))) }.toTypedArray()
|
||||||
|
|
||||||
val nameConstraints = NameConstraints(acceptableNames, arrayOf())
|
val nameConstraints = NameConstraints(acceptableNames, arrayOf())
|
||||||
val certFactory = X509CertificateFactory().delegate
|
|
||||||
Crypto.ECDSA_SECP256R1_SHA256
|
Crypto.ECDSA_SECP256R1_SHA256
|
||||||
val pathValidator = CertPathValidator.getInstance("PKIX", BouncyCastleProvider.PROVIDER_NAME)
|
val pathValidator = CertPathValidator.getInstance("PKIX", BouncyCastleProvider.PROVIDER_NAME)
|
||||||
|
|
||||||
assertFailsWith(CertPathValidatorException::class) {
|
assertFailsWith(CertPathValidatorException::class) {
|
||||||
val (keystore, trustStore) = makeKeyStores(X500Name("CN=Bank A"), nameConstraints)
|
val (keystore, trustStore) = makeKeyStores(X500Name("CN=Bank A"), nameConstraints)
|
||||||
val params = PKIXParameters(trustStore)
|
val params = PKIXParameters(trustStore.internal)
|
||||||
params.isRevocationEnabled = false
|
params.isRevocationEnabled = false
|
||||||
val certPath = certFactory.generateCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).asList())
|
val certPath = X509Utilities.buildCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS))
|
||||||
pathValidator.validate(certPath, params)
|
pathValidator.validate(certPath, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
assertFailsWith(CertPathValidatorException::class) {
|
assertFailsWith(CertPathValidatorException::class) {
|
||||||
val (keystore, trustStore) = makeKeyStores(X500Name("CN=Bank A, UID=12345"), nameConstraints)
|
val (keystore, trustStore) = makeKeyStores(X500Name("CN=Bank A, UID=12345"), nameConstraints)
|
||||||
val params = PKIXParameters(trustStore)
|
val params = PKIXParameters(trustStore.internal)
|
||||||
params.isRevocationEnabled = false
|
params.isRevocationEnabled = false
|
||||||
val certPath = certFactory.generateCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).asList())
|
val certPath = X509Utilities.buildCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS))
|
||||||
pathValidator.validate(certPath, params)
|
pathValidator.validate(certPath, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue {
|
assertTrue {
|
||||||
val (keystore, trustStore) = makeKeyStores(X500Name("CN=Bank A TLS, UID=, E=me@email.com, C=GB"), nameConstraints)
|
val (keystore, trustStore) = makeKeyStores(X500Name("CN=Bank A TLS, UID=, E=me@email.com, C=GB"), nameConstraints)
|
||||||
val params = PKIXParameters(trustStore)
|
val params = PKIXParameters(trustStore.internal)
|
||||||
params.isRevocationEnabled = false
|
params.isRevocationEnabled = false
|
||||||
val certPath = certFactory.generateCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).asList())
|
val certPath = X509Utilities.buildCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS))
|
||||||
pathValidator.validate(certPath, params)
|
pathValidator.validate(certPath, params)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue {
|
assertTrue {
|
||||||
val (keystore, trustStore) = makeKeyStores(X500Name("O=Bank A, UID=, E=me@email.com, C=GB"), nameConstraints)
|
val (keystore, trustStore) = makeKeyStores(X500Name("O=Bank A, UID=, E=me@email.com, C=GB"), nameConstraints)
|
||||||
val params = PKIXParameters(trustStore)
|
val params = PKIXParameters(trustStore.internal)
|
||||||
params.isRevocationEnabled = false
|
params.isRevocationEnabled = false
|
||||||
val certPath = certFactory.generateCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).asList())
|
val certPath = X509Utilities.buildCertPath(keystore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS))
|
||||||
pathValidator.validate(certPath, params)
|
pathValidator.validate(certPath, params)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,8 @@ import com.google.common.jimfs.Jimfs
|
|||||||
import net.corda.core.crypto.entropyToKeyPair
|
import net.corda.core.crypto.entropyToKeyPair
|
||||||
import net.corda.core.serialization.deserialize
|
import net.corda.core.serialization.deserialize
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
|
||||||
import net.corda.nodeapi.internal.crypto.X509KeyStore
|
import net.corda.nodeapi.internal.crypto.X509KeyStore
|
||||||
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
import net.corda.testing.core.DEV_ROOT_CA
|
import net.corda.testing.core.DEV_ROOT_CA
|
||||||
import net.corda.testing.core.SerializationEnvironmentRule
|
import net.corda.testing.core.SerializationEnvironmentRule
|
||||||
import net.corda.testing.core.getTestPartyAndCertificate
|
import net.corda.testing.core.getTestPartyAndCertificate
|
||||||
@ -23,7 +23,7 @@ class PartyAndCertificateTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `reject a path with no roles`() {
|
fun `reject a path with no roles`() {
|
||||||
val path = X509CertificateFactory().generateCertPath(DEV_ROOT_CA.certificate)
|
val path = X509Utilities.buildCertPath(DEV_ROOT_CA.certificate)
|
||||||
assertFailsWith<IllegalArgumentException> { PartyAndCertificate(path) }
|
assertFailsWith<IllegalArgumentException> { PartyAndCertificate(path) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ fun X509KeyStore.storeLegalIdentity(alias: String, keyPair: KeyPair = Crypto.gen
|
|||||||
val identityCertPath = listOf(identityCert) + nodeCaCertPath
|
val identityCertPath = listOf(identityCert) + nodeCaCertPath
|
||||||
setPrivateKey(alias, keyPair.private, identityCertPath)
|
setPrivateKey(alias, keyPair.private, identityCertPath)
|
||||||
save()
|
save()
|
||||||
return PartyAndCertificate(X509CertificateFactory().generateCertPath(identityCertPath))
|
return PartyAndCertificate(X509Utilities.buildCertPath(identityCertPath))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createDevNetworkMapCa(rootCa: CertificateAndKeyPair = DEV_ROOT_CA): CertificateAndKeyPair {
|
fun createDevNetworkMapCa(rootCa: CertificateAndKeyPair = DEV_ROOT_CA): CertificateAndKeyPair {
|
||||||
|
@ -13,7 +13,13 @@ import java.security.cert.X509Certificate
|
|||||||
*/
|
*/
|
||||||
class X509KeyStore private constructor(val internal: KeyStore, private val storePassword: String, private val keyStoreFile: Path? = null) {
|
class X509KeyStore private constructor(val internal: KeyStore, private val storePassword: String, private val keyStoreFile: Path? = null) {
|
||||||
/** Wrap an existing [KeyStore]. [save] is not supported. */
|
/** Wrap an existing [KeyStore]. [save] is not supported. */
|
||||||
constructor(internal: KeyStore, storePassword: String) : this(internal, storePassword, null)
|
constructor(keyStore: KeyStore, storePassword: String) : this(keyStore, storePassword, null)
|
||||||
|
|
||||||
|
/** Create an empty [KeyStore] using the given password. [save] is not supported. */
|
||||||
|
constructor(storePassword: String) : this(
|
||||||
|
KeyStore.getInstance(KEYSTORE_TYPE).apply { load(null, storePassword.toCharArray()) },
|
||||||
|
storePassword
|
||||||
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
@ -49,12 +55,10 @@ class X509KeyStore private constructor(val internal: KeyStore, private val store
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setPrivateKey(alias: String, key: PrivateKey, certificates: List<X509Certificate>, keyPassword: String = storePassword) {
|
fun setPrivateKey(alias: String, key: PrivateKey, certificates: List<X509Certificate>, keyPassword: String = storePassword) {
|
||||||
checkWritableToFile()
|
|
||||||
internal.setKeyEntry(alias, key, keyPassword.toCharArray(), certificates.toTypedArray())
|
internal.setKeyEntry(alias, key, keyPassword.toCharArray(), certificates.toTypedArray())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setCertificate(alias: String, certificate: X509Certificate) {
|
fun setCertificate(alias: String, certificate: X509Certificate) {
|
||||||
checkWritableToFile()
|
|
||||||
internal.setCertificateEntry(alias, certificate)
|
internal.setCertificateEntry(alias, certificate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import net.corda.core.crypto.SignatureScheme
|
|||||||
import net.corda.core.crypto.random63BitValue
|
import net.corda.core.crypto.random63BitValue
|
||||||
import net.corda.core.internal.CertRole
|
import net.corda.core.internal.CertRole
|
||||||
import net.corda.core.internal.reader
|
import net.corda.core.internal.reader
|
||||||
|
import net.corda.core.internal.uncheckedCast
|
||||||
import net.corda.core.internal.writer
|
import net.corda.core.internal.writer
|
||||||
import net.corda.core.utilities.days
|
import net.corda.core.utilities.days
|
||||||
import net.corda.core.utilities.millis
|
import net.corda.core.utilities.millis
|
||||||
@ -93,15 +94,19 @@ object X509Utilities {
|
|||||||
return createCertificate(CertificateType.ROOT_CA, subject, keyPair, subject, keyPair.public, window)
|
return createCertificate(CertificateType.ROOT_CA, subject, keyPair, subject, keyPair.public, window)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Provide an overload which takes in a List or a CertPath
|
fun validateCertificateChain(trustedRoot: X509Certificate, vararg certificates: X509Certificate) {
|
||||||
@Throws(CertPathValidatorException::class)
|
validateCertificateChain(trustedRoot, certificates.asList())
|
||||||
fun validateCertificateChain(trustedRoot: X509Certificate, vararg certificates: Certificate) {
|
}
|
||||||
|
|
||||||
|
fun validateCertificateChain(trustedRoot: X509Certificate, certificates: List<X509Certificate>) {
|
||||||
require(certificates.isNotEmpty()) { "Certificate path must contain at least one certificate" }
|
require(certificates.isNotEmpty()) { "Certificate path must contain at least one certificate" }
|
||||||
|
validateCertPath(trustedRoot, buildCertPath(certificates))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun validateCertPath(trustedRoot: X509Certificate, certPath: CertPath) {
|
||||||
val params = PKIXParameters(setOf(TrustAnchor(trustedRoot, null)))
|
val params = PKIXParameters(setOf(TrustAnchor(trustedRoot, null)))
|
||||||
params.isRevocationEnabled = false
|
params.isRevocationEnabled = false
|
||||||
val certPath = X509CertificateFactory().generateCertPath(*certificates)
|
CertPathValidator.getInstance("PKIX").validate(certPath, params)
|
||||||
val pathValidator = CertPathValidator.getInstance("PKIX")
|
|
||||||
pathValidator.validate(certPath, params)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -265,6 +270,21 @@ object X509Utilities {
|
|||||||
fun createCertificateSigningRequest(subject: X500Principal, email: String, keyPair: KeyPair): PKCS10CertificationRequest {
|
fun createCertificateSigningRequest(subject: X500Principal, email: String, keyPair: KeyPair): PKCS10CertificationRequest {
|
||||||
return createCertificateSigningRequest(subject, email, keyPair, DEFAULT_TLS_SIGNATURE_SCHEME)
|
return createCertificateSigningRequest(subject, email, keyPair, DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun buildCertPath(first: X509Certificate, remaining: List<X509Certificate>): CertPath {
|
||||||
|
val certificates = ArrayList<X509Certificate>(1 + remaining.size)
|
||||||
|
certificates += first
|
||||||
|
certificates += remaining
|
||||||
|
return buildCertPath(certificates)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun buildCertPath(vararg certificates: X509Certificate): CertPath {
|
||||||
|
return X509CertificateFactory().generateCertPath(*certificates)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun buildCertPath(certificates: List<X509Certificate>): CertPath {
|
||||||
|
return X509CertificateFactory().generateCertPath(certificates)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -275,6 +295,16 @@ object X509Utilities {
|
|||||||
fun X509Certificate.toBc() = X509CertificateHolder(encoded)
|
fun X509Certificate.toBc() = X509CertificateHolder(encoded)
|
||||||
fun X509CertificateHolder.toJca(): X509Certificate = X509CertificateFactory().generateCertificate(encoded.inputStream())
|
fun X509CertificateHolder.toJca(): X509Certificate = X509CertificateFactory().generateCertificate(encoded.inputStream())
|
||||||
|
|
||||||
|
val CertPath.x509Certificates: List<X509Certificate> get() {
|
||||||
|
require(type == "X.509") { "Not an X.509 cert path: $this" }
|
||||||
|
// We're not mapping the list to avoid creating a new one.
|
||||||
|
return uncheckedCast(certificates)
|
||||||
|
}
|
||||||
|
|
||||||
|
val Certificate.x509: X509Certificate get() = requireNotNull(this as? X509Certificate) { "Not an X.509 certificate: $this" }
|
||||||
|
|
||||||
|
val Array<Certificate>.x509: List<X509Certificate> get() = map { it.x509 }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps a [CertificateFactory] to remove boilerplate. It's unclear whether [CertificateFactory] is threadsafe so best
|
* Wraps a [CertificateFactory] to remove boilerplate. It's unclear whether [CertificateFactory] is threadsafe so best
|
||||||
* so assume this class is not.
|
* so assume this class is not.
|
||||||
@ -282,19 +312,11 @@ fun X509CertificateHolder.toJca(): X509Certificate = X509CertificateFactory().ge
|
|||||||
class X509CertificateFactory {
|
class X509CertificateFactory {
|
||||||
val delegate: CertificateFactory = CertificateFactory.getInstance("X.509")
|
val delegate: CertificateFactory = CertificateFactory.getInstance("X.509")
|
||||||
|
|
||||||
fun generateCertificate(input: InputStream): X509Certificate {
|
fun generateCertificate(input: InputStream): X509Certificate = delegate.generateCertificate(input).x509
|
||||||
return delegate.generateCertificate(input) as X509Certificate
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO X509Certificate
|
fun generateCertPath(vararg certificates: X509Certificate): CertPath = generateCertPath(certificates.asList())
|
||||||
fun generateCertPath(certificates: List<Certificate>): CertPath {
|
|
||||||
return delegate.generateCertPath(certificates)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO X509Certificate
|
fun generateCertPath(certificates: List<X509Certificate>): CertPath = delegate.generateCertPath(certificates)
|
||||||
fun generateCertPath(vararg certificates: Certificate): CertPath {
|
|
||||||
return delegate.generateCertPath(certificates.asList())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class CertificateType(val keyUsage: KeyUsage, vararg val purposes: KeyPurposeId, val isCA: Boolean, val role: CertRole?) {
|
enum class CertificateType(val keyUsage: KeyUsage, vararg val purposes: KeyPurposeId, val isCA: Boolean, val role: CertRole?) {
|
||||||
|
@ -266,10 +266,10 @@ class X509UtilitiesTest {
|
|||||||
assertTrue(clientSocket.isConnected)
|
assertTrue(clientSocket.isConnected)
|
||||||
|
|
||||||
// Double check hostname manually
|
// Double check hostname manually
|
||||||
val peerChain = clientSocket.session.peerCertificates
|
val peerChain = clientSocket.session.peerCertificates.x509
|
||||||
val peerX500Principal = (peerChain[0] as X509Certificate).subjectX500Principal
|
val peerX500Principal = peerChain[0].subjectX500Principal
|
||||||
assertEquals(MEGA_CORP.name.x500Principal, peerX500Principal)
|
assertEquals(MEGA_CORP.name.x500Principal, peerX500Principal)
|
||||||
X509Utilities.validateCertificateChain(rootCa.certificate, *peerChain)
|
X509Utilities.validateCertificateChain(rootCa.certificate, peerChain)
|
||||||
val output = DataOutputStream(clientSocket.outputStream)
|
val output = DataOutputStream(clientSocket.outputStream)
|
||||||
output.writeUTF("Hello World")
|
output.writeUTF("Hello World")
|
||||||
var timeout = 0
|
var timeout = 0
|
||||||
@ -338,7 +338,7 @@ class X509UtilitiesTest {
|
|||||||
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val rootCACert = X509Utilities.createSelfSignedCACertificate(ALICE_NAME.x500Principal, rootCAKey)
|
val rootCACert = X509Utilities.createSelfSignedCACertificate(ALICE_NAME.x500Principal, rootCAKey)
|
||||||
val certificate = X509Utilities.createCertificate(CertificateType.TLS, rootCACert, rootCAKey, BOB_NAME.x500Principal, BOB.publicKey)
|
val certificate = X509Utilities.createCertificate(CertificateType.TLS, rootCACert, rootCAKey, BOB_NAME.x500Principal, BOB.publicKey)
|
||||||
val expected = X509CertificateFactory().generateCertPath(certificate, rootCACert)
|
val expected = X509Utilities.buildCertPath(certificate, rootCACert)
|
||||||
val serialized = expected.serialize(factory, context).bytes
|
val serialized = expected.serialize(factory, context).bytes
|
||||||
val actual: CertPath = serialized.deserialize(factory, context)
|
val actual: CertPath = serialized.deserialize(factory, context)
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
|
@ -12,25 +12,27 @@ import net.corda.finance.DOLLARS
|
|||||||
import net.corda.finance.flows.CashIssueAndPaymentFlow
|
import net.corda.finance.flows.CashIssueAndPaymentFlow
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
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_CLIENT_CA
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_INTERMEDIATE_CA
|
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_INTERMEDIATE_CA
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA
|
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA
|
||||||
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
import net.corda.testing.core.DEV_ROOT_CA
|
import net.corda.testing.core.DEV_ROOT_CA
|
||||||
import net.corda.testing.core.SerializationEnvironmentRule
|
import net.corda.testing.core.SerializationEnvironmentRule
|
||||||
import net.corda.testing.common.internal.testNetworkParameters
|
import net.corda.testing.core.singleIdentity
|
||||||
import net.corda.testing.driver.PortAllocation
|
import net.corda.testing.driver.PortAllocation
|
||||||
import net.corda.testing.node.NotarySpec
|
import net.corda.testing.node.NotarySpec
|
||||||
import net.corda.testing.node.internal.CompatibilityZoneParams
|
import net.corda.testing.node.internal.CompatibilityZoneParams
|
||||||
import net.corda.testing.node.internal.internalDriver
|
import net.corda.testing.node.internal.internalDriver
|
||||||
import net.corda.testing.node.internal.network.NetworkMapServer
|
import net.corda.testing.node.internal.network.NetworkMapServer
|
||||||
import net.corda.testing.core.singleIdentity
|
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
||||||
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
|
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
|
||||||
import org.junit.*
|
import org.junit.After
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
@ -151,7 +153,7 @@ class RegistrationHandler(private val rootCertAndKeyPair: CertificateAndKeyPair)
|
|||||||
val (certPath, name) = createSignedClientCertificate(
|
val (certPath, name) = createSignedClientCertificate(
|
||||||
certificationRequest,
|
certificationRequest,
|
||||||
rootCertAndKeyPair.keyPair,
|
rootCertAndKeyPair.keyPair,
|
||||||
arrayOf(rootCertAndKeyPair.certificate))
|
listOf(rootCertAndKeyPair.certificate))
|
||||||
require(!name.organisation.contains("\\s".toRegex())) { "Whitespace in the organisation name not supported" }
|
require(!name.organisation.contains("\\s".toRegex())) { "Whitespace in the organisation name not supported" }
|
||||||
certPaths[name.organisation] = certPath
|
certPaths[name.organisation] = certPath
|
||||||
return Response.ok(name.organisation).build()
|
return Response.ok(name.organisation).build()
|
||||||
@ -180,17 +182,17 @@ class RegistrationHandler(private val rootCertAndKeyPair: CertificateAndKeyPair)
|
|||||||
|
|
||||||
private fun createSignedClientCertificate(certificationRequest: PKCS10CertificationRequest,
|
private fun createSignedClientCertificate(certificationRequest: PKCS10CertificationRequest,
|
||||||
caKeyPair: KeyPair,
|
caKeyPair: KeyPair,
|
||||||
caCertPath: Array<Certificate>): Pair<CertPath, CordaX500Name> {
|
caCertPath: List<X509Certificate>): Pair<CertPath, CordaX500Name> {
|
||||||
val request = JcaPKCS10CertificationRequest(certificationRequest)
|
val request = JcaPKCS10CertificationRequest(certificationRequest)
|
||||||
val name = CordaX500Name.parse(request.subject.toString())
|
val name = CordaX500Name.parse(request.subject.toString())
|
||||||
val nodeCaCert = X509Utilities.createCertificate(
|
val nodeCaCert = X509Utilities.createCertificate(
|
||||||
CertificateType.NODE_CA,
|
CertificateType.NODE_CA,
|
||||||
caCertPath[0] as X509Certificate ,
|
caCertPath[0],
|
||||||
caKeyPair,
|
caKeyPair,
|
||||||
name.x500Principal,
|
name.x500Principal,
|
||||||
request.publicKey,
|
request.publicKey,
|
||||||
nameConstraints = null)
|
nameConstraints = null)
|
||||||
val certPath = X509CertificateFactory().generateCertPath(nodeCaCert, *caCertPath)
|
val certPath = X509Utilities.buildCertPath(nodeCaCert, caCertPath)
|
||||||
return Pair(certPath, name)
|
return Pair(certPath, name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,6 @@ import net.corda.node.shell.InteractiveShell
|
|||||||
import net.corda.node.utilities.AffinityExecutor
|
import net.corda.node.utilities.AffinityExecutor
|
||||||
import net.corda.nodeapi.internal.DevIdentityGenerator
|
import net.corda.nodeapi.internal.DevIdentityGenerator
|
||||||
import net.corda.nodeapi.internal.SignedNodeInfo
|
import net.corda.nodeapi.internal.SignedNodeInfo
|
||||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
import net.corda.nodeapi.internal.network.NETWORK_PARAMS_FILE_NAME
|
import net.corda.nodeapi.internal.network.NETWORK_PARAMS_FILE_NAME
|
||||||
import net.corda.nodeapi.internal.network.NetworkParameters
|
import net.corda.nodeapi.internal.network.NetworkParameters
|
||||||
@ -755,7 +754,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
|||||||
throw ConfigurationException("The name '$singleName' for $id doesn't match what's in the key store: $subject")
|
throw ConfigurationException("The name '$singleName' for $id doesn't match what's in the key store: $subject")
|
||||||
}
|
}
|
||||||
|
|
||||||
val certPath = X509CertificateFactory().generateCertPath(certificates)
|
val certPath = X509Utilities.buildCertPath(certificates)
|
||||||
return Pair(PartyAndCertificate(certPath), keyPair)
|
return Pair(PartyAndCertificate(certPath), keyPair)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ import net.corda.node.internal.protonwrapper.engine.EventProcessor
|
|||||||
import net.corda.node.internal.protonwrapper.messages.ReceivedMessage
|
import net.corda.node.internal.protonwrapper.messages.ReceivedMessage
|
||||||
import net.corda.node.internal.protonwrapper.messages.impl.ReceivedMessageImpl
|
import net.corda.node.internal.protonwrapper.messages.impl.ReceivedMessageImpl
|
||||||
import net.corda.node.internal.protonwrapper.messages.impl.SendableMessageImpl
|
import net.corda.node.internal.protonwrapper.messages.impl.SendableMessageImpl
|
||||||
|
import net.corda.nodeapi.internal.crypto.x509
|
||||||
import org.apache.qpid.proton.engine.ProtonJTransport
|
import org.apache.qpid.proton.engine.ProtonJTransport
|
||||||
import org.apache.qpid.proton.engine.Transport
|
import org.apache.qpid.proton.engine.Transport
|
||||||
import org.apache.qpid.proton.engine.impl.ProtocolTracer
|
import org.apache.qpid.proton.engine.impl.ProtocolTracer
|
||||||
@ -80,8 +81,8 @@ internal class AMQPChannelHandler(private val serverMode: Boolean,
|
|||||||
if (evt is SslHandshakeCompletionEvent) {
|
if (evt is SslHandshakeCompletionEvent) {
|
||||||
if (evt.isSuccess) {
|
if (evt.isSuccess) {
|
||||||
val sslHandler = ctx.pipeline().get(SslHandler::class.java)
|
val sslHandler = ctx.pipeline().get(SslHandler::class.java)
|
||||||
localCert = sslHandler.engine().session.localCertificates[0] as X509Certificate
|
localCert = sslHandler.engine().session.localCertificates[0].x509
|
||||||
remoteCert = sslHandler.engine().session.peerCertificates[0] as X509Certificate
|
remoteCert = sslHandler.engine().session.peerCertificates[0].x509
|
||||||
try {
|
try {
|
||||||
val remoteX500Name = CordaX500Name.build(remoteCert.subjectX500Principal)
|
val remoteX500Name = CordaX500Name.build(remoteCert.subjectX500Principal)
|
||||||
require(allowedRemoteLegalNames == null || remoteX500Name in allowedRemoteLegalNames)
|
require(allowedRemoteLegalNames == null || remoteX500Name in allowedRemoteLegalNames)
|
||||||
|
@ -9,7 +9,8 @@ import net.corda.core.serialization.SingletonSerializeAsToken
|
|||||||
import net.corda.core.utilities.contextLogger
|
import net.corda.core.utilities.contextLogger
|
||||||
import net.corda.core.utilities.trace
|
import net.corda.core.utilities.trace
|
||||||
import net.corda.node.services.api.IdentityServiceInternal
|
import net.corda.node.services.api.IdentityServiceInternal
|
||||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
|
import net.corda.nodeapi.internal.crypto.x509Certificates
|
||||||
import java.security.InvalidAlgorithmParameterException
|
import java.security.InvalidAlgorithmParameterException
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.security.cert.*
|
import java.security.cert.*
|
||||||
@ -45,24 +46,24 @@ class InMemoryIdentityService(identities: Array<out PartyAndCertificate>,
|
|||||||
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
|
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
|
||||||
override fun verifyAndRegisterIdentity(identity: PartyAndCertificate): PartyAndCertificate? {
|
override fun verifyAndRegisterIdentity(identity: PartyAndCertificate): PartyAndCertificate? {
|
||||||
// Validate the chain first, before we do anything clever with it
|
// Validate the chain first, before we do anything clever with it
|
||||||
|
val identityCertChain = identity.certPath.x509Certificates
|
||||||
try {
|
try {
|
||||||
identity.verify(trustAnchor)
|
identity.verify(trustAnchor)
|
||||||
} catch (e: CertPathValidatorException) {
|
} catch (e: CertPathValidatorException) {
|
||||||
log.warn("Certificate validation failed for ${identity.name} against trusted root ${trustAnchor.trustedCert.subjectX500Principal}.")
|
log.warn("Certificate validation failed for ${identity.name} against trusted root ${trustAnchor.trustedCert.subjectX500Principal}.")
|
||||||
log.warn("Certificate path :")
|
log.warn("Certificate path :")
|
||||||
identity.certPath.certificates.reversed().forEachIndexed { index, certificate ->
|
identityCertChain.reversed().forEachIndexed { index, certificate ->
|
||||||
val space = (0 until index).joinToString("") { " " }
|
val space = (0 until index).joinToString("") { " " }
|
||||||
log.warn("$space${(certificate as X509Certificate).subjectX500Principal}")
|
log.warn("$space${certificate.subjectX500Principal}")
|
||||||
}
|
}
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure we record the first identity of the same name, first
|
// Ensure we record the first identity of the same name, first
|
||||||
val wellKnownCert: Certificate = identity.certPath.certificates.single { CertRole.extract(it)?.isWellKnown ?: false }
|
val wellKnownCert = identityCertChain.single { CertRole.extract(it)?.isWellKnown ?: false }
|
||||||
if (wellKnownCert != identity.certificate) {
|
if (wellKnownCert != identity.certificate) {
|
||||||
val certificates = identity.certPath.certificates
|
val idx = identityCertChain.lastIndexOf(wellKnownCert)
|
||||||
val idx = certificates.lastIndexOf(wellKnownCert)
|
val firstPath = X509Utilities.buildCertPath(identityCertChain.slice(idx until identityCertChain.size))
|
||||||
val firstPath = X509CertificateFactory().generateCertPath(certificates.slice(idx until certificates.size))
|
|
||||||
verifyAndRegisterIdentity(PartyAndCertificate(firstPath))
|
verifyAndRegisterIdentity(PartyAndCertificate(firstPath))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +71,7 @@ class InMemoryIdentityService(identities: Array<out PartyAndCertificate>,
|
|||||||
keyToParties[identity.owningKey] = identity
|
keyToParties[identity.owningKey] = identity
|
||||||
// Always keep the first party we registered, as that's the well known identity
|
// Always keep the first party we registered, as that's the well known identity
|
||||||
principalToParties.computeIfAbsent(identity.name) { identity }
|
principalToParties.computeIfAbsent(identity.name) { identity }
|
||||||
return keyToParties[identity.certPath.certificates[1].publicKey]
|
return keyToParties[identityCertChain[1].publicKey]
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun certificateFromKey(owningKey: PublicKey): PartyAndCertificate? = keyToParties[owningKey]
|
override fun certificateFromKey(owningKey: PublicKey): PartyAndCertificate? = keyToParties[owningKey]
|
||||||
@ -103,7 +104,7 @@ class InMemoryIdentityService(identities: Array<out PartyAndCertificate>,
|
|||||||
val results = LinkedHashSet<Party>()
|
val results = LinkedHashSet<Party>()
|
||||||
for ((x500name, partyAndCertificate) in principalToParties) {
|
for ((x500name, partyAndCertificate) in principalToParties) {
|
||||||
val party = partyAndCertificate.party
|
val party = partyAndCertificate.party
|
||||||
val components = listOf(x500name.commonName, x500name.organisationUnit, x500name.organisation, x500name.locality, x500name.state, x500name.country).filterNotNull()
|
val components = listOfNotNull(x500name.commonName, x500name.organisationUnit, x500name.organisation, x500name.locality, x500name.state, x500name.country)
|
||||||
components.forEach { component ->
|
components.forEach { component ->
|
||||||
if (exactMatch && component == query) {
|
if (exactMatch && component == query) {
|
||||||
results += party
|
results += party
|
||||||
|
@ -13,6 +13,8 @@ import net.corda.core.utilities.debug
|
|||||||
import net.corda.node.services.api.IdentityServiceInternal
|
import net.corda.node.services.api.IdentityServiceInternal
|
||||||
import net.corda.node.utilities.AppendOnlyPersistentMap
|
import net.corda.node.utilities.AppendOnlyPersistentMap
|
||||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||||
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
|
import net.corda.nodeapi.internal.crypto.x509Certificates
|
||||||
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
|
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
|
||||||
import java.security.InvalidAlgorithmParameterException
|
import java.security.InvalidAlgorithmParameterException
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
@ -111,23 +113,23 @@ class PersistentIdentityService(override val trustRoot: X509Certificate,
|
|||||||
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
|
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
|
||||||
override fun verifyAndRegisterIdentity(identity: PartyAndCertificate): PartyAndCertificate? {
|
override fun verifyAndRegisterIdentity(identity: PartyAndCertificate): PartyAndCertificate? {
|
||||||
// Validate the chain first, before we do anything clever with it
|
// Validate the chain first, before we do anything clever with it
|
||||||
|
val identityCertChain = identity.certPath.x509Certificates
|
||||||
try {
|
try {
|
||||||
identity.verify(trustAnchor)
|
identity.verify(trustAnchor)
|
||||||
} catch (e: CertPathValidatorException) {
|
} catch (e: CertPathValidatorException) {
|
||||||
log.warn(e.localizedMessage)
|
log.warn(e.localizedMessage)
|
||||||
log.warn("Path = ")
|
log.warn("Path = ")
|
||||||
identity.certPath.certificates.reversed().forEach {
|
identityCertChain.reversed().forEach {
|
||||||
log.warn((it as X509Certificate).subjectX500Principal.toString())
|
log.warn(it.subjectX500Principal.toString())
|
||||||
}
|
}
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure we record the first identity of the same name, first
|
// Ensure we record the first identity of the same name, first
|
||||||
val wellKnownCert: Certificate = identity.certPath.certificates.single { CertRole.extract(it)?.isWellKnown ?: false }
|
val wellKnownCert = identityCertChain.single { CertRole.extract(it)?.isWellKnown ?: false }
|
||||||
if (wellKnownCert != identity.certificate) {
|
if (wellKnownCert != identity.certificate) {
|
||||||
val certificates = identity.certPath.certificates
|
val idx = identityCertChain.lastIndexOf(wellKnownCert)
|
||||||
val idx = certificates.lastIndexOf(wellKnownCert)
|
val firstPath = X509Utilities.buildCertPath(identityCertChain.slice(idx until identityCertChain.size))
|
||||||
val firstPath = X509CertificateFactory().generateCertPath(certificates.slice(idx until certificates.size))
|
|
||||||
verifyAndRegisterIdentity(PartyAndCertificate(firstPath))
|
verifyAndRegisterIdentity(PartyAndCertificate(firstPath))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,7 +138,7 @@ class PersistentIdentityService(override val trustRoot: X509Certificate,
|
|||||||
keyToParties.addWithDuplicatesAllowed(key, identity)
|
keyToParties.addWithDuplicatesAllowed(key, identity)
|
||||||
// Always keep the first party we registered, as that's the well known identity
|
// Always keep the first party we registered, as that's the well known identity
|
||||||
principalToParties.addWithDuplicatesAllowed(identity.name, key, false)
|
principalToParties.addWithDuplicatesAllowed(identity.name, key, false)
|
||||||
val parentId = mapToKey(identity.certPath.certificates[1].publicKey)
|
val parentId = mapToKey(identityCertChain[1].publicKey)
|
||||||
return keyToParties[parentId]
|
return keyToParties[parentId]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,7 +177,7 @@ class PersistentIdentityService(override val trustRoot: X509Certificate,
|
|||||||
val results = LinkedHashSet<Party>()
|
val results = LinkedHashSet<Party>()
|
||||||
for ((x500name, partyId) in principalToParties.allPersisted()) {
|
for ((x500name, partyId) in principalToParties.allPersisted()) {
|
||||||
val party = keyToParties[partyId]!!.party
|
val party = keyToParties[partyId]!!.party
|
||||||
val components = listOf(x500name.commonName, x500name.organisationUnit, x500name.organisation, x500name.locality, x500name.state, x500name.country).filterNotNull()
|
val components = listOfNotNull(x500name.commonName, x500name.organisationUnit, x500name.organisation, x500name.locality, x500name.state, x500name.country)
|
||||||
components.forEach { component ->
|
components.forEach { component ->
|
||||||
if (exactMatch && component == query) {
|
if (exactMatch && component == query) {
|
||||||
results += party
|
results += party
|
||||||
|
@ -7,8 +7,8 @@ import net.corda.core.utilities.days
|
|||||||
import net.corda.node.services.api.IdentityServiceInternal
|
import net.corda.node.services.api.IdentityServiceInternal
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
import net.corda.nodeapi.internal.crypto.ContentSignerBuilder
|
import net.corda.nodeapi.internal.crypto.ContentSignerBuilder
|
||||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
|
import net.corda.nodeapi.internal.crypto.x509Certificates
|
||||||
import org.bouncycastle.operator.ContentSigner
|
import org.bouncycastle.operator.ContentSigner
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
@ -43,7 +43,7 @@ fun freshCertificate(identityService: IdentityServiceInternal,
|
|||||||
issuer.name.x500Principal,
|
issuer.name.x500Principal,
|
||||||
subjectPublicKey,
|
subjectPublicKey,
|
||||||
window)
|
window)
|
||||||
val ourCertPath = X509CertificateFactory().generateCertPath(listOf(ourCertificate) + issuer.certPath.certificates)
|
val ourCertPath = X509Utilities.buildCertPath(ourCertificate, issuer.certPath.x509Certificates)
|
||||||
val anonymisedIdentity = PartyAndCertificate(ourCertPath)
|
val anonymisedIdentity = PartyAndCertificate(ourCertPath)
|
||||||
identityService.justVerifyAndRegisterIdentity(anonymisedIdentity)
|
identityService.justVerifyAndRegisterIdentity(anonymisedIdentity)
|
||||||
return anonymisedIdentity
|
return anonymisedIdentity
|
||||||
|
@ -13,6 +13,7 @@ import net.corda.nodeapi.internal.ArtemisMessagingComponent
|
|||||||
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEER_USER
|
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEER_USER
|
||||||
import net.corda.nodeapi.internal.ArtemisMessagingComponent.RemoteInboxAddress.Companion.translateLocalQueueToInboxAddress
|
import net.corda.nodeapi.internal.ArtemisMessagingComponent.RemoteInboxAddress.Companion.translateLocalQueueToInboxAddress
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
|
import net.corda.nodeapi.internal.crypto.x509
|
||||||
import org.apache.activemq.artemis.api.core.Message
|
import org.apache.activemq.artemis.api.core.Message
|
||||||
import org.apache.activemq.artemis.core.config.BridgeConfiguration
|
import org.apache.activemq.artemis.core.config.BridgeConfiguration
|
||||||
import org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnection
|
import org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnection
|
||||||
@ -23,7 +24,6 @@ import org.apache.activemq.artemis.core.server.ActiveMQServer
|
|||||||
import org.apache.activemq.artemis.core.server.cluster.Transformer
|
import org.apache.activemq.artemis.core.server.cluster.Transformer
|
||||||
import org.apache.activemq.artemis.spi.core.remoting.*
|
import org.apache.activemq.artemis.spi.core.remoting.*
|
||||||
import org.apache.activemq.artemis.utils.ConfigurationHelper
|
import org.apache.activemq.artemis.utils.ConfigurationHelper
|
||||||
import java.security.cert.X509Certificate
|
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.util.concurrent.Executor
|
import java.util.concurrent.Executor
|
||||||
import java.util.concurrent.ScheduledExecutorService
|
import java.util.concurrent.ScheduledExecutorService
|
||||||
@ -162,7 +162,9 @@ class VerifyingNettyConnectorFactory : NettyConnectorFactory() {
|
|||||||
"Peer has wrong subject name in the certificate - expected $expectedLegalNames but got $peerCertificateName. This is either a fatal " +
|
"Peer has wrong subject name in the certificate - expected $expectedLegalNames but got $peerCertificateName. This is either a fatal " +
|
||||||
"misconfiguration by the remote peer or an SSL man-in-the-middle attack!"
|
"misconfiguration by the remote peer or an SSL man-in-the-middle attack!"
|
||||||
}
|
}
|
||||||
X509Utilities.validateCertificateChain(session.localCertificates.last() as X509Certificate, *session.peerCertificates)
|
X509Utilities.validateCertificateChain(
|
||||||
|
session.localCertificates.last().x509,
|
||||||
|
session.peerCertificates.x509)
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
connection.close()
|
connection.close()
|
||||||
log.error(e.message)
|
log.error(e.message)
|
||||||
|
@ -10,6 +10,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_CLIENT_CA
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_TLS
|
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_TLS
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA
|
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA
|
||||||
|
import net.corda.nodeapi.internal.crypto.x509
|
||||||
import org.bouncycastle.openssl.jcajce.JcaPEMWriter
|
import org.bouncycastle.openssl.jcajce.JcaPEMWriter
|
||||||
import org.bouncycastle.util.io.pem.PemObject
|
import org.bouncycastle.util.io.pem.PemObject
|
||||||
import java.io.StringWriter
|
import java.io.StringWriter
|
||||||
@ -43,7 +44,7 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v
|
|||||||
"This file must contain the root CA cert of your compatibility zone. " +
|
"This file must contain the root CA cert of your compatibility zone. " +
|
||||||
"Please contact your CZ operator."
|
"Please contact your CZ operator."
|
||||||
}
|
}
|
||||||
this.rootCert = rootCert as X509Certificate
|
this.rootCert = rootCert.x509
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -109,7 +110,7 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v
|
|||||||
}
|
}
|
||||||
|
|
||||||
println("Checking root of the certificate path is what we expect.")
|
println("Checking root of the certificate path is what we expect.")
|
||||||
X509Utilities.validateCertificateChain(rootCert, *certificates.toTypedArray())
|
X509Utilities.validateCertificateChain(rootCert, certificates)
|
||||||
|
|
||||||
println("Certificate signing request approved, storing private key with the certificate chain.")
|
println("Certificate signing request approved, storing private key with the certificate chain.")
|
||||||
// Save private key and certificate chain to the key store.
|
// Save private key and certificate chain to the key store.
|
||||||
|
@ -8,8 +8,8 @@ import net.corda.core.identity.Party
|
|||||||
import net.corda.core.identity.PartyAndCertificate
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
import net.corda.core.node.services.UnknownAnonymousPartyException
|
import net.corda.core.node.services.UnknownAnonymousPartyException
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
|
import net.corda.nodeapi.internal.crypto.x509Certificates
|
||||||
import net.corda.testing.core.*
|
import net.corda.testing.core.*
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@ -173,7 +173,7 @@ class InMemoryIdentityServiceTests {
|
|||||||
issuerKeyPair,
|
issuerKeyPair,
|
||||||
x500Name.x500Principal,
|
x500Name.x500Principal,
|
||||||
txKeyPair.public)
|
txKeyPair.public)
|
||||||
val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert) + issuer.certPath.certificates)
|
val txCertPath = X509Utilities.buildCertPath(txCert, issuer.certPath.x509Certificates)
|
||||||
return Pair(issuer, PartyAndCertificate(txCertPath))
|
return Pair(issuer, PartyAndCertificate(txCertPath))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,8 +10,8 @@ import net.corda.core.node.services.IdentityService
|
|||||||
import net.corda.core.node.services.UnknownAnonymousPartyException
|
import net.corda.core.node.services.UnknownAnonymousPartyException
|
||||||
import net.corda.node.internal.configureDatabase
|
import net.corda.node.internal.configureDatabase
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
|
import net.corda.nodeapi.internal.crypto.x509Certificates
|
||||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||||
import net.corda.testing.core.*
|
import net.corda.testing.core.*
|
||||||
@ -264,8 +264,13 @@ class PersistentIdentityServiceTests {
|
|||||||
val issuerKeyPair = generateKeyPair()
|
val issuerKeyPair = generateKeyPair()
|
||||||
val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public)
|
val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public)
|
||||||
val txKey = Crypto.generateKeyPair()
|
val txKey = Crypto.generateKeyPair()
|
||||||
val txCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuer.certificate, issuerKeyPair, x500Name.x500Principal, txKey.public)
|
val txCert = X509Utilities.createCertificate(
|
||||||
val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert) + issuer.certPath.certificates)
|
CertificateType.CONFIDENTIAL_LEGAL_IDENTITY,
|
||||||
|
issuer.certificate,
|
||||||
|
issuerKeyPair,
|
||||||
|
x500Name.x500Principal,
|
||||||
|
txKey.public)
|
||||||
|
val txCertPath = X509Utilities.buildCertPath(txCert, issuer.certPath.x509Certificates)
|
||||||
return Pair(issuer, PartyAndCertificate(txCertPath))
|
return Pair(issuer, PartyAndCertificate(txCertPath))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ fun getTestPartyAndCertificate(party: Party): PartyAndCertificate {
|
|||||||
party.name.x500Principal,
|
party.name.x500Principal,
|
||||||
party.owningKey)
|
party.owningKey)
|
||||||
|
|
||||||
val certPath = X509CertificateFactory().generateCertPath(identityCert, nodeCaCert, intermediate.certificate, trustRoot)
|
val certPath = X509Utilities.buildCertPath(identityCert, nodeCaCert, intermediate.certificate, trustRoot)
|
||||||
return PartyAndCertificate(certPath)
|
return PartyAndCertificate(certPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,6 @@ import net.corda.nodeapi.internal.SignedNodeInfo
|
|||||||
import net.corda.nodeapi.internal.createDevNodeCa
|
import net.corda.nodeapi.internal.createDevNodeCa
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
import net.corda.testing.core.DEV_INTERMEDIATE_CA
|
import net.corda.testing.core.DEV_INTERMEDIATE_CA
|
||||||
import net.corda.testing.core.DEV_ROOT_CA
|
import net.corda.testing.core.DEV_ROOT_CA
|
||||||
@ -31,8 +30,8 @@ class TestNodeInfoBuilder(private val intermediateAndRoot: Pair<CertificateAndKe
|
|||||||
nodeCertificateAndKeyPair.keyPair,
|
nodeCertificateAndKeyPair.keyPair,
|
||||||
nodeCertificateAndKeyPair.certificate.subjectX500Principal,
|
nodeCertificateAndKeyPair.certificate.subjectX500Principal,
|
||||||
identityKeyPair.public)
|
identityKeyPair.public)
|
||||||
val certPath = X509CertificateFactory()
|
val certPath = X509Utilities.buildCertPath(
|
||||||
.generateCertPath(identityCert,
|
identityCert,
|
||||||
nodeCertificateAndKeyPair.certificate,
|
nodeCertificateAndKeyPair.certificate,
|
||||||
intermediateAndRoot.first.certificate,
|
intermediateAndRoot.first.certificate,
|
||||||
intermediateAndRoot.second)
|
intermediateAndRoot.second)
|
||||||
|
Reference in New Issue
Block a user