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

@ -37,16 +37,16 @@ object ServiceIdentityGenerator {
val notaryKey = CompositeKey.Builder().addKeys(keyPairs.map { it.public }).build(threshold)
val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
val issuer = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
val intermediateCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
val rootCert = customRootCert ?: caKeyStore.getCertificate(X509Utilities.CORDA_ROOT_CA)
keyPairs.zip(dirs) { keyPair, dir ->
val serviceKeyCert = X509Utilities.createCertificate(CertificateType.NODE_CA, issuer.certificate, issuer.keyPair, serviceName, keyPair.public)
val compositeKeyCert = X509Utilities.createCertificate(CertificateType.NODE_CA, issuer.certificate, issuer.keyPair, serviceName, notaryKey)
val serviceKeyCert = X509Utilities.createCertificate(CertificateType.SERVICE_IDENTITY, intermediateCa.certificate, intermediateCa.keyPair, serviceName, keyPair.public)
val compositeKeyCert = X509Utilities.createCertificate(CertificateType.SERVICE_IDENTITY, intermediateCa.certificate, intermediateCa.keyPair, serviceName, notaryKey)
val certPath = (dir / "certificates").createDirectories() / "distributedService.jks"
val keystore = loadOrCreateKeyStore(certPath, "cordacadevpass")
keystore.setCertificateEntry("$serviceId-composite-key", compositeKeyCert.cert)
keystore.setKeyEntry("$serviceId-private-key", keyPair.private, "cordacadevkeypass".toCharArray(), arrayOf(serviceKeyCert.cert, issuer.certificate.cert, rootCert))
keystore.setKeyEntry("$serviceId-private-key", keyPair.private, "cordacadevkeypass".toCharArray(), arrayOf(serviceKeyCert.cert, intermediateCa.certificate.cert, rootCert))
keystore.save(certPath, "cordacadevpass")
}
return Party(serviceName, notaryKey)

View File

@ -17,7 +17,7 @@ class KeyStoreWrapper(private val storePath: Path, private val storePassword: St
// Assume key password = store password.
val clientCA = certificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)
// Create new keys and store in keystore.
val cert = X509Utilities.createCertificate(CertificateType.WELL_KNOWN_IDENTITY, clientCA.certificate, clientCA.keyPair, serviceName, pubKey)
val cert = X509Utilities.createCertificate(CertificateType.LEGAL_IDENTITY, clientCA.certificate, clientCA.keyPair, serviceName, pubKey)
val certPath = X509CertificateFactory().generateCertPath(cert.cert, *clientCertPath)
require(certPath.certificates.isNotEmpty()) { "Certificate path cannot be empty" }
// TODO: X509Utilities.validateCertificateChain()

View File

@ -1,18 +1,17 @@
package net.corda.nodeapi.internal.crypto
import net.corda.core.CordaOID
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SignatureScheme
import net.corda.core.crypto.random63BitValue
import net.corda.core.internal.CertRole
import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.cert
import net.corda.core.internal.read
import net.corda.core.internal.x500Name
import net.corda.core.utilities.days
import net.corda.core.utilities.millis
import org.bouncycastle.asn1.ASN1EncodableVector
import org.bouncycastle.asn1.ASN1Sequence
import org.bouncycastle.asn1.DERSequence
import org.bouncycastle.asn1.DERUTF8String
import org.bouncycastle.asn1.*
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.asn1.x500.style.BCStyle
import org.bouncycastle.asn1.x509.*
@ -221,6 +220,7 @@ object X509Utilities {
val serial = BigInteger.valueOf(random63BitValue())
val keyPurposes = DERSequence(ASN1EncodableVector().apply { certificateType.purposes.forEach { add(it) } })
val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(subjectPublicKey.encoded))
val role = certificateType.role
val builder = JcaX509v3CertificateBuilder(issuer, serial, validityWindow.first, validityWindow.second,
subject, subjectPublicKey)
@ -229,9 +229,13 @@ object X509Utilities {
.addExtension(Extension.keyUsage, false, certificateType.keyUsage)
.addExtension(Extension.extendedKeyUsage, false, keyPurposes)
if (role != null) {
builder.addExtension(ASN1ObjectIdentifier(CordaOID.X509_EXTENSION_CORDA_ROLE), false, role)
}
if (nameConstraints != null) {
builder.addExtension(Extension.nameConstraints, true, nameConstraints)
}
return builder
}
@ -322,13 +326,14 @@ class X509CertificateFactory {
}
}
enum class CertificateType(val keyUsage: KeyUsage, vararg val purposes: KeyPurposeId, val isCA: Boolean) {
enum class CertificateType(val keyUsage: KeyUsage, vararg val purposes: KeyPurposeId, val isCA: Boolean, val role: CertRole?) {
ROOT_CA(
KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyCertSign or KeyUsage.cRLSign),
KeyPurposeId.id_kp_serverAuth,
KeyPurposeId.id_kp_clientAuth,
KeyPurposeId.anyExtendedKeyUsage,
isCA = true
isCA = true,
role = null
),
INTERMEDIATE_CA(
@ -336,7 +341,26 @@ enum class CertificateType(val keyUsage: KeyUsage, vararg val purposes: KeyPurpo
KeyPurposeId.id_kp_serverAuth,
KeyPurposeId.id_kp_clientAuth,
KeyPurposeId.anyExtendedKeyUsage,
isCA = true
isCA = true,
role = CertRole.INTERMEDIATE_CA
),
NETWORK_MAP(
KeyUsage(KeyUsage.digitalSignature),
KeyPurposeId.id_kp_serverAuth,
KeyPurposeId.id_kp_clientAuth,
KeyPurposeId.anyExtendedKeyUsage,
isCA = false,
role = CertRole.NETWORK_MAP
),
SERVICE_IDENTITY(
KeyUsage(KeyUsage.digitalSignature),
KeyPurposeId.id_kp_serverAuth,
KeyPurposeId.id_kp_clientAuth,
KeyPurposeId.anyExtendedKeyUsage,
isCA = false,
role = CertRole.SERVICE_IDENTITY
),
NODE_CA(
@ -344,7 +368,8 @@ enum class CertificateType(val keyUsage: KeyUsage, vararg val purposes: KeyPurpo
KeyPurposeId.id_kp_serverAuth,
KeyPurposeId.id_kp_clientAuth,
KeyPurposeId.anyExtendedKeyUsage,
isCA = true
isCA = true,
role = CertRole.NODE_CA
),
TLS(
@ -352,24 +377,27 @@ enum class CertificateType(val keyUsage: KeyUsage, vararg val purposes: KeyPurpo
KeyPurposeId.id_kp_serverAuth,
KeyPurposeId.id_kp_clientAuth,
KeyPurposeId.anyExtendedKeyUsage,
isCA = false
isCA = false,
role = CertRole.TLS
),
// TODO: Identity certs should have only limited depth (i.e. 1) CA signing capability, with tight name constraints
WELL_KNOWN_IDENTITY(
// TODO: Identity certs should have tight name constraints on child certificates
LEGAL_IDENTITY(
KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyCertSign),
KeyPurposeId.id_kp_serverAuth,
KeyPurposeId.id_kp_clientAuth,
KeyPurposeId.anyExtendedKeyUsage,
isCA = true
isCA = true,
role = CertRole.LEGAL_IDENTITY
),
CONFIDENTIAL_IDENTITY(
CONFIDENTIAL_LEGAL_IDENTITY(
KeyUsage(KeyUsage.digitalSignature),
KeyPurposeId.id_kp_serverAuth,
KeyPurposeId.id_kp_clientAuth,
KeyPurposeId.anyExtendedKeyUsage,
isCA = false
isCA = false,
role = CertRole.CONFIDENTIAL_LEGAL_IDENTITY
)
}