CORDA-831: Add confidential identity certificate type (#2198)

* Rename certificate types
* Create separate certificate type for confidential identities
* Add name constraints to dev node CA
* Move dev node CA into getTestPartyAndCertificate()
This commit is contained in:
Ross Nicoll
2017-12-08 13:17:29 +00:00
committed by GitHub
parent 32ea59d085
commit e6adbe7137
11 changed files with 61 additions and 38 deletions

View File

@ -337,7 +337,7 @@ class CompositeKeyTests {
val ca = X509Utilities.createSelfSignedCACertificate(caName, caKeyPair) val ca = X509Utilities.createSelfSignedCACertificate(caName, caKeyPair)
// Sign the composite key with the self sign CA. // Sign the composite key with the self sign CA.
val compositeKeyCert = X509Utilities.createCertificate(CertificateType.IDENTITY, ca, caKeyPair, caName.copy(commonName = "CompositeKey"), compositeKey) val compositeKeyCert = X509Utilities.createCertificate(CertificateType.WELL_KNOWN_IDENTITY, ca, caKeyPair, caName.copy(commonName = "CompositeKey"), compositeKey)
// Store certificate to keystore. // Store certificate to keystore.
val keystorePath = tempFolder.root.toPath() / "keystore.jks" val keystorePath = tempFolder.root.toPath() / "keystore.jks"

View File

@ -8,7 +8,6 @@ import java.security.KeyPair
import java.security.PublicKey import java.security.PublicKey
import java.security.cert.CertPath import java.security.cert.CertPath
import java.security.cert.Certificate import java.security.cert.Certificate
import java.security.cert.CertificateFactory
class KeyStoreWrapper(private val storePath: Path, private val storePassword: String) { class KeyStoreWrapper(private val storePath: Path, private val storePassword: String) {
private val keyStore = storePath.read { loadKeyStore(it, storePassword) } private val keyStore = storePath.read { loadKeyStore(it, storePassword) }
@ -18,7 +17,7 @@ class KeyStoreWrapper(private val storePath: Path, private val storePassword: St
// Assume key password = store password. // Assume key password = store password.
val clientCA = certificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA) val clientCA = certificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)
// Create new keys and store in keystore. // Create new keys and store in keystore.
val cert = X509Utilities.createCertificate(CertificateType.IDENTITY, clientCA.certificate, clientCA.keyPair, serviceName, pubKey) val cert = X509Utilities.createCertificate(CertificateType.WELL_KNOWN_IDENTITY, clientCA.certificate, clientCA.keyPair, serviceName, pubKey)
val certPath = X509CertificateFactory().delegate.generateCertPath(listOf(cert.cert) + clientCertPath) val certPath = X509CertificateFactory().delegate.generateCertPath(listOf(cert.cert) + clientCertPath)
require(certPath.certificates.isNotEmpty()) { "Certificate path cannot be empty" } require(certPath.certificates.isNotEmpty()) { "Certificate path cannot be empty" }
// TODO: X509Utilities.validateCertificateChain() // TODO: X509Utilities.validateCertificateChain()

View File

@ -332,7 +332,7 @@ enum class CertificateType(val keyUsage: KeyUsage, vararg val purposes: KeyPurpo
isCA = true isCA = true
), ),
CLIENT_CA( NODE_CA(
KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyCertSign or KeyUsage.cRLSign), KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyCertSign or KeyUsage.cRLSign),
KeyPurposeId.id_kp_serverAuth, KeyPurposeId.id_kp_serverAuth,
KeyPurposeId.id_kp_clientAuth, KeyPurposeId.id_kp_clientAuth,
@ -349,12 +349,20 @@ enum class CertificateType(val keyUsage: KeyUsage, vararg val purposes: KeyPurpo
), ),
// TODO: Identity certs should have only limited depth (i.e. 1) CA signing capability, with tight name constraints // TODO: Identity certs should have only limited depth (i.e. 1) CA signing capability, with tight name constraints
IDENTITY( WELL_KNOWN_IDENTITY(
KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyCertSign), KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyCertSign),
KeyPurposeId.id_kp_serverAuth, KeyPurposeId.id_kp_serverAuth,
KeyPurposeId.id_kp_clientAuth, KeyPurposeId.id_kp_clientAuth,
KeyPurposeId.anyExtendedKeyUsage, KeyPurposeId.anyExtendedKeyUsage,
isCA = true isCA = true
),
CONFIDENTIAL_IDENTITY(
KeyUsage(KeyUsage.digitalSignature),
KeyPurposeId.id_kp_serverAuth,
KeyPurposeId.id_kp_clientAuth,
KeyPurposeId.anyExtendedKeyUsage,
isCA = false
) )
} }

View File

@ -35,7 +35,7 @@ fun freshCertificate(identityService: IdentityServiceInternal,
revocationEnabled: Boolean = false): PartyAndCertificate { revocationEnabled: Boolean = false): PartyAndCertificate {
val issuerCert = issuer.certificate.toX509CertHolder() val issuerCert = issuer.certificate.toX509CertHolder()
val window = X509Utilities.getCertificateValidityWindow(Duration.ZERO, 3650.days, issuerCert) val window = X509Utilities.getCertificateValidityWindow(Duration.ZERO, 3650.days, issuerCert)
val ourCertificate = X509Utilities.createCertificate(CertificateType.IDENTITY, issuerCert.subject, val ourCertificate = X509Utilities.createCertificate(CertificateType.WELL_KNOWN_IDENTITY, issuerCert.subject,
issuerSigner, issuer.name, subjectPublicKey, window) issuerSigner, issuer.name, subjectPublicKey, window)
val ourCertPath = X509CertificateFactory().delegate.generateCertPath(listOf(ourCertificate.cert) + issuer.certPath.certificates) val ourCertPath = X509CertificateFactory().delegate.generateCertPath(listOf(ourCertificate.cert) + issuer.certPath.certificates)
val anonymisedIdentity = PartyAndCertificate(ourCertPath) val anonymisedIdentity = PartyAndCertificate(ourCertPath)

View File

@ -35,8 +35,8 @@ object ServiceIdentityGenerator {
val rootCert = caKeyStore.getCertificate(X509Utilities.CORDA_ROOT_CA) val rootCert = caKeyStore.getCertificate(X509Utilities.CORDA_ROOT_CA)
keyPairs.zip(dirs) { keyPair, dir -> keyPairs.zip(dirs) { keyPair, dir ->
val serviceKeyCert = X509Utilities.createCertificate(CertificateType.CLIENT_CA, issuer.certificate, issuer.keyPair, serviceName, keyPair.public) val serviceKeyCert = X509Utilities.createCertificate(CertificateType.NODE_CA, issuer.certificate, issuer.keyPair, serviceName, keyPair.public)
val compositeKeyCert = X509Utilities.createCertificate(CertificateType.CLIENT_CA, issuer.certificate, issuer.keyPair, serviceName, notaryKey) val compositeKeyCert = X509Utilities.createCertificate(CertificateType.NODE_CA, issuer.certificate, issuer.keyPair, serviceName, notaryKey)
val certPath = (dir / "certificates").createDirectories() / "distributedService.jks" val certPath = (dir / "certificates").createDirectories() / "distributedService.jks"
val keystore = loadOrCreateKeyStore(certPath, "cordacadevpass") val keystore = loadOrCreateKeyStore(certPath, "cordacadevpass")
val serviceId = serviceName.commonName val serviceId = serviceName.commonName

View File

@ -9,7 +9,6 @@ import net.corda.core.identity.PartyAndCertificate
import net.corda.core.internal.cert import net.corda.core.internal.cert
import net.corda.core.internal.toX509CertHolder import net.corda.core.internal.toX509CertHolder
import net.corda.core.node.services.UnknownAnonymousPartyException import net.corda.core.node.services.UnknownAnonymousPartyException
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.X509CertificateFactory
import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities
@ -108,8 +107,8 @@ class InMemoryIdentityServiceTests {
*/ */
@Test @Test
fun `get anonymous identity by key`() { fun `get anonymous identity by key`() {
val (alice, aliceTxIdentity) = createParty(ALICE.name, DEV_CA) val (alice, aliceTxIdentity) = createConfidentialIdentity(ALICE.name)
val (_, bobTxIdentity) = createParty(ALICE.name, DEV_CA) val (_, bobTxIdentity) = createConfidentialIdentity(ALICE.name)
// Now we have identities, construct the service and let it know about both // Now we have identities, construct the service and let it know about both
val service = createService(alice) val service = createService(alice)
@ -131,8 +130,8 @@ class InMemoryIdentityServiceTests {
@Test @Test
fun `assert ownership`() { fun `assert ownership`() {
withTestSerialization { withTestSerialization {
val (alice, anonymousAlice) = createParty(ALICE.name, DEV_CA) val (alice, anonymousAlice) = createConfidentialIdentity(ALICE.name)
val (bob, anonymousBob) = createParty(BOB.name, DEV_CA) val (bob, anonymousBob) = createConfidentialIdentity(BOB.name)
// Now we have identities, construct the service and let it know about both // Now we have identities, construct the service and let it know about both
val service = createService(alice, bob) val service = createService(alice, bob)
@ -157,11 +156,11 @@ class InMemoryIdentityServiceTests {
} }
} }
private fun createParty(x500Name: CordaX500Name, ca: CertificateAndKeyPair): Pair<PartyAndCertificate, PartyAndCertificate> { private fun createConfidentialIdentity(x500Name: CordaX500Name): Pair<PartyAndCertificate, PartyAndCertificate> {
val issuerKeyPair = generateKeyPair() val issuerKeyPair = generateKeyPair()
val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public, ca) val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public)
val txKey = Crypto.generateKeyPair() val txKey = Crypto.generateKeyPair()
val txCert = X509Utilities.createCertificate(CertificateType.IDENTITY, issuer.certificate.toX509CertHolder(), issuerKeyPair, x500Name, txKey.public) val txCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_IDENTITY, issuer.certificate.toX509CertHolder(), issuerKeyPair, x500Name, txKey.public)
val txCertPath = X509CertificateFactory().delegate.generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates) val txCertPath = X509CertificateFactory().delegate.generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates)
return Pair(issuer, PartyAndCertificate(txCertPath)) return Pair(issuer, PartyAndCertificate(txCertPath))
} }

View File

@ -11,7 +11,6 @@ import net.corda.core.internal.toX509CertHolder
import net.corda.core.node.services.IdentityService 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.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.X509CertificateFactory
import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities
@ -149,8 +148,8 @@ class PersistentIdentityServiceTests {
*/ */
@Test @Test
fun `get anonymous identity by key`() { fun `get anonymous identity by key`() {
val (alice, aliceTxIdentity) = createParty(ALICE.name, DEV_CA) val (alice, aliceTxIdentity) = createConfidentialIdentity(ALICE.name)
val (_, bobTxIdentity) = createParty(ALICE.name, DEV_CA) val (_, bobTxIdentity) = createConfidentialIdentity(ALICE.name)
// Now we have identities, construct the service and let it know about both // Now we have identities, construct the service and let it know about both
database.transaction { database.transaction {
@ -182,8 +181,8 @@ class PersistentIdentityServiceTests {
@Test @Test
fun `assert ownership`() { fun `assert ownership`() {
withTestSerialization { withTestSerialization {
val (alice, anonymousAlice) = createParty(ALICE.name, DEV_CA) val (alice, anonymousAlice) = createConfidentialIdentity(ALICE.name)
val (bob, anonymousBob) = createParty(BOB.name, DEV_CA) val (bob, anonymousBob) = createConfidentialIdentity(BOB.name)
database.transaction { database.transaction {
// Now we have identities, construct the service and let it know about both // Now we have identities, construct the service and let it know about both
@ -219,8 +218,8 @@ class PersistentIdentityServiceTests {
@Test @Test
fun `Test Persistence`() { fun `Test Persistence`() {
val (alice, anonymousAlice) = createParty(ALICE.name, DEV_CA) val (alice, anonymousAlice) = createConfidentialIdentity(ALICE.name)
val (bob, anonymousBob) = createParty(BOB.name, DEV_CA) val (bob, anonymousBob) = createConfidentialIdentity(BOB.name)
database.transaction { database.transaction {
// Register well known identities // Register well known identities
@ -252,11 +251,11 @@ class PersistentIdentityServiceTests {
assertEquals(anonymousBob, bobReload!!) assertEquals(anonymousBob, bobReload!!)
} }
private fun createParty(x500Name: CordaX500Name, ca: CertificateAndKeyPair): Pair<PartyAndCertificate, PartyAndCertificate> { private fun createConfidentialIdentity(x500Name: CordaX500Name): Pair<PartyAndCertificate, PartyAndCertificate> {
val issuerKeyPair = generateKeyPair() val issuerKeyPair = generateKeyPair()
val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public, ca) val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public)
val txKey = Crypto.generateKeyPair() val txKey = Crypto.generateKeyPair()
val txCert = X509Utilities.createCertificate(CertificateType.IDENTITY, issuer.certificate.toX509CertHolder(), issuerKeyPair, x500Name, txKey.public) val txCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_IDENTITY, issuer.certificate.toX509CertHolder(), issuerKeyPair, x500Name, txKey.public)
val txCertPath = X509CertificateFactory().delegate.generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates) val txCertPath = X509CertificateFactory().delegate.generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates)
return Pair(issuer, PartyAndCertificate(txCertPath)) return Pair(issuer, PartyAndCertificate(txCertPath))
} }

View File

@ -26,7 +26,7 @@ object TestNodeInfoFactory {
fun createNodeInfo(organisation: String): SignedData<NodeInfo> { fun createNodeInfo(organisation: String): SignedData<NodeInfo> {
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val clientCert = X509Utilities.createCertificate(CertificateType.CLIENT_CA, intermediateCACert, intermediateCAKey, CordaX500Name(organisation = organisation, locality = "London", country = "GB"), keyPair.public) val clientCert = X509Utilities.createCertificate(CertificateType.NODE_CA, intermediateCACert, intermediateCAKey, CordaX500Name(organisation = organisation, locality = "London", country = "GB"), keyPair.public)
val certPath = buildCertPath(clientCert.toX509Certificate(), intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate()) val certPath = buildCertPath(clientCert.toX509Certificate(), intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
val nodeInfo = NodeInfo(listOf(NetworkHostAndPort("my.$organisation.com", 1234)), listOf(PartyAndCertificate(certPath)), 1, serial = 1L) val nodeInfo = NodeInfo(listOf(NetworkHostAndPort("my.$organisation.com", 1234)), listOf(PartyAndCertificate(certPath)), 1, serial = 1L)
return sign(keyPair, nodeInfo) return sign(keyPair, nodeInfo)

View File

@ -242,7 +242,7 @@ class TLSAuthenticationTests {
// Client 1 keys, certs and SSLKeyStore. // Client 1 keys, certs and SSLKeyStore.
val client1CAKeyPair = Crypto.generateKeyPair(client1CAScheme) val client1CAKeyPair = Crypto.generateKeyPair(client1CAScheme)
val client1CACert = X509Utilities.createCertificate( val client1CACert = X509Utilities.createCertificate(
CertificateType.CLIENT_CA, CertificateType.NODE_CA,
intermediateCACert, intermediateCACert,
intermediateCAKeyPair, intermediateCAKeyPair,
CLIENT_1_X500, CLIENT_1_X500,
@ -269,7 +269,7 @@ class TLSAuthenticationTests {
// Client 2 keys, certs and SSLKeyStore. // Client 2 keys, certs and SSLKeyStore.
val client2CAKeyPair = Crypto.generateKeyPair(client2CAScheme) val client2CAKeyPair = Crypto.generateKeyPair(client2CAScheme)
val client2CACert = X509Utilities.createCertificate( val client2CACert = X509Utilities.createCertificate(
CertificateType.CLIENT_CA, CertificateType.NODE_CA,
intermediateCACert, intermediateCACert,
intermediateCAKeyPair, intermediateCAKeyPair,
CLIENT_2_X500, CLIENT_2_X500,

View File

@ -4,12 +4,14 @@
package net.corda.testing package net.corda.testing
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.generateKeyPair
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate import net.corda.core.identity.PartyAndCertificate
import net.corda.core.internal.cert import net.corda.core.internal.cert
import net.corda.core.internal.x500Name
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.OpaqueBytes
@ -22,6 +24,10 @@ import net.corda.nodeapi.internal.crypto.CertificateType
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.X509Utilities
import net.corda.nodeapi.internal.serialization.amqp.AMQP_ENABLED import net.corda.nodeapi.internal.serialization.amqp.AMQP_ENABLED
import org.bouncycastle.asn1.x509.GeneralName
import org.bouncycastle.asn1.x509.GeneralSubtree
import org.bouncycastle.asn1.x509.NameConstraints
import org.bouncycastle.cert.X509CertificateHolder
import org.mockito.Mockito.mock import org.mockito.Mockito.mock
import org.mockito.internal.stubbing.answers.ThrowsException import org.mockito.internal.stubbing.answers.ThrowsException
import java.lang.reflect.Modifier import java.lang.reflect.Modifier
@ -128,18 +134,27 @@ fun configureTestSSL(legalName: CordaX500Name = MEGA_CORP.name): SSLConfiguratio
configureDevKeyAndTrustStores(legalName) configureDevKeyAndTrustStores(legalName)
} }
} }
fun getTestPartyAndCertificate(party: Party): PartyAndCertificate {
val trustRoot: X509CertificateHolder = DEV_TRUST_ROOT
val intermediate: CertificateAndKeyPair = DEV_CA
fun getTestPartyAndCertificate(party: Party, trustRoot: CertificateAndKeyPair = DEV_CA): PartyAndCertificate { val nodeCaName = party.name.copy(commonName = X509Utilities.CORDA_CLIENT_CA_CN)
val certHolder = X509Utilities.createCertificate(CertificateType.IDENTITY, trustRoot.certificate, trustRoot.keyPair, party.name, party.owningKey) val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, party.name.x500Name))), arrayOf())
val certPath = X509CertificateFactory().delegate.generateCertPath(listOf(certHolder.cert, trustRoot.certificate.cert)) val issuerKeyPair = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
val issuerCertificate = X509Utilities.createCertificate(CertificateType.NODE_CA, intermediate.certificate, intermediate.keyPair, nodeCaName, issuerKeyPair.public,
nameConstraints = nameConstraints)
val certHolder = X509Utilities.createCertificate(CertificateType.WELL_KNOWN_IDENTITY, issuerCertificate, issuerKeyPair, party.name, party.owningKey)
val pathElements = listOf(certHolder, issuerCertificate, intermediate.certificate, trustRoot)
val certPath = X509CertificateFactory().delegate.generateCertPath(pathElements.map(X509CertificateHolder::cert))
return PartyAndCertificate(certPath) return PartyAndCertificate(certPath)
} }
/** /**
* Build a test party with a nonsense certificate authority for testing purposes. * Build a test party with a nonsense certificate authority for testing purposes.
*/ */
fun getTestPartyAndCertificate(name: CordaX500Name, publicKey: PublicKey, trustRoot: CertificateAndKeyPair = DEV_CA): PartyAndCertificate { fun getTestPartyAndCertificate(name: CordaX500Name, publicKey: PublicKey): PartyAndCertificate {
return getTestPartyAndCertificate(Party(name, publicKey), trustRoot) return getTestPartyAndCertificate(Party(name, publicKey))
} }
@Suppress("unused") @Suppress("unused")

View File

@ -4,20 +4,23 @@ package net.corda.testing
import net.corda.core.contracts.Command import net.corda.core.contracts.Command
import net.corda.core.contracts.TypeOnlyCommandData import net.corda.core.contracts.TypeOnlyCommandData
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.entropyToKeyPair import net.corda.core.crypto.entropyToKeyPair
import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.generateKeyPair
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate import net.corda.core.identity.PartyAndCertificate
import net.corda.core.internal.toX509CertHolder import net.corda.core.internal.toX509CertHolder
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.core.internal.x500Name
import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.*
import net.corda.nodeapi.internal.crypto.getCertificateAndKeyPair import org.bouncycastle.asn1.x509.GeneralName
import net.corda.nodeapi.internal.crypto.loadKeyStore import org.bouncycastle.asn1.x509.GeneralSubtree
import org.bouncycastle.asn1.x509.NameConstraints
import org.bouncycastle.cert.X509CertificateHolder import org.bouncycastle.cert.X509CertificateHolder
import java.math.BigInteger import java.math.BigInteger
import java.security.KeyPair import java.security.KeyPair
import java.security.PublicKey import java.security.PublicKey
import java.security.Security
import java.time.Instant import java.time.Instant
// A dummy time at which we will be pretending test transactions are created. // A dummy time at which we will be pretending test transactions are created.