mirror of
https://github.com/corda/corda.git
synced 2025-06-07 09:51:45 +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.days
|
||||||
import net.corda.core.utilities.millis
|
import net.corda.core.utilities.millis
|
||||||
import org.bouncycastle.asn1.*
|
import org.bouncycastle.asn1.*
|
||||||
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
import org.bouncycastle.asn1.x500.style.BCStyle
|
import org.bouncycastle.asn1.x500.style.BCStyle
|
||||||
import org.bouncycastle.asn1.x509.*
|
import org.bouncycastle.asn1.x509.*
|
||||||
import org.bouncycastle.asn1.x509.Extension
|
import org.bouncycastle.asn1.x509.Extension
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
import org.bouncycastle.cert.X509CertificateHolder
|
||||||
import org.bouncycastle.cert.X509v3CertificateBuilder
|
import org.bouncycastle.cert.X509v3CertificateBuilder
|
||||||
import org.bouncycastle.cert.bc.BcX509ExtensionUtils
|
import org.bouncycastle.cert.bc.BcX509ExtensionUtils
|
||||||
|
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils
|
||||||
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder
|
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder
|
||||||
import org.bouncycastle.openssl.jcajce.JcaPEMWriter
|
import org.bouncycastle.openssl.jcajce.JcaPEMWriter
|
||||||
import org.bouncycastle.operator.ContentSigner
|
import org.bouncycastle.operator.ContentSigner
|
||||||
@ -149,18 +151,25 @@ $trustedRoot""", e, certPath, e.index)
|
|||||||
/**
|
/**
|
||||||
* Build a partial X.509 certificate ready for signing.
|
* Build a partial X.509 certificate ready for signing.
|
||||||
*
|
*
|
||||||
|
* @param certificateType type of the certificate.
|
||||||
* @param issuer name of the issuing entity.
|
* @param issuer name of the issuing entity.
|
||||||
|
* @param issuerPublicKey public key of the issuing entity.
|
||||||
* @param subject name of the certificate subject.
|
* @param subject name of the certificate subject.
|
||||||
* @param subjectPublicKey public key of the certificate subject.
|
* @param subjectPublicKey public key of the certificate subject.
|
||||||
* @param validityWindow the time period the certificate is valid for.
|
* @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 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,
|
issuer: X500Principal,
|
||||||
|
issuerPublicKey: PublicKey,
|
||||||
subject: X500Principal,
|
subject: X500Principal,
|
||||||
subjectPublicKey: PublicKey,
|
subjectPublicKey: PublicKey,
|
||||||
validityWindow: Pair<Date, Date>,
|
validityWindow: Pair<Date, Date>,
|
||||||
nameConstraints: NameConstraints? = null): X509v3CertificateBuilder {
|
nameConstraints: NameConstraints? = null,
|
||||||
|
crlDistPoint: String? = null,
|
||||||
|
crlIssuer: X500Name? = null): X509v3CertificateBuilder {
|
||||||
val serial = BigInteger.valueOf(random63BitValue())
|
val serial = BigInteger.valueOf(random63BitValue())
|
||||||
val keyPurposes = DERSequence(ASN1EncodableVector().apply { certificateType.purposes.forEach { add(it) } })
|
val keyPurposes = DERSequence(ASN1EncodableVector().apply { certificateType.purposes.forEach { add(it) } })
|
||||||
val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(subjectPublicKey.encoded))
|
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.basicConstraints, true, BasicConstraints(certificateType.isCA))
|
||||||
.addExtension(Extension.keyUsage, false, certificateType.keyUsage)
|
.addExtension(Extension.keyUsage, false, certificateType.keyUsage)
|
||||||
.addExtension(Extension.extendedKeyUsage, false, keyPurposes)
|
.addExtension(Extension.extendedKeyUsage, false, keyPurposes)
|
||||||
|
.addExtension(Extension.authorityKeyIdentifier, false, JcaX509ExtensionUtils().createAuthorityKeyIdentifier(issuerPublicKey))
|
||||||
|
|
||||||
if (role != null) {
|
if (role != null) {
|
||||||
builder.addExtension(ASN1ObjectIdentifier(CordaOID.X509_EXTENSION_CORDA_ROLE), false, role)
|
builder.addExtension(ASN1ObjectIdentifier(CordaOID.X509_EXTENSION_CORDA_ROLE), false, role)
|
||||||
}
|
}
|
||||||
|
addCrlInfo(builder, crlDistPoint, crlIssuer)
|
||||||
if (nameConstraints != null) {
|
if (nameConstraints != null) {
|
||||||
builder.addExtension(Extension.nameConstraints, true, nameConstraints)
|
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.
|
* 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 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 issuerKeyPair The KeyPair of the root CA above this used to sign it.
|
||||||
* @param subject subject of the generated certificate.
|
* @param subject subject of the generated certificate.
|
||||||
* @param subjectPublicKey subject's public key.
|
* @param subjectPublicKey subject's public key.
|
||||||
* @param validityWindow The certificate's validity window. Default to [DEFAULT_VALIDITY_WINDOW] if not provided.
|
* @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.
|
* @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.
|
* 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,
|
subject: X500Principal,
|
||||||
subjectPublicKey: PublicKey,
|
subjectPublicKey: PublicKey,
|
||||||
validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW,
|
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)
|
val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second, issuerCertificate)
|
||||||
return createCertificate(
|
return createCertificate(
|
||||||
certificateType,
|
certificateType,
|
||||||
@ -209,28 +226,36 @@ $trustedRoot""", e, certPath, e.index)
|
|||||||
subject,
|
subject,
|
||||||
subjectPublicKey,
|
subjectPublicKey,
|
||||||
window,
|
window,
|
||||||
nameConstraints
|
nameConstraints,
|
||||||
|
crlDistPoint,
|
||||||
|
crlIssuer
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build and sign an X.509 certificate with the given signer.
|
* 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 issuer name of the issuing entity.
|
||||||
* @param issuerSigner content signer to sign the certificate with.
|
* @param issuerSigner content signer to sign the certificate with.
|
||||||
* @param subject name of the certificate subject.
|
* @param subject name of the certificate subject.
|
||||||
* @param subjectPublicKey public key of the certificate subject.
|
* @param subjectPublicKey public key of the certificate subject.
|
||||||
* @param validityWindow the time period the certificate is valid for.
|
* @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 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,
|
fun createCertificate(certificateType: CertificateType,
|
||||||
issuer: X500Principal,
|
issuer: X500Principal,
|
||||||
|
issuerPublicKey: PublicKey,
|
||||||
issuerSigner: ContentSigner,
|
issuerSigner: ContentSigner,
|
||||||
subject: X500Principal,
|
subject: X500Principal,
|
||||||
subjectPublicKey: PublicKey,
|
subjectPublicKey: PublicKey,
|
||||||
validityWindow: Pair<Date, Date>,
|
validityWindow: Pair<Date, Date>,
|
||||||
nameConstraints: NameConstraints? = null): X509Certificate {
|
nameConstraints: NameConstraints? = null,
|
||||||
val builder = createPartialCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints)
|
crlDistPoint: String? = null,
|
||||||
|
crlIssuer: X500Name? = null): X509Certificate {
|
||||||
|
val builder = createPartialCertificate(certificateType, issuer, issuerPublicKey, subject, subjectPublicKey, validityWindow, nameConstraints, crlDistPoint, crlIssuer)
|
||||||
return builder.build(issuerSigner).run {
|
return builder.build(issuerSigner).run {
|
||||||
require(isValidOn(Date()))
|
require(isValidOn(Date()))
|
||||||
toJca()
|
toJca()
|
||||||
@ -240,6 +265,7 @@ $trustedRoot""", e, certPath, e.index)
|
|||||||
/**
|
/**
|
||||||
* Build and sign an X.509 certificate with CA cert private key.
|
* 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 issuer name of the issuing entity.
|
||||||
* @param issuerKeyPair the public & private key to sign the certificate with.
|
* @param issuerKeyPair the public & private key to sign the certificate with.
|
||||||
* @param subject name of the certificate subject.
|
* @param subject name of the certificate subject.
|
||||||
@ -253,11 +279,21 @@ $trustedRoot""", e, certPath, e.index)
|
|||||||
subject: X500Principal,
|
subject: X500Principal,
|
||||||
subjectPublicKey: PublicKey,
|
subjectPublicKey: PublicKey,
|
||||||
validityWindow: Pair<Date, Date>,
|
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 signatureScheme = Crypto.findSignatureScheme(issuerKeyPair.private)
|
||||||
val provider = Crypto.findProvider(signatureScheme.providerName)
|
val provider = Crypto.findProvider(signatureScheme.providerName)
|
||||||
val signer = ContentSignerBuilder.build(signatureScheme, issuerKeyPair.private, provider)
|
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 {
|
return builder.build(signer).run {
|
||||||
require(isValidOn(Date()))
|
require(isValidOn(Date()))
|
||||||
require(isSignatureValid(JcaContentVerifierProviderBuilder().build(issuerKeyPair.public)))
|
require(isSignatureValid(JcaContentVerifierProviderBuilder().build(issuerKeyPair.public)))
|
||||||
@ -302,6 +338,21 @@ $trustedRoot""", e, certPath, e.index)
|
|||||||
fun buildCertPath(certificates: List<X509Certificate>): CertPath {
|
fun buildCertPath(certificates: List<X509Certificate>): CertPath {
|
||||||
return X509CertificateFactory().generateCertPath(certificates)
|
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
|
// 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.core.TestIdentity
|
||||||
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.bouncycastle.asn1.x509.BasicConstraints
|
import org.bouncycastle.asn1.x509.*
|
||||||
import org.bouncycastle.asn1.x509.Extension
|
|
||||||
import org.bouncycastle.asn1.x509.KeyUsage
|
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.rules.TemporaryFolder
|
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
|
@Test
|
||||||
fun `storing EdDSA key in java keystore`() {
|
fun `storing EdDSA key in java keystore`() {
|
||||||
val tmpKeyStore = tempFile("keystore.jks")
|
val tmpKeyStore = tempFile("keystore.jks")
|
||||||
|
@ -40,6 +40,7 @@ fun freshCertificate(identityService: IdentityService,
|
|||||||
val ourCertificate = X509Utilities.createCertificate(
|
val ourCertificate = X509Utilities.createCertificate(
|
||||||
CertificateType.CONFIDENTIAL_LEGAL_IDENTITY,
|
CertificateType.CONFIDENTIAL_LEGAL_IDENTITY,
|
||||||
issuerCert.subjectX500Principal,
|
issuerCert.subjectX500Principal,
|
||||||
|
issuerCert.publicKey,
|
||||||
issuerSigner,
|
issuerSigner,
|
||||||
issuer.name.x500Principal,
|
issuer.name.x500Principal,
|
||||||
subjectPublicKey,
|
subjectPublicKey,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user