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:
Ross Nicoll
2017-12-18 16:23:34 +00:00
committed by GitHub
parent 166918c62c
commit 21f0892deb
23 changed files with 345 additions and 51 deletions

View File

@ -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),

View File

@ -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))
}

View File

@ -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))
}

View File

@ -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)

View File

@ -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))
}

View File

@ -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))
}

View File

@ -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)
}
}