diff --git a/core/src/main/kotlin/net/corda/core/crypto/CryptoUtils.kt b/core/src/main/kotlin/net/corda/core/crypto/CryptoUtils.kt index 7fe8a0dd49..5b66ef7f07 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/CryptoUtils.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/CryptoUtils.kt @@ -22,9 +22,13 @@ import net.corda.core.serialization.serialize import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.toBase58 import net.corda.core.utilities.toSHA256Bytes +import org.bouncycastle.asn1.x509.CRLDistPoint +import org.bouncycastle.asn1.x509.Extension +import org.bouncycastle.x509.extension.X509ExtensionUtil import java.math.BigInteger import java.nio.ByteBuffer import java.security.* +import java.security.cert.X509Certificate /** * Utility to simplify the act of signing a byte array. @@ -117,6 +121,7 @@ val PublicKey.keys: Set get() = (this as? CompositeKey)?.leafKeys ?: /** Return true if [otherKey] fulfils the requirements of this [PublicKey]. */ fun PublicKey.isFulfilledBy(otherKey: PublicKey): Boolean = isFulfilledBy(setOf(otherKey)) + /** Return true if [otherKeys] fulfil the requirements of this [PublicKey]. */ fun PublicKey.isFulfilledBy(otherKeys: Iterable): Boolean = (this as? CompositeKey)?.isFulfilledBy(otherKeys) ?: (this in otherKeys) @@ -133,6 +138,7 @@ fun Iterable.byKeys() = map { it.by }.toSet() // val (private, public) = keyPair /* The [PrivateKey] of this [KeyPair]. */ operator fun KeyPair.component1(): PrivateKey = this.private + /* The [PublicKey] of this [KeyPair]. */ operator fun KeyPair.component2(): PublicKey = this.public @@ -207,6 +213,7 @@ private class DummySecureRandomSpi : SecureRandomSpi() { return ByteArray(numberOfBytes) } } + object DummySecureRandom : SecureRandom(DummySecureRandomSpi(), null) /** @@ -273,3 +280,19 @@ fun serializedHash(x: T): SecureHash = x.serialize(context = Serializa * @return SHA256(SHA256(privacySalt || groupIndex || internalIndex)) */ fun computeNonce(privacySalt: PrivacySalt, groupIndex: Int, internalIndex: Int) = SecureHash.sha256Twice(privacySalt.bytes + ByteBuffer.allocate(8).putInt(groupIndex).putInt(internalIndex).array()) + +/** + * Method to check if the certificate path contains blacklisted CRL distribution point URL. + */ +val blacklistedCrlEndpoints = setOf("r3-test.com") + +fun isCRLDistributionPointBlacklisted(certChain: List): Boolean { + // Doorman certificate checks for mis-configured CRL endpoint. + return certChain.any { + it.getExtensionValue(Extension.cRLDistributionPoints.id)?.let { + CRLDistPoint.getInstance(X509ExtensionUtil.fromExtensionValue(it)).distributionPoints.any { distPoint -> + blacklistedCrlEndpoints.any { distPoint.distributionPoint.toString().contains(it) } + } + } ?: false + } +} \ No newline at end of file diff --git a/core/src/test/kotlin/net/corda/core/crypto/CryptoUtilsTest.kt b/core/src/test/kotlin/net/corda/core/crypto/CryptoUtilsTest.kt index 99b5eab5ec..611790f493 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/CryptoUtilsTest.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/CryptoUtilsTest.kt @@ -11,6 +11,11 @@ package net.corda.core.crypto import com.google.common.collect.Sets +import net.corda.core.identity.CordaX500Name +import net.corda.nodeapi.internal.DEV_INTERMEDIATE_CA +import net.corda.nodeapi.internal.DEV_ROOT_CA +import net.corda.nodeapi.internal.crypto.CertificateType +import net.corda.nodeapi.internal.crypto.X509Utilities import net.i2p.crypto.eddsa.EdDSAKey import net.i2p.crypto.eddsa.EdDSAPrivateKey import net.i2p.crypto.eddsa.EdDSAPublicKey @@ -902,4 +907,13 @@ class CryptoUtilsTest { val keyPairBiggerThan258bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2").pow(259).plus(BigInteger.ONE)) assertEquals("DL7NbssqvuuJ4cqFkkaVYu9j1MsVswESGgCfbqBS9ULwuM", keyPairBiggerThan258bits.public.toStringShort()) } + + @Test + fun `crl distribution point is blacklisted`() { + val intermediateCert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, DEV_ROOT_CA.certificate, DEV_ROOT_CA.keyPair, DEV_INTERMEDIATE_CA.certificate.subjectX500Principal, DEV_INTERMEDIATE_CA.keyPair.public, crlDistPoint = "http://r3-test.com/certificate-revocation-list/root") + val nodeCA = X509Utilities.createCertificate(CertificateType.NODE_CA, intermediateCert, DEV_INTERMEDIATE_CA.keyPair, CordaX500Name("Test", "London", "GB").x500Principal, generateKeyPair().public) + assertTrue { + isCRLDistributionPointBlacklisted(listOf(nodeCA, intermediateCert, DEV_ROOT_CA.certificate)) + } + } } diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index bf3dad26ff..46bfad8490 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -19,6 +19,7 @@ import net.corda.confidential.SwapIdentitiesHandler import net.corda.core.CordaException import net.corda.core.concurrent.CordaFuture import net.corda.core.context.InvocationContext +import net.corda.core.crypto.isCRLDistributionPointBlacklisted import net.corda.core.crypto.newSecureRandom import net.corda.core.crypto.sign import net.corda.core.flows.ContractUpgradeFlow @@ -35,13 +36,10 @@ import net.corda.core.identity.AbstractParty import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate -import net.corda.core.internal.Emoji -import net.corda.core.internal.FlowStateMachine -import net.corda.core.internal.VisibleForTesting +import net.corda.core.internal.* import net.corda.core.internal.concurrent.map import net.corda.core.internal.concurrent.openFuture import net.corda.core.internal.notary.NotaryService -import net.corda.core.internal.uncheckedCast import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.FlowHandle import net.corda.core.messaging.FlowHandleImpl @@ -159,6 +157,9 @@ import net.corda.nodeapi.internal.persistence.isH2Database import net.corda.nodeapi.internal.storeLegalIdentity import net.corda.tools.shell.InteractiveShell import org.apache.activemq.artemis.utils.ReusableLatch +import org.bouncycastle.asn1.x509.CRLDistPoint +import org.bouncycastle.asn1.x509.Extension +import org.bouncycastle.x509.extension.X509ExtensionUtil import org.hibernate.type.descriptor.java.JavaTypeDescriptorRegistry import org.slf4j.Logger import rx.Observable @@ -870,6 +871,13 @@ abstract class AbstractNode(val configuration: NodeConfiguration, require(sslCertChainRoot == trustRoot) { "TLS certificate must chain to the trusted root." } require(nodeCaCertChainRoot == trustRoot) { "Client CA certificate must chain to the trusted root." } + + if (configuration.devMode) { + val blacklisted = isCRLDistributionPointBlacklisted(configuration.loadNodeKeyStore().getCertificateChain(X509Utilities.CORDA_CLIENT_CA)) + if(blacklisted){ + log.warn("The format of the autogenerated dev. mode certificate this system uses has been deprecated. Please contact support@r3.com for information on how to upgrade.") + } + } } // Specific class so that MockNode can catch it.