mirror of
https://github.com/corda/corda.git
synced 2025-06-12 20:28:18 +00:00
CORDA-831: Add roles to X509 certificates (#2180)
* Add roles to X509 certificates so that the identity service can always determine which certificate in a hierarchy is the well known identity * Rename CLIENT_CA certificate type to NODE_CA * Rename DOORMAN role to INTERMEDIATE_CA * Correct issue in CashTests where instead of providing a well known identity to generateSpend(), a confidential identity was passed in and a confidential identity generated from it. * Enforce role hierarchy in PKI * Enforce that party certificates must be well known or confidential identities * Add network map certificate role
This commit is contained in:
@ -101,7 +101,7 @@ fun createKeystoreForCordaNode(sslKeyStorePath: Path,
|
||||
val clientName = legalName.copy(commonName = null)
|
||||
|
||||
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, clientName.x500Name))), arrayOf())
|
||||
val clientCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA,
|
||||
val clientCACert = X509Utilities.createCertificate(CertificateType.NODE_CA,
|
||||
intermediateCACert,
|
||||
intermediateCAKeyPair,
|
||||
clientName.copy(commonName = X509Utilities.CORDA_CLIENT_CA_CN),
|
||||
|
@ -3,6 +3,7 @@ package net.corda.node.services.identity
|
||||
import net.corda.core.contracts.PartyAndReference
|
||||
import net.corda.core.crypto.toStringShort
|
||||
import net.corda.core.identity.*
|
||||
import net.corda.core.internal.CertRole
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
import net.corda.core.node.services.UnknownAnonymousPartyException
|
||||
@ -61,14 +62,10 @@ class InMemoryIdentityService(identities: Array<out PartyAndCertificate>,
|
||||
}
|
||||
|
||||
// Ensure we record the first identity of the same name, first
|
||||
val identityPrincipal = identity.name.x500Principal
|
||||
val firstCertWithThisName: Certificate = identity.certPath.certificates.last { it ->
|
||||
val principal = (it as? X509Certificate)?.subjectX500Principal
|
||||
principal == identityPrincipal
|
||||
}
|
||||
if (firstCertWithThisName != identity.certificate) {
|
||||
val wellKnownCert: Certificate = identity.certPath.certificates.single { CertRole.extract(it)?.isWellKnown ?: false }
|
||||
if (wellKnownCert != identity.certificate) {
|
||||
val certificates = identity.certPath.certificates
|
||||
val idx = certificates.lastIndexOf(firstCertWithThisName)
|
||||
val idx = certificates.lastIndexOf(wellKnownCert)
|
||||
val firstPath = X509CertificateFactory().generateCertPath(certificates.slice(idx until certificates.size))
|
||||
verifyAndRegisterIdentity(PartyAndCertificate(firstPath))
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import net.corda.core.contracts.PartyAndReference
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.toStringShort
|
||||
import net.corda.core.identity.*
|
||||
import net.corda.core.internal.CertRole
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
import net.corda.core.node.services.UnknownAnonymousPartyException
|
||||
@ -13,8 +14,8 @@ import net.corda.core.utilities.contextLogger
|
||||
import net.corda.core.utilities.debug
|
||||
import net.corda.node.services.api.IdentityServiceInternal
|
||||
import net.corda.node.utilities.AppendOnlyPersistentMap
|
||||
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
|
||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import java.security.InvalidAlgorithmParameterException
|
||||
import java.security.PublicKey
|
||||
@ -125,14 +126,10 @@ class PersistentIdentityService(override val trustRoot: X509Certificate,
|
||||
}
|
||||
|
||||
// Ensure we record the first identity of the same name, first
|
||||
val identityPrincipal = identity.name.x500Principal
|
||||
val firstCertWithThisName: Certificate = identity.certPath.certificates.last { it ->
|
||||
val principal = (it as? X509Certificate)?.subjectX500Principal
|
||||
principal == identityPrincipal
|
||||
}
|
||||
if (firstCertWithThisName != identity.certificate) {
|
||||
val wellKnownCert: Certificate = identity.certPath.certificates.single { CertRole.extract(it)?.isWellKnown ?: false }
|
||||
if (wellKnownCert != identity.certificate) {
|
||||
val certificates = identity.certPath.certificates
|
||||
val idx = certificates.lastIndexOf(firstCertWithThisName)
|
||||
val idx = certificates.lastIndexOf(wellKnownCert)
|
||||
val firstPath = X509CertificateFactory().generateCertPath(certificates.slice(idx until certificates.size))
|
||||
verifyAndRegisterIdentity(PartyAndCertificate(firstPath))
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package net.corda.node.services.keys
|
||||
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.internal.CertRole
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
import net.corda.core.utilities.days
|
||||
@ -33,9 +34,11 @@ fun freshCertificate(identityService: IdentityServiceInternal,
|
||||
issuer: PartyAndCertificate,
|
||||
issuerSigner: ContentSigner,
|
||||
revocationEnabled: Boolean = false): PartyAndCertificate {
|
||||
val issuerRole = CertRole.extract(issuer.certificate)
|
||||
require(issuerRole == CertRole.LEGAL_IDENTITY) { "Confidential identities can only be issued from well known identities, provided issuer ${issuer.name} has role $issuerRole" }
|
||||
val issuerCert = issuer.certificate.toX509CertHolder()
|
||||
val window = X509Utilities.getCertificateValidityWindow(Duration.ZERO, 3650.days, issuerCert)
|
||||
val ourCertificate = X509Utilities.createCertificate(CertificateType.WELL_KNOWN_IDENTITY, issuerCert.subject,
|
||||
val ourCertificate = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuerCert.subject,
|
||||
issuerSigner, issuer.name, subjectPublicKey, window)
|
||||
val ourCertPath = X509CertificateFactory().generateCertPath(listOf(ourCertificate.cert) + issuer.certPath.certificates)
|
||||
val anonymisedIdentity = PartyAndCertificate(ourCertPath)
|
||||
|
@ -169,7 +169,7 @@ class InMemoryIdentityServiceTests {
|
||||
val issuerKeyPair = generateKeyPair()
|
||||
val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public)
|
||||
val txKey = Crypto.generateKeyPair()
|
||||
val txCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_IDENTITY, issuer.certificate.toX509CertHolder(), issuerKeyPair, x500Name, txKey.public)
|
||||
val txCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuer.certificate.toX509CertHolder(), issuerKeyPair, x500Name, txKey.public)
|
||||
val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates)
|
||||
return Pair(issuer, PartyAndCertificate(txCertPath))
|
||||
}
|
||||
|
@ -266,7 +266,7 @@ class PersistentIdentityServiceTests {
|
||||
val issuerKeyPair = generateKeyPair()
|
||||
val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public)
|
||||
val txKey = Crypto.generateKeyPair()
|
||||
val txCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_IDENTITY, issuer.certificate.toX509CertHolder(), issuerKeyPair, x500Name, txKey.public)
|
||||
val txCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, issuer.certificate.toX509CertHolder(), issuerKeyPair, x500Name, txKey.public)
|
||||
val txCertPath = X509CertificateFactory().generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates)
|
||||
return Pair(issuer, PartyAndCertificate(txCertPath))
|
||||
}
|
||||
|
@ -0,0 +1,29 @@
|
||||
package net.corda.node.services.keys
|
||||
|
||||
import net.corda.core.CordaOID
|
||||
import net.corda.core.crypto.generateKeyPair
|
||||
import net.corda.core.internal.CertRole
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.node.MockServices
|
||||
import net.corda.testing.node.makeTestIdentityService
|
||||
import org.bouncycastle.asn1.DEROctetString
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class KMSUtilsTests {
|
||||
@Test
|
||||
fun `should generate certificates with the correct role`() {
|
||||
val aliceKey = generateKeyPair()
|
||||
val alice = getTestPartyAndCertificate(ALICE_NAME, aliceKey.public)
|
||||
val cordappPackages = emptyList<String>()
|
||||
val ledgerIdentityService = makeTestIdentityService(alice)
|
||||
val mockServices = MockServices(cordappPackages, ledgerIdentityService, alice.name, aliceKey)
|
||||
val wellKnownIdentity = mockServices.myInfo.singleIdentityAndCert()
|
||||
val confidentialIdentity = mockServices.keyManagementService.freshKeyAndCert(wellKnownIdentity, false)
|
||||
val cert = confidentialIdentity.certificate
|
||||
val extensionData = DEROctetString.getInstance(cert.getExtensionValue(CordaOID.X509_EXTENSION_CORDA_ROLE))
|
||||
val expected = CertRole.CONFIDENTIAL_LEGAL_IDENTITY
|
||||
val actual = CertRole.getInstance(extensionData.octets)
|
||||
assertEquals(expected, actual)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user