Doorman can sign TLS certs directly. (#4078)

This commit is contained in:
Konstantinos Chalkias 2018-10-16 11:16:28 +01:00 committed by GitHub
parent 87a6585573
commit 68d736dd81
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 87 additions and 9 deletions

View File

@ -26,20 +26,25 @@ import java.security.cert.X509Certificate
enum class CertRole(val validParents: NonEmptySet<CertRole?>, val isIdentity: Boolean, val isWellKnown: Boolean) : ASN1Encodable { enum class CertRole(val validParents: NonEmptySet<CertRole?>, val isIdentity: Boolean, val isWellKnown: Boolean) : ASN1Encodable {
/** Signing certificate for the Doorman CA. */ /** Signing certificate for the Doorman CA. */
DOORMAN_CA(NonEmptySet.of(null), false, false), DOORMAN_CA(NonEmptySet.of(null), false, false),
/** Signing certificate for the network map. */ /** Signing certificate for the network map. */
NETWORK_MAP(NonEmptySet.of(null), false, false), NETWORK_MAP(NonEmptySet.of(null), false, false),
/** Well known (publicly visible) identity of a service (such as notary). */ /** Well known (publicly visible) identity of a service (such as notary). */
SERVICE_IDENTITY(NonEmptySet.of(DOORMAN_CA), true, true), SERVICE_IDENTITY(NonEmptySet.of(DOORMAN_CA), true, true),
/** Node level CA from which the TLS and well known identity certificates are issued. */ /** Node level CA from which the TLS and well known identity certificates are issued. */
NODE_CA(NonEmptySet.of(DOORMAN_CA), false, false), NODE_CA(NonEmptySet.of(DOORMAN_CA), false, false),
// [DOORMAN_CA] is also added as a valid parent of [TLS] and [LEGAL_IDENTITY] for backwards compatibility purposes
// (eg. if we decide [TLS] has its own [ROOT_CA] and [DOORMAN_CA] directly issues [TLS] and [LEGAL_IDENTITY]; thus,
// there won't be a requirement for [NODE_CA]).
/** Transport layer security certificate for a node. */ /** Transport layer security certificate for a node. */
TLS(NonEmptySet.of(NODE_CA), false, false), TLS(NonEmptySet.of(DOORMAN_CA, NODE_CA), false, false),
/** Well known (publicly visible) identity of a legal entity. */ /** Well known (publicly visible) identity of a legal entity. */
// TODO: at the moment, Legal Identity certs are issued by Node CA only. However, [DOORMAN_CA] is also added
// as a valid parent of [LEGAL_IDENTITY] for backwards compatibility purposes (eg. if we decide TLS has its
// own Root CA and Doorman CA directly issues Legal Identities; thus, there won't be a requirement for
// Node CA). Consider removing [DOORMAN_CA] from [validParents] when the model is finalised.
LEGAL_IDENTITY(NonEmptySet.of(DOORMAN_CA, NODE_CA), true, true), LEGAL_IDENTITY(NonEmptySet.of(DOORMAN_CA, NODE_CA), true, true),
/** Confidential (limited visibility) identity of a legal entity. */ /** Confidential (limited visibility) identity of a legal entity. */
CONFIDENTIAL_LEGAL_IDENTITY(NonEmptySet.of(LEGAL_IDENTITY), true, false); CONFIDENTIAL_LEGAL_IDENTITY(NonEmptySet.of(LEGAL_IDENTITY), true, false);
@ -88,4 +93,4 @@ enum class CertRole(val validParents: NonEmptySet<CertRole?>, val isIdentity: Bo
fun isValidParent(parent: CertRole?): Boolean = parent in validParents fun isValidParent(parent: CertRole?): Boolean = parent in validParents
override fun toASN1Primitive(): ASN1Primitive = ASN1Integer(this.ordinal + 1L) override fun toASN1Primitive(): ASN1Primitive = ASN1Integer(this.ordinal + 1L)
} }

View File

@ -1,9 +1,12 @@
package net.corda.core.internal package net.corda.core.internal
import net.corda.core.crypto.Crypto
import net.corda.nodeapi.internal.crypto.CertificateType
import net.corda.nodeapi.internal.crypto.X509Utilities
import org.bouncycastle.asn1.ASN1Integer import org.bouncycastle.asn1.ASN1Integer
import org.junit.Test import org.junit.Test
import kotlin.test.assertEquals import javax.security.auth.x500.X500Principal
import kotlin.test.assertFailsWith import kotlin.test.*
class CertRoleTests { class CertRoleTests {
@Test @Test
@ -22,4 +25,74 @@ class CertRoleTests {
// Outside of the range of integers // Outside of the range of integers
assertFailsWith<IllegalArgumentException> { CertRole.getInstance(ASN1Integer(Integer.MAX_VALUE + 1L)) } assertFailsWith<IllegalArgumentException> { CertRole.getInstance(ASN1Integer(Integer.MAX_VALUE + 1L)) }
} }
}
@Test
fun `check cert roles verify for various cert hierarchies`(){
// Testing for various certificate hierarchies (with or without NodeCA).
// ROOT -> Intermediate Root -> Doorman -> NodeCA -> Legal Identity cert -> Confidential key cert
// -> NodeCA -> TLS
// -> Legal Identity cert -> Confidential key cert
// -> TLS
val rootSubject = X500Principal("CN=Root,O=R3 Ltd,L=London,C=GB")
val intermediateRootSubject = X500Principal("CN=Intermediate Root,O=R3 Ltd,L=London,C=GB")
val doormanSubject = X500Principal("CN=Doorman,O=R3 Ltd,L=London,C=GB")
val nodeSubject = X500Principal("CN=Node,O=R3 Ltd,L=London,C=GB")
val rootKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val rootCert = X509Utilities.createSelfSignedCACertificate(rootSubject, rootKeyPair)
val rootCertRole = CertRole.extract(rootCert)
val intermediateRootKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
// Note that [CertificateType.ROOT_CA] is used for both root and intermediate root.
val intermediateRootCert = X509Utilities.createCertificate(CertificateType.ROOT_CA, rootCert, rootKeyPair, intermediateRootSubject, intermediateRootKeyPair.public)
val intermediateRootCertRole = CertRole.extract(intermediateRootCert)
val doormanKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
// Note that [CertificateType.INTERMEDIATE_CA] has actually role = CertRole.DOORMAN_CA, see [CertificateType] in [X509Utilities].
val doormanCert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, intermediateRootCert, intermediateRootKeyPair, doormanSubject, doormanKeyPair.public)
val doormanCertRole = CertRole.extract(doormanCert)!!
val nodeCAKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val nodeCACert = X509Utilities.createCertificate(CertificateType.NODE_CA, doormanCert, doormanKeyPair, nodeSubject, nodeCAKeyPair.public)
val nodeCACertRole = CertRole.extract(nodeCACert)!!
val tlsKeyPairFromNodeCA = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val tlsCertFromNodeCA = X509Utilities.createCertificate(CertificateType.TLS, nodeCACert, nodeCAKeyPair, nodeSubject, tlsKeyPairFromNodeCA.public)
val tlsCertFromNodeCARole = CertRole.extract(tlsCertFromNodeCA)!!
val tlsKeyPairFromDoorman = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val tlsCertFromDoorman = X509Utilities.createCertificate(CertificateType.TLS, doormanCert, doormanKeyPair, nodeSubject, tlsKeyPairFromDoorman.public)
val tlsCertFromDoormanRole = CertRole.extract(tlsCertFromDoorman)!!
val legalIDKeyPairFromNodeCA = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val legalIDCertFromNodeCA = X509Utilities.createCertificate(CertificateType.LEGAL_IDENTITY, nodeCACert, nodeCAKeyPair, nodeSubject, legalIDKeyPairFromNodeCA.public)
val legalIDCertFromNodeCARole = CertRole.extract(legalIDCertFromNodeCA)!!
val legalIDKeyPairFromDoorman = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val legalIDCertFromDoorman = X509Utilities.createCertificate(CertificateType.LEGAL_IDENTITY, doormanCert, doormanKeyPair, nodeSubject, legalIDKeyPairFromDoorman.public)
val legalIDCertFromDoormanRole = CertRole.extract(legalIDCertFromDoorman)!!
val confidentialKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val confidentialCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, legalIDCertFromNodeCA, legalIDKeyPairFromNodeCA, nodeSubject, confidentialKeyPair.public)
val confidentialCertRole = CertRole.extract(confidentialCert)!!
assertNull(rootCertRole)
assertNull(intermediateRootCertRole)
assertEquals(tlsCertFromNodeCARole, tlsCertFromDoormanRole)
assertEquals(legalIDCertFromNodeCARole, legalIDCertFromDoormanRole)
assertTrue { doormanCertRole.isValidParent(intermediateRootCertRole) } // Doorman is signed by Intermediate Root.
assertTrue { nodeCACertRole.isValidParent(doormanCertRole) } // NodeCA is signed by Doorman.
assertTrue { tlsCertFromNodeCARole.isValidParent(nodeCACertRole) } // TLS is signed by NodeCA.
assertTrue { tlsCertFromDoormanRole.isValidParent(doormanCertRole) } // TLS can also be signed by Doorman.
assertTrue { legalIDCertFromNodeCARole.isValidParent(nodeCACertRole) } // Legal Identity is signed by NodeCA.
assertTrue { legalIDCertFromDoormanRole.isValidParent(doormanCertRole) } // Legal Identity can also be signed by Doorman.
assertTrue { confidentialCertRole.isValidParent(legalIDCertFromNodeCARole) } // Confidential key cert is signed by Legal Identity.
assertFalse { legalIDCertFromDoormanRole.isValidParent(tlsCertFromDoormanRole) } // Legal Identity cannot be signed by TLS.
assertFalse { tlsCertFromNodeCARole.isValidParent(legalIDCertFromNodeCARole) } // TLS cannot be signed by Legal Identity.
assertFalse { confidentialCertRole.isValidParent(nodeCACertRole) } // Confidential key cert cannot be signed by NodeCA.
assertFalse { confidentialCertRole.isValidParent(doormanCertRole) } // Confidential key cert cannot be signed by Doorman.
}
}