From e92ad538cfcc06324c2d6d27654e0ccabb2a8ef9 Mon Sep 17 00:00:00 2001 From: Konstantinos Chalkias Date: Thu, 27 Sep 2018 15:21:12 +0100 Subject: [PATCH] CORDA-2031 put "AlgorithmParameters.SHA256WITHECDSA" to BC (#3997) --- .../corda/core/crypto/internal/ProviderMap.kt | 3 + .../CertificateRevocationListNodeTests.kt | 99 +++++++++++++------ 2 files changed, 73 insertions(+), 29 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/crypto/internal/ProviderMap.kt b/core/src/main/kotlin/net/corda/core/crypto/internal/ProviderMap.kt index 3a99cb4436..03c4a01d72 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/internal/ProviderMap.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/internal/ProviderMap.kt @@ -11,6 +11,7 @@ import net.i2p.crypto.eddsa.EdDSASecurityProvider import org.bouncycastle.asn1.ASN1ObjectIdentifier import org.bouncycastle.asn1.pkcs.PrivateKeyInfo import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo +import org.bouncycastle.jcajce.provider.asymmetric.ec.AlgorithmParametersSpi import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter import org.bouncycastle.jce.provider.BouncyCastleProvider import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider @@ -30,6 +31,8 @@ internal val cordaBouncyCastleProvider = BouncyCastleProvider().apply { override fun generatePublic(keyInfo: SubjectPublicKeyInfo) = decodePublicKey(EDDSA_ED25519_SHA512, keyInfo.encoded) override fun generatePrivate(keyInfo: PrivateKeyInfo) = decodePrivateKey(EDDSA_ED25519_SHA512, keyInfo.encoded) }) + // Required due to [X509CRL].verify() reported issues in network-services after BC 1.60 update. + put("AlgorithmParameters.SHA256WITHECDSA", AlgorithmParametersSpi::class.java.name) }.also { // This registration is needed for reading back EdDSA key from java keystore. // TODO: Find a way to make JKS work with bouncy castle provider or implement our own provide so we don't have to register bouncy castle provider. diff --git a/node/src/integration-test/kotlin/net/corda/node/amqp/CertificateRevocationListNodeTests.kt b/node/src/integration-test/kotlin/net/corda/node/amqp/CertificateRevocationListNodeTests.kt index d2ef1dbe5c..66682cffa4 100644 --- a/node/src/integration-test/kotlin/net/corda/node/amqp/CertificateRevocationListNodeTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/amqp/CertificateRevocationListNodeTests.kt @@ -25,10 +25,10 @@ import net.corda.testing.core.BOB_NAME import net.corda.testing.core.CHARLIE_NAME import net.corda.testing.core.MAX_MESSAGE_SIZE import net.corda.testing.driver.PortAllocation -import net.corda.testing.internal.stubs.CertificateStoreStubs import net.corda.testing.internal.DEV_INTERMEDIATE_CA import net.corda.testing.internal.DEV_ROOT_CA import net.corda.testing.internal.rigorousMock +import net.corda.testing.internal.stubs.CertificateStoreStubs import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x509.* import org.bouncycastle.cert.jcajce.JcaX509CRLConverter @@ -62,6 +62,7 @@ import javax.ws.rs.Path import javax.ws.rs.Produces import javax.ws.rs.core.Response import kotlin.test.assertEquals +import kotlin.test.assertFailsWith class CertificateRevocationListNodeTests { @Rule @@ -81,6 +82,32 @@ class CertificateRevocationListNodeTests { private abstract class AbstractNodeConfiguration : NodeConfiguration + companion object { + fun createRevocationList(clrServer: CrlServer, signatureAlgorithm: String, caCertificate: X509Certificate, + caPrivateKey: PrivateKey, + endpoint: String, + indirect: Boolean, + vararg serialNumbers: BigInteger): X509CRL { + println("Generating CRL for $endpoint") + val builder = JcaX509v2CRLBuilder(caCertificate.subjectX500Principal, Date(System.currentTimeMillis() - 1.minutes.toMillis())) + val extensionUtils = JcaX509ExtensionUtils() + builder.addExtension(Extension.authorityKeyIdentifier, + false, extensionUtils.createAuthorityKeyIdentifier(caCertificate)) + val issuingDistPointName = GeneralName( + GeneralName.uniformResourceIdentifier, + "http://${clrServer.hostAndPort.host}:${clrServer.hostAndPort.port}/crl/$endpoint") + // This is required and needs to match the certificate settings with respect to being indirect + val issuingDistPoint = IssuingDistributionPoint(DistributionPointName(GeneralNames(issuingDistPointName)), indirect, false) + builder.addExtension(Extension.issuingDistributionPoint, true, issuingDistPoint) + builder.setNextUpdate(Date(System.currentTimeMillis() + 1.seconds.toMillis())) + serialNumbers.forEach { + builder.addCRLEntry(it, Date(System.currentTimeMillis() - 10.minutes.toMillis()), ReasonFlags.certificateHold) + } + val signer = JcaContentSignerBuilder(signatureAlgorithm).setProvider(Crypto.findProvider("BC")).build(caPrivateKey) + return JcaX509CRLConverter().setProvider(Crypto.findProvider("BC")).getCRL(builder.build(signer)) + } + } + @Before fun setUp() { Security.addProvider(BouncyCastleProvider()) @@ -455,12 +482,15 @@ class CertificateRevocationListNodeTests { @Path("node.crl") @Produces("application/pkcs7-crl") fun getNodeCRL(): Response { - return Response.ok(createRevocationList( + return Response.ok(CertificateRevocationListNodeTests.createRevocationList( + server, + SIGNATURE_ALGORITHM, INTERMEDIATE_CA.certificate, INTERMEDIATE_CA.keyPair.private, NODE_CRL, false, - *revokedNodeCerts.toTypedArray()).encoded).build() + *revokedNodeCerts.toTypedArray()).encoded) + .build() } @GET @@ -468,11 +498,14 @@ class CertificateRevocationListNodeTests { @Produces("application/pkcs7-crl") fun getIntermediateCRL(): Response { return Response.ok(createRevocationList( + server, + SIGNATURE_ALGORITHM, ROOT_CA.certificate, ROOT_CA.keyPair.private, INTEMEDIATE_CRL, false, - *revokedIntermediateCerts.toTypedArray()).encoded).build() + *revokedIntermediateCerts.toTypedArray()).encoded) + .build() } @GET @@ -480,33 +513,13 @@ class CertificateRevocationListNodeTests { @Produces("application/pkcs7-crl") fun getEmptyCRL(): Response { return Response.ok(createRevocationList( + server, + SIGNATURE_ALGORITHM, ROOT_CA.certificate, ROOT_CA.keyPair.private, - EMPTY_CRL, true).encoded).build() - } - - private fun createRevocationList(caCertificate: X509Certificate, - caPrivateKey: PrivateKey, - endpoint: String, - indirect: Boolean, - vararg serialNumbers: BigInteger): X509CRL { - println("Generating CRL for $endpoint") - val builder = JcaX509v2CRLBuilder(caCertificate.subjectX500Principal, Date(System.currentTimeMillis() - 1.minutes.toMillis())) - val extensionUtils = JcaX509ExtensionUtils() - builder.addExtension(Extension.authorityKeyIdentifier, - false, extensionUtils.createAuthorityKeyIdentifier(caCertificate)) - val issuingDistPointName = GeneralName( - GeneralName.uniformResourceIdentifier, - "http://${server.hostAndPort.host}:${server.hostAndPort.port}/crl/$endpoint") - // This is required and needs to match the certificate settings with respect to being indirect - val issuingDistPoint = IssuingDistributionPoint(DistributionPointName(GeneralNames(issuingDistPointName)), indirect, false) - builder.addExtension(Extension.issuingDistributionPoint, true, issuingDistPoint) - builder.setNextUpdate(Date(System.currentTimeMillis() + 1.seconds.toMillis())) - serialNumbers.forEach { - builder.addCRLEntry(it, Date(System.currentTimeMillis() - 10.minutes.toMillis()), ReasonFlags.certificateHold) - } - val signer = JcaContentSignerBuilder(SIGNATURE_ALGORITHM).setProvider(BouncyCastleProvider.PROVIDER_NAME).build(caPrivateKey) - return JcaX509CRLConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME).getCRL(builder.build(signer)) + EMPTY_CRL, + true).encoded) + .build() } } @@ -546,4 +559,32 @@ class CertificateRevocationListNodeTests { } } } + + @Test + fun `verify CRL algorithms`() { + val ECDSA_ALGORITHM = "SHA256withECDSA" + val EC_ALGORITHM = "EC" + val EMPTY_CRL = "empty.crl" + + val crl = createRevocationList( + server, + ECDSA_ALGORITHM, + ROOT_CA.certificate, + ROOT_CA.keyPair.private, + EMPTY_CRL, + true) + // This should pass. + crl.verify(ROOT_CA.keyPair.public) + + // Try changing the algorithm to EC will fail. + assertFailsWith("Unknown signature type requested: EC") { + createRevocationList( + server, + EC_ALGORITHM, + ROOT_CA.certificate, + ROOT_CA.keyPair.private, + EMPTY_CRL, + true) + } + } }