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), 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), 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), 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. * Build a test party with a nonsense certificate authority for testing purposes.
*/ */
fun getTestPartyAndCertificate(name: X500Name, publicKey: PublicKey): PartyAndCertificate { fun getTestPartyAndCertificate(name: X500Name, publicKey: PublicKey, ca: CertificateAndKeyPair = DUMMY_CA): PartyAndCertificate {
val cert = X509Utilities.createCertificate(CertificateType.IDENTITY, DUMMY_CA.certificate, DUMMY_CA.keyPair, name, publicKey) val cert = X509Utilities.createCertificate(CertificateType.IDENTITY, ca.certificate, ca.keyPair, name, publicKey)
val certPath = X509Utilities.createCertificatePath(DUMMY_CA.certificate, cert, revocationEnabled = false) val certPath = X509Utilities.createCertificatePath(ca.certificate, cert, revocationEnabled = false)
return PartyAndCertificate(name, publicKey, cert, certPath) return PartyAndCertificate(name, publicKey, cert, certPath)
} }

View File

@ -117,14 +117,12 @@ class InMemoryIdentityService(identities: Iterable<PartyAndCertificate>,
@Throws(IdentityService.UnknownAnonymousPartyException::class) @Throws(IdentityService.UnknownAnonymousPartyException::class)
override fun assertOwnership(party: Party, anonymousParty: AnonymousParty) { override fun assertOwnership(party: Party, anonymousParty: AnonymousParty) {
val path = partyToPath[anonymousParty] ?: throw IdentityService.UnknownAnonymousPartyException("Unknown anonymous party ${anonymousParty.owningKey.toStringShort()}") val path = partyToPath[anonymousParty] ?: throw IdentityService.UnknownAnonymousPartyException("Unknown anonymous party ${anonymousParty.owningKey.toStringShort()}")
val target = path.certificates.last() as X509Certificate val root: X509Certificate = path.certificates
requireThat { .filterIsInstance<X509Certificate>()
"Certificate path ends with \"${target.issuerX500Principal}\" expected \"${party.name}\"" using (X500Name(target.subjectX500Principal.name) == party.name) .lastOrNull { it.publicKey == party.owningKey } ?: throw IllegalArgumentException("Certificate path must include a certificate for the party public key.")
"Certificate path ends with correct public key" using (target.publicKey == anonymousParty.owningKey)
}
// Verify there's a previous certificate in the path, which matches // Verify there's a previous certificate in the path, which matches
val root = path.certificates.first() as X509Certificate val target = path.certificates.first() as X509Certificate
require(X500Name(root.issuerX500Principal.name) == party.name) { "Certificate path starts with \"${root.issuerX500Principal}\" expected \"${party.name}\"" } require(target.publicKey == anonymousParty.owningKey) { "Certificate path starts with a certificate for the anonymous party" }
} }
override fun pathForAnonymous(anonymousParty: AnonymousParty): CertPath? = partyToPath[anonymousParty] 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.identity.PartyAndCertificate
import net.corda.core.node.services.IdentityService import net.corda.core.node.services.IdentityService
import net.corda.core.utilities.* import net.corda.core.utilities.*
import net.corda.flows.TxKeyFlow
import net.corda.node.services.identity.InMemoryIdentityService import net.corda.node.services.identity.InMemoryIdentityService
import net.corda.testing.ALICE_PUBKEY import net.corda.testing.ALICE_PUBKEY
import net.corda.testing.BOB_PUBKEY import net.corda.testing.BOB_PUBKEY
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
import org.junit.Test import org.junit.Test
import java.security.KeyPair import java.security.cert.CertificateFactory
import java.security.cert.CertPath
import java.security.cert.X509Certificate
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
import kotlin.test.assertNull import kotlin.test.assertNull
@ -57,10 +56,11 @@ class InMemoryIdentityServiceTests {
@Test @Test
fun `get identity by substring match`() { 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(ALICE_IDENTITY)
service.registerIdentity(BOB_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) service.registerIdentity(alicente)
assertEquals(setOf(ALICE, alicente.party), service.partiesFromName("Alice", false)) assertEquals(setOf(ALICE, alicente.party), service.partiesFromName("Alice", false))
assertEquals(setOf(ALICE), service.partiesFromName("Alice Corp", true)) assertEquals(setOf(ALICE), service.partiesFromName("Alice Corp", true))
@ -101,41 +101,44 @@ class InMemoryIdentityServiceTests {
*/ */
@Test @Test
fun `assert ownership`() { 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 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 bobTxKey = Crypto.generateKeyPair()
val bobTxCert = X509Utilities.createCertificate(CertificateType.IDENTITY, bobRootCert, bobRootKey, BOB.name, bobTxKey.public) 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) val bob = PartyAndCertificate(BOB.name, bobRootKey.public, bobRootCert, bobCertPath)
// 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 = InMemoryIdentityService(setOf(alice, bob), emptyMap(), null as X509Certificate?) val service = InMemoryIdentityService(setOf(alice, bob), emptyMap(), trustRoot.certificate.cert)
val anonymousAlice = AnonymousParty(aliceTxKey.public) service.registerAnonymousIdentity(aliceTxIdentity.identity, alice.party, aliceTxIdentity.certPath)
service.registerAnonymousIdentity(anonymousAlice, alice.party, aliceCertPath)
val anonymousBob = AnonymousParty(bobTxKey.public) val anonymousBob = AnonymousParty(bobTxKey.public)
service.registerAnonymousIdentity(anonymousBob, bob.party, bobCertPath) service.registerAnonymousIdentity(anonymousBob, bob.party, bobCertPath)
// Verify that paths are verified // Verify that paths are verified
service.assertOwnership(alice.party, anonymousAlice) service.assertOwnership(alice.party, aliceTxIdentity.identity)
service.assertOwnership(bob.party, anonymousBob) service.assertOwnership(bob.party, anonymousBob)
assertFailsWith<IllegalArgumentException> { assertFailsWith<IllegalArgumentException> {
service.assertOwnership(alice.party, anonymousBob) service.assertOwnership(alice.party, anonymousBob)
} }
assertFailsWith<IllegalArgumentException> { assertFailsWith<IllegalArgumentException> {
service.assertOwnership(bob.party, anonymousAlice) service.assertOwnership(bob.party, aliceTxIdentity.identity)
} }
} }
private fun createParty(x500Name: X500Name): Triple<KeyPair, CertPath, PartyAndCertificate> { private fun createParty(x500Name: X500Name, ca: CertificateAndKeyPair): Pair<PartyAndCertificate, TxKeyFlow.AnonymousIdentity> {
val rootKey = Crypto.generateKeyPair() val certFactory = CertificateFactory.getInstance("X509")
val rootCert = X509Utilities.createSelfSignedCACertificate(x500Name, rootKey) val issuerKeyPair = generateKeyPair()
val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public, ca)
val txKey = Crypto.generateKeyPair() val txKey = Crypto.generateKeyPair()
val txCert = X509Utilities.createCertificate(CertificateType.IDENTITY, rootCert, rootKey, x500Name, txKey.public) val txCert = X509Utilities.createCertificate(CertificateType.IDENTITY, issuer.certificate, issuerKeyPair, x500Name, txKey.public)
val certPath = X509Utilities.createCertificatePath(rootCert, txCert, revocationEnabled = false) val txCertPath = certFactory.generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates)
return Triple(txKey, certPath, PartyAndCertificate(x500Name, rootKey.public, rootCert, certPath)) return Pair(issuer, TxKeyFlow.AnonymousIdentity(txCertPath, txCert, AnonymousParty(txKey.public)))
} }
/** /**
@ -144,7 +147,7 @@ class InMemoryIdentityServiceTests {
@Test @Test
fun `deanonymising a well known identity`() { fun `deanonymising a well known identity`() {
val expected = ALICE val expected = ALICE
val actual = InMemoryIdentityService(trustRoot = null).partyFromAnonymous(expected) val actual = InMemoryIdentityService(trustRoot = DUMMY_CA.certificate).partyFromAnonymous(expected)
assertEquals(expected, actual) assertEquals(expected, actual)
} }
} }