mirror of
https://github.com/corda/corda.git
synced 2024-12-20 05:28:21 +00:00
Adding CRL support in the generated certificates. (#2932)
This commit is contained in:
parent
53a0aae489
commit
c8b58a601f
@ -8,12 +8,14 @@ import net.corda.core.internal.*
|
||||
import net.corda.core.utilities.days
|
||||
import net.corda.core.utilities.millis
|
||||
import org.bouncycastle.asn1.*
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import org.bouncycastle.asn1.x500.style.BCStyle
|
||||
import org.bouncycastle.asn1.x509.*
|
||||
import org.bouncycastle.asn1.x509.Extension
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import org.bouncycastle.cert.X509v3CertificateBuilder
|
||||
import org.bouncycastle.cert.bc.BcX509ExtensionUtils
|
||||
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils
|
||||
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder
|
||||
import org.bouncycastle.openssl.jcajce.JcaPEMWriter
|
||||
import org.bouncycastle.operator.ContentSigner
|
||||
@ -149,18 +151,25 @@ $trustedRoot""", e, certPath, e.index)
|
||||
/**
|
||||
* Build a partial X.509 certificate ready for signing.
|
||||
*
|
||||
* @param certificateType type of the certificate.
|
||||
* @param issuer name of the issuing entity.
|
||||
* @param issuerPublicKey public key of the issuing entity.
|
||||
* @param subject name of the certificate subject.
|
||||
* @param subjectPublicKey public key of the certificate subject.
|
||||
* @param validityWindow the time period the certificate is valid for.
|
||||
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
|
||||
* @param crlDistPoint CRL distribution point.
|
||||
* @param crlIssuer X500Name of the CRL issuer.
|
||||
*/
|
||||
fun createPartialCertificate(certificateType: CertificateType,
|
||||
private fun createPartialCertificate(certificateType: CertificateType,
|
||||
issuer: X500Principal,
|
||||
issuerPublicKey: PublicKey,
|
||||
subject: X500Principal,
|
||||
subjectPublicKey: PublicKey,
|
||||
validityWindow: Pair<Date, Date>,
|
||||
nameConstraints: NameConstraints? = null): X509v3CertificateBuilder {
|
||||
nameConstraints: NameConstraints? = null,
|
||||
crlDistPoint: String? = null,
|
||||
crlIssuer: X500Name? = null): X509v3CertificateBuilder {
|
||||
val serial = BigInteger.valueOf(random63BitValue())
|
||||
val keyPurposes = DERSequence(ASN1EncodableVector().apply { certificateType.purposes.forEach { add(it) } })
|
||||
val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(subjectPublicKey.encoded))
|
||||
@ -171,10 +180,12 @@ $trustedRoot""", e, certPath, e.index)
|
||||
.addExtension(Extension.basicConstraints, true, BasicConstraints(certificateType.isCA))
|
||||
.addExtension(Extension.keyUsage, false, certificateType.keyUsage)
|
||||
.addExtension(Extension.extendedKeyUsage, false, keyPurposes)
|
||||
.addExtension(Extension.authorityKeyIdentifier, false, JcaX509ExtensionUtils().createAuthorityKeyIdentifier(issuerPublicKey))
|
||||
|
||||
if (role != null) {
|
||||
builder.addExtension(ASN1ObjectIdentifier(CordaOID.X509_EXTENSION_CORDA_ROLE), false, role)
|
||||
}
|
||||
addCrlInfo(builder, crlDistPoint, crlIssuer)
|
||||
if (nameConstraints != null) {
|
||||
builder.addExtension(Extension.nameConstraints, true, nameConstraints)
|
||||
}
|
||||
@ -185,11 +196,15 @@ $trustedRoot""", e, certPath, e.index)
|
||||
/**
|
||||
* Create a X509 v3 certificate using the given issuer certificate and key pair.
|
||||
*
|
||||
* @param certificateType type of the certificate.
|
||||
* @param issuerCertificate The Public certificate of the root CA above this used to sign it.
|
||||
* @param issuerKeyPair The KeyPair of the root CA above this used to sign it.
|
||||
* @param subject subject of the generated certificate.
|
||||
* @param subjectPublicKey subject's public key.
|
||||
* @param validityWindow The certificate's validity window. Default to [DEFAULT_VALIDITY_WINDOW] if not provided.
|
||||
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
|
||||
* @param crlDistPoint CRL distribution point.
|
||||
* @param crlIssuer X500Name of the CRL issuer.
|
||||
* @return A data class is returned containing the new intermediate CA Cert and its KeyPair for signing downstream certificates.
|
||||
* Note the generated certificate tree is capped at max depth of 1 below this to be in line with commercially available certificates.
|
||||
*/
|
||||
@ -200,7 +215,9 @@ $trustedRoot""", e, certPath, e.index)
|
||||
subject: X500Principal,
|
||||
subjectPublicKey: PublicKey,
|
||||
validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW,
|
||||
nameConstraints: NameConstraints? = null): X509Certificate {
|
||||
nameConstraints: NameConstraints? = null,
|
||||
crlDistPoint: String? = null,
|
||||
crlIssuer: X500Name? = null): X509Certificate {
|
||||
val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second, issuerCertificate)
|
||||
return createCertificate(
|
||||
certificateType,
|
||||
@ -209,28 +226,36 @@ $trustedRoot""", e, certPath, e.index)
|
||||
subject,
|
||||
subjectPublicKey,
|
||||
window,
|
||||
nameConstraints
|
||||
nameConstraints,
|
||||
crlDistPoint,
|
||||
crlIssuer
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Build and sign an X.509 certificate with the given signer.
|
||||
*
|
||||
* @param certificateType type of the certificate.
|
||||
* @param issuer name of the issuing entity.
|
||||
* @param issuerSigner content signer to sign the certificate with.
|
||||
* @param subject name of the certificate subject.
|
||||
* @param subjectPublicKey public key of the certificate subject.
|
||||
* @param validityWindow the time period the certificate is valid for.
|
||||
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
|
||||
* @param crlDistPoint CRL distribution point.
|
||||
* @param crlIssuer X500Name of the CRL issuer.
|
||||
*/
|
||||
fun createCertificate(certificateType: CertificateType,
|
||||
issuer: X500Principal,
|
||||
issuerPublicKey: PublicKey,
|
||||
issuerSigner: ContentSigner,
|
||||
subject: X500Principal,
|
||||
subjectPublicKey: PublicKey,
|
||||
validityWindow: Pair<Date, Date>,
|
||||
nameConstraints: NameConstraints? = null): X509Certificate {
|
||||
val builder = createPartialCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints)
|
||||
nameConstraints: NameConstraints? = null,
|
||||
crlDistPoint: String? = null,
|
||||
crlIssuer: X500Name? = null): X509Certificate {
|
||||
val builder = createPartialCertificate(certificateType, issuer, issuerPublicKey, subject, subjectPublicKey, validityWindow, nameConstraints, crlDistPoint, crlIssuer)
|
||||
return builder.build(issuerSigner).run {
|
||||
require(isValidOn(Date()))
|
||||
toJca()
|
||||
@ -240,6 +265,7 @@ $trustedRoot""", e, certPath, e.index)
|
||||
/**
|
||||
* Build and sign an X.509 certificate with CA cert private key.
|
||||
*
|
||||
* @param certificateType type of the certificate.
|
||||
* @param issuer name of the issuing entity.
|
||||
* @param issuerKeyPair the public & private key to sign the certificate with.
|
||||
* @param subject name of the certificate subject.
|
||||
@ -253,11 +279,21 @@ $trustedRoot""", e, certPath, e.index)
|
||||
subject: X500Principal,
|
||||
subjectPublicKey: PublicKey,
|
||||
validityWindow: Pair<Date, Date>,
|
||||
nameConstraints: NameConstraints? = null): X509Certificate {
|
||||
nameConstraints: NameConstraints? = null,
|
||||
crlDistPoint: String? = null,
|
||||
crlIssuer: X500Name? = null): X509Certificate {
|
||||
val signatureScheme = Crypto.findSignatureScheme(issuerKeyPair.private)
|
||||
val provider = Crypto.findProvider(signatureScheme.providerName)
|
||||
val signer = ContentSignerBuilder.build(signatureScheme, issuerKeyPair.private, provider)
|
||||
val builder = createPartialCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints)
|
||||
val builder = createPartialCertificate(
|
||||
certificateType,
|
||||
issuer,
|
||||
issuerKeyPair.public,
|
||||
subject, subjectPublicKey,
|
||||
validityWindow,
|
||||
nameConstraints,
|
||||
crlDistPoint,
|
||||
crlIssuer)
|
||||
return builder.build(signer).run {
|
||||
require(isValidOn(Date()))
|
||||
require(isSignatureValid(JcaContentVerifierProviderBuilder().build(issuerKeyPair.public)))
|
||||
@ -302,6 +338,21 @@ $trustedRoot""", e, certPath, e.index)
|
||||
fun buildCertPath(certificates: List<X509Certificate>): CertPath {
|
||||
return X509CertificateFactory().generateCertPath(certificates)
|
||||
}
|
||||
|
||||
private fun addCrlInfo(builder: X509v3CertificateBuilder, crlDistPoint: String?, crlIssuer: X500Name?) {
|
||||
if (crlDistPoint != null) {
|
||||
val distPointName = DistributionPointName(GeneralNames(GeneralName(GeneralName.uniformResourceIdentifier, crlDistPoint)))
|
||||
val crlIssuerGeneralNames = crlIssuer?.let {
|
||||
GeneralNames(GeneralName(crlIssuer))
|
||||
}
|
||||
// The second argument is flag that allows you to define what reason of certificate revocation is served by this distribution point see [ReasonFlags].
|
||||
// The idea is that you have different revocation per revocation reason. Since we won't go into such a granularity, we can skip that parameter.
|
||||
// The third argument allows you to specify the name of the CRL issuer, it needs to be consistent with the crl (IssuingDistributionPoint) extension and the idp argument.
|
||||
// If idp == true, set it, if idp == false, leave it null as done here.
|
||||
val distPoint = DistributionPoint(distPointName, null, crlIssuerGeneralNames)
|
||||
builder.addExtension(Extension.cRLDistributionPoints, false, CRLDistPoint(arrayOf(distPoint)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Assuming cert type to role is 1:1
|
||||
|
@ -20,9 +20,7 @@ import net.corda.testing.core.BOB_NAME
|
||||
import net.corda.testing.core.TestIdentity
|
||||
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.bouncycastle.asn1.x509.BasicConstraints
|
||||
import org.bouncycastle.asn1.x509.Extension
|
||||
import org.bouncycastle.asn1.x509.KeyUsage
|
||||
import org.bouncycastle.asn1.x509.*
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
@ -103,6 +101,28 @@ class X509UtilitiesTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `create valid server certificate chain includes CRL info`() {
|
||||
val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val caCert = X509Utilities.createSelfSignedCACertificate(X500Principal("CN=Test CA Cert,O=R3 Ltd,L=London,C=GB"), caKey)
|
||||
val caSubjectKeyIdentifier = SubjectKeyIdentifier.getInstance(caCert.toBc().getExtension(Extension.subjectKeyIdentifier).parsedValue)
|
||||
val keyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val crlDistPoint = "http://test.com"
|
||||
val serverCert = X509Utilities.createCertificate(
|
||||
CertificateType.TLS,
|
||||
caCert,
|
||||
caKey,
|
||||
X500Principal("CN=Server Cert,O=R3 Ltd,L=London,C=GB"),
|
||||
keyPair.public,
|
||||
crlDistPoint = crlDistPoint)
|
||||
serverCert.toBc().run {
|
||||
val certCrlDistPoint = CRLDistPoint.getInstance(getExtension(Extension.cRLDistributionPoints).parsedValue)
|
||||
assertTrue(certCrlDistPoint.distributionPoints.first().distributionPoint.toString().contains(crlDistPoint))
|
||||
val certCaAuthorityKeyIdentifier = AuthorityKeyIdentifier.getInstance(getExtension(Extension.authorityKeyIdentifier).parsedValue)
|
||||
assertTrue(Arrays.equals(caSubjectKeyIdentifier.keyIdentifier, certCaAuthorityKeyIdentifier.keyIdentifier))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `storing EdDSA key in java keystore`() {
|
||||
val tmpKeyStore = tempFile("keystore.jks")
|
||||
|
@ -40,6 +40,7 @@ fun freshCertificate(identityService: IdentityService,
|
||||
val ourCertificate = X509Utilities.createCertificate(
|
||||
CertificateType.CONFIDENTIAL_LEGAL_IDENTITY,
|
||||
issuerCert.subjectX500Principal,
|
||||
issuerCert.publicKey,
|
||||
issuerSigner,
|
||||
issuer.name.x500Principal,
|
||||
subjectPublicKey,
|
||||
|
Loading…
Reference in New Issue
Block a user