mirror of
https://github.com/corda/corda.git
synced 2025-06-13 04:38:19 +00:00
CORDA-1319 Adding CRL checking for nodes (#2987)
* Adding CRL support for nodes * Addressing review comments
This commit is contained in:
@ -35,6 +35,7 @@ object DevIdentityGenerator {
|
||||
override val baseDirectory = nodeDir
|
||||
override val keyStorePassword: String = "cordacadevpass"
|
||||
override val trustStorePassword get() = throw NotImplementedError("Not expected to be called")
|
||||
override val crlCheckSoftFail: Boolean = true
|
||||
}
|
||||
|
||||
nodeSslConfig.certificatesDirectory.createDirectories()
|
||||
|
@ -46,6 +46,7 @@ class AMQPBridgeManager(config: NodeSSLConfiguration, val artemisMessageClientFa
|
||||
private val keyStorePrivateKeyPassword: String = config.keyStorePassword
|
||||
private val trustStore = config.loadTrustStore().internal
|
||||
private var artemis: ArtemisSessionProvider? = null
|
||||
private val crlCheckSoftFail: Boolean = config.crlCheckSoftFail
|
||||
|
||||
constructor(config: NodeSSLConfiguration, p2pAddress: NetworkHostAndPort, maxMessageSize: Int) : this(config, { ArtemisMessagingClient(config, p2pAddress, maxMessageSize) })
|
||||
|
||||
@ -67,6 +68,7 @@ class AMQPBridgeManager(config: NodeSSLConfiguration, val artemisMessageClientFa
|
||||
keyStore: KeyStore,
|
||||
keyStorePrivateKeyPassword: String,
|
||||
trustStore: KeyStore,
|
||||
crlCheckSoftFail: Boolean,
|
||||
sharedEventGroup: EventLoopGroup,
|
||||
private val artemis: ArtemisSessionProvider) {
|
||||
companion object {
|
||||
@ -75,7 +77,7 @@ class AMQPBridgeManager(config: NodeSSLConfiguration, val artemisMessageClientFa
|
||||
|
||||
private val log = LoggerFactory.getLogger("$bridgeName:${legalNames.first()}")
|
||||
|
||||
val amqpClient = AMQPClient(listOf(target), legalNames, PEER_USER, PEER_USER, keyStore, keyStorePrivateKeyPassword, trustStore, sharedThreadPool = sharedEventGroup)
|
||||
val amqpClient = AMQPClient(listOf(target), legalNames, PEER_USER, PEER_USER, keyStore, keyStorePrivateKeyPassword, trustStore, crlCheckSoftFail, sharedThreadPool = sharedEventGroup)
|
||||
val bridgeName: String get() = getBridgeName(queueName, target)
|
||||
private val lock = ReentrantLock() // lock to serialise session level access
|
||||
private var session: ClientSession? = null
|
||||
@ -169,7 +171,7 @@ class AMQPBridgeManager(config: NodeSSLConfiguration, val artemisMessageClientFa
|
||||
if (bridgeExists(getBridgeName(queueName, target))) {
|
||||
return
|
||||
}
|
||||
val newBridge = AMQPBridge(queueName, target, legalNames, keyStore, keyStorePrivateKeyPassword, trustStore, sharedEventLoopGroup!!, artemis!!)
|
||||
val newBridge = AMQPBridge(queueName, target, legalNames, keyStore, keyStorePrivateKeyPassword, trustStore, crlCheckSoftFail, sharedEventLoopGroup!!, artemis!!)
|
||||
lock.withLock {
|
||||
bridgeNameToBridgeMap[newBridge.bridgeName] = newBridge
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ interface SSLConfiguration {
|
||||
// TODO This looks like it should be in NodeSSLConfiguration
|
||||
val nodeKeystore: Path get() = certificatesDirectory / "nodekeystore.jks"
|
||||
val trustStoreFile: Path get() = certificatesDirectory / "truststore.jks"
|
||||
val crlCheckSoftFail: Boolean
|
||||
|
||||
fun loadTrustStore(createNew: Boolean = false): X509KeyStore {
|
||||
return X509KeyStore.fromFile(trustStoreFile, trustStorePassword, createNew)
|
||||
|
@ -148,7 +148,7 @@ object X509Utilities {
|
||||
* @param crlDistPoint CRL distribution point.
|
||||
* @param crlIssuer X500Name of the CRL issuer.
|
||||
*/
|
||||
private fun createPartialCertificate(certificateType: CertificateType,
|
||||
fun createPartialCertificate(certificateType: CertificateType,
|
||||
issuer: X500Principal,
|
||||
issuerPublicKey: PublicKey,
|
||||
subject: X500Principal,
|
||||
|
@ -38,6 +38,7 @@ class AMQPClient(val targets: List<NetworkHostAndPort>,
|
||||
private val keyStore: KeyStore,
|
||||
private val keyStorePrivateKeyPassword: String,
|
||||
private val trustStore: KeyStore,
|
||||
private val crlCheckSoftFail: Boolean,
|
||||
private val trace: Boolean = false,
|
||||
private val sharedThreadPool: EventLoopGroup? = null) : AutoCloseable {
|
||||
companion object {
|
||||
@ -102,7 +103,7 @@ class AMQPClient(val targets: List<NetworkHostAndPort>,
|
||||
|
||||
init {
|
||||
keyManagerFactory.init(parent.keyStore, parent.keyStorePrivateKeyPassword.toCharArray())
|
||||
trustManagerFactory.init(parent.trustStore)
|
||||
trustManagerFactory.init(initialiseTrustStoreAndEnableCrlChecking(parent.trustStore, parent.crlCheckSoftFail))
|
||||
}
|
||||
|
||||
override fun initChannel(ch: SocketChannel) {
|
||||
@ -132,9 +133,7 @@ class AMQPClient(val targets: List<NetworkHostAndPort>,
|
||||
private fun restart() {
|
||||
val bootstrap = Bootstrap()
|
||||
// TODO Needs more configuration control when we profile. e.g. to use EPOLL on Linux
|
||||
bootstrap.group(workerGroup).
|
||||
channel(NioSocketChannel::class.java).
|
||||
handler(ClientChannelInitializer(this))
|
||||
bootstrap.group(workerGroup).channel(NioSocketChannel::class.java).handler(ClientChannelInitializer(this))
|
||||
currentTarget = targets[targetIndex]
|
||||
val clientFuture = bootstrap.connect(currentTarget.host, currentTarget.port)
|
||||
clientFuture.addListener(connectListener)
|
||||
|
@ -40,6 +40,7 @@ class AMQPServer(val hostName: String,
|
||||
private val keyStore: KeyStore,
|
||||
private val keyStorePrivateKeyPassword: CharArray,
|
||||
private val trustStore: KeyStore,
|
||||
private val crlCheckSoftFail: Boolean,
|
||||
private val trace: Boolean = false) : AutoCloseable {
|
||||
|
||||
companion object {
|
||||
@ -66,7 +67,8 @@ class AMQPServer(val hostName: String,
|
||||
keyStore: KeyStore,
|
||||
keyStorePrivateKeyPassword: String,
|
||||
trustStore: KeyStore,
|
||||
trace: Boolean = false) : this(hostName, port, userName, password, keyStore, keyStorePrivateKeyPassword.toCharArray(), trustStore, trace)
|
||||
crlCheckSoftFail: Boolean,
|
||||
trace: Boolean = false) : this(hostName, port, userName, password, keyStore, keyStorePrivateKeyPassword.toCharArray(), trustStore, crlCheckSoftFail, trace)
|
||||
|
||||
private class ServerChannelInitializer(val parent: AMQPServer) : ChannelInitializer<SocketChannel>() {
|
||||
private val keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
|
||||
@ -74,7 +76,7 @@ class AMQPServer(val hostName: String,
|
||||
|
||||
init {
|
||||
keyManagerFactory.init(parent.keyStore, parent.keyStorePrivateKeyPassword)
|
||||
trustManagerFactory.init(parent.trustStore)
|
||||
trustManagerFactory.init(initialiseTrustStoreAndEnableCrlChecking(parent.trustStore, parent.crlCheckSoftFail))
|
||||
}
|
||||
|
||||
override fun initChannel(ch: SocketChannel) {
|
||||
@ -108,11 +110,7 @@ class AMQPServer(val hostName: String,
|
||||
|
||||
val server = ServerBootstrap()
|
||||
// TODO Needs more configuration control when we profile. e.g. to use EPOLL on Linux
|
||||
server.group(bossGroup, workerGroup).
|
||||
channel(NioServerSocketChannel::class.java).
|
||||
option(ChannelOption.SO_BACKLOG, 100).
|
||||
handler(LoggingHandler(LogLevel.INFO)).
|
||||
childHandler(ServerChannelInitializer(this))
|
||||
server.group(bossGroup, workerGroup).channel(NioServerSocketChannel::class.java).option(ChannelOption.SO_BACKLOG, 100).handler(LoggingHandler(LogLevel.INFO)).childHandler(ServerChannelInitializer(this))
|
||||
|
||||
log.info("Try to bind $port")
|
||||
val channelFuture = server.bind(hostName, port).sync() // block/throw here as better to know we failed to claim port than carry on
|
||||
|
@ -3,10 +3,14 @@ package net.corda.nodeapi.internal.protonwrapper.netty
|
||||
import io.netty.handler.ssl.SslHandler
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.nodeapi.ArtemisTcpTransport
|
||||
import java.security.KeyStore
|
||||
import java.security.SecureRandom
|
||||
import javax.net.ssl.KeyManagerFactory
|
||||
import javax.net.ssl.SSLContext
|
||||
import javax.net.ssl.TrustManagerFactory
|
||||
import java.security.cert.CertPathBuilder
|
||||
import java.security.cert.PKIXBuilderParameters
|
||||
import java.security.cert.PKIXRevocationChecker
|
||||
import java.security.cert.X509CertSelector
|
||||
import java.util.*
|
||||
import javax.net.ssl.*
|
||||
|
||||
internal fun createClientSslHelper(target: NetworkHostAndPort,
|
||||
keyManagerFactory: KeyManagerFactory,
|
||||
@ -36,4 +40,22 @@ internal fun createServerSslHelper(keyManagerFactory: KeyManagerFactory,
|
||||
sslEngine.enabledCipherSuites = ArtemisTcpTransport.CIPHER_SUITES.toTypedArray()
|
||||
sslEngine.enableSessionCreation = true
|
||||
return SslHandler(sslEngine)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun initialiseTrustStoreAndEnableCrlChecking(trustStore: KeyStore, crlCheckSoftFail: Boolean): ManagerFactoryParameters {
|
||||
val certPathBuilder = CertPathBuilder.getInstance("PKIX")
|
||||
val revocationChecker = certPathBuilder.revocationChecker as PKIXRevocationChecker
|
||||
revocationChecker.options = EnumSet.of(
|
||||
// Prefer CRL over OCSP
|
||||
PKIXRevocationChecker.Option.PREFER_CRLS,
|
||||
// Don't fall back to OCSP checking
|
||||
PKIXRevocationChecker.Option.NO_FALLBACK)
|
||||
if (crlCheckSoftFail) {
|
||||
// Allow revocation check to succeed if the revocation status cannot be determined for one of
|
||||
// the following reasons: The CRL or OCSP response cannot be obtained because of a network error.
|
||||
revocationChecker.options = revocationChecker.options + PKIXRevocationChecker.Option.SOFT_FAIL
|
||||
}
|
||||
val pkixParams = PKIXBuilderParameters(trustStore, X509CertSelector())
|
||||
pkixParams.addCertPathChecker(revocationChecker)
|
||||
return CertPathTrustManagerParameters(pkixParams)
|
||||
}
|
||||
|
@ -183,6 +183,7 @@ class X509UtilitiesTest {
|
||||
override val certificatesDirectory = tempFolder.root.toPath()
|
||||
override val keyStorePassword = "serverstorepass"
|
||||
override val trustStorePassword = "trustpass"
|
||||
override val crlCheckSoftFail: Boolean = true
|
||||
}
|
||||
|
||||
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
|
||||
@ -218,6 +219,7 @@ class X509UtilitiesTest {
|
||||
override val certificatesDirectory = tempFolder.root.toPath()
|
||||
override val keyStorePassword = "serverstorepass"
|
||||
override val trustStorePassword = "trustpass"
|
||||
override val crlCheckSoftFail: Boolean = true
|
||||
}
|
||||
|
||||
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
|
||||
|
Reference in New Issue
Block a user