Correct identity service tests

* Correct path composition in identity service tests
* Correct identity service certificate path verification to handle the owning certificate being anywhere in the path, rather than expecting it to be trust root
This commit is contained in:
Ross Nicoll 2017-06-16 16:15:23 +01:00 committed by GitHub
parent 56ad64c524
commit b874b3e62a
4 changed files with 33 additions and 31 deletions

View File

@ -295,5 +295,6 @@ enum class CertificateType(val keyUsage: KeyUsage, vararg val purposes: KeyPurpo
INTERMEDIATE_CA(KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyCertSign or KeyUsage.cRLSign), KeyPurposeId.id_kp_serverAuth, KeyPurposeId.id_kp_clientAuth, KeyPurposeId.anyExtendedKeyUsage, isCA = true),
CLIENT_CA(KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyCertSign or KeyUsage.cRLSign), KeyPurposeId.id_kp_serverAuth, KeyPurposeId.id_kp_clientAuth, KeyPurposeId.anyExtendedKeyUsage, isCA = true),
TLS(KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyEncipherment or KeyUsage.keyAgreement), KeyPurposeId.id_kp_serverAuth, KeyPurposeId.id_kp_clientAuth, KeyPurposeId.anyExtendedKeyUsage, isCA = false),
IDENTITY(KeyUsage(KeyUsage.digitalSignature), KeyPurposeId.id_kp_serverAuth, KeyPurposeId.id_kp_clientAuth, KeyPurposeId.anyExtendedKeyUsage, isCA = false)
// TODO: Identity certs should have only limited depth (i.e. 1) CA signing capability, with tight name constraints
IDENTITY(KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyCertSign), KeyPurposeId.id_kp_serverAuth, KeyPurposeId.id_kp_clientAuth, KeyPurposeId.anyExtendedKeyUsage, isCA = true)
}

View File

@ -69,8 +69,8 @@ val DUMMY_CA: CertificateAndKeyPair by lazy {
/**
* Build a test party with a nonsense certificate authority for testing purposes.
*/
fun getTestPartyAndCertificate(name: X500Name, publicKey: PublicKey): PartyAndCertificate {
val cert = X509Utilities.createCertificate(CertificateType.IDENTITY, DUMMY_CA.certificate, DUMMY_CA.keyPair, name, publicKey)
val certPath = X509Utilities.createCertificatePath(DUMMY_CA.certificate, cert, revocationEnabled = false)
fun getTestPartyAndCertificate(name: X500Name, publicKey: PublicKey, ca: CertificateAndKeyPair = DUMMY_CA): PartyAndCertificate {
val cert = X509Utilities.createCertificate(CertificateType.IDENTITY, ca.certificate, ca.keyPair, name, publicKey)
val certPath = X509Utilities.createCertificatePath(ca.certificate, cert, revocationEnabled = false)
return PartyAndCertificate(name, publicKey, cert, certPath)
}

View File

@ -117,14 +117,12 @@ class InMemoryIdentityService(identities: Iterable<PartyAndCertificate>,
@Throws(IdentityService.UnknownAnonymousPartyException::class)
override fun assertOwnership(party: Party, anonymousParty: AnonymousParty) {
val path = partyToPath[anonymousParty] ?: throw IdentityService.UnknownAnonymousPartyException("Unknown anonymous party ${anonymousParty.owningKey.toStringShort()}")
val target = path.certificates.last() as X509Certificate
requireThat {
"Certificate path ends with \"${target.issuerX500Principal}\" expected \"${party.name}\"" using (X500Name(target.subjectX500Principal.name) == party.name)
"Certificate path ends with correct public key" using (target.publicKey == anonymousParty.owningKey)
}
val root: X509Certificate = path.certificates
.filterIsInstance<X509Certificate>()
.lastOrNull { it.publicKey == party.owningKey } ?: throw IllegalArgumentException("Certificate path must include a certificate for the party public key.")
// Verify there's a previous certificate in the path, which matches
val root = path.certificates.first() as X509Certificate
require(X500Name(root.issuerX500Principal.name) == party.name) { "Certificate path starts with \"${root.issuerX500Principal}\" expected \"${party.name}\"" }
val target = path.certificates.first() as X509Certificate
require(target.publicKey == anonymousParty.owningKey) { "Certificate path starts with a certificate for the anonymous party" }
}
override fun pathForAnonymous(anonymousParty: AnonymousParty): CertPath? = partyToPath[anonymousParty]

View File

@ -6,14 +6,13 @@ import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.services.IdentityService
import net.corda.core.utilities.*
import net.corda.flows.TxKeyFlow
import net.corda.node.services.identity.InMemoryIdentityService
import net.corda.testing.ALICE_PUBKEY
import net.corda.testing.BOB_PUBKEY
import org.bouncycastle.asn1.x500.X500Name
import org.junit.Test
import java.security.KeyPair
import java.security.cert.CertPath
import java.security.cert.X509Certificate
import java.security.cert.CertificateFactory
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertNull
@ -57,10 +56,11 @@ class InMemoryIdentityServiceTests {
@Test
fun `get identity by substring match`() {
val service = InMemoryIdentityService(trustRoot = DUMMY_CA.certificate)
val trustRoot = DUMMY_CA
val service = InMemoryIdentityService(trustRoot = trustRoot.certificate)
service.registerIdentity(ALICE_IDENTITY)
service.registerIdentity(BOB_IDENTITY)
val (_, _, alicente) = createParty(X500Name("O=Alicente Worldwide,L=London,C=UK"))
val alicente = getTestPartyAndCertificate(X500Name("O=Alicente Worldwide,L=London,C=UK"), generateKeyPair().public)
service.registerIdentity(alicente)
assertEquals(setOf(ALICE, alicente.party), service.partiesFromName("Alice", false))
assertEquals(setOf(ALICE), service.partiesFromName("Alice Corp", true))
@ -101,41 +101,44 @@ class InMemoryIdentityServiceTests {
*/
@Test
fun `assert ownership`() {
val (aliceTxKey, aliceCertPath, alice) = createParty(ALICE.name)
val trustRoot = DUMMY_CA
val (alice, aliceTxIdentity) = createParty(ALICE.name, trustRoot)
val certFactory = CertificateFactory.getInstance("X509")
val bobRootKey = Crypto.generateKeyPair()
val bobRootCert = X509Utilities.createSelfSignedCACertificate(BOB.name, bobRootKey)
val bobRoot = getTestPartyAndCertificate(BOB.name, bobRootKey.public)
val bobRootCert = bobRoot.certificate
val bobTxKey = Crypto.generateKeyPair()
val bobTxCert = X509Utilities.createCertificate(CertificateType.IDENTITY, bobRootCert, bobRootKey, BOB.name, bobTxKey.public)
val bobCertPath = X509Utilities.createCertificatePath(bobRootCert, bobTxCert, revocationEnabled = false)
val bobCertPath = certFactory.generateCertPath(listOf(bobTxCert.cert, bobRootCert.cert))
val bob = PartyAndCertificate(BOB.name, bobRootKey.public, bobRootCert, bobCertPath)
// Now we have identities, construct the service and let it know about both
val service = InMemoryIdentityService(setOf(alice, bob), emptyMap(), null as X509Certificate?)
val anonymousAlice = AnonymousParty(aliceTxKey.public)
service.registerAnonymousIdentity(anonymousAlice, alice.party, aliceCertPath)
val service = InMemoryIdentityService(setOf(alice, bob), emptyMap(), trustRoot.certificate.cert)
service.registerAnonymousIdentity(aliceTxIdentity.identity, alice.party, aliceTxIdentity.certPath)
val anonymousBob = AnonymousParty(bobTxKey.public)
service.registerAnonymousIdentity(anonymousBob, bob.party, bobCertPath)
// Verify that paths are verified
service.assertOwnership(alice.party, anonymousAlice)
service.assertOwnership(alice.party, aliceTxIdentity.identity)
service.assertOwnership(bob.party, anonymousBob)
assertFailsWith<IllegalArgumentException> {
service.assertOwnership(alice.party, anonymousBob)
}
assertFailsWith<IllegalArgumentException> {
service.assertOwnership(bob.party, anonymousAlice)
service.assertOwnership(bob.party, aliceTxIdentity.identity)
}
}
private fun createParty(x500Name: X500Name): Triple<KeyPair, CertPath, PartyAndCertificate> {
val rootKey = Crypto.generateKeyPair()
val rootCert = X509Utilities.createSelfSignedCACertificate(x500Name, rootKey)
private fun createParty(x500Name: X500Name, ca: CertificateAndKeyPair): Pair<PartyAndCertificate, TxKeyFlow.AnonymousIdentity> {
val certFactory = CertificateFactory.getInstance("X509")
val issuerKeyPair = generateKeyPair()
val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public, ca)
val txKey = Crypto.generateKeyPair()
val txCert = X509Utilities.createCertificate(CertificateType.IDENTITY, rootCert, rootKey, x500Name, txKey.public)
val certPath = X509Utilities.createCertificatePath(rootCert, txCert, revocationEnabled = false)
return Triple(txKey, certPath, PartyAndCertificate(x500Name, rootKey.public, rootCert, certPath))
val txCert = X509Utilities.createCertificate(CertificateType.IDENTITY, issuer.certificate, issuerKeyPair, x500Name, txKey.public)
val txCertPath = certFactory.generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates)
return Pair(issuer, TxKeyFlow.AnonymousIdentity(txCertPath, txCert, AnonymousParty(txKey.public)))
}
/**
@ -144,7 +147,7 @@ class InMemoryIdentityServiceTests {
@Test
fun `deanonymising a well known identity`() {
val expected = ALICE
val actual = InMemoryIdentityService(trustRoot = null).partyFromAnonymous(expected)
val actual = InMemoryIdentityService(trustRoot = DUMMY_CA.certificate).partyFromAnonymous(expected)
assertEquals(expected, actual)
}
}