diff --git a/bridge/bridgecapsule/src/smoke-test/kotlin/net/corda/bridge/smoketest/BridgeSmokeTest.kt b/bridge/bridgecapsule/src/smoke-test/kotlin/net/corda/bridge/smoketest/BridgeSmokeTest.kt
index 3648f0cd33..0d7a16b993 100644
--- a/bridge/bridgecapsule/src/smoke-test/kotlin/net/corda/bridge/smoketest/BridgeSmokeTest.kt
+++ b/bridge/bridgecapsule/src/smoke-test/kotlin/net/corda/bridge/smoketest/BridgeSmokeTest.kt
@@ -17,12 +17,12 @@ import net.corda.node.services.config.NodeConfiguration
 import net.corda.node.services.messaging.ArtemisMessagingServer
 import net.corda.nodeapi.internal.*
 import net.corda.nodeapi.internal.bridging.BridgeControl
-import net.corda.nodeapi.internal.config.NodeSSLConfiguration
-import net.corda.nodeapi.internal.config.SSLConfiguration
+import net.corda.nodeapi.internal.config.MutualSslConfiguration
 import net.corda.nodeapi.internal.crypto.*
 import net.corda.nodeapi.internal.network.NetworkParametersCopier
 import net.corda.testing.core.*
 import net.corda.testing.internal.rigorousMock
+import net.corda.testing.internal.stubs.CertificateStoreStubs
 import org.apache.activemq.artemis.api.core.RoutingType
 import org.apache.activemq.artemis.api.core.SimpleString
 import org.apache.curator.test.TestingServer
@@ -67,22 +67,20 @@ class BridgeSmokeTest {
 
     @Test
     fun `Run full features bridge from jar to ensure everything works`() {
-        val artemisConfig = object : NodeSSLConfiguration {
-            override val baseDirectory: Path = tempFolder.root.toPath()
-            override val keyStorePassword: String = "cordacadevpass"
-            override val trustStorePassword: String = "trustpass"
-            override val crlCheckSoftFail: Boolean = true
-        }
+
+        val baseDirectory = tempFolder.root.toPath().createDirectories()
+        val artemisConfig = CertificateStoreStubs.P2P.withBaseDirectory(baseDirectory)
+
         artemisConfig.createBridgeKeyStores(DUMMY_BANK_A_NAME)
         copyBridgeResource("corda-firewall.jar")
         copyBridgeResource("firewall.conf")
-        createNetworkParams(tempFolder.root.toPath())
+        createNetworkParams(baseDirectory)
         val (artemisServer, artemisClient) = createArtemis()
         val zkServer = TestingServer(11105, false)
         try {
             installBridgeControlResponder(artemisClient)
             zkServer.start()
-            val bridge = startBridge(tempFolder.root.toPath())
+            val bridge = startBridge(baseDirectory)
             waitForBridge(bridge)
         } finally {
             zkServer.close()
@@ -115,18 +113,18 @@ class BridgeSmokeTest {
         copier.install(baseDirectory)
     }
 
-    private fun SSLConfiguration.createBridgeKeyStores(legalName: CordaX500Name,
-                                                       rootCert: X509Certificate = DEV_ROOT_CA.certificate,
-                                                       intermediateCa: CertificateAndKeyPair = DEV_INTERMEDIATE_CA) {
+    private fun MutualSslConfiguration.createBridgeKeyStores(legalName: CordaX500Name,
+                                                             rootCert: X509Certificate = DEV_ROOT_CA.certificate,
+                                                             intermediateCa: CertificateAndKeyPair = DEV_INTERMEDIATE_CA) {
 
-        certificatesDirectory.createDirectories()
-        if (!trustStoreFile.exists()) {
-            loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/$DEV_CA_TRUST_STORE_FILE"), DEV_CA_TRUST_STORE_PASS).save(trustStoreFile, trustStorePassword)
+        if (!trustStore.path.exists()) {
+            val trustStore = trustStore.get(true)
+            loadDevCaTrustStore().copyTo(trustStore)
         }
 
         val (nodeCaCert, nodeCaKeyPair) = createDevNodeCa(intermediateCa, legalName)
 
-        val sslKeyStore = loadSslKeyStore(createNew = true)
+        val sslKeyStore = keyStore.get(createNew = true)
         sslKeyStore.update {
             val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
             val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, legalName.x500Principal, tlsKeyPair.public)
@@ -203,17 +201,23 @@ class BridgeSmokeTest {
     }
 
     private fun createArtemis(): Pair<ArtemisMessagingServer, ArtemisMessagingClient> {
+        val baseDirectory = tempFolder.root.toPath()
+        val certificatesDirectory = baseDirectory / "certificates"
+        val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)
+        val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)
+
         val artemisConfig = rigorousMock<AbstractNodeConfiguration>().also {
-            doReturn(tempFolder.root.toPath()).whenever(it).baseDirectory
+            doReturn(baseDirectory).whenever(it).baseDirectory
+            doReturn(certificatesDirectory).whenever(it).certificatesDirectory
             doReturn(ALICE_NAME).whenever(it).myLegalName
-            doReturn("trustpass").whenever(it).trustStorePassword
-            doReturn("cordacadevpass").whenever(it).keyStorePassword
+            doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
+            doReturn(signingCertificateStore).whenever(it).signingCertificateStore
             doReturn(NetworkHostAndPort("localhost", 11005)).whenever(it).p2pAddress
             doReturn(null).whenever(it).jmxMonitoringHttpPort
             doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000), externalBridge = true)).whenever(it).enterpriseConfiguration
         }
         val artemisServer = ArtemisMessagingServer(artemisConfig, NetworkHostAndPort("0.0.0.0", 11005), MAX_MESSAGE_SIZE)
-        val artemisClient = ArtemisMessagingClient(artemisConfig, NetworkHostAndPort("localhost", 11005), MAX_MESSAGE_SIZE)
+        val artemisClient = ArtemisMessagingClient(artemisConfig.p2pSslOptions, NetworkHostAndPort("localhost", 11005), MAX_MESSAGE_SIZE)
         artemisServer.start()
         artemisClient.start()
         return Pair(artemisServer, artemisClient)
diff --git a/bridge/src/integration-test/kotlin/net/corda/bridge/BridgeIntegrationTest.kt b/bridge/src/integration-test/kotlin/net/corda/bridge/BridgeIntegrationTest.kt
index 050f3f7fa5..926dae3e16 100644
--- a/bridge/src/integration-test/kotlin/net/corda/bridge/BridgeIntegrationTest.kt
+++ b/bridge/src/integration-test/kotlin/net/corda/bridge/BridgeIntegrationTest.kt
@@ -28,6 +28,7 @@ import net.corda.testing.core.DUMMY_BANK_A_NAME
 import net.corda.testing.core.MAX_MESSAGE_SIZE
 import net.corda.testing.core.SerializationEnvironmentRule
 import net.corda.testing.internal.rigorousMock
+import net.corda.testing.internal.stubs.CertificateStoreStubs
 import org.apache.activemq.artemis.api.core.RoutingType
 import org.apache.activemq.artemis.api.core.SimpleString
 import org.apache.curator.test.TestingServer
@@ -387,61 +388,76 @@ class BridgeIntegrationTest {
 
 
     private fun createArtemis(): Pair<ArtemisMessagingServer, ArtemisMessagingClient> {
+        val baseDirectory = tempFolder.root.toPath()
+        val certificatesDirectory = baseDirectory / "certificates"
+        val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)
+        val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)
         val artemisConfig = rigorousMock<AbstractNodeConfiguration>().also {
-            doReturn(tempFolder.root.toPath()).whenever(it).baseDirectory
+            doReturn(baseDirectory).whenever(it).baseDirectory
+            doReturn(certificatesDirectory).whenever(it).certificatesDirectory
             doReturn(ALICE_NAME).whenever(it).myLegalName
-            doReturn("trustpass").whenever(it).trustStorePassword
-            doReturn("cordacadevpass").whenever(it).keyStorePassword
+            doReturn(signingCertificateStore).whenever(it).signingCertificateStore
+            doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
             doReturn(NetworkHostAndPort("localhost", 11005)).whenever(it).p2pAddress
             doReturn(null).whenever(it).jmxMonitoringHttpPort
             doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000), externalBridge = true)).whenever(it).enterpriseConfiguration
         }
         val artemisServer = ArtemisMessagingServer(artemisConfig, NetworkHostAndPort("0.0.0.0", 11005), MAX_MESSAGE_SIZE)
-        val artemisClient = ArtemisMessagingClient(artemisConfig, NetworkHostAndPort("localhost", 11005), MAX_MESSAGE_SIZE)
+        val artemisClient = ArtemisMessagingClient(artemisConfig.p2pSslOptions, NetworkHostAndPort("localhost", 11005), MAX_MESSAGE_SIZE)
         artemisServer.start()
         artemisClient.start()
         return Pair(artemisServer, artemisClient)
     }
 
     private fun createArtemis2(): Pair<ArtemisMessagingServer, ArtemisMessagingClient> {
-        val originalCertsFolderPath = tempFolder.root.toPath() / "certificates"
-        val folderPath = tempFolder.root.toPath() / "artemis2"
+        val baseDirectory = tempFolder.root.toPath()
+        val originalCertsFolderPath = baseDirectory / "certificates"
+        val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(originalCertsFolderPath)
+        val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(originalCertsFolderPath)
+
+        val folderPath = baseDirectory / "artemis2"
         val newCertsFolderPath = folderPath / "certificates"
         newCertsFolderPath.createDirectories()
         (originalCertsFolderPath / "truststore.jks").copyToDirectory(newCertsFolderPath)
         (originalCertsFolderPath / "sslkeystore.jks").copyToDirectory(newCertsFolderPath)
         val artemisConfig = rigorousMock<AbstractNodeConfiguration>().also {
             doReturn(folderPath).whenever(it).baseDirectory
+            doReturn(originalCertsFolderPath).whenever(it).certificatesDirectory
             doReturn(ALICE_NAME).whenever(it).myLegalName
-            doReturn("trustpass").whenever(it).trustStorePassword
-            doReturn("cordacadevpass").whenever(it).keyStorePassword
+            doReturn(signingCertificateStore).whenever(it).signingCertificateStore
+            doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
             doReturn(NetworkHostAndPort("localhost", 12005)).whenever(it).p2pAddress
             doReturn(null).whenever(it).jmxMonitoringHttpPort
             doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000), externalBridge = true)).whenever(it).enterpriseConfiguration
         }
         val artemisServer = ArtemisMessagingServer(artemisConfig, NetworkHostAndPort("0.0.0.0", 12005), MAX_MESSAGE_SIZE)
-        val artemisClient = ArtemisMessagingClient(artemisConfig, NetworkHostAndPort("localhost", 12005), MAX_MESSAGE_SIZE)
+        val artemisClient = ArtemisMessagingClient(artemisConfig.p2pSslOptions, NetworkHostAndPort("localhost", 12005), MAX_MESSAGE_SIZE)
         return Pair(artemisServer, artemisClient)
     }
 
     private fun createDummyPeerArtemis(): Pair<ArtemisMessagingServer, ArtemisMessagingClient> {
-        val originalCertsFolderPath = tempFolder.root.toPath() / "certificates"
-        val folderPath = tempFolder.root.toPath() / "artemis3"
+        val baseDirectory = tempFolder.root.toPath()
+        val originalCertsFolderPath = baseDirectory / "certificates"
+        val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(originalCertsFolderPath)
+        val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(originalCertsFolderPath)
+
+        val folderPath = baseDirectory / "artemis3"
         val newCertsFolderPath = folderPath / "certificates"
         newCertsFolderPath.createDirectories()
         (originalCertsFolderPath / "truststore.jks").copyToDirectory(newCertsFolderPath)
         (originalCertsFolderPath / "sslkeystore.jks").copyToDirectory(newCertsFolderPath)
         val artemisConfig = rigorousMock<AbstractNodeConfiguration>().also {
             doReturn(folderPath).whenever(it).baseDirectory
+            doReturn(newCertsFolderPath).whenever(it).certificatesDirectory
             doReturn(DUMMY_BANK_A_NAME).whenever(it).myLegalName
-            doReturn("trustpass").whenever(it).trustStorePassword
-            doReturn("cordacadevpass").whenever(it).keyStorePassword
+            doReturn(signingCertificateStore).whenever(it).signingCertificateStore
+            doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
             doReturn(NetworkHostAndPort("localhost", 7890)).whenever(it).p2pAddress
             doReturn(null).whenever(it).jmxMonitoringHttpPort
             doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000), externalBridge = true)).whenever(it).enterpriseConfiguration
         }
         val artemisServer = ArtemisMessagingServer(artemisConfig, NetworkHostAndPort("0.0.0.0", 7890), MAX_MESSAGE_SIZE)
-        val artemisClient = ArtemisMessagingClient(artemisConfig, NetworkHostAndPort("localhost", 7890), MAX_MESSAGE_SIZE)
+        val artemisClient = ArtemisMessagingClient(artemisConfig.p2pSslOptions, NetworkHostAndPort("localhost", 7890), MAX_MESSAGE_SIZE)
         artemisServer.start()
         artemisClient.start()
         artemisClient.started!!.session.createQueue(SimpleString("${P2P_PREFIX}12345"), RoutingType.ANYCAST, SimpleString("${P2P_PREFIX}12345"), true)
diff --git a/bridge/src/integration-test/kotlin/net/corda/bridge/services/AMQPListenerTest.kt b/bridge/src/integration-test/kotlin/net/corda/bridge/services/AMQPListenerTest.kt
index 75133a6b4c..63bfbeae50 100644
--- a/bridge/src/integration-test/kotlin/net/corda/bridge/services/AMQPListenerTest.kt
+++ b/bridge/src/integration-test/kotlin/net/corda/bridge/services/AMQPListenerTest.kt
@@ -12,6 +12,7 @@ import net.corda.core.internal.div
 import net.corda.core.internal.readAll
 import net.corda.core.utilities.NetworkHostAndPort
 import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEERS_PREFIX
+import net.corda.nodeapi.internal.config.CertificateStore
 import net.corda.nodeapi.internal.crypto.X509KeyStore
 import net.corda.nodeapi.internal.crypto.X509Utilities
 import net.corda.nodeapi.internal.protonwrapper.messages.MessageStatus
@@ -25,7 +26,6 @@ import org.junit.Assert.assertArrayEquals
 import org.junit.Rule
 import org.junit.Test
 import org.junit.rules.TemporaryFolder
-import java.security.KeyStore
 import kotlin.test.assertEquals
 
 class AMQPListenerTest {
@@ -58,14 +58,14 @@ class AMQPListenerTest {
         assertEquals(true, stateFollower.next())
         assertEquals(true, amqpListenerService.active)
         assertEquals(false, serverListening("localhost", 10005))
-        val keyStoreBytes = bridgeConfig.sslKeystore.readAll()
-        val trustStoreBytes = bridgeConfig.trustStoreFile.readAll()
+        val keyStoreBytes = bridgeConfig.p2pSslOptions.keyStore.path.readAll()
+        val trustStoreBytes = bridgeConfig.p2pSslOptions.trustStore.path.readAll()
         // start listening
         amqpListenerService.provisionKeysAndActivate(keyStoreBytes,
-                bridgeConfig.keyStorePassword.toCharArray(),
-                bridgeConfig.keyStorePassword.toCharArray(),
+                bridgeConfig.p2pSslOptions.keyStore.password.toCharArray(),
+                bridgeConfig.p2pSslOptions.keyStore.password.toCharArray(),
                 trustStoreBytes,
-                bridgeConfig.trustStorePassword.toCharArray())
+                bridgeConfig.p2pSslOptions.trustStore.password.toCharArray())
         // Fire lots of activity to prove we are good
         assertEquals(TestAuditService.AuditEvent.STATUS_CHANGE, auditFollower.next())
         assertEquals(true, amqpListenerService.active)
@@ -76,12 +76,11 @@ class AMQPListenerTest {
         assertEquals(TestAuditService.AuditEvent.FAILED_CONNECTION, auditFollower.next())
         val clientConfig = createAndLoadConfigFromResource(tempFolder.root.toPath() / "client", configResource)
         clientConfig.createBridgeKeyStores(DUMMY_BANK_B_NAME)
-        val clientKeyStore = clientConfig.loadSslKeyStore().internal
-        val clientTrustStore = clientConfig.loadTrustStore().internal
+        val clientKeyStore = clientConfig.p2pSslOptions.keyStore.get()
+        val clientTrustStore = clientConfig.p2pSslOptions.trustStore.get()
         val amqpConfig = object : AMQPConfiguration {
-            override val keyStore: KeyStore = clientKeyStore
-            override val keyStorePrivateKeyPassword: CharArray = clientConfig.keyStorePassword.toCharArray()
-            override val trustStore: KeyStore = clientTrustStore
+            override val keyStore = clientKeyStore
+            override val trustStore = clientTrustStore
             override val maxMessageSize: Int = maxMessageSize
             override val trace: Boolean = true
         }
@@ -134,26 +133,28 @@ class AMQPListenerTest {
         val amqpListenerService = BridgeAMQPListenerServiceImpl(bridgeConfig, maxMessageSize, auditService)
         amqpListenerService.start()
         auditService.start()
-        val keyStoreBytes = bridgeConfig.sslKeystore.readAll()
-        val trustStoreBytes = bridgeConfig.trustStoreFile.readAll()
+        val keyStoreBytes = bridgeConfig.p2pSslOptions.keyStore.path.readAll()
+        val trustStoreBytes = bridgeConfig.p2pSslOptions.trustStore.path.readAll()
         // start listening
         amqpListenerService.provisionKeysAndActivate(keyStoreBytes,
-                bridgeConfig.keyStorePassword.toCharArray(),
-                bridgeConfig.keyStorePassword.toCharArray(),
+                bridgeConfig.p2pSslOptions.keyStore.password.toCharArray(),
+                bridgeConfig.p2pSslOptions.keyStore.password.toCharArray(),
                 trustStoreBytes,
-                bridgeConfig.trustStorePassword.toCharArray())
+                bridgeConfig.p2pSslOptions.trustStore.password.toCharArray())
         val connectionFollower = amqpListenerService.onConnection.toBlocking().iterator
         val auditFollower = auditService.onAuditEvent.toBlocking().iterator
         val clientKeys = Crypto.generateKeyPair(ECDSA_SECP256R1_SHA256)
         val clientCert = X509Utilities.createSelfSignedCACertificate(ALICE_NAME.x500Principal, clientKeys)
-        val clientKeyStore = X509KeyStore("password")
-        clientKeyStore.setPrivateKey("TLS_CERT", clientKeys.private, listOf(clientCert))
-        val clientTrustStore = X509KeyStore("password")
-        clientTrustStore.setCertificate("TLS_ROOT", clientCert)
+        val clientKeyStoreRaw = X509KeyStore("password")
+        clientKeyStoreRaw.setPrivateKey("TLS_CERT", clientKeys.private, listOf(clientCert))
+        val clientKeyStore = CertificateStore.of(clientKeyStoreRaw, "password")
+
+        val clientTrustStoreRaw = X509KeyStore("password")
+        clientTrustStoreRaw.setCertificate("TLS_ROOT", clientCert)
+        val clientTrustStore = CertificateStore.of(clientTrustStoreRaw, "password")
         val amqpConfig = object : AMQPConfiguration {
-            override val keyStore: KeyStore = clientKeyStore.internal
-            override val keyStorePrivateKeyPassword: CharArray = "password".toCharArray()
-            override val trustStore: KeyStore = clientTrustStore.internal
+            override val keyStore = clientKeyStore
+            override val trustStore = clientTrustStore
             override val maxMessageSize: Int = maxMessageSize
             override val trace: Boolean = true
         }
diff --git a/bridge/src/integration-test/kotlin/net/corda/bridge/services/ArtemisConnectionTest.kt b/bridge/src/integration-test/kotlin/net/corda/bridge/services/ArtemisConnectionTest.kt
index 94e7d0d675..f9f46a2b60 100644
--- a/bridge/src/integration-test/kotlin/net/corda/bridge/services/ArtemisConnectionTest.kt
+++ b/bridge/src/integration-test/kotlin/net/corda/bridge/services/ArtemisConnectionTest.kt
@@ -6,6 +6,7 @@ import net.corda.bridge.createAndLoadConfigFromResource
 import net.corda.bridge.createBridgeKeyStores
 import net.corda.bridge.createNetworkParams
 import net.corda.bridge.services.artemis.BridgeArtemisConnectionServiceImpl
+import net.corda.core.internal.div
 import net.corda.core.utilities.NetworkHostAndPort
 import net.corda.node.services.config.EnterpriseConfiguration
 import net.corda.node.services.config.MutualExclusionConfiguration
@@ -15,6 +16,7 @@ import net.corda.testing.core.DUMMY_BANK_A_NAME
 import net.corda.testing.core.MAX_MESSAGE_SIZE
 import net.corda.testing.core.SerializationEnvironmentRule
 import net.corda.testing.internal.rigorousMock
+import net.corda.testing.internal.stubs.CertificateStoreStubs
 import org.junit.Rule
 import org.junit.Test
 import org.junit.rules.TemporaryFolder
@@ -84,11 +86,17 @@ class ArtemisConnectionTest {
 
 
     private fun createArtemis(): ArtemisMessagingServer {
+
+        val baseDirectory = tempFolder.root.toPath()
+        val certificatesDirectory = baseDirectory / "certificates"
+        val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)
+        val p2pSslOptions = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)
         val artemisConfig = rigorousMock<AbstractNodeConfiguration>().also {
-            doReturn(tempFolder.root.toPath()).whenever(it).baseDirectory
+            doReturn(baseDirectory).whenever(it).baseDirectory
+            doReturn(certificatesDirectory).whenever(it).certificatesDirectory
             doReturn(DUMMY_BANK_A_NAME).whenever(it).myLegalName
-            doReturn("trustpass").whenever(it).trustStorePassword
-            doReturn("cordacadevpass").whenever(it).keyStorePassword
+            doReturn(signingCertificateStore).whenever(it).signingCertificateStore
+            doReturn(p2pSslOptions).whenever(it).p2pSslOptions
             doReturn(NetworkHostAndPort("localhost", 11005)).whenever(it).p2pAddress
             doReturn(null).whenever(it).jmxMonitoringHttpPort
             doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000), externalBridge = true)).whenever(it).enterpriseConfiguration
diff --git a/bridge/src/main/kotlin/net/corda/bridge/services/api/FirewallConfiguration.kt b/bridge/src/main/kotlin/net/corda/bridge/services/api/FirewallConfiguration.kt
index e67e5e8973..720f93362e 100644
--- a/bridge/src/main/kotlin/net/corda/bridge/services/api/FirewallConfiguration.kt
+++ b/bridge/src/main/kotlin/net/corda/bridge/services/api/FirewallConfiguration.kt
@@ -2,8 +2,7 @@ package net.corda.bridge.services.api
 
 import net.corda.core.identity.CordaX500Name
 import net.corda.core.utilities.NetworkHostAndPort
-import net.corda.nodeapi.internal.config.NodeSSLConfiguration
-import net.corda.nodeapi.internal.config.SSLConfiguration
+import net.corda.nodeapi.internal.config.MutualSslConfiguration
 import net.corda.nodeapi.internal.protonwrapper.netty.SocksProxyConfig
 import java.nio.file.Path
 
@@ -28,12 +27,7 @@ enum class FirewallMode {
     FloatOuter
 }
 
-interface BridgeSSLConfiguration : SSLConfiguration {
-    override val keyStorePassword: String
-    override val trustStorePassword: String
-    override val sslKeystore: Path
-    override val trustStoreFile: Path
-}
+interface BridgeSSLConfiguration : MutualSslConfiguration
 
 
 /**
@@ -91,7 +85,8 @@ interface FloatOuterConfiguration {
     val customSSLConfiguration: BridgeSSLConfiguration?
 }
 
-interface FirewallConfiguration : NodeSSLConfiguration {
+interface FirewallConfiguration {
+    val baseDirectory: Path
     val firewallMode: FirewallMode
     val outboundConfig: BridgeOutboundConfiguration?
     val inboundConfig: BridgeInboundConfiguration?
@@ -115,4 +110,6 @@ interface FirewallConfiguration : NodeSSLConfiguration {
     // This is relevant to bridges, because we push messages into the inbox and use the async acknowledgement responses to reply to sender.
     val p2pConfirmationWindowSize: Int
     val whitelistedHeaders: List<String>
+    val crlCheckSoftFail: Boolean
+    val p2pSslOptions: MutualSslConfiguration
 }
\ No newline at end of file
diff --git a/bridge/src/main/kotlin/net/corda/bridge/services/artemis/BridgeArtemisConnectionServiceImpl.kt b/bridge/src/main/kotlin/net/corda/bridge/services/artemis/BridgeArtemisConnectionServiceImpl.kt
index b2e987aede..fcabe28b38 100644
--- a/bridge/src/main/kotlin/net/corda/bridge/services/artemis/BridgeArtemisConnectionServiceImpl.kt
+++ b/bridge/src/main/kotlin/net/corda/bridge/services/artemis/BridgeArtemisConnectionServiceImpl.kt
@@ -1,15 +1,15 @@
 package net.corda.bridge.services.artemis
 
 import net.corda.bridge.services.api.*
-import net.corda.bridge.services.config.BridgeSSLConfigurationImpl
 import net.corda.bridge.services.util.ServiceStateCombiner
 import net.corda.bridge.services.util.ServiceStateHelper
 import net.corda.core.internal.ThreadBox
 import net.corda.core.serialization.internal.nodeSerializationEnv
 import net.corda.core.utilities.contextLogger
-import net.corda.nodeapi.ArtemisTcpTransport
 import net.corda.nodeapi.internal.ArtemisMessagingClient
 import net.corda.nodeapi.internal.ArtemisMessagingComponent
+import net.corda.nodeapi.internal.InternalArtemisTcpTransport
+import net.corda.nodeapi.internal.config.MutualSslConfiguration
 import org.apache.activemq.artemis.api.core.client.ActiveMQClient
 import org.apache.activemq.artemis.api.core.client.FailoverEventType
 import org.apache.activemq.artemis.api.core.client.ServerLocator
@@ -34,13 +34,13 @@ class BridgeArtemisConnectionServiceImpl(val conf: FirewallConfiguration,
     }
 
     private val state = ThreadBox(InnerState())
-    private val sslConfiguration: BridgeSSLConfiguration
+    private val sslConfiguration: MutualSslConfiguration
     private val statusFollower: ServiceStateCombiner
     private var statusSubscriber: Subscription? = null
 
     init {
         statusFollower = ServiceStateCombiner(listOf(auditService))
-        sslConfiguration = conf.outboundConfig?.customSSLConfiguration ?: BridgeSSLConfigurationImpl(conf)
+        sslConfiguration = conf.outboundConfig?.customSSLConfiguration ?: conf.p2pSslOptions
     }
 
     override fun start() {
@@ -61,7 +61,7 @@ class BridgeArtemisConnectionServiceImpl(val conf: FirewallConfiguration,
             log.info("Connecting to message broker: ${outboundConf.artemisBrokerAddress}")
             val brokerAddresses = listOf(outboundConf.artemisBrokerAddress) + outboundConf.alternateArtemisBrokerAddresses
             // TODO Add broker CN to config for host verification in case the embedded broker isn't used
-            val tcpTransports = brokerAddresses.map { ArtemisTcpTransport.p2pConnectorTcpTransport(it, sslConfiguration) }
+            val tcpTransports = brokerAddresses.map { InternalArtemisTcpTransport.p2pConnectorTcpTransport(it, sslConfiguration) }
             locator = ActiveMQClient.createServerLocatorWithoutHA(*tcpTransports.toTypedArray()).apply {
                 // Never time out on our loopback Artemis connections. If we switch back to using the InVM transport this
                 // would be the default and the two lines below can be deleted.
diff --git a/bridge/src/main/kotlin/net/corda/bridge/services/config/FirewallConfigurationImpl.kt b/bridge/src/main/kotlin/net/corda/bridge/services/config/FirewallConfigurationImpl.kt
index 42f3bb485a..980c145bc6 100644
--- a/bridge/src/main/kotlin/net/corda/bridge/services/config/FirewallConfigurationImpl.kt
+++ b/bridge/src/main/kotlin/net/corda/bridge/services/config/FirewallConfigurationImpl.kt
@@ -6,22 +6,23 @@ import net.corda.core.identity.CordaX500Name
 import net.corda.core.internal.div
 import net.corda.core.utilities.NetworkHostAndPort
 import net.corda.nodeapi.internal.ArtemisMessagingComponent
-import net.corda.nodeapi.internal.config.NodeSSLConfiguration
+import net.corda.nodeapi.internal.config.FileBasedCertificateStoreSupplier
+import net.corda.nodeapi.internal.config.SslConfiguration
+import net.corda.nodeapi.internal.config.MutualSslConfiguration
 import net.corda.nodeapi.internal.config.parseAs
 import net.corda.nodeapi.internal.protonwrapper.netty.SocksProxyConfig
 import java.nio.file.Path
-import java.nio.file.Paths
-
 
 fun Config.parseAsFirewallConfiguration(): FirewallConfiguration = parseAs<FirewallConfigurationImpl>()
 
-data class BridgeSSLConfigurationImpl(override val keyStorePassword: String,
-                                      override val trustStorePassword: String,
-                                      override val certificatesDirectory: Path = Paths.get("certificates"),
-                                      override val sslKeystore: Path = certificatesDirectory / "sslkeystore.jks",
-                                      override val trustStoreFile: Path = certificatesDirectory / "truststore.jks",
-                                      override val crlCheckSoftFail: Boolean) : BridgeSSLConfiguration {
-    constructor(config: NodeSSLConfiguration) : this(config.keyStorePassword, config.trustStorePassword, config.certificatesDirectory, config.sslKeystore, config.trustStoreFile, config.crlCheckSoftFail)
+data class BridgeSSLConfigurationImpl(private val sslKeystore: Path,
+                                      private val keyStorePassword: String,
+                                      private val trustStoreFile: Path,
+                                      private val trustStorePassword: String,
+                                      private val crlCheckSoftFail: Boolean) : BridgeSSLConfiguration {
+
+    override val keyStore = FileBasedCertificateStoreSupplier(sslKeystore, keyStorePassword)
+    override val trustStore = FileBasedCertificateStoreSupplier(trustStoreFile, trustStorePassword)
 }
 
 data class BridgeOutboundConfigurationImpl(override val artemisBrokerAddress: NetworkHostAndPort,
@@ -45,12 +46,12 @@ data class BridgeHAConfigImpl(override val haConnectionString: String, override
 
 data class FirewallConfigurationImpl(
         override val baseDirectory: Path,
-        override val certificatesDirectory: Path = baseDirectory / "certificates",
-        override val sslKeystore: Path = certificatesDirectory / "sslkeystore.jks",
-        override val trustStoreFile: Path = certificatesDirectory / "truststore.jks",
+        private val certificatesDirectory: Path = baseDirectory / "certificates",
+        private val sslKeystore: Path = certificatesDirectory / "sslkeystore.jks",
+        private val trustStoreFile: Path = certificatesDirectory / "truststore.jks",
         override val crlCheckSoftFail: Boolean,
-        override val keyStorePassword: String,
-        override val trustStorePassword: String,
+        private val keyStorePassword: String,
+        private val trustStorePassword: String,
         override val firewallMode: FirewallMode,
         override val networkParametersPath: Path,
         override val outboundConfig: BridgeOutboundConfigurationImpl?,
@@ -74,6 +75,12 @@ data class FirewallConfigurationImpl(
             require(inboundConfig != null && floatOuterConfig != null) { "Missing required configuration" }
         }
     }
+
+    private val p2pKeystorePath = sslKeystore
+    private val p2pKeyStore = FileBasedCertificateStoreSupplier(p2pKeystorePath, keyStorePassword)
+    private val p2pTrustStoreFilePath = trustStoreFile
+    private val p2pTrustStore = FileBasedCertificateStoreSupplier(p2pTrustStoreFilePath, trustStorePassword)
+    override val p2pSslOptions: MutualSslConfiguration = SslConfiguration.mutual(p2pKeyStore, p2pTrustStore)
 }
 
 
diff --git a/bridge/src/main/kotlin/net/corda/bridge/services/receiver/BridgeAMQPListenerServiceImpl.kt b/bridge/src/main/kotlin/net/corda/bridge/services/receiver/BridgeAMQPListenerServiceImpl.kt
index 8371b5d112..6b14b98cc3 100644
--- a/bridge/src/main/kotlin/net/corda/bridge/services/receiver/BridgeAMQPListenerServiceImpl.kt
+++ b/bridge/src/main/kotlin/net/corda/bridge/services/receiver/BridgeAMQPListenerServiceImpl.kt
@@ -7,7 +7,9 @@ import net.corda.bridge.services.api.ServiceStateSupport
 import net.corda.bridge.services.util.ServiceStateCombiner
 import net.corda.bridge.services.util.ServiceStateHelper
 import net.corda.core.utilities.contextLogger
+import net.corda.nodeapi.internal.config.CertificateStore
 import net.corda.nodeapi.internal.crypto.KEYSTORE_TYPE
+import net.corda.nodeapi.internal.crypto.X509KeyStore
 import net.corda.nodeapi.internal.protonwrapper.messages.ReceivedMessage
 import net.corda.nodeapi.internal.protonwrapper.netty.AMQPConfiguration
 import net.corda.nodeapi.internal.protonwrapper.netty.AMQPServer
@@ -48,13 +50,13 @@ class BridgeAMQPListenerServiceImpl(val conf: FirewallConfiguration,
                                           trustStorePassword: CharArray) {
         require(active) { "AuditService must be active" }
         require(keyStorePassword !== keyStorePrivateKeyPassword) { "keyStorePassword and keyStorePrivateKeyPassword must reference distinct arrays!" }
-        val keyStore = loadKeyStoreAndWipeKeys(keyStoreBytes, keyStorePassword)
-        val trustStore = loadKeyStoreAndWipeKeys(trustStoreBytes, trustStorePassword)
+
+        val keyStore = CertificateStore.of(loadKeyStore(keyStoreBytes, keyStorePassword), java.lang.String.valueOf(keyStorePrivateKeyPassword)).also { wipeKeys(keyStoreBytes, keyStorePassword) }
+        val trustStore = CertificateStore.of(loadKeyStore(trustStoreBytes, trustStorePassword), java.lang.String.valueOf(trustStorePassword)).also { wipeKeys(trustStoreBytes, trustStorePassword) }
         val bindAddress = conf.inboundConfig!!.listeningAddress
         val amqpConfiguration = object : AMQPConfiguration {
-            override val keyStore: KeyStore = keyStore
-            override val keyStorePrivateKeyPassword: CharArray = keyStorePrivateKeyPassword
-            override val trustStore: KeyStore = trustStore
+            override val keyStore = keyStore
+            override val trustStore = trustStore
             override val crlCheckSoftFail: Boolean = conf.crlCheckSoftFail
             override val maxMessageSize: Int = maximumMessageSize
             override val trace: Boolean = conf.enableAMQPPacketTrace
@@ -80,15 +82,18 @@ class BridgeAMQPListenerServiceImpl(val conf: FirewallConfiguration,
         consoleLogger.info(msg)
     }
 
-    private fun loadKeyStoreAndWipeKeys(keyStoreBytes: ByteArray, keyStorePassword: CharArray): KeyStore {
+    private fun wipeKeys(keyStoreBytes: ByteArray, keyStorePassword: CharArray) {
+        // We overwrite the keys we don't need anymore
+        Arrays.fill(keyStoreBytes, 0xAA.toByte())
+        Arrays.fill(keyStorePassword, 0xAA55.toChar())
+    }
+
+    private fun loadKeyStore(keyStoreBytes: ByteArray, keyStorePassword: CharArray): X509KeyStore {
         val keyStore = KeyStore.getInstance(KEYSTORE_TYPE)
         ByteArrayInputStream(keyStoreBytes).use {
             keyStore.load(it, keyStorePassword)
         }
-        // We overwrite the keys we don't need anymore
-        Arrays.fill(keyStoreBytes, 0xAA.toByte())
-        Arrays.fill(keyStorePassword, 0xAA55.toChar())
-        return keyStore
+        return X509KeyStore(keyStore, java.lang.String.valueOf(keyStorePassword))
     }
 
     override fun wipeKeysAndDeactivate() {
diff --git a/bridge/src/main/kotlin/net/corda/bridge/services/receiver/FloatControlListenerService.kt b/bridge/src/main/kotlin/net/corda/bridge/services/receiver/FloatControlListenerService.kt
index b7fe56ee52..6871ca6806 100644
--- a/bridge/src/main/kotlin/net/corda/bridge/services/receiver/FloatControlListenerService.kt
+++ b/bridge/src/main/kotlin/net/corda/bridge/services/receiver/FloatControlListenerService.kt
@@ -1,7 +1,6 @@
 package net.corda.bridge.services.receiver
 
 import net.corda.bridge.services.api.*
-import net.corda.bridge.services.config.BridgeSSLConfigurationImpl
 import net.corda.bridge.services.receiver.FloatControlTopics.FLOAT_DATA_TOPIC
 import net.corda.bridge.services.util.ServiceStateCombiner
 import net.corda.bridge.services.util.ServiceStateHelper
@@ -12,13 +11,13 @@ import net.corda.core.serialization.serialize
 import net.corda.core.utilities.NetworkHostAndPort
 import net.corda.core.utilities.contextLogger
 import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_PREFIX
+import net.corda.nodeapi.internal.config.MutualSslConfiguration
 import net.corda.nodeapi.internal.protonwrapper.messages.MessageStatus
 import net.corda.nodeapi.internal.protonwrapper.messages.ReceivedMessage
 import net.corda.nodeapi.internal.protonwrapper.netty.AMQPConfiguration
 import net.corda.nodeapi.internal.protonwrapper.netty.AMQPServer
 import net.corda.nodeapi.internal.protonwrapper.netty.ConnectionChange
 import rx.Subscription
-import java.security.KeyStore
 import java.util.concurrent.locks.ReentrantLock
 import kotlin.concurrent.withLock
 
@@ -39,7 +38,7 @@ class FloatControlListenerService(val conf: FirewallConfiguration,
     private var connectSubscriber: Subscription? = null
     private var receiveSubscriber: Subscription? = null
     private var amqpControlServer: AMQPServer? = null
-    private val sslConfiguration: BridgeSSLConfiguration
+    private val sslConfiguration: MutualSslConfiguration
     private val floatControlAddress = conf.floatOuterConfig!!.floatAddress
     private val floatClientName = conf.floatOuterConfig!!.expectedCertificateSubject
     private var activeConnectionInfo: ConnectionChange? = null
@@ -48,7 +47,7 @@ class FloatControlListenerService(val conf: FirewallConfiguration,
 
     init {
         statusFollower = ServiceStateCombiner(listOf(auditService, amqpListener))
-        sslConfiguration = conf.floatOuterConfig?.customSSLConfiguration ?: BridgeSSLConfigurationImpl(conf)
+        sslConfiguration = conf.floatOuterConfig?.customSSLConfiguration ?: conf.p2pSslOptions
     }
 
 
@@ -68,15 +67,13 @@ class FloatControlListenerService(val conf: FirewallConfiguration,
 
     private fun startControlListener() {
         lock.withLock {
-            val keyStore = sslConfiguration.loadSslKeyStore().internal
-            val keyStorePrivateKeyPassword = sslConfiguration.keyStorePassword
-            val trustStore = sslConfiguration.loadTrustStore().internal
+            val keyStore = sslConfiguration.keyStore.get()
+            val trustStore = sslConfiguration.trustStore.get()
             val amqpConfig = object : AMQPConfiguration {
                 override val userName: String? = null
                 override val password: String? = null
-                override val keyStore: KeyStore = keyStore
-                override val keyStorePrivateKeyPassword: CharArray = keyStorePrivateKeyPassword.toCharArray()
-                override val trustStore: KeyStore = trustStore
+                override val keyStore = keyStore
+                override val trustStore = trustStore
                 override val crlCheckSoftFail: Boolean = conf.crlCheckSoftFail
                 override val maxMessageSize: Int = maximumMessageSize
                 override val trace: Boolean = conf.enableAMQPPacketTrace
diff --git a/bridge/src/main/kotlin/net/corda/bridge/services/receiver/InProcessBridgeReceiverService.kt b/bridge/src/main/kotlin/net/corda/bridge/services/receiver/InProcessBridgeReceiverService.kt
index bd751819ff..540cf2c320 100644
--- a/bridge/src/main/kotlin/net/corda/bridge/services/receiver/InProcessBridgeReceiverService.kt
+++ b/bridge/src/main/kotlin/net/corda/bridge/services/receiver/InProcessBridgeReceiverService.kt
@@ -5,7 +5,7 @@ import net.corda.bridge.services.util.ServiceStateCombiner
 import net.corda.bridge.services.util.ServiceStateHelper
 import net.corda.core.internal.readAll
 import net.corda.core.utilities.contextLogger
-import net.corda.nodeapi.internal.config.SSLConfiguration
+import net.corda.nodeapi.internal.config.MutualSslConfiguration
 import net.corda.nodeapi.internal.protonwrapper.messages.ReceivedMessage
 import rx.Subscription
 
@@ -22,23 +22,23 @@ class InProcessBridgeReceiverService(val conf: FirewallConfiguration,
     private val statusFollower: ServiceStateCombiner
     private var statusSubscriber: Subscription? = null
     private var receiveSubscriber: Subscription? = null
-    private val sslConfiguration: SSLConfiguration
+    private val sslConfiguration: MutualSslConfiguration
 
     init {
         statusFollower = ServiceStateCombiner(listOf(auditService, haService, amqpListenerService, filterService))
-        sslConfiguration = conf.inboundConfig?.customSSLConfiguration ?: conf
+        sslConfiguration = conf.inboundConfig?.customSSLConfiguration ?: conf.p2pSslOptions
     }
 
     override fun start() {
         statusSubscriber = statusFollower.activeChange.subscribe({
             if (it) {
-                val keyStoreBytes = sslConfiguration.sslKeystore.readAll()
-                val trustStoreBytes = sslConfiguration.trustStoreFile.readAll()
+                val keyStoreBytes = sslConfiguration.keyStore.path.readAll()
+                val trustStoreBytes = sslConfiguration.trustStore.path.readAll()
                 amqpListenerService.provisionKeysAndActivate(keyStoreBytes,
-                        sslConfiguration.keyStorePassword.toCharArray(),
-                        sslConfiguration.keyStorePassword.toCharArray(),
+                        sslConfiguration.keyStore.password.toCharArray(),
+                        sslConfiguration.keyStore.password.toCharArray(),
                         trustStoreBytes,
-                        sslConfiguration.trustStorePassword.toCharArray())
+                        sslConfiguration.trustStore.password.toCharArray())
             } else {
                 if (amqpListenerService.running) {
                     amqpListenerService.wipeKeysAndDeactivate()
diff --git a/bridge/src/main/kotlin/net/corda/bridge/services/receiver/TunnelingBridgeReceiverService.kt b/bridge/src/main/kotlin/net/corda/bridge/services/receiver/TunnelingBridgeReceiverService.kt
index 5555455e57..887946d7a7 100644
--- a/bridge/src/main/kotlin/net/corda/bridge/services/receiver/TunnelingBridgeReceiverService.kt
+++ b/bridge/src/main/kotlin/net/corda/bridge/services/receiver/TunnelingBridgeReceiverService.kt
@@ -13,7 +13,7 @@ import net.corda.core.serialization.serialize
 import net.corda.core.utilities.NetworkHostAndPort
 import net.corda.core.utilities.contextLogger
 import net.corda.core.utilities.debug
-import net.corda.nodeapi.internal.config.SSLConfiguration
+import net.corda.nodeapi.internal.config.MutualSslConfiguration
 import net.corda.nodeapi.internal.protonwrapper.messages.MessageStatus
 import net.corda.nodeapi.internal.protonwrapper.messages.ReceivedMessage
 import net.corda.nodeapi.internal.protonwrapper.netty.AMQPClient
@@ -21,7 +21,6 @@ import net.corda.nodeapi.internal.protonwrapper.netty.AMQPConfiguration
 import net.corda.nodeapi.internal.protonwrapper.netty.ConnectionChange
 import rx.Subscription
 import java.io.ByteArrayOutputStream
-import java.security.KeyStore
 import java.security.SecureRandom
 import java.util.concurrent.TimeUnit
 import java.util.concurrent.TimeoutException
@@ -41,15 +40,15 @@ class TunnelingBridgeReceiverService(val conf: FirewallConfiguration,
     private var connectSubscriber: Subscription? = null
     private var receiveSubscriber: Subscription? = null
     private var amqpControlClient: AMQPClient? = null
-    private val controlLinkSSLConfiguration: SSLConfiguration
-    private val floatListenerSSLConfiguration: SSLConfiguration
+    private val controlLinkSSLConfiguration: MutualSslConfiguration
+    private val floatListenerSSLConfiguration: MutualSslConfiguration
     private val expectedCertificateSubject: CordaX500Name
     private val secureRandom: SecureRandom = newSecureRandom()
 
     init {
         statusFollower = ServiceStateCombiner(listOf(auditService, haService, filterService))
-        controlLinkSSLConfiguration = conf.bridgeInnerConfig?.customSSLConfiguration ?: conf
-        floatListenerSSLConfiguration = conf.bridgeInnerConfig?.customFloatOuterSSLConfiguration ?: conf
+        controlLinkSSLConfiguration = conf.bridgeInnerConfig?.customSSLConfiguration ?: conf.p2pSslOptions
+        floatListenerSSLConfiguration = conf.bridgeInnerConfig?.customFloatOuterSSLConfiguration ?: conf.p2pSslOptions
         expectedCertificateSubject = conf.bridgeInnerConfig!!.expectedCertificateSubject
     }
 
@@ -58,15 +57,13 @@ class TunnelingBridgeReceiverService(val conf: FirewallConfiguration,
         statusSubscriber = statusFollower.activeChange.subscribe({
             if (it) {
                 val floatAddresses = conf.bridgeInnerConfig!!.floatAddresses
-                val controlLinkKeyStore = controlLinkSSLConfiguration.loadSslKeyStore().internal
-                val controLinkKeyStorePrivateKeyPassword = controlLinkSSLConfiguration.keyStorePassword
-                val controlLinkTrustStore = controlLinkSSLConfiguration.loadTrustStore().internal
+                val controlLinkKeyStore = controlLinkSSLConfiguration.keyStore.get()
+                val controlLinkTrustStore = controlLinkSSLConfiguration.trustStore.get()
                 val amqpConfig = object : AMQPConfiguration {
                     override val userName: String? = null
                     override val password: String? = null
-                    override val keyStore: KeyStore = controlLinkKeyStore
-                    override val keyStorePrivateKeyPassword: CharArray = controLinkKeyStorePrivateKeyPassword.toCharArray()
-                    override val trustStore: KeyStore = controlLinkTrustStore
+                    override val keyStore = controlLinkKeyStore
+                    override val trustStore = controlLinkTrustStore
                     override val crlCheckSoftFail: Boolean = conf.crlCheckSoftFail
                     override val maxMessageSize: Int = maximumMessageSize
                     override val trace: Boolean = conf.enableAMQPPacketTrace
@@ -124,12 +121,12 @@ class TunnelingBridgeReceiverService(val conf: FirewallConfiguration,
         auditService.statusChangeEvent("Connection change on float control port $connectionChange")
         if (connectionChange.connected) {
             val (freshKeyStorePassword, freshKeyStoreKeyPassword, recodedKeyStore) = recodeKeyStore(floatListenerSSLConfiguration)
-            val trustStoreBytes = floatListenerSSLConfiguration.trustStoreFile.readAll()
+            val trustStoreBytes = floatListenerSSLConfiguration.trustStore.path.readAll()
             val activateMessage = ActivateFloat(recodedKeyStore,
                     freshKeyStorePassword,
                     freshKeyStoreKeyPassword,
                     trustStoreBytes,
-                    floatListenerSSLConfiguration.trustStorePassword.toCharArray())
+                    floatListenerSSLConfiguration.trustStore.password.toCharArray())
             val amqpActivateMessage = amqpControlClient!!.createMessage(activateMessage.serialize(context = SerializationDefaults.P2P_CONTEXT).bytes,
                     FLOAT_CONTROL_TOPIC,
                     expectedCertificateSubject.toString(),
@@ -150,9 +147,9 @@ class TunnelingBridgeReceiverService(val conf: FirewallConfiguration,
     }
 
     // Recode KeyStore to use a fresh random password for entries and overall
-    private fun recodeKeyStore(sslConfiguration: SSLConfiguration): Triple<CharArray, CharArray, ByteArray> {
-        val keyStoreOriginal = sslConfiguration.loadSslKeyStore().internal
-        val originalKeyStorePassword = sslConfiguration.keyStorePassword.toCharArray()
+    private fun recodeKeyStore(sslConfiguration: MutualSslConfiguration): Triple<CharArray, CharArray, ByteArray> {
+        val keyStoreOriginal = sslConfiguration.keyStore.get().value.internal
+        val originalKeyStorePassword = sslConfiguration.keyStore.password.toCharArray()
         val freshKeyStorePassword = CharArray(20) { secureRandom.nextInt(0xD800).toChar() } // Stick to single character Unicode range
         val freshPrivateKeyPassword = CharArray(20) { secureRandom.nextInt(0xD800).toChar() } // Stick to single character Unicode range
         for (alias in keyStoreOriginal.aliases()) {
diff --git a/bridge/src/main/kotlin/net/corda/bridge/services/sender/DirectBridgeSenderService.kt b/bridge/src/main/kotlin/net/corda/bridge/services/sender/DirectBridgeSenderService.kt
index 9948394432..e27f788a0c 100644
--- a/bridge/src/main/kotlin/net/corda/bridge/services/sender/DirectBridgeSenderService.kt
+++ b/bridge/src/main/kotlin/net/corda/bridge/services/sender/DirectBridgeSenderService.kt
@@ -23,7 +23,7 @@ class DirectBridgeSenderService(val conf: FirewallConfiguration,
     private val statusFollower: ServiceStateCombiner
     private var statusSubscriber: Subscription? = null
     private var listenerActiveSubscriber: Subscription? = null
-    private var bridgeControlListener: BridgeControlListener = BridgeControlListener(conf, conf.outboundConfig!!.socksProxyConfig, maxMessageSize, { ForwardingArtemisMessageClient(artemisConnectionService) })
+    private var bridgeControlListener: BridgeControlListener = BridgeControlListener(conf.p2pSslOptions, conf.outboundConfig!!.socksProxyConfig, maxMessageSize, { ForwardingArtemisMessageClient(artemisConnectionService) })
 
     init {
         statusFollower = ServiceStateCombiner(listOf(auditService, artemisConnectionService, haService))
diff --git a/bridge/src/test/kotlin/net/corda/bridge/BridgeTestHelper.kt b/bridge/src/test/kotlin/net/corda/bridge/BridgeTestHelper.kt
index 467a533423..a9a9bde0c0 100644
--- a/bridge/src/test/kotlin/net/corda/bridge/BridgeTestHelper.kt
+++ b/bridge/src/test/kotlin/net/corda/bridge/BridgeTestHelper.kt
@@ -7,9 +7,8 @@ import net.corda.core.internal.createDirectories
 import net.corda.core.internal.exists
 import net.corda.core.node.NetworkParameters
 import net.corda.core.node.NotaryInfo
-import net.corda.core.node.services.AttachmentId
 import net.corda.nodeapi.internal.*
-import net.corda.nodeapi.internal.config.SSLConfiguration
+import net.corda.nodeapi.internal.config.MutualSslConfiguration
 import net.corda.nodeapi.internal.crypto.*
 import net.corda.nodeapi.internal.network.NetworkParametersCopier
 import net.corda.testing.core.DUMMY_NOTARY_NAME
@@ -33,7 +32,7 @@ fun createNetworkParams(baseDirectory: Path): Int {
             maxMessageSize = 10485760,
             maxTransactionSize = 40000,
             epoch = 1,
-            whitelistedContractImplementations = emptyMap<String, List<AttachmentId>>()
+            whitelistedContractImplementations = emptyMap()
     )
     val copier = NetworkParametersCopier(networkParameters, overwriteFile = true)
     copier.install(baseDirectory)
@@ -55,18 +54,22 @@ fun createAndLoadConfigFromResource(baseDirectory: Path, configResource: String)
     return config
 }
 
-fun SSLConfiguration.createBridgeKeyStores(legalName: CordaX500Name,
-                                           rootCert: X509Certificate = DEV_ROOT_CA.certificate,
-                                           intermediateCa: CertificateAndKeyPair = DEV_INTERMEDIATE_CA) {
+fun FirewallConfiguration.createBridgeKeyStores(legalName: CordaX500Name,
+                                                rootCert: X509Certificate = DEV_ROOT_CA.certificate,
+                                                intermediateCa: CertificateAndKeyPair = DEV_INTERMEDIATE_CA) = p2pSslOptions.createBridgeKeyStores(legalName, rootCert, intermediateCa)
 
-    certificatesDirectory.createDirectories()
-    if (!trustStoreFile.exists()) {
-        loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/${DEV_CA_TRUST_STORE_FILE}"), DEV_CA_TRUST_STORE_PASS).save(trustStoreFile, trustStorePassword)
+fun MutualSslConfiguration.createBridgeKeyStores(legalName: CordaX500Name,
+                                                 rootCert: X509Certificate = DEV_ROOT_CA.certificate,
+                                                 intermediateCa: CertificateAndKeyPair = DEV_INTERMEDIATE_CA) {
+
+    if (!trustStore.path.exists()) {
+        val trustStore = trustStore.get(true)
+        loadDevCaTrustStore().copyTo(trustStore)
     }
 
     val (nodeCaCert, nodeCaKeyPair) = createDevNodeCa(intermediateCa, legalName)
 
-    val sslKeyStore = loadSslKeyStore(createNew = true)
+    val sslKeyStore = keyStore.get(createNew = true)
     sslKeyStore.update {
         val tlsKeyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
         val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, legalName.x500Principal, tlsKeyPair.public)
diff --git a/bridge/src/test/kotlin/net/corda/bridge/ConfigTest.kt b/bridge/src/test/kotlin/net/corda/bridge/ConfigTest.kt
index ce660ce9fc..2bdada596c 100644
--- a/bridge/src/test/kotlin/net/corda/bridge/ConfigTest.kt
+++ b/bridge/src/test/kotlin/net/corda/bridge/ConfigTest.kt
@@ -62,23 +62,23 @@ class ConfigTest {
     fun `Load overridden cert config`() {
         val configResource = "/net/corda/bridge/custombasecerts/firewall.conf"
         val config = createAndLoadConfigFromResource(tempFolder.root.toPath(), configResource)
-        assertEquals(Paths.get("customcerts/mysslkeystore.jks"), config.sslKeystore)
-        assertEquals(Paths.get("customcerts/mytruststore.jks"), config.trustStoreFile)
+        assertEquals(Paths.get("customcerts/mysslkeystore.jks"), config.p2pSslOptions.keyStore.path)
+        assertEquals(Paths.get("customcerts/mytruststore.jks"), config.p2pSslOptions.trustStore.path)
     }
 
     @Test
     fun `Load custom inner certificate config`() {
         val configResource = "/net/corda/bridge/separatedwithcustomcerts/bridge/firewall.conf"
         val config = createAndLoadConfigFromResource(tempFolder.root.toPath(), configResource)
-        assertEquals(Paths.get("outboundcerts/outboundkeys.jks"), config.outboundConfig!!.customSSLConfiguration!!.sslKeystore)
-        assertEquals(Paths.get("outboundcerts/outboundtrust.jks"), config.outboundConfig!!.customSSLConfiguration!!.trustStoreFile)
-        assertEquals("outboundkeypassword", config.outboundConfig!!.customSSLConfiguration!!.keyStorePassword)
-        assertEquals("outboundtrustpassword", config.outboundConfig!!.customSSLConfiguration!!.trustStorePassword)
+        assertEquals(Paths.get("outboundcerts/outboundkeys.jks"), config.outboundConfig!!.customSSLConfiguration!!.keyStore.path)
+        assertEquals(Paths.get("outboundcerts/outboundtrust.jks"), config.outboundConfig!!.customSSLConfiguration!!.trustStore.path)
+        assertEquals("outboundkeypassword", config.outboundConfig!!.customSSLConfiguration!!.keyStore.password)
+        assertEquals("outboundtrustpassword", config.outboundConfig!!.customSSLConfiguration!!.trustStore.password)
         assertNull(config.inboundConfig)
-        assertEquals(Paths.get("tunnelcerts/tunnelkeys.jks"), config.bridgeInnerConfig!!.customSSLConfiguration!!.sslKeystore)
-        assertEquals(Paths.get("tunnelcerts/tunneltrust.jks"), config.bridgeInnerConfig!!.customSSLConfiguration!!.trustStoreFile)
-        assertEquals("tunnelkeypassword", config.bridgeInnerConfig!!.customSSLConfiguration!!.keyStorePassword)
-        assertEquals("tunneltrustpassword", config.bridgeInnerConfig!!.customSSLConfiguration!!.trustStorePassword)
+        assertEquals(Paths.get("tunnelcerts/tunnelkeys.jks"), config.bridgeInnerConfig!!.customSSLConfiguration!!.keyStore.path)
+        assertEquals(Paths.get("tunnelcerts/tunneltrust.jks"), config.bridgeInnerConfig!!.customSSLConfiguration!!.trustStore.path)
+        assertEquals("tunnelkeypassword", config.bridgeInnerConfig!!.customSSLConfiguration!!.keyStore.password)
+        assertEquals("tunneltrustpassword", config.bridgeInnerConfig!!.customSSLConfiguration!!.trustStore.password)
         assertNull(config.floatOuterConfig)
     }
 
@@ -86,15 +86,15 @@ class ConfigTest {
     fun `Load custom outer certificate config`() {
         val configResource = "/net/corda/bridge/separatedwithcustomcerts/float/firewall.conf"
         val config = createAndLoadConfigFromResource(tempFolder.root.toPath(), configResource)
-        assertEquals(Paths.get("inboundcerts/inboundkeys.jks"), config.inboundConfig!!.customSSLConfiguration!!.sslKeystore)
-        assertEquals(Paths.get("inboundcerts/inboundtrust.jks"), config.inboundConfig!!.customSSLConfiguration!!.trustStoreFile)
-        assertEquals("inboundkeypassword", config.inboundConfig!!.customSSLConfiguration!!.keyStorePassword)
-        assertEquals("inboundtrustpassword", config.inboundConfig!!.customSSLConfiguration!!.trustStorePassword)
+        assertEquals(Paths.get("inboundcerts/inboundkeys.jks"), config.inboundConfig!!.customSSLConfiguration!!.keyStore.path)
+        assertEquals(Paths.get("inboundcerts/inboundtrust.jks"), config.inboundConfig!!.customSSLConfiguration!!.trustStore.path)
+        assertEquals("inboundkeypassword", config.inboundConfig!!.customSSLConfiguration!!.keyStore.password)
+        assertEquals("inboundtrustpassword", config.inboundConfig!!.customSSLConfiguration!!.trustStore.password)
         assertNull(config.outboundConfig)
-        assertEquals(Paths.get("tunnelcerts/tunnelkeys.jks"), config.floatOuterConfig!!.customSSLConfiguration!!.sslKeystore)
-        assertEquals(Paths.get("tunnelcerts/tunneltrust.jks"), config.floatOuterConfig!!.customSSLConfiguration!!.trustStoreFile)
-        assertEquals("tunnelkeypassword", config.floatOuterConfig!!.customSSLConfiguration!!.keyStorePassword)
-        assertEquals("tunneltrustpassword", config.floatOuterConfig!!.customSSLConfiguration!!.trustStorePassword)
+        assertEquals(Paths.get("tunnelcerts/tunnelkeys.jks"), config.floatOuterConfig!!.customSSLConfiguration!!.keyStore.path)
+        assertEquals(Paths.get("tunnelcerts/tunneltrust.jks"), config.floatOuterConfig!!.customSSLConfiguration!!.trustStore.path)
+        assertEquals("tunnelkeypassword", config.floatOuterConfig!!.customSSLConfiguration!!.keyStore.password)
+        assertEquals("tunneltrustpassword", config.floatOuterConfig!!.customSSLConfiguration!!.trustStore.password)
         assertNull(config.bridgeInnerConfig)
     }
 
diff --git a/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java b/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java
index ed26949527..97fbc522f6 100644
--- a/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java
+++ b/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java
@@ -61,7 +61,6 @@ public class CordaRPCJavaClientTest extends NodeBasedTest {
 
     @Before
     public void setUp() throws Exception {
-        super.setUp();
         node = startNode(ALICE_NAME, 1000, singletonList(rpcUser));
         client = new CordaRPCClient(requireNonNull(node.getNode().getConfiguration().getRpcOptions().getAddress()));
     }
diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/CordaRPCClient.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/CordaRPCClient.kt
index d2b3fa0f09..9a90b28476 100644
--- a/client/rpc/src/main/kotlin/net/corda/client/rpc/CordaRPCClient.kt
+++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/CordaRPCClient.kt
@@ -7,12 +7,11 @@ import net.corda.core.context.Trace
 import net.corda.core.messaging.CordaRPCOps
 import net.corda.core.serialization.internal.effectiveSerializationEnv
 import net.corda.core.utilities.NetworkHostAndPort
-import net.corda.nodeapi.ArtemisTcpTransport.Companion.rpcConnectorTcpTransport
 import net.corda.core.messaging.ClientRpcSslOptions
 import net.corda.core.utilities.days
 import net.corda.core.utilities.minutes
 import net.corda.core.utilities.seconds
-import net.corda.nodeapi.internal.config.SSLConfiguration
+import net.corda.nodeapi.internal.InternalArtemisTcpTransport.Companion.rpcConnectorTcpTransport
 import net.corda.serialization.internal.AMQP_RPC_CLIENT_CONTEXT
 import java.time.Duration
 
@@ -243,10 +242,8 @@ class CordaRPCClient private constructor(
         private val hostAndPort: NetworkHostAndPort,
         private val configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT,
         private val sslConfiguration: ClientRpcSslOptions? = null,
-        private val nodeSslConfiguration: SSLConfiguration? = null,
         private val classLoader: ClassLoader? = null,
-        private val haAddressPool: List<NetworkHostAndPort> = emptyList(),
-        private val internalConnection: Boolean = false
+        private val haAddressPool: List<NetworkHostAndPort> = emptyList()
 ) {
     @JvmOverloads
     constructor(hostAndPort: NetworkHostAndPort,
@@ -260,7 +257,7 @@ class CordaRPCClient private constructor(
      * @param configuration An optional configuration used to tweak client behaviour.
      */
     @JvmOverloads
-    constructor(haAddressPool: List<NetworkHostAndPort>, configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT) : this(haAddressPool.first(), configuration, null, null, null, haAddressPool)
+    constructor(haAddressPool: List<NetworkHostAndPort>, configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT) : this(haAddressPool.first(), configuration, null, null, haAddressPool)
 
     companion object {
         fun createWithSsl(
@@ -285,16 +282,7 @@ class CordaRPCClient private constructor(
                 sslConfiguration: ClientRpcSslOptions? = null,
                 classLoader: ClassLoader? = null
         ): CordaRPCClient {
-            return CordaRPCClient(hostAndPort, configuration, sslConfiguration, null, classLoader)
-        }
-
-        internal fun createWithInternalSslAndClassLoader(
-                hostAndPort: NetworkHostAndPort,
-                configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT,
-                sslConfiguration: SSLConfiguration?,
-                classLoader: ClassLoader? = null
-        ): CordaRPCClient {
-            return CordaRPCClient(hostAndPort, configuration, null, sslConfiguration, classLoader, internalConnection = true)
+            return CordaRPCClient(hostAndPort, configuration, sslConfiguration, classLoader)
         }
 
         internal fun createWithSslAndClassLoader(
@@ -303,7 +291,7 @@ class CordaRPCClient private constructor(
                 sslConfiguration: ClientRpcSslOptions? = null,
                 classLoader: ClassLoader? = null
         ): CordaRPCClient {
-            return CordaRPCClient(haAddressPool.first(), configuration, sslConfiguration, null, classLoader, haAddressPool)
+            return CordaRPCClient(haAddressPool.first(), configuration, sslConfiguration, classLoader, haAddressPool)
         }
     }
 
@@ -321,10 +309,7 @@ class CordaRPCClient private constructor(
 
     private fun getRpcClient(): RPCClient<CordaRPCOps> {
         return when {
-        // Node->RPC broker, mutually authenticated SSL. This is used when connecting the integrated shell
-            internalConnection == true -> RPCClient(hostAndPort, nodeSslConfiguration!!)
-
-        // Client->RPC broker
+            // Client->RPC broker
             haAddressPool.isEmpty() -> RPCClient(
                     rpcConnectorTcpTransport(hostAndPort, config = sslConfiguration),
                     configuration,
diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/CordaRPCClientUtils.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/CordaRPCClientUtils.kt
index 5908bb3fbe..2f60b2514a 100644
--- a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/CordaRPCClientUtils.kt
+++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/CordaRPCClientUtils.kt
@@ -6,7 +6,6 @@ import net.corda.core.messaging.CordaRPCOps
 import net.corda.core.messaging.pendingFlowsCount
 import net.corda.core.utilities.NetworkHostAndPort
 import net.corda.core.messaging.ClientRpcSslOptions
-import net.corda.nodeapi.internal.config.SSLConfiguration
 import rx.Observable
 
 /** Utility which exposes the internal Corda RPC constructor to other internal Corda components */
@@ -17,13 +16,6 @@ fun createCordaRPCClientWithSslAndClassLoader(
         classLoader: ClassLoader? = null
 ) = CordaRPCClient.createWithSslAndClassLoader(hostAndPort, configuration, sslConfiguration, classLoader)
 
-fun createCordaRPCClientWithInternalSslAndClassLoader(
-        hostAndPort: NetworkHostAndPort,
-        configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT,
-        sslConfiguration: SSLConfiguration? = null,
-        classLoader: ClassLoader? = null
-) = CordaRPCClient.createWithInternalSslAndClassLoader(hostAndPort, configuration, sslConfiguration, classLoader)
-
 fun createCordaRPCClientWithSslAndClassLoader(
         haAddressPool: List<NetworkHostAndPort>,
         configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT,
diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClient.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClient.kt
index 1d7969caaa..1665aa0159 100644
--- a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClient.kt
+++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClient.kt
@@ -15,11 +15,11 @@ import net.corda.core.serialization.SerializationDefaults
 import net.corda.core.serialization.internal.nodeSerializationEnv
 import net.corda.core.utilities.NetworkHostAndPort
 import net.corda.core.utilities.contextLogger
-import net.corda.nodeapi.ArtemisTcpTransport.Companion.rpcConnectorTcpTransport
-import net.corda.nodeapi.ArtemisTcpTransport.Companion.rpcConnectorTcpTransportsFromList
-import net.corda.nodeapi.ArtemisTcpTransport.Companion.rpcInternalClientTcpTransport
 import net.corda.nodeapi.RPCApi
-import net.corda.nodeapi.internal.config.SSLConfiguration
+import net.corda.nodeapi.internal.InternalArtemisTcpTransport.Companion.rpcConnectorTcpTransport
+import net.corda.nodeapi.internal.InternalArtemisTcpTransport.Companion.rpcConnectorTcpTransportsFromList
+import net.corda.nodeapi.internal.InternalArtemisTcpTransport.Companion.rpcInternalClientTcpTransport
+import net.corda.nodeapi.internal.config.SslConfiguration
 import org.apache.activemq.artemis.api.core.SimpleString
 import org.apache.activemq.artemis.api.core.TransportConfiguration
 import org.apache.activemq.artemis.api.core.client.ActiveMQClient
@@ -43,7 +43,7 @@ class RPCClient<I : RPCOps>(
 
     constructor(
             hostAndPort: NetworkHostAndPort,
-            sslConfiguration: SSLConfiguration,
+            sslConfiguration: SslConfiguration,
             configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT,
             serializationContext: SerializationContext = SerializationDefaults.RPC_CLIENT_CONTEXT
     ) : this(rpcInternalClientTcpTransport(hostAndPort, sslConfiguration), configuration, serializationContext)
diff --git a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt
index 8de2892c33..6d11610d5f 100644
--- a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt
+++ b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt
@@ -4,22 +4,14 @@ package net.corda.core.internal
 
 import net.corda.core.DeleteForDJVM
 import net.corda.core.KeepForDJVM
-import net.corda.core.cordapp.Cordapp
-import net.corda.core.cordapp.CordappConfig
-import net.corda.core.cordapp.CordappContext
 import net.corda.core.crypto.*
 import net.corda.core.flows.FlowLogic
 import net.corda.core.node.ServicesForResolution
 import net.corda.core.schemas.MappedSchema
 import net.corda.core.serialization.*
-import net.corda.core.transactions.LedgerTransaction
-import net.corda.core.transactions.SignedTransaction
-import net.corda.core.transactions.TransactionBuilder
-import net.corda.core.transactions.WireTransaction
 import net.corda.core.utilities.OpaqueBytes
 import net.corda.core.utilities.UntrustworthyData
 import org.slf4j.Logger
-import org.slf4j.MDC
 import rx.Observable
 import rx.Observer
 import rx.subjects.PublishSubject
diff --git a/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt b/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt
index 6213c7794a..29d261cb92 100644
--- a/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt
+++ b/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt
@@ -96,6 +96,12 @@ data class StateMachineTransactionMapping(val stateMachineRunId: StateMachineRun
 
 /** RPC operations that the node exposes to clients. */
 interface CordaRPCOps : RPCOps {
+    /**
+     * Returns the RPC protocol version, which is the same the node's Platform Version. Exists since version 1 so guaranteed
+     * to be present.
+     */
+    override val protocolVersion: Int get() = nodeInfo().platformVersion
+
     /** Returns a list of currently in-progress state machine infos. */
     fun stateMachinesSnapshot(): List<StateMachineInfo>
 
diff --git a/experimental/flow-worker/src/integration-test/kotlin/net/corda/flowworker/FlowWorkerTest.kt b/experimental/flow-worker/src/integration-test/kotlin/net/corda/flowworker/FlowWorkerTest.kt
index b0d5513ef8..8ac9d49e09 100644
--- a/experimental/flow-worker/src/integration-test/kotlin/net/corda/flowworker/FlowWorkerTest.kt
+++ b/experimental/flow-worker/src/integration-test/kotlin/net/corda/flowworker/FlowWorkerTest.kt
@@ -91,8 +91,8 @@ class FlowWorkerTest {
         // create test certificates
         config.configureWithDevSSLCertificate()
 
-        val trustRoot = config.loadTrustStore().getCertificate(X509Utilities.CORDA_ROOT_CA)
-        val nodeCa = config.loadNodeKeyStore().getCertificate(X509Utilities.CORDA_CLIENT_CA)
+        val trustRoot = config.p2pSslOptions.trustStore.get().query { getCertificate(X509Utilities.CORDA_ROOT_CA) }
+        val nodeCa = config.signingCertificateStore.get().query { getCertificate(X509Utilities.CORDA_CLIENT_CA) }
 
         val broker = createFlowWorkerBroker(config, networkParameters.maxMessageSize)
         val bridgeControlListener = createBridgeControlListener(config, networkParameters.maxMessageSize)
@@ -146,8 +146,8 @@ class FlowWorkerTest {
         // create test certificates
         bankAConfig.configureWithDevSSLCertificate()
 
-        val bankATrustRoot = bankAConfig.loadTrustStore().getCertificate(X509Utilities.CORDA_ROOT_CA)
-        val bankANodeCa = bankAConfig.loadNodeKeyStore().getCertificate(X509Utilities.CORDA_CLIENT_CA)
+        val bankATrustRoot = bankAConfig.p2pSslOptions.trustStore.get().query { getCertificate(X509Utilities.CORDA_ROOT_CA) }
+        val bankANodeCa = bankAConfig.signingCertificateStore.get().query { getCertificate(X509Utilities.CORDA_CLIENT_CA) }
 
         val bankABroker = createFlowWorkerBroker(bankAConfig, networkParameters.maxMessageSize)
         val bankABridgeControlListener = createBridgeControlListener(bankAConfig, networkParameters.maxMessageSize)
@@ -166,8 +166,8 @@ class FlowWorkerTest {
         // create test certificates
         bankBConfig.configureWithDevSSLCertificate()
 
-        val bankBTrustRoot = bankBConfig.loadTrustStore().getCertificate(X509Utilities.CORDA_ROOT_CA)
-        val bankBNodeCa = bankBConfig.loadNodeKeyStore().getCertificate(X509Utilities.CORDA_CLIENT_CA)
+        val bankBTrustRoot = bankBConfig.p2pSslOptions.trustStore.get().query { getCertificate(X509Utilities.CORDA_ROOT_CA) }
+        val bankBNodeCa = bankBConfig.signingCertificateStore.get().query { getCertificate(X509Utilities.CORDA_CLIENT_CA) }
         // NetworkParametersCopier(networkParameters).install(bankBConfig.baseDirectory)
 
         val bankBBroker = createFlowWorkerBroker(bankBConfig, networkParameters.maxMessageSize)
@@ -234,13 +234,13 @@ class FlowWorkerTest {
     }
 
     private fun createBridgeControlListener(config: NodeConfiguration, maxMessageSize: Int): BridgeControlListener {
-        val bridgeControlListener = BridgeControlListener(config, config.messagingServerAddress!!, maxMessageSize)
+        val bridgeControlListener = BridgeControlListener(config.p2pSslOptions, config.messagingServerAddress!!, maxMessageSize)
         bridgeControlListener.start()
         return bridgeControlListener
     }
 
     private fun createArtemisClient(config: NodeConfiguration, queueAddress: String): Triple<ClientSession, ClientConsumer, ClientProducer> {
-        val artemisClient = ArtemisMessagingClient(config, config.messagingServerAddress!!, MAX_MESSAGE_SIZE)
+        val artemisClient = ArtemisMessagingClient(config.p2pSslOptions, config.messagingServerAddress!!, MAX_MESSAGE_SIZE)
         val started = artemisClient.start()
         started.session.createQueue(queueAddress, RoutingType.ANYCAST, queueAddress, true)
         return Triple(started.session, started.session.createConsumer(queueAddress), started.session.createProducer())
diff --git a/experimental/flow-worker/src/main/kotlin/net/corda/flowworker/FlowWorker.kt b/experimental/flow-worker/src/main/kotlin/net/corda/flowworker/FlowWorker.kt
index c13de9727c..4454d99e09 100644
--- a/experimental/flow-worker/src/main/kotlin/net/corda/flowworker/FlowWorker.kt
+++ b/experimental/flow-worker/src/main/kotlin/net/corda/flowworker/FlowWorker.kt
@@ -43,7 +43,7 @@ class FlowWorker(flowWorkerId: String, private val flowWorkerServiceHub: FlowWor
         flowWorkerServiceHub.start()
         runOnStop += { flowWorkerServiceHub.stop() }
 
-        val flowWorkerMessagingClient = ArtemisMessagingClient(flowWorkerServiceHub.configuration, flowWorkerServiceHub.configuration.messagingServerAddress!!, flowWorkerServiceHub.networkParameters.maxMessageSize)
+        val flowWorkerMessagingClient = ArtemisMessagingClient(flowWorkerServiceHub.configuration.p2pSslOptions, flowWorkerServiceHub.configuration.messagingServerAddress!!, flowWorkerServiceHub.networkParameters.maxMessageSize)
         runOnStop += { flowWorkerMessagingClient.stop() }
 
         val session = flowWorkerMessagingClient.start().session
diff --git a/experimental/rpc-worker/src/integration-test/kotlin/net/corda/rpcWorker/RpcFlowWorkerDriver.kt b/experimental/rpc-worker/src/integration-test/kotlin/net/corda/rpcWorker/RpcFlowWorkerDriver.kt
index 8b0a913352..4e2b152b43 100644
--- a/experimental/rpc-worker/src/integration-test/kotlin/net/corda/rpcWorker/RpcFlowWorkerDriver.kt
+++ b/experimental/rpc-worker/src/integration-test/kotlin/net/corda/rpcWorker/RpcFlowWorkerDriver.kt
@@ -59,8 +59,8 @@ data class RpcFlowWorkerDriverDSL(private val driverDSL: DriverDSLImpl) : Intern
     fun startRpcFlowWorker(myLegalName: CordaX500Name, rpcUsers: List<net.corda.testing.node.User>, numberOfFlowWorkers: Int = 1): CordaFuture<RpcFlowWorkerHandle> {
         val (config, rpcWorkerConfig, flowWorkerConfigs) = generateConfigs(myLegalName, rpcUsers, numberOfFlowWorkers)
 
-        val trustRoot = rpcWorkerConfig.loadTrustStore().getCertificate(X509Utilities.CORDA_ROOT_CA)
-        val nodeCa = rpcWorkerConfig.loadNodeKeyStore().getCertificate(X509Utilities.CORDA_CLIENT_CA)
+        val trustRoot = rpcWorkerConfig.p2pSslOptions.trustStore.get().query { getCertificate(X509Utilities.CORDA_ROOT_CA) }
+        val nodeCa = rpcWorkerConfig.signingCertificateStore.get().query { getCertificate(X509Utilities.CORDA_CLIENT_CA) }
 
         val ourKeyPair = Crypto.generateKeyPair()
         val ourParty = Party(myLegalName, ourKeyPair.public)
@@ -151,9 +151,9 @@ private fun createRpcWorkerBroker(config: NodeConfiguration, maxMessageSize: Int
     val rpcOptions = config.rpcOptions
     val securityManager = RPCSecurityManagerImpl(SecurityConfiguration.AuthService.fromUsers(config.rpcUsers))
     val broker = if (rpcOptions.useSsl) {
-        ArtemisRpcBroker.withSsl(config, rpcOptions.address, rpcOptions.adminAddress, rpcOptions.sslConfig!!, securityManager, maxMessageSize, false, config.baseDirectory / "artemis", false)
+        ArtemisRpcBroker.withSsl(config.p2pSslOptions, rpcOptions.address, rpcOptions.adminAddress, rpcOptions.sslConfig!!, securityManager, maxMessageSize, false, config.baseDirectory / "artemis", false)
     } else {
-        ArtemisRpcBroker.withoutSsl(config, rpcOptions.address, rpcOptions.adminAddress, securityManager, maxMessageSize, false, config.baseDirectory / "artemis", false)
+        ArtemisRpcBroker.withoutSsl(config.p2pSslOptions, rpcOptions.address, rpcOptions.adminAddress, securityManager, maxMessageSize, false, config.baseDirectory / "artemis", false)
     }
     broker.start()
     return broker
@@ -180,7 +180,7 @@ private fun createFlowWorker(config: NodeConfiguration, myInfo: NodeInfo, networ
 }
 
 private fun createBridgeControlListener(config: NodeConfiguration, maxMessageSize: Int): BridgeControlListener {
-    val bridgeControlListener = BridgeControlListener(config, config.messagingServerAddress!!, maxMessageSize)
+    val bridgeControlListener = BridgeControlListener(config.p2pSslOptions, config.messagingServerAddress!!, maxMessageSize)
     bridgeControlListener.start()
     return bridgeControlListener
 }
\ No newline at end of file
diff --git a/experimental/rpc-worker/src/main/kotlin/net/corda/rpcWorker/CordaRpcWorkerOps.kt b/experimental/rpc-worker/src/main/kotlin/net/corda/rpcWorker/CordaRpcWorkerOps.kt
index 074a933db5..21628ed90f 100644
--- a/experimental/rpc-worker/src/main/kotlin/net/corda/rpcWorker/CordaRpcWorkerOps.kt
+++ b/experimental/rpc-worker/src/main/kotlin/net/corda/rpcWorker/CordaRpcWorkerOps.kt
@@ -56,14 +56,13 @@ class CordaRpcWorkerOps(
         const val RPC_WORKER_QUEUE_ADDRESS_PREFIX = "${ArtemisMessagingComponent.INTERNAL_PREFIX}rpc.worker."
     }
 
-    override val protocolVersion: Int = 1000
     private val flowWorkerQueueAddress = "${FlowWorker.FLOW_WORKER_QUEUE_ADDRESS_PREFIX}${services.myInfo.legalIdentities[0].owningKey.toStringShort()}"
 
     private val rpcWorkerQueueAddress = "$RPC_WORKER_QUEUE_ADDRESS_PREFIX${services.myInfo.legalIdentities[0].owningKey.toStringShort()}"
     private val rpcWorkerId = UUID.randomUUID().toString()
     private val rpcWorkerQueueName = "$rpcWorkerQueueAddress.$rpcWorkerId"
 
-    private val artemisClient = ArtemisMessagingClient(services.configuration, services.configuration.messagingServerAddress!!, services.networkParameters.maxMessageSize)
+    private val artemisClient = ArtemisMessagingClient(services.configuration.p2pSslOptions, services.configuration.messagingServerAddress!!, services.networkParameters.maxMessageSize)
     private lateinit var session: ClientSession
     private lateinit var producer: ClientProducer
 
diff --git a/experimental/rpc-worker/src/main/kotlin/net/corda/rpcWorker/RpcWorker.kt b/experimental/rpc-worker/src/main/kotlin/net/corda/rpcWorker/RpcWorker.kt
index bdd71d0acc..ff2b8b1430 100644
--- a/experimental/rpc-worker/src/main/kotlin/net/corda/rpcWorker/RpcWorker.kt
+++ b/experimental/rpc-worker/src/main/kotlin/net/corda/rpcWorker/RpcWorker.kt
@@ -77,8 +77,8 @@ class Main : Runnable {
         val ourKeyPair = getIdentity()
         val myInfo = getNodeInfo()
 
-        val trustRoot = rpcWorkerConfig.loadTrustStore().getCertificate(X509Utilities.CORDA_ROOT_CA)
-        val nodeCa = rpcWorkerConfig.loadNodeKeyStore().getCertificate(X509Utilities.CORDA_CLIENT_CA)
+        val trustRoot = rpcWorkerConfig.p2pSslOptions.trustStore.get().query { getCertificate(X509Utilities.CORDA_ROOT_CA) }
+        val nodeCa = rpcWorkerConfig.signingCertificateStore.get().query { getCertificate(X509Utilities.CORDA_CLIENT_CA) }
 
         val signedNetworkParameters = NetworkParametersReader(trustRoot, null, rpcWorkerConfig.baseDirectory).read()
         val rpcWorkerBroker = createRpcWorkerBroker(rpcWorkerConfig, signedNetworkParameters.networkParameters.maxMessageSize)
@@ -101,9 +101,9 @@ class Main : Runnable {
         val rpcOptions = config.rpcOptions
         val securityManager = RPCSecurityManagerImpl(SecurityConfiguration.AuthService.fromUsers(config.rpcUsers))
         val broker = if (rpcOptions.useSsl) {
-            ArtemisRpcBroker.withSsl(config, rpcOptions.address, rpcOptions.adminAddress, rpcOptions.sslConfig!!, securityManager, maxMessageSize, false, config.baseDirectory / "artemis", false)
+            ArtemisRpcBroker.withSsl(config.p2pSslOptions, rpcOptions.address, rpcOptions.adminAddress, rpcOptions.sslConfig!!, securityManager, maxMessageSize, false, config.baseDirectory / "artemis", false)
         } else {
-            ArtemisRpcBroker.withoutSsl(config, rpcOptions.address, rpcOptions.adminAddress, securityManager, maxMessageSize, false, config.baseDirectory / "artemis", false)
+            ArtemisRpcBroker.withoutSsl(config.p2pSslOptions, rpcOptions.address, rpcOptions.adminAddress, securityManager, maxMessageSize, false, config.baseDirectory / "artemis", false)
         }
         broker.start()
         return broker
@@ -126,9 +126,9 @@ class RpcWorker(private val rpcWorkerServiceHub: RpcWorkerServiceHub, private va
                 rpcThreadPoolSize = rpcWorkerServiceHub.configuration.enterpriseConfiguration.tuning.rpcThreadPoolSize
         )
         val securityManager = RPCSecurityManagerImpl(SecurityConfiguration.AuthService.fromUsers(rpcWorkerServiceHub.configuration.rpcUsers))
-        val nodeName = CordaX500Name.build(rpcWorkerServiceHub.configuration.loadSslKeyStore().getCertificate(X509Utilities.CORDA_CLIENT_TLS).subjectX500Principal)
+        val nodeName = CordaX500Name.build(rpcWorkerServiceHub.configuration.p2pSslOptions.keyStore.get().query { getCertificate(X509Utilities.CORDA_CLIENT_TLS).subjectX500Principal })
 
-        val internalRpcMessagingClient = InternalRPCMessagingClient(rpcWorkerServiceHub.configuration, rpcWorkerServiceHub.configuration.rpcOptions.adminAddress, Node.MAX_RPC_MESSAGE_SIZE, nodeName, rpcServerConfiguration)
+        val internalRpcMessagingClient = InternalRPCMessagingClient(rpcWorkerServiceHub.configuration.p2pSslOptions, rpcWorkerServiceHub.configuration.rpcOptions.adminAddress, Node.MAX_RPC_MESSAGE_SIZE, nodeName, rpcServerConfiguration)
         internalRpcMessagingClient.init(rpcWorkerServiceHub.rpcOps, securityManager)
         internalRpcMessagingClient.start(serverControl)
 
diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/ArtemisTcpTransport.kt b/node-api/src/main/kotlin/net/corda/nodeapi/ArtemisTcpTransport.kt
index 18fbad6ff2..210f04586a 100644
--- a/node-api/src/main/kotlin/net/corda/nodeapi/ArtemisTcpTransport.kt
+++ b/node-api/src/main/kotlin/net/corda/nodeapi/ArtemisTcpTransport.kt
@@ -1,12 +1,16 @@
 package net.corda.nodeapi
 
 import net.corda.core.messaging.ClientRpcSslOptions
-import net.corda.core.serialization.internal.nodeSerializationEnv
 import net.corda.core.utilities.NetworkHostAndPort
+import net.corda.nodeapi.internal.InternalArtemisTcpTransport
+import net.corda.nodeapi.internal.InternalArtemisTcpTransport.Companion.acceptorFactoryClassName
+import net.corda.nodeapi.internal.InternalArtemisTcpTransport.Companion.connectorFactoryClassName
+import net.corda.nodeapi.internal.InternalArtemisTcpTransport.Companion.defaultArtemisOptions
+import net.corda.nodeapi.internal.InternalArtemisTcpTransport.Companion.defaultSSLOptions
 import net.corda.nodeapi.internal.config.SSLConfiguration
+import net.corda.nodeapi.internal.config.SslConfiguration
 import net.corda.nodeapi.internal.requireOnDefaultFileSystem
 import org.apache.activemq.artemis.api.core.TransportConfiguration
-import org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnectorFactory
 import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants
 import java.nio.file.Path
 
@@ -38,26 +42,6 @@ class ArtemisTcpTransport {
         /** Supported TLS versions, currently TLSv1.2 only. */
         val TLS_VERSIONS = listOf("TLSv1.2")
 
-        private fun defaultArtemisOptions(hostAndPort: NetworkHostAndPort) = mapOf(
-                // Basic TCP target details.
-                TransportConstants.HOST_PROP_NAME to hostAndPort.host,
-                TransportConstants.PORT_PROP_NAME to hostAndPort.port,
-
-                // Turn on AMQP support, which needs the protocol jar on the classpath.
-                // Unfortunately we cannot disable core protocol as artemis only uses AMQP for interop.
-                // It does not use AMQP messages for its own messages e.g. topology and heartbeats.
-                // TODO further investigate how to ensure we use a well defined wire level protocol for Node to Node communications.
-                TransportConstants.PROTOCOLS_PROP_NAME to "CORE,AMQP",
-                TransportConstants.USE_GLOBAL_WORKER_POOL_PROP_NAME to (nodeSerializationEnv != null),
-                TransportConstants.REMOTING_THREADS_PROPNAME to (if (nodeSerializationEnv != null) -1 else 1),
-                // turn off direct delivery in Artemis - this is latency optimisation that can lead to
-                //hick-ups under high load (CORDA-1336)
-                TransportConstants.DIRECT_DELIVER to false)
-
-        private val defaultSSLOptions = mapOf(
-                TransportConstants.ENABLED_CIPHER_SUITES_PROP_NAME to CIPHER_SUITES.joinToString(","),
-                TransportConstants.ENABLED_PROTOCOLS_PROP_NAME to TLS_VERSIONS.joinToString(","))
-
         private fun SSLConfiguration.toTransportOptions() = mapOf(
                 TransportConstants.SSL_ENABLED_PROP_NAME to true,
                 TransportConstants.KEYSTORE_PROVIDER_PROP_NAME to "JKS",
@@ -68,22 +52,6 @@ class ArtemisTcpTransport {
                 TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME to trustStorePassword,
                 TransportConstants.NEED_CLIENT_AUTH_PROP_NAME to true)
 
-        private fun ClientRpcSslOptions.toTransportOptions() = mapOf(
-                TransportConstants.SSL_ENABLED_PROP_NAME to true,
-                TransportConstants.TRUSTSTORE_PROVIDER_PROP_NAME to trustStoreProvider,
-                TransportConstants.TRUSTSTORE_PATH_PROP_NAME to trustStorePath,
-                TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME to trustStorePassword)
-
-        private fun BrokerRpcSslOptions.toTransportOptions() = mapOf(
-                TransportConstants.SSL_ENABLED_PROP_NAME to true,
-                TransportConstants.KEYSTORE_PROVIDER_PROP_NAME to "JKS",
-                TransportConstants.KEYSTORE_PATH_PROP_NAME to keyStorePath,
-                TransportConstants.KEYSTORE_PASSWORD_PROP_NAME to keyStorePassword,
-                TransportConstants.NEED_CLIENT_AUTH_PROP_NAME to false)
-
-        private val acceptorFactoryClassName = "org.apache.activemq.artemis.core.remoting.impl.netty.NettyAcceptorFactory"
-        private val connectorFactoryClassName = NettyConnectorFactory::class.java.name
-
         fun p2pAcceptorTcpTransport(hostAndPort: NetworkHostAndPort, config: SSLConfiguration?, enableSSL: Boolean = true): TransportConfiguration {
             val options = defaultArtemisOptions(hostAndPort).toMutableMap()
 
@@ -110,27 +78,13 @@ class ArtemisTcpTransport {
 
         /** [TransportConfiguration] for RPC TCP communication - server side. */
         fun rpcAcceptorTcpTransport(hostAndPort: NetworkHostAndPort, config: BrokerRpcSslOptions?, enableSSL: Boolean = true): TransportConfiguration {
-            val options = defaultArtemisOptions(hostAndPort).toMutableMap()
-
-            if (config != null && enableSSL) {
-                config.keyStorePath.requireOnDefaultFileSystem()
-                options.putAll(config.toTransportOptions())
-                options.putAll(defaultSSLOptions)
-            }
-            return TransportConfiguration(acceptorFactoryClassName, options)
+            return InternalArtemisTcpTransport.rpcAcceptorTcpTransport(hostAndPort, config, enableSSL)
         }
 
         /** [TransportConfiguration] for RPC TCP communication
          * This is the Transport that connects the client JVM to the broker. */
         fun rpcConnectorTcpTransport(hostAndPort: NetworkHostAndPort, config: ClientRpcSslOptions?, enableSSL: Boolean = true): TransportConfiguration {
-            val options = defaultArtemisOptions(hostAndPort).toMutableMap()
-
-            if (config != null && enableSSL) {
-                config.trustStorePath.requireOnDefaultFileSystem()
-                options.putAll(config.toTransportOptions())
-                options.putAll(defaultSSLOptions)
-            }
-            return TransportConfiguration(connectorFactoryClassName, options)
+            return InternalArtemisTcpTransport.rpcConnectorTcpTransport(hostAndPort, config, enableSSL)
         }
 
         /** Create as list of [TransportConfiguration]. **/
@@ -138,12 +92,12 @@ class ArtemisTcpTransport {
             rpcConnectorTcpTransport(it, config, enableSSL)
         }
 
-        fun rpcInternalClientTcpTransport(hostAndPort: NetworkHostAndPort, config: SSLConfiguration): TransportConfiguration {
-            return TransportConfiguration(connectorFactoryClassName, defaultArtemisOptions(hostAndPort) + defaultSSLOptions + config.toTransportOptions())
+        fun rpcInternalClientTcpTransport(hostAndPort: NetworkHostAndPort, config: SslConfiguration): TransportConfiguration {
+            return InternalArtemisTcpTransport.rpcInternalClientTcpTransport(hostAndPort, config)
         }
 
-        fun rpcInternalAcceptorTcpTransport(hostAndPort: NetworkHostAndPort, config: SSLConfiguration): TransportConfiguration {
-            return TransportConfiguration(acceptorFactoryClassName, defaultArtemisOptions(hostAndPort) + defaultSSLOptions + config.toTransportOptions())
+        fun rpcInternalAcceptorTcpTransport(hostAndPort: NetworkHostAndPort, config: SslConfiguration): TransportConfiguration {
+            return InternalArtemisTcpTransport.rpcInternalAcceptorTcpTransport(hostAndPort, config)
         }
     }
 }
diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/ArtemisMessagingClient.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/ArtemisMessagingClient.kt
index f814f18795..ae1a9defca 100644
--- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/ArtemisMessagingClient.kt
+++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/ArtemisMessagingClient.kt
@@ -3,9 +3,9 @@ package net.corda.nodeapi.internal
 import net.corda.core.serialization.internal.nodeSerializationEnv
 import net.corda.core.utilities.NetworkHostAndPort
 import net.corda.core.utilities.loggerFor
-import net.corda.nodeapi.ArtemisTcpTransport
 import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_P2P_USER
-import net.corda.nodeapi.internal.config.SSLConfiguration
+import net.corda.nodeapi.internal.config.MutualSslConfiguration
+import org.apache.activemq.artemis.api.core.client.ActiveMQClient
 import org.apache.activemq.artemis.api.core.client.*
 import org.apache.activemq.artemis.api.core.client.ActiveMQClient.DEFAULT_ACK_BATCH_SIZE
 
@@ -15,14 +15,12 @@ interface ArtemisSessionProvider {
     val started: ArtemisMessagingClient.Started?
 }
 
-class ArtemisMessagingClient(
-        private val config: SSLConfiguration,
-        private val serverAddress: NetworkHostAndPort,
-        private val maxMessageSize: Int,
-        private val autoCommitSends: Boolean = true,
-        private val autoCommitAcks: Boolean = true,
-        private val confirmationWindowSize: Int = -1
-)  : ArtemisSessionProvider {
+class ArtemisMessagingClient(private val config: MutualSslConfiguration,
+                             private val serverAddress: NetworkHostAndPort,
+                             private val maxMessageSize: Int,
+                             private val autoCommitSends: Boolean = true,
+                             private val autoCommitAcks: Boolean = true,
+                             private val confirmationWindowSize: Int = -1) : ArtemisSessionProvider {
     companion object {
         private val log = loggerFor<ArtemisMessagingClient>()
     }
@@ -36,7 +34,7 @@ class ArtemisMessagingClient(
         check(started == null) { "start can't be called twice" }
         log.info("Connecting to message broker: $serverAddress")
         // TODO Add broker CN to config for host verification in case the embedded broker isn't used
-        val tcpTransport = ArtemisTcpTransport.p2pConnectorTcpTransport(serverAddress, config)
+        val tcpTransport = InternalArtemisTcpTransport.p2pConnectorTcpTransport(serverAddress, config)
         val locator = ActiveMQClient.createServerLocatorWithoutHA(tcpTransport).apply {
             // Never time out on our loopback Artemis connections. If we switch back to using the InVM transport this
             // would be the default and the two lines below can be deleted.
diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/DevIdentityGenerator.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/DevIdentityGenerator.kt
index 080bf680c4..ab840ed387 100644
--- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/DevIdentityGenerator.kt
+++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/DevIdentityGenerator.kt
@@ -7,7 +7,8 @@ import net.corda.core.identity.Party
 import net.corda.core.internal.createDirectories
 import net.corda.core.internal.div
 import net.corda.core.utilities.trace
-import net.corda.nodeapi.internal.config.NodeSSLConfiguration
+import net.corda.nodeapi.internal.config.FileBasedCertificateStoreSupplier
+import net.corda.nodeapi.internal.config.SslConfiguration
 import net.corda.nodeapi.internal.crypto.CertificateType
 import net.corda.nodeapi.internal.crypto.X509KeyStore
 import net.corda.nodeapi.internal.crypto.X509Utilities
@@ -31,15 +32,15 @@ object DevIdentityGenerator {
 
     /** Install a node key store for the given node directory using the given legal name. */
     fun installKeyStoreWithNodeIdentity(nodeDir: Path, legalName: CordaX500Name): Party {
-        val nodeSslConfig = object : NodeSSLConfiguration {
-            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
-        }
+        val certificatesDirectory = nodeDir / "certificates"
+        val signingCertStore = FileBasedCertificateStoreSupplier(certificatesDirectory / "nodekeystore.jks", "cordacadevpass")
+        val p2pKeyStore = FileBasedCertificateStoreSupplier(certificatesDirectory / "sslkeystore.jks", "cordacadevpass")
+        val p2pTrustStore = FileBasedCertificateStoreSupplier(certificatesDirectory / "truststore.jks", "trustpass")
+        val p2pSslConfig = SslConfiguration.mutual(p2pKeyStore, p2pTrustStore)
 
-        nodeSslConfig.certificatesDirectory.createDirectories()
-        val (nodeKeyStore) = nodeSslConfig.createDevKeyStores(legalName)
+        certificatesDirectory.createDirectories()
+        val nodeKeyStore = signingCertStore.get(true).also { it.registerDevSigningCertificates(legalName) }
+        p2pSslConfig.keyStore.get(true).also { it.registerDevP2pCertificates(legalName) }
 
         val identity = nodeKeyStore.storeLegalIdentity("$NODE_IDENTITY_ALIAS_PREFIX-private-key")
         return identity.party
diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/InternalArtemisTcpTransport.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/InternalArtemisTcpTransport.kt
new file mode 100644
index 0000000000..85513c9ad1
--- /dev/null
+++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/InternalArtemisTcpTransport.kt
@@ -0,0 +1,166 @@
+package net.corda.nodeapi.internal
+
+import net.corda.core.messaging.ClientRpcSslOptions
+import net.corda.core.serialization.internal.nodeSerializationEnv
+import net.corda.core.utilities.NetworkHostAndPort
+import net.corda.nodeapi.BrokerRpcSslOptions
+import net.corda.nodeapi.internal.config.CertificateStore
+import net.corda.nodeapi.internal.config.FileBasedCertificateStoreSupplier
+import net.corda.nodeapi.internal.config.SslConfiguration
+import net.corda.nodeapi.internal.config.MutualSslConfiguration
+import org.apache.activemq.artemis.api.core.TransportConfiguration
+import org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnectorFactory
+import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants
+import java.nio.file.Path
+
+// This avoids internal types from leaking in the public API. The "external" ArtemisTcpTransport delegates to this internal one.
+class InternalArtemisTcpTransport {
+    companion object {
+        val CIPHER_SUITES = listOf(
+                "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
+                "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
+                "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256"
+        )
+
+        val TLS_VERSIONS = listOf("TLSv1.2")
+
+        internal fun defaultArtemisOptions(hostAndPort: NetworkHostAndPort) = mapOf(
+                // Basic TCP target details.
+                TransportConstants.HOST_PROP_NAME to hostAndPort.host,
+                TransportConstants.PORT_PROP_NAME to hostAndPort.port,
+
+                // Turn on AMQP support, which needs the protocol jar on the classpath.
+                // Unfortunately we cannot disable core protocol as artemis only uses AMQP for interop.
+                // It does not use AMQP messages for its own messages e.g. topology and heartbeats.
+                // TODO further investigate how to ensure we use a well defined wire level protocol for Node to Node communications.
+                TransportConstants.PROTOCOLS_PROP_NAME to "CORE,AMQP",
+                TransportConstants.USE_GLOBAL_WORKER_POOL_PROP_NAME to (nodeSerializationEnv != null),
+                TransportConstants.REMOTING_THREADS_PROPNAME to (if (nodeSerializationEnv != null) -1 else 1),
+                // turn off direct delivery in Artemis - this is latency optimisation that can lead to
+                //hick-ups under high load (CORDA-1336)
+                TransportConstants.DIRECT_DELIVER to false)
+
+        internal val defaultSSLOptions = mapOf(
+                TransportConstants.ENABLED_CIPHER_SUITES_PROP_NAME to CIPHER_SUITES.joinToString(","),
+                TransportConstants.ENABLED_PROTOCOLS_PROP_NAME to TLS_VERSIONS.joinToString(","))
+
+        private fun SslConfiguration.toTransportOptions(): Map<String, Any> {
+
+            val options = mutableMapOf<String, Any>()
+            (keyStore to trustStore).addToTransportOptions(options)
+            return options
+        }
+
+        private fun Pair<FileBasedCertificateStoreSupplier?, FileBasedCertificateStoreSupplier?>.addToTransportOptions(options: MutableMap<String, Any>) {
+
+            val keyStore = first
+            val trustStore = second
+            keyStore?.let {
+                with (it) {
+                    path.requireOnDefaultFileSystem()
+                    options.putAll(get().toKeyStoreTransportOptions(path))
+                }
+            }
+            trustStore?.let {
+                with (it) {
+                    path.requireOnDefaultFileSystem()
+                    options.putAll(get().toTrustStoreTransportOptions(path))
+                }
+            }
+        }
+
+        private fun CertificateStore.toKeyStoreTransportOptions(path: Path) = mapOf(
+                TransportConstants.SSL_ENABLED_PROP_NAME to true,
+                TransportConstants.KEYSTORE_PROVIDER_PROP_NAME to "JKS",
+                TransportConstants.KEYSTORE_PATH_PROP_NAME to path,
+                TransportConstants.KEYSTORE_PASSWORD_PROP_NAME to password,
+                TransportConstants.NEED_CLIENT_AUTH_PROP_NAME to true)
+
+        private fun CertificateStore.toTrustStoreTransportOptions(path: Path) = mapOf(
+                TransportConstants.SSL_ENABLED_PROP_NAME to true,
+                TransportConstants.TRUSTSTORE_PROVIDER_PROP_NAME to "JKS",
+                TransportConstants.TRUSTSTORE_PATH_PROP_NAME to path,
+                TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME to password,
+                TransportConstants.NEED_CLIENT_AUTH_PROP_NAME to true)
+
+        private fun ClientRpcSslOptions.toTransportOptions() = mapOf(
+                TransportConstants.SSL_ENABLED_PROP_NAME to true,
+                TransportConstants.TRUSTSTORE_PROVIDER_PROP_NAME to trustStoreProvider,
+                TransportConstants.TRUSTSTORE_PATH_PROP_NAME to trustStorePath,
+                TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME to trustStorePassword)
+
+        private fun BrokerRpcSslOptions.toTransportOptions() = mapOf(
+                TransportConstants.SSL_ENABLED_PROP_NAME to true,
+                TransportConstants.KEYSTORE_PROVIDER_PROP_NAME to "JKS",
+                TransportConstants.KEYSTORE_PATH_PROP_NAME to keyStorePath,
+                TransportConstants.KEYSTORE_PASSWORD_PROP_NAME to keyStorePassword,
+                TransportConstants.NEED_CLIENT_AUTH_PROP_NAME to false)
+
+        internal val acceptorFactoryClassName = "org.apache.activemq.artemis.core.remoting.impl.netty.NettyAcceptorFactory"
+        internal val connectorFactoryClassName = NettyConnectorFactory::class.java.name
+
+        fun p2pAcceptorTcpTransport(hostAndPort: NetworkHostAndPort, config: MutualSslConfiguration?, enableSSL: Boolean = true): TransportConfiguration {
+
+            return p2pAcceptorTcpTransport(hostAndPort, config?.keyStore, config?.trustStore, enableSSL = enableSSL)
+        }
+
+        fun p2pConnectorTcpTransport(hostAndPort: NetworkHostAndPort, config: MutualSslConfiguration?, enableSSL: Boolean = true): TransportConfiguration {
+
+            return p2pConnectorTcpTransport(hostAndPort, config?.keyStore, config?.trustStore, enableSSL = enableSSL)
+        }
+
+        fun p2pAcceptorTcpTransport(hostAndPort: NetworkHostAndPort, keyStore: FileBasedCertificateStoreSupplier?, trustStore: FileBasedCertificateStoreSupplier?, enableSSL: Boolean = true): TransportConfiguration {
+
+            val options = defaultArtemisOptions(hostAndPort).toMutableMap()
+            if (enableSSL) {
+                options.putAll(defaultSSLOptions)
+                (keyStore to trustStore).addToTransportOptions(options)
+            }
+            return TransportConfiguration(acceptorFactoryClassName, options)
+        }
+
+        fun p2pConnectorTcpTransport(hostAndPort: NetworkHostAndPort, keyStore: FileBasedCertificateStoreSupplier?, trustStore: FileBasedCertificateStoreSupplier?, enableSSL: Boolean = true): TransportConfiguration {
+
+            val options = defaultArtemisOptions(hostAndPort).toMutableMap()
+            if (enableSSL) {
+                options.putAll(defaultSSLOptions)
+                (keyStore to trustStore).addToTransportOptions(options)
+            }
+            return TransportConfiguration(connectorFactoryClassName, options)
+        }
+
+        fun rpcAcceptorTcpTransport(hostAndPort: NetworkHostAndPort, config: BrokerRpcSslOptions?, enableSSL: Boolean = true): TransportConfiguration {
+            val options = defaultArtemisOptions(hostAndPort).toMutableMap()
+
+            if (config != null && enableSSL) {
+                config.keyStorePath.requireOnDefaultFileSystem()
+                options.putAll(config.toTransportOptions())
+                options.putAll(defaultSSLOptions)
+            }
+            return TransportConfiguration(acceptorFactoryClassName, options)
+        }
+
+        fun rpcConnectorTcpTransport(hostAndPort: NetworkHostAndPort, config: ClientRpcSslOptions?, enableSSL: Boolean = true): TransportConfiguration {
+            val options = defaultArtemisOptions(hostAndPort).toMutableMap()
+
+            if (config != null && enableSSL) {
+                config.trustStorePath.requireOnDefaultFileSystem()
+                options.putAll(config.toTransportOptions())
+                options.putAll(defaultSSLOptions)
+            }
+            return TransportConfiguration(connectorFactoryClassName, options)
+        }
+
+        fun rpcConnectorTcpTransportsFromList(hostAndPortList: List<NetworkHostAndPort>, config: ClientRpcSslOptions?, enableSSL: Boolean = true): List<TransportConfiguration> = hostAndPortList.map {
+            rpcConnectorTcpTransport(it, config, enableSSL)
+        }
+
+        fun rpcInternalClientTcpTransport(hostAndPort: NetworkHostAndPort, config: SslConfiguration): TransportConfiguration {
+            return TransportConfiguration(connectorFactoryClassName, defaultArtemisOptions(hostAndPort) + defaultSSLOptions + config.toTransportOptions())
+        }
+
+        fun rpcInternalAcceptorTcpTransport(hostAndPort: NetworkHostAndPort, config: SslConfiguration): TransportConfiguration {
+            return TransportConfiguration(acceptorFactoryClassName, defaultArtemisOptions(hostAndPort) + defaultSSLOptions + config.toTransportOptions())
+        }
+    }
+}
\ No newline at end of file
diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt
index 6d2ca5b271..d8a856afa5 100644
--- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt
+++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/KeyStoreConfigHelpers.kt
@@ -5,7 +5,7 @@ import net.corda.core.crypto.Crypto.generateKeyPair
 import net.corda.core.identity.CordaX500Name
 import net.corda.core.identity.PartyAndCertificate
 import net.corda.core.internal.toX500Name
-import net.corda.nodeapi.internal.config.SSLConfiguration
+import net.corda.nodeapi.internal.config.CertificateStore
 import net.corda.nodeapi.internal.crypto.*
 import org.bouncycastle.asn1.x509.GeneralName
 import org.bouncycastle.asn1.x509.GeneralSubtree
@@ -20,48 +20,43 @@ import javax.security.auth.x500.X500Principal
  * Create the node and SSL key stores needed by a node. The node key store will be populated with a node CA cert (using
  * the given legal name), and the SSL key store will store the TLS cert which is a sub-cert of the node CA.
  */
-fun SSLConfiguration.createDevKeyStores(legalName: CordaX500Name,
-                                        rootCert: X509Certificate = DEV_ROOT_CA.certificate,
-                                        intermediateCa: CertificateAndKeyPair = DEV_INTERMEDIATE_CA): Pair<X509KeyStore, X509KeyStore> {
-    val (nodeCaCert, nodeCaKeyPair) = createDevNodeCa(intermediateCa, legalName)
 
-    val nodeKeyStore = loadNodeKeyStore(createNew = true)
-    nodeKeyStore.update {
-        setPrivateKey(
-                X509Utilities.CORDA_CLIENT_CA,
-                nodeCaKeyPair.private,
-                listOf(nodeCaCert, intermediateCa.certificate, rootCert))
+fun CertificateStore.registerDevSigningCertificates(legalName: CordaX500Name,
+                                                    rootCert: X509Certificate = DEV_ROOT_CA.certificate,
+                                                    intermediateCa: CertificateAndKeyPair = DEV_INTERMEDIATE_CA,
+                                                    devNodeCa: CertificateAndKeyPair = createDevNodeCa(intermediateCa, legalName)) {
+
+    update {
+        setPrivateKey(X509Utilities.CORDA_CLIENT_CA, devNodeCa.keyPair.private, listOf(devNodeCa.certificate, intermediateCa.certificate, rootCert))
     }
-
-    val sslKeyStore = loadSslKeyStore(createNew = true)
-    sslKeyStore.update {
-        val tlsKeyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
-        val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, legalName.x500Principal, tlsKeyPair.public)
-        setPrivateKey(
-                X509Utilities.CORDA_CLIENT_TLS,
-                tlsKeyPair.private,
-                listOf(tlsCert, nodeCaCert, intermediateCa.certificate, rootCert))
-    }
-
-    return Pair(nodeKeyStore, sslKeyStore)
 }
 
-fun X509KeyStore.storeLegalIdentity(alias: String, keyPair: KeyPair = Crypto.generateKeyPair()): PartyAndCertificate {
-    val nodeCaCertPath = getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
-    // Assume key password = store password.
-    val nodeCaCertAndKeyPair = getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)
-    // Create new keys and store in keystore.
-    val identityCert = X509Utilities.createCertificate(
-            CertificateType.LEGAL_IDENTITY,
-            nodeCaCertAndKeyPair.certificate,
-            nodeCaCertAndKeyPair.keyPair,
-            nodeCaCertAndKeyPair.certificate.subjectX500Principal,
-            keyPair.public)
-    // TODO: X509Utilities.validateCertificateChain()
-    // Assume key password = store password.
-    val identityCertPath = listOf(identityCert) + nodeCaCertPath
-    setPrivateKey(alias, keyPair.private, identityCertPath)
-    save()
+fun CertificateStore.registerDevP2pCertificates(legalName: CordaX500Name,
+                                                rootCert: X509Certificate = DEV_ROOT_CA.certificate,
+                                                intermediateCa: CertificateAndKeyPair = DEV_INTERMEDIATE_CA,
+                                                devNodeCa: CertificateAndKeyPair = createDevNodeCa(intermediateCa, legalName)) {
+
+    update {
+        val tlsKeyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
+        val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, devNodeCa.certificate, devNodeCa.keyPair, legalName.x500Principal, tlsKeyPair.public)
+        setPrivateKey(X509Utilities.CORDA_CLIENT_TLS, tlsKeyPair.private, listOf(tlsCert, devNodeCa.certificate, intermediateCa.certificate, rootCert))
+    }
+}
+
+fun CertificateStore.storeLegalIdentity(alias: String, keyPair: KeyPair = Crypto.generateKeyPair()): PartyAndCertificate {
+    val identityCertPath = query {
+        val nodeCaCertPath = getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
+        // Assume key password = store password.
+        val nodeCaCertAndKeyPair = getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)
+        // Create new keys and store in keystore.
+        val identityCert = X509Utilities.createCertificate(CertificateType.LEGAL_IDENTITY, nodeCaCertAndKeyPair.certificate, nodeCaCertAndKeyPair.keyPair, nodeCaCertAndKeyPair.certificate.subjectX500Principal, keyPair.public)
+        // TODO: X509Utilities.validateCertificateChain()
+        // Assume key password = store password.
+        listOf(identityCert) + nodeCaCertPath
+    }
+    update {
+        setPrivateKey(alias, keyPair.private, identityCertPath)
+    }
     return PartyAndCertificate(X509Utilities.buildCertPath(identityCertPath))
 }
 
@@ -105,8 +100,10 @@ const val DEV_CA_TRUST_STORE_PASS: String = "trustpass"
 // We need a class so that we can get hold of the class loader
 internal object DevCaHelper {
     fun loadDevCa(alias: String): CertificateAndKeyPair {
-        // TODO: Should be identity scheme
-        val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/$DEV_CA_KEY_STORE_FILE"), DEV_CA_KEY_STORE_PASS)
-        return caKeyStore.getCertificateAndKeyPair(alias, DEV_CA_PRIVATE_KEY_PASS)
+        return loadDevCaKeyStore().query { getCertificateAndKeyPair(alias, DEV_CA_PRIVATE_KEY_PASS) }
     }
 }
+
+fun loadDevCaKeyStore(classLoader: ClassLoader = DevCaHelper::class.java.classLoader): CertificateStore = CertificateStore.fromResource("certificates/$DEV_CA_KEY_STORE_FILE", DEV_CA_KEY_STORE_PASS, classLoader)
+
+fun loadDevCaTrustStore(classLoader: ClassLoader = DevCaHelper::class.java.classLoader): CertificateStore = CertificateStore.fromResource("certificates/$DEV_CA_TRUST_STORE_FILE", DEV_CA_TRUST_STORE_PASS, classLoader)
diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/bridging/AMQPBridgeManager.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/bridging/AMQPBridgeManager.kt
index cf46ee8b6a..26093597ab 100644
--- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/bridging/AMQPBridgeManager.kt
+++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/bridging/AMQPBridgeManager.kt
@@ -14,7 +14,8 @@ import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2PMessagi
 import net.corda.nodeapi.internal.ArtemisMessagingComponent.RemoteInboxAddress.Companion.translateLocalQueueToInboxAddress
 import net.corda.nodeapi.internal.ArtemisSessionProvider
 import net.corda.nodeapi.internal.bridging.AMQPBridgeManager.AMQPBridge.Companion.getBridgeName
-import net.corda.nodeapi.internal.config.NodeSSLConfiguration
+import net.corda.nodeapi.internal.config.CertificateStore
+import net.corda.nodeapi.internal.config.MutualSslConfiguration
 import net.corda.nodeapi.internal.protonwrapper.messages.MessageStatus
 import net.corda.nodeapi.internal.protonwrapper.netty.AMQPClient
 import net.corda.nodeapi.internal.protonwrapper.netty.AMQPConfiguration
@@ -26,7 +27,6 @@ import org.apache.activemq.artemis.api.core.client.ClientMessage
 import org.apache.activemq.artemis.api.core.client.ClientSession
 import org.slf4j.MDC
 import rx.Subscription
-import java.security.KeyStore
 import java.util.concurrent.locks.ReentrantLock
 import kotlin.concurrent.withLock
 
@@ -38,30 +38,23 @@ import kotlin.concurrent.withLock
  *  The Netty thread pool used by the AMQPBridges is also shared and managed by the AMQPBridgeManager.
  */
 @VisibleForTesting
-class AMQPBridgeManager(config: NodeSSLConfiguration, socksProxyConfig: SocksProxyConfig? = null,
-                        maxMessageSize: Int, private val artemisMessageClientFactory: () -> ArtemisSessionProvider) : BridgeManager {
+class AMQPBridgeManager(config: MutualSslConfiguration, socksProxyConfig: SocksProxyConfig? = null, maxMessageSize: Int, private val artemisMessageClientFactory: () -> ArtemisSessionProvider) : BridgeManager {
 
     private val lock = ReentrantLock()
     private val bridgeNameToBridgeMap = mutableMapOf<String, AMQPBridge>()
 
-    private class AMQPConfigurationImpl private constructor(override val keyStore: KeyStore,
-                                                            override val keyStorePrivateKeyPassword: CharArray,
-                                                            override val trustStore: KeyStore,
+    private class AMQPConfigurationImpl private constructor(override val keyStore: CertificateStore,
+                                                            override val trustStore: CertificateStore,
                                                             override val socksProxyConfig: SocksProxyConfig?,
                                                             override val maxMessageSize: Int) : AMQPConfiguration {
-        constructor(config: NodeSSLConfiguration, socksProxyConfig: SocksProxyConfig?, maxMessageSize: Int) : this(config.loadSslKeyStore().internal,
-                config.keyStorePassword.toCharArray(),
-                config.loadTrustStore().internal,
-                socksProxyConfig,
-                maxMessageSize)
+        constructor(config: MutualSslConfiguration, socksProxyConfig: SocksProxyConfig?, maxMessageSize: Int) : this(config.keyStore.get(), config.trustStore.get(), socksProxyConfig, maxMessageSize)
     }
 
     private val amqpConfig: AMQPConfiguration = AMQPConfigurationImpl(config, socksProxyConfig, maxMessageSize)
     private var sharedEventLoopGroup: EventLoopGroup? = null
     private var artemis: ArtemisSessionProvider? = null
 
-    constructor(config: NodeSSLConfiguration, p2pAddress: NetworkHostAndPort, maxMessageSize: Int, socksProxyConfig: SocksProxyConfig? = null) : this(config, socksProxyConfig, maxMessageSize, { ArtemisMessagingClient(config, p2pAddress, maxMessageSize) })
-
+    constructor(config: MutualSslConfiguration, p2pAddress: NetworkHostAndPort, maxMessageSize: Int, socksProxyConfig: SocksProxyConfig? = null) : this(config, socksProxyConfig, maxMessageSize, { ArtemisMessagingClient(config, p2pAddress, maxMessageSize) })
 
     companion object {
         private const val NUM_BRIDGE_THREADS = 0 // Default sized pool
diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/bridging/BridgeControlListener.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/bridging/BridgeControlListener.kt
index dca59de998..b9037e3f87 100644
--- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/bridging/BridgeControlListener.kt
+++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/bridging/BridgeControlListener.kt
@@ -11,9 +11,9 @@ import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.BRIDGE_NOT
 import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_PREFIX
 import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEERS_PREFIX
 import net.corda.nodeapi.internal.ArtemisSessionProvider
-import net.corda.nodeapi.internal.config.NodeSSLConfiguration
 import net.corda.nodeapi.internal.protonwrapper.netty.SocksProxyConfig
 import org.apache.activemq.artemis.api.core.ActiveMQQueueExistsException
+import net.corda.nodeapi.internal.config.MutualSslConfiguration
 import org.apache.activemq.artemis.api.core.RoutingType
 import org.apache.activemq.artemis.api.core.SimpleString
 import org.apache.activemq.artemis.api.core.client.ClientConsumer
@@ -23,8 +23,7 @@ import rx.Observable
 import rx.subjects.PublishSubject
 import java.util.*
 
-class BridgeControlListener(val config: NodeSSLConfiguration,
-
+class BridgeControlListener(val config: MutualSslConfiguration,
                             socksProxyConfig: SocksProxyConfig? = null,
                             maxMessageSize: Int,
                             val artemisMessageClientFactory: () -> ArtemisSessionProvider) : AutoCloseable {
@@ -38,7 +37,7 @@ class BridgeControlListener(val config: NodeSSLConfiguration,
     private var controlConsumer: ClientConsumer? = null
     private var notifyConsumer: ClientConsumer? = null
 
-    constructor(config: NodeSSLConfiguration,
+    constructor(config: MutualSslConfiguration,
                 p2pAddress: NetworkHostAndPort,
 
                 maxMessageSize: Int,
diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/CertificateStore.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/CertificateStore.kt
new file mode 100644
index 0000000000..3ca6be6d6b
--- /dev/null
+++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/CertificateStore.kt
@@ -0,0 +1,82 @@
+package net.corda.nodeapi.internal.config
+
+import net.corda.core.internal.outputStream
+import net.corda.nodeapi.internal.crypto.X509KeyStore
+import net.corda.nodeapi.internal.crypto.X509Utilities
+import net.corda.nodeapi.internal.crypto.addOrReplaceCertificate
+import java.io.InputStream
+import java.io.OutputStream
+import java.nio.file.OpenOption
+import java.nio.file.Path
+import java.security.cert.X509Certificate
+
+interface CertificateStore : Iterable<Pair<String, X509Certificate>> {
+
+    companion object {
+
+        fun of(store: X509KeyStore, password: String): CertificateStore = DelegatingCertificateStore(store, password)
+
+        fun fromFile(storePath: Path, password: String, createNew: Boolean): CertificateStore = DelegatingCertificateStore(X509KeyStore.fromFile(storePath, password, createNew), password)
+
+        fun fromInputStream(stream: InputStream, password: String): CertificateStore = DelegatingCertificateStore(X509KeyStore.fromInputStream(stream, password), password)
+
+        fun fromResource(storeResourceName: String, password: String, classLoader: ClassLoader = Thread.currentThread().contextClassLoader): CertificateStore = fromInputStream(classLoader.getResourceAsStream(storeResourceName), password)
+    }
+
+    val value: X509KeyStore
+    val password: String
+
+    fun writeTo(stream: OutputStream) = value.internal.store(stream, password.toCharArray())
+
+    fun writeTo(path: Path, vararg options: OpenOption) = path.outputStream(*options)
+
+    fun update(action: X509KeyStore.() -> Unit) {
+        val result = action.invoke(value)
+        value.save()
+        return result
+    }
+
+    fun <RESULT> query(action: X509KeyStore.() -> RESULT): RESULT {
+        return action.invoke(value)
+    }
+
+    operator fun set(alias: String, certificate: X509Certificate) {
+
+        update {
+            internal.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, certificate)
+        }
+    }
+
+    override fun iterator(): Iterator<Pair<String, X509Certificate>> {
+
+        return query {
+            aliases()
+        }.asSequence().map { alias -> alias to get(alias) }.iterator()
+    }
+
+    fun forEach(action: (alias: String, certificate: X509Certificate) -> Unit) {
+
+        forEach { (alias, certificate) -> action.invoke(alias, certificate) }
+    }
+
+    /**
+     * @throws IllegalArgumentException if no certificate for the alias is found, or if the certificate is not an [X509Certificate].
+     */
+    operator fun get(alias: String): X509Certificate {
+
+        return query {
+            getCertificate(alias)
+        }
+    }
+
+    operator fun contains(alias: String): Boolean = value.contains(alias)
+
+    fun copyTo(certificateStore: CertificateStore) {
+
+        certificateStore.update {
+            this@CertificateStore.forEach(::setCertificate)
+        }
+    }
+}
+
+private class DelegatingCertificateStore(override val value: X509KeyStore, override val password: String) : CertificateStore
\ No newline at end of file
diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/CertificateStoreSupplier.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/CertificateStoreSupplier.kt
new file mode 100644
index 0000000000..3703742813
--- /dev/null
+++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/CertificateStoreSupplier.kt
@@ -0,0 +1,24 @@
+package net.corda.nodeapi.internal.config
+
+import java.io.IOException
+import java.nio.file.Path
+
+interface CertificateStoreSupplier {
+
+    fun get(createNew: Boolean = false): CertificateStore
+
+    fun getOptional(): CertificateStore? {
+
+        return try {
+            get()
+        } catch (e: IOException) {
+            null
+        }
+    }
+}
+
+// TODO replace reference to FileBasedCertificateStoreSupplier with CertificateStoreSupplier, after coming up with a way of passing certificate stores to Artemis.
+class FileBasedCertificateStoreSupplier(val path: Path, val password: String) : CertificateStoreSupplier {
+
+    override fun get(createNew: Boolean) = CertificateStore.fromFile(path, password, createNew)
+}
\ No newline at end of file
diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/SSLConfiguration.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/SslConfiguration.kt
similarity index 54%
rename from node-api/src/main/kotlin/net/corda/nodeapi/internal/config/SSLConfiguration.kt
rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/config/SslConfiguration.kt
index e8c63fc1f3..1011d49f9d 100644
--- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/SSLConfiguration.kt
+++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/SslConfiguration.kt
@@ -4,12 +4,34 @@ import net.corda.core.internal.div
 import net.corda.nodeapi.internal.crypto.X509KeyStore
 import java.nio.file.Path
 
+interface SslConfiguration {
+
+    val keyStore: FileBasedCertificateStoreSupplier?
+    val trustStore: FileBasedCertificateStoreSupplier?
+
+    companion object {
+
+        fun mutual(keyStore: FileBasedCertificateStoreSupplier, trustStore: FileBasedCertificateStoreSupplier): MutualSslConfiguration {
+
+            return MutualSslOptions(keyStore, trustStore)
+        }
+    }
+}
+
+interface MutualSslConfiguration : SslConfiguration {
+
+    override val keyStore: FileBasedCertificateStoreSupplier
+    override val trustStore: FileBasedCertificateStoreSupplier
+}
+
+private class MutualSslOptions(override val keyStore: FileBasedCertificateStoreSupplier, override val trustStore: FileBasedCertificateStoreSupplier) : MutualSslConfiguration
+
+// Don't use this internally. It's still here because it's used by ArtemisTcpTransport, which is in public node-api by mistake.
 interface SSLConfiguration {
     val keyStorePassword: String
     val trustStorePassword: String
     val certificatesDirectory: Path
     val sslKeystore: Path get() = certificatesDirectory / "sslkeystore.jks"
-    // 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
@@ -25,9 +47,4 @@ interface SSLConfiguration {
     fun loadSslKeyStore(createNew: Boolean = false): X509KeyStore {
         return X509KeyStore.fromFile(sslKeystore, keyStorePassword, createNew)
     }
-}
-
-interface NodeSSLConfiguration : SSLConfiguration {
-    val baseDirectory: Path
-    override val certificatesDirectory: Path get() = baseDirectory / "certificates"
-}
+}
\ No newline at end of file
diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509KeyStore.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509KeyStore.kt
index 1fa56bebea..5c0c4b501b 100644
--- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509KeyStore.kt
+++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509KeyStore.kt
@@ -2,6 +2,7 @@ package net.corda.nodeapi.internal.crypto
 
 import net.corda.core.crypto.Crypto
 import net.corda.core.internal.uncheckedCast
+import java.io.InputStream
 import java.nio.file.Path
 import java.security.KeyPair
 import java.security.KeyStore
@@ -30,6 +31,14 @@ class X509KeyStore private constructor(val internal: KeyStore, private val store
             val internal: KeyStore = if (createNew) loadOrCreateKeyStore(keyStoreFile, storePassword) else loadKeyStore(keyStoreFile, storePassword)
             return X509KeyStore(internal, storePassword, keyStoreFile)
         }
+
+        /**
+         * Reads a [KeyStore] from an [InputStream].
+         */
+        fun fromInputStream(stream: InputStream, storePassword: String): X509KeyStore {
+            val internal = loadKeyStore(stream, storePassword)
+            return X509KeyStore(internal, storePassword)
+        }
     }
 
     operator fun contains(alias: String): Boolean = internal.containsAlias(alias)
diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPClient.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPClient.kt
index b73b6a8bb4..db2bba18d3 100644
--- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPClient.kt
+++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPClient.kt
@@ -132,7 +132,7 @@ class AMQPClient(val targets: List<NetworkHostAndPort>,
         private val conf = parent.configuration
 
         init {
-            keyManagerFactory.init(conf.keyStore, conf.keyStorePrivateKeyPassword)
+            keyManagerFactory.init(conf.keyStore)
             trustManagerFactory.init(initialiseTrustStoreAndEnableCrlChecking(conf.trustStore, conf.crlCheckSoftFail))
         }
 
diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPConfiguration.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPConfiguration.kt
index 4e873cdbaa..d39c5b4647 100644
--- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPConfiguration.kt
+++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPConfiguration.kt
@@ -1,6 +1,7 @@
 package net.corda.nodeapi.internal.protonwrapper.netty
 
 import net.corda.nodeapi.internal.ArtemisMessagingComponent
+import net.corda.nodeapi.internal.config.CertificateStore
 import java.security.KeyStore
 
 interface AMQPConfiguration {
@@ -21,19 +22,14 @@ interface AMQPConfiguration {
         get() = ArtemisMessagingComponent.PEER_USER
 
     /**
-     * The keystore used for TLS connections
+     * The key store used for TLS connections
      */
-    val keyStore: KeyStore
+    val keyStore: CertificateStore
 
     /**
-     * Password used to unlock TLS private keys in the KeyStore.
+     * The trust root key store to validate the peer certificates against
      */
-    val keyStorePrivateKeyPassword: CharArray
-
-    /**
-     * The trust root KeyStore to validate the peer certificates against
-     */
-    val trustStore: KeyStore
+    val trustStore: CertificateStore
 
     /**
      * Setting crlCheckSoftFail to true allows certificate paths where some leaf certificates do not contain cRLDistributionPoints
diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPServer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPServer.kt
index 7c16a0547e..d65b77139c 100644
--- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPServer.kt
+++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPServer.kt
@@ -60,7 +60,7 @@ class AMQPServer(val hostName: String,
         private val conf = parent.configuration
 
         init {
-            keyManagerFactory.init(conf.keyStore, conf.keyStorePrivateKeyPassword)
+            keyManagerFactory.init(conf.keyStore.value.internal, conf.keyStore.password.toCharArray())
             trustManagerFactory.init(initialiseTrustStoreAndEnableCrlChecking(conf.trustStore, conf.crlCheckSoftFail))
         }
 
diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/SSLHelper.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/SSLHelper.kt
index 1f4328a8ee..6ffd49cc2d 100644
--- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/SSLHelper.kt
+++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/SSLHelper.kt
@@ -5,14 +5,13 @@ import net.corda.core.crypto.newSecureRandom
 import net.corda.core.utilities.NetworkHostAndPort
 import net.corda.core.utilities.contextLogger
 import net.corda.core.utilities.toHex
-import net.corda.nodeapi.ArtemisTcpTransport
+import net.corda.nodeapi.internal.InternalArtemisTcpTransport
+import net.corda.nodeapi.internal.config.CertificateStore
 import net.corda.nodeapi.internal.crypto.toBc
 import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier
 import org.bouncycastle.asn1.x509.Extension
 import org.bouncycastle.asn1.x509.SubjectKeyIdentifier
 import java.net.Socket
-import java.security.KeyStore
-import java.security.SecureRandom
 import java.security.cert.*
 import java.util.*
 import javax.net.ssl.*
@@ -111,8 +110,8 @@ internal fun createClientSslHelper(target: NetworkHostAndPort,
     sslContext.init(keyManagers, trustManagers, newSecureRandom())
     val sslEngine = sslContext.createSSLEngine(target.host, target.port)
     sslEngine.useClientMode = true
-    sslEngine.enabledProtocols = ArtemisTcpTransport.TLS_VERSIONS.toTypedArray()
-    sslEngine.enabledCipherSuites = ArtemisTcpTransport.CIPHER_SUITES.toTypedArray()
+    sslEngine.enabledProtocols = InternalArtemisTcpTransport.TLS_VERSIONS.toTypedArray()
+    sslEngine.enabledCipherSuites = InternalArtemisTcpTransport.CIPHER_SUITES.toTypedArray()
     sslEngine.enableSessionCreation = true
     return SslHandler(sslEngine)
 }
@@ -126,13 +125,13 @@ internal fun createServerSslHelper(keyManagerFactory: KeyManagerFactory,
     val sslEngine = sslContext.createSSLEngine()
     sslEngine.useClientMode = false
     sslEngine.needClientAuth = true
-    sslEngine.enabledProtocols = ArtemisTcpTransport.TLS_VERSIONS.toTypedArray()
-    sslEngine.enabledCipherSuites = ArtemisTcpTransport.CIPHER_SUITES.toTypedArray()
+    sslEngine.enabledProtocols = InternalArtemisTcpTransport.TLS_VERSIONS.toTypedArray()
+    sslEngine.enabledCipherSuites = InternalArtemisTcpTransport.CIPHER_SUITES.toTypedArray()
     sslEngine.enableSessionCreation = true
     return SslHandler(sslEngine)
 }
 
-internal fun initialiseTrustStoreAndEnableCrlChecking(trustStore: KeyStore, crlCheckSoftFail: Boolean): ManagerFactoryParameters {
+internal fun initialiseTrustStoreAndEnableCrlChecking(trustStore: CertificateStore, crlCheckSoftFail: Boolean): ManagerFactoryParameters {
     val certPathBuilder = CertPathBuilder.getInstance("PKIX")
     val revocationChecker = certPathBuilder.revocationChecker as PKIXRevocationChecker
     revocationChecker.options = EnumSet.of(
@@ -145,7 +144,11 @@ internal fun initialiseTrustStoreAndEnableCrlChecking(trustStore: KeyStore, crlC
         // 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())
+    val pkixParams = PKIXBuilderParameters(trustStore.value.internal, X509CertSelector())
     pkixParams.addCertPathChecker(revocationChecker)
     return CertPathTrustManagerParameters(pkixParams)
 }
+
+fun KeyManagerFactory.init(keyStore: CertificateStore) = init(keyStore.value.internal, keyStore.password.toCharArray())
+
+fun TrustManagerFactory.init(trustStore: CertificateStore) = init(trustStore.value.internal)
diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/DevCertificatesTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/DevCertificatesTest.kt
index 4fdc43dd2d..e586a6c642 100644
--- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/DevCertificatesTest.kt
+++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/DevCertificatesTest.kt
@@ -1,8 +1,7 @@
 package net.corda.nodeapi.internal.crypto
 
 import net.corda.core.internal.validate
-import net.corda.nodeapi.internal.DEV_CA_TRUST_STORE_FILE
-import net.corda.nodeapi.internal.DEV_CA_TRUST_STORE_PASS
+import net.corda.nodeapi.internal.loadDevCaTrustStore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.rules.TemporaryFolder
@@ -22,8 +21,8 @@ class DevCertificatesTest {
     @Test
     fun `create server certificate in keystore for SSL`() {
         // given
-        val newTrustStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/$DEV_CA_TRUST_STORE_FILE"), DEV_CA_TRUST_STORE_PASS)
-        val newTrustRoot = newTrustStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA)
+        val newTrustStore = loadDevCaTrustStore()
+        val newTrustRoot = newTrustStore[X509Utilities.CORDA_ROOT_CA]
         val newTrustAnchor = TrustAnchor(newTrustRoot, null)
 
         val oldNodeCaKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("regression-test/$OLD_NODE_DEV_KEYSTORE_FILE_NAME"), OLD_DEV_KEYSTORE_PASS)
diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt
index 1df89e1070..d389e5b27d 100644
--- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt
+++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt
@@ -10,8 +10,11 @@ import net.corda.core.serialization.SerializationContext
 import net.corda.core.serialization.deserialize
 import net.corda.core.serialization.serialize
 import net.corda.node.serialization.amqp.AMQPServerSerializationScheme
-import net.corda.nodeapi.internal.config.SSLConfiguration
-import net.corda.nodeapi.internal.createDevKeyStores
+import net.corda.nodeapi.internal.config.MutualSslConfiguration
+import net.corda.nodeapi.internal.createDevNodeCa
+import net.corda.nodeapi.internal.protonwrapper.netty.init
+import net.corda.nodeapi.internal.registerDevP2pCertificates
+import net.corda.nodeapi.internal.registerDevSigningCertificates
 import net.corda.serialization.internal.AllWhitelist
 import net.corda.serialization.internal.SerializationContextImpl
 import net.corda.serialization.internal.SerializationFactoryImpl
@@ -19,6 +22,7 @@ import net.corda.serialization.internal.amqp.amqpMagic
 import net.corda.testing.core.ALICE_NAME
 import net.corda.testing.core.BOB_NAME
 import net.corda.testing.core.TestIdentity
+import net.corda.testing.internal.stubs.CertificateStoreStubs
 import net.corda.testing.internal.createDevIntermediateCaCertPath
 import org.assertj.core.api.Assertions.assertThat
 import org.bouncycastle.asn1.x509.*
@@ -31,7 +35,6 @@ import java.io.IOException
 import java.net.InetAddress
 import java.net.InetSocketAddress
 import java.nio.file.Path
-import java.security.SecureRandom
 import java.security.cert.CertPath
 import java.security.cert.X509Certificate
 import java.util.*
@@ -180,29 +183,27 @@ class X509UtilitiesTest {
 
     @Test
     fun `create server certificate in keystore for SSL`() {
-        val sslConfig = object : SSLConfiguration {
-            override val certificatesDirectory = tempFolder.root.toPath()
-            override val keyStorePassword = "serverstorepass"
-            override val trustStorePassword = "trustpass"
-            override val crlCheckSoftFail: Boolean = true
-        }
+        val certificatesDirectory = tempFolder.root.toPath()
+        val signingCertStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory, "serverstorepass")
+        val p2pSslConfig = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory, keyStorePassword = "serverstorepass")
 
         val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
 
         // Generate server cert and private key and populate another keystore suitable for SSL
-        sslConfig.createDevKeyStores(MEGA_CORP.name, rootCa.certificate, intermediateCa)
-
+        val nodeCa = createDevNodeCa(intermediateCa, MEGA_CORP.name)
+        signingCertStore.get(createNew = true).also { it.registerDevSigningCertificates(MEGA_CORP.name, rootCa.certificate, intermediateCa, nodeCa) }
+        p2pSslConfig.keyStore.get(createNew = true).also { it.registerDevP2pCertificates(MEGA_CORP.name, rootCa.certificate, intermediateCa, nodeCa) }
         // Load back server certificate
-        val serverKeyStore = loadKeyStore(sslConfig.nodeKeystore, sslConfig.keyStorePassword)
-        val (serverCert, serverKeyPair) = serverKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, sslConfig.keyStorePassword)
+        val serverKeyStore = signingCertStore.get().value
+        val (serverCert, serverKeyPair) = serverKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)
 
         serverCert.checkValidity()
         serverCert.verify(intermediateCa.certificate.publicKey)
         assertThat(CordaX500Name.build(serverCert.subjectX500Principal)).isEqualTo(MEGA_CORP.name)
 
         // Load back SSL certificate
-        val sslKeyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword)
-        val (sslCert) = sslKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_TLS, sslConfig.keyStorePassword)
+        val sslKeyStoreReloaded = p2pSslConfig.keyStore.get()
+        val (sslCert) = sslKeyStoreReloaded.query { getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_TLS, p2pSslConfig.keyStore.password) }
 
         sslCert.checkValidity()
         sslCert.verify(serverCert.publicKey)
@@ -216,25 +217,20 @@ class X509UtilitiesTest {
 
     @Test
     fun `create server cert and use in SSL socket`() {
-        val sslConfig = object : SSLConfiguration {
-            override val certificatesDirectory = tempFolder.root.toPath()
-            override val keyStorePassword = "serverstorepass"
-            override val trustStorePassword = "trustpass"
-            override val crlCheckSoftFail: Boolean = true
-        }
+        val sslConfig = CertificateStoreStubs.P2P.withCertificatesDirectory(tempFolder.root.toPath(), keyStorePassword = "serverstorepass")
 
         val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
 
         // Generate server cert and private key and populate another keystore suitable for SSL
-        sslConfig.createDevKeyStores(MEGA_CORP.name, rootCa.certificate, intermediateCa)
+        sslConfig.keyStore.get(true).registerDevP2pCertificates(MEGA_CORP.name, rootCa.certificate, intermediateCa)
         sslConfig.createTrustStore(rootCa.certificate)
 
-        val keyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword)
-        val trustStore = loadKeyStore(sslConfig.trustStoreFile, sslConfig.trustStorePassword)
+        val keyStore = sslConfig.keyStore.get()
+        val trustStore = sslConfig.trustStore.get()
 
         val context = SSLContext.getInstance("TLS")
         val keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
-        keyManagerFactory.init(keyStore, sslConfig.keyStorePassword.toCharArray())
+        keyManagerFactory.init(keyStore)
         val keyManagers = keyManagerFactory.keyManagers
         val trustMgrFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
         trustMgrFactory.init(trustStore)
@@ -313,10 +309,9 @@ class X509UtilitiesTest {
 
     private fun tempFile(name: String): Path = tempFolder.root.toPath() / name
 
-    private fun SSLConfiguration.createTrustStore(rootCert: X509Certificate) {
-        val trustStore = loadOrCreateKeyStore(trustStoreFile, trustStorePassword)
-        trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCert)
-        trustStore.save(trustStoreFile, trustStorePassword)
+    private fun MutualSslConfiguration.createTrustStore(rootCert: X509Certificate) {
+        val trustStore = this.trustStore.get(true)
+        trustStore[X509Utilities.CORDA_ROOT_CA] = rootCert
     }
 
     @Test
diff --git a/node/src/integration-test/kotlin/net/corda/node/BootTests.kt b/node/src/integration-test/kotlin/net/corda/node/BootTests.kt
index 410ce8b2de..94cafdf4f6 100644
--- a/node/src/integration-test/kotlin/net/corda/node/BootTests.kt
+++ b/node/src/integration-test/kotlin/net/corda/node/BootTests.kt
@@ -12,19 +12,15 @@ import net.corda.core.messaging.startFlow
 import net.corda.core.utilities.getOrThrow
 import net.corda.node.internal.NodeStartup
 import net.corda.node.services.Permissions.Companion.startFlow
-import net.corda.nodeapi.exceptions.InternalNodeException
 import net.corda.testing.core.ALICE_NAME
 import net.corda.testing.core.BOB_NAME
 import net.corda.testing.core.DUMMY_BANK_A_NAME
 import net.corda.testing.driver.DriverParameters
-import net.corda.testing.driver.NodeHandle
-import net.corda.testing.driver.NodeParameters
 import net.corda.testing.driver.driver
 import net.corda.testing.internal.IntegrationTest
 import net.corda.testing.internal.IntegrationTestSchemas
 import net.corda.testing.internal.toDatabaseSchemaName
 import net.corda.testing.node.User
-import net.corda.testing.node.internal.startNode
 import org.assertj.core.api.Assertions.assertThatThrownBy
 import org.junit.ClassRule
 import org.junit.Test
diff --git a/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt b/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt
index e2a3cd4eec..7cf7830a5d 100644
--- a/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt
+++ b/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt
@@ -4,20 +4,19 @@ import net.corda.core.crypto.Crypto
 import net.corda.core.internal.div
 import net.corda.core.utilities.getOrThrow
 import net.corda.node.services.config.configureDevKeyAndTrustStores
-import net.corda.nodeapi.internal.config.SSLConfiguration
 import net.corda.nodeapi.internal.crypto.CertificateType
 import net.corda.nodeapi.internal.crypto.X509Utilities
 import net.corda.testing.core.ALICE_NAME
 import net.corda.testing.core.DUMMY_NOTARY_NAME
 import net.corda.testing.driver.DriverParameters
 import net.corda.testing.driver.driver
+import net.corda.testing.internal.stubs.CertificateStoreStubs
 import net.corda.testing.internal.IntegrationTest
 import net.corda.testing.internal.IntegrationTestSchemas
 import net.corda.testing.internal.toDatabaseSchemaName
 import org.assertj.core.api.Assertions.assertThatThrownBy
 import org.junit.ClassRule
 import org.junit.Test
-import java.nio.file.Path
 import javax.security.auth.x500.X500Principal
 
 class NodeKeystoreCheckTest : IntegrationTest() {
@@ -41,13 +40,11 @@ class NodeKeystoreCheckTest : IntegrationTest() {
         driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList())) {
             // Create keystores
             val keystorePassword = "password"
-            val config = object : SSLConfiguration {
-                override val keyStorePassword: String = keystorePassword
-                override val trustStorePassword: String = keystorePassword
-                override val certificatesDirectory: Path = baseDirectory(ALICE_NAME) / "certificates"
-                override val crlCheckSoftFail: Boolean = true
-            }
-            config.configureDevKeyAndTrustStores(ALICE_NAME)
+            val certificatesDirectory = baseDirectory(ALICE_NAME) / "certificates"
+            val signingCertStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory, keystorePassword)
+            val p2pSslConfig = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory, keyStorePassword = keystorePassword, trustStorePassword = keystorePassword)
+
+            p2pSslConfig.configureDevKeyAndTrustStores(ALICE_NAME, signingCertStore, certificatesDirectory)
 
             // This should pass with correct keystore.
             val node = startNode(
@@ -59,7 +56,7 @@ class NodeKeystoreCheckTest : IntegrationTest() {
             node.stop()
 
             // Fiddle with node keystore.
-            config.loadNodeKeyStore().update {
+            signingCertStore.get().update {
                 // Self signed root
                 val badRootKeyPair = Crypto.generateKeyPair()
                 val badRoot = X509Utilities.createSelfSignedCACertificate(X500Principal("O=Bad Root,L=Lodnon,C=GB"), badRootKeyPair)
diff --git a/node/src/integration-test/kotlin/net/corda/node/amqp/AMQPBridgeTest.kt b/node/src/integration-test/kotlin/net/corda/node/amqp/AMQPBridgeTest.kt
index 6d1d97f6a9..9ce9900b62 100644
--- a/node/src/integration-test/kotlin/net/corda/node/amqp/AMQPBridgeTest.kt
+++ b/node/src/integration-test/kotlin/net/corda/node/amqp/AMQPBridgeTest.kt
@@ -23,6 +23,7 @@ import net.corda.testing.core.BOB_NAME
 import net.corda.testing.core.MAX_MESSAGE_SIZE
 import net.corda.testing.core.TestIdentity
 import net.corda.testing.driver.PortAllocation
+import net.corda.testing.internal.stubs.CertificateStoreStubs
 import net.corda.testing.internal.rigorousMock
 import org.apache.activemq.artemis.api.core.Message.HDR_DUPLICATE_DETECTION_ID
 import org.apache.activemq.artemis.api.core.RoutingType
@@ -33,7 +34,6 @@ import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.rules.TemporaryFolder
-import java.security.KeyStore
 import java.util.*
 import kotlin.system.measureNanoTime
 import kotlin.system.measureTimeMillis
@@ -242,12 +242,17 @@ class AMQPBridgeTest {
 
 
     private fun createArtemis(sourceQueueName: String?): Triple<ArtemisMessagingServer, ArtemisMessagingClient, BridgeManager> {
+        val baseDir = temporaryFolder.root.toPath() / "artemis"
+        val certificatesDirectory = baseDir / "certificates"
+        val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)
+        val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)
         val artemisConfig = rigorousMock<AbstractNodeConfiguration>().also {
-            doReturn(temporaryFolder.root.toPath() / "artemis").whenever(it).baseDirectory
+            doReturn(baseDir).whenever(it).baseDirectory
             doReturn(ALICE_NAME).whenever(it).myLegalName
-            doReturn("trustpass").whenever(it).trustStorePassword
+            doReturn(certificatesDirectory).whenever(it).certificatesDirectory
+            doReturn(signingCertificateStore).whenever(it).signingCertificateStore
+            doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
             doReturn(true).whenever(it).crlCheckSoftFail
-            doReturn("cordacadevpass").whenever(it).keyStorePassword
             doReturn(artemisAddress).whenever(it).p2pAddress
             doReturn(null).whenever(it).jmxMonitoringHttpPort
             doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000))).whenever(it).enterpriseConfiguration
@@ -255,11 +260,11 @@ class AMQPBridgeTest {
         artemisConfig.configureWithDevSSLCertificate()
 
         val artemisServer = ArtemisMessagingServer(artemisConfig, artemisAddress.copy(host = "0.0.0.0"), MAX_MESSAGE_SIZE)
-        val artemisClient = ArtemisMessagingClient(artemisConfig, artemisAddress, MAX_MESSAGE_SIZE)
+        val artemisClient = ArtemisMessagingClient(artemisConfig.p2pSslOptions, artemisAddress, MAX_MESSAGE_SIZE)
 
         artemisServer.start()
         artemisClient.start()
-        val bridgeManager = AMQPBridgeManager(artemisConfig, artemisAddress, MAX_MESSAGE_SIZE)
+        val bridgeManager = AMQPBridgeManager(artemisConfig.p2pSslOptions, artemisAddress, MAX_MESSAGE_SIZE)
         bridgeManager.start()
         val artemis = artemisClient.started!!
         if (sourceQueueName != null) {
@@ -272,18 +277,23 @@ class AMQPBridgeTest {
 
 
     private fun createArtemisReceiver(targetAdress: NetworkHostAndPort, workingDir: String): Pair<ArtemisMessagingServer, ArtemisMessagingClient> {
+        val baseDir = temporaryFolder.root.toPath() / workingDir
+        val certificatesDirectory = baseDir / "certificates"
+        val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)
+        val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)
         val artemisConfig = rigorousMock<AbstractNodeConfiguration>().also {
-            doReturn(temporaryFolder.root.toPath() / workingDir).whenever(it).baseDirectory
+            doReturn(baseDir).whenever(it).baseDirectory
+            doReturn(certificatesDirectory).whenever(it).certificatesDirectory
             doReturn(BOB_NAME).whenever(it).myLegalName
-            doReturn("trustpass").whenever(it).trustStorePassword
-            doReturn("cordacadevpass").whenever(it).keyStorePassword
+            doReturn(signingCertificateStore).whenever(it).signingCertificateStore
+            doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
             doReturn(targetAdress).whenever(it).p2pAddress
             doReturn("").whenever(it).jmxMonitoringHttpPort
             doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000))).whenever(it).enterpriseConfiguration
         }
         artemisConfig.configureWithDevSSLCertificate()
         val artemisServer = ArtemisMessagingServer(artemisConfig, NetworkHostAndPort("0.0.0.0", targetAdress.port), MAX_MESSAGE_SIZE)
-        val artemisClient = ArtemisMessagingClient(artemisConfig, targetAdress, MAX_MESSAGE_SIZE, confirmationWindowSize = 10 * 1024)
+        val artemisClient = ArtemisMessagingClient(artemisConfig.p2pSslOptions, targetAdress, MAX_MESSAGE_SIZE, confirmationWindowSize = 10 * 1024)
         artemisServer.start()
         artemisClient.start()
 
@@ -292,18 +302,23 @@ class AMQPBridgeTest {
     }
 
     private fun createAMQPServer(maxMessageSize: Int = MAX_MESSAGE_SIZE): AMQPServer {
+        val baseDir = temporaryFolder.root.toPath() / "server"
+        val certificatesDirectory = baseDir / "certificates"
+        val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)
+        val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)
         val serverConfig = rigorousMock<AbstractNodeConfiguration>().also {
             doReturn(temporaryFolder.root.toPath() / "server").whenever(it).baseDirectory
             doReturn(BOB_NAME).whenever(it).myLegalName
-            doReturn("trustpass").whenever(it).trustStorePassword
-            doReturn("cordacadevpass").whenever(it).keyStorePassword
+            doReturn(certificatesDirectory).whenever(it).certificatesDirectory
+            doReturn(signingCertificateStore).whenever(it).signingCertificateStore
+            doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
         }
         serverConfig.configureWithDevSSLCertificate()
 
+        val keyStore = serverConfig.p2pSslOptions.keyStore.get()
         val amqpConfig = object : AMQPConfiguration {
-            override val keyStore: KeyStore = serverConfig.loadSslKeyStore().internal
-            override val keyStorePrivateKeyPassword: CharArray = serverConfig.keyStorePassword.toCharArray()
-            override val trustStore: KeyStore = serverConfig.loadTrustStore().internal
+            override val keyStore = keyStore
+            override val trustStore  = serverConfig.p2pSslOptions.trustStore.get()
             override val trace: Boolean = true
             override val maxMessageSize: Int = maxMessageSize
         }
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 cb766bbcf8..d2ef1dbe5c 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
@@ -13,7 +13,8 @@ import net.corda.core.utilities.seconds
 import net.corda.node.services.config.NodeConfiguration
 import net.corda.node.services.config.configureWithDevSSLCertificate
 import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_PREFIX
-import net.corda.nodeapi.internal.config.SSLConfiguration
+import net.corda.nodeapi.internal.config.CertificateStoreSupplier
+import net.corda.nodeapi.internal.config.MutualSslConfiguration
 import net.corda.nodeapi.internal.crypto.*
 import net.corda.nodeapi.internal.protonwrapper.messages.MessageStatus
 import net.corda.nodeapi.internal.protonwrapper.netty.AMQPClient
@@ -24,6 +25,7 @@ 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
@@ -50,7 +52,6 @@ import java.io.Closeable
 import java.math.BigInteger
 import java.net.InetSocketAddress
 import java.security.KeyPair
-import java.security.KeyStore
 import java.security.PrivateKey
 import java.security.Security
 import java.security.cert.X509CRL
@@ -330,22 +331,25 @@ class CertificateRevocationListNodeTests {
                              nodeCrlDistPoint: String = "http://${server.hostAndPort}/crl/node.crl",
                              tlsCrlDistPoint: String? = "http://${server.hostAndPort}/crl/empty.crl",
                              maxMessageSize: Int = MAX_MESSAGE_SIZE): Pair<AMQPClient, X509Certificate> {
+        val baseDirectory = temporaryFolder.root.toPath() / "client"
+        val certificatesDirectory = baseDirectory / "certificates"
+        val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)
+        val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)
         val clientConfig = rigorousMock<AbstractNodeConfiguration>().also {
-            doReturn(temporaryFolder.root.toPath() / "client").whenever(it).baseDirectory
+            doReturn(baseDirectory).whenever(it).baseDirectory
+            doReturn(certificatesDirectory).whenever(it).certificatesDirectory
             doReturn(BOB_NAME).whenever(it).myLegalName
-            doReturn("trustpass").whenever(it).trustStorePassword
-            doReturn("cordacadevpass").whenever(it).keyStorePassword
+            doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
+            doReturn(signingCertificateStore).whenever(it).signingCertificateStore
             doReturn(crlCheckSoftFail).whenever(it).crlCheckSoftFail
         }
         clientConfig.configureWithDevSSLCertificate()
-        val nodeCert = clientConfig.recreateNodeCaAndTlsCertificates(nodeCrlDistPoint, tlsCrlDistPoint)
-        val clientTruststore = clientConfig.loadTrustStore().internal
-        val clientKeystore = clientConfig.loadSslKeyStore().internal
+        val nodeCert = (signingCertificateStore to p2pSslConfiguration).recreateNodeCaAndTlsCertificates(nodeCrlDistPoint, tlsCrlDistPoint)
+        val keyStore = clientConfig.p2pSslOptions.keyStore.get()
 
         val amqpConfig = object : AMQPConfiguration {
-            override val keyStore: KeyStore = clientKeystore
-            override val keyStorePrivateKeyPassword: CharArray = clientConfig.keyStorePassword.toCharArray()
-            override val trustStore: KeyStore = clientTruststore
+            override val keyStore = keyStore
+            override val trustStore = clientConfig.p2pSslOptions.trustStore.get()
             override val crlCheckSoftFail: Boolean = crlCheckSoftFail
             override val maxMessageSize: Int = maxMessageSize
         }
@@ -360,21 +364,24 @@ class CertificateRevocationListNodeTests {
                              nodeCrlDistPoint: String = "http://${server.hostAndPort}/crl/node.crl",
                              tlsCrlDistPoint: String? = "http://${server.hostAndPort}/crl/empty.crl",
                              maxMessageSize: Int = MAX_MESSAGE_SIZE): Pair<AMQPServer, X509Certificate> {
+        val baseDirectory = temporaryFolder.root.toPath() / "server"
+        val certificatesDirectory = baseDirectory / "certificates"
+        val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)
+        val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)
         val serverConfig = rigorousMock<AbstractNodeConfiguration>().also {
-            doReturn(temporaryFolder.root.toPath() / "server").whenever(it).baseDirectory
+            doReturn(baseDirectory).whenever(it).baseDirectory
+            doReturn(certificatesDirectory).whenever(it).certificatesDirectory
             doReturn(name).whenever(it).myLegalName
-            doReturn("trustpass").whenever(it).trustStorePassword
-            doReturn("cordacadevpass").whenever(it).keyStorePassword
+            doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
+            doReturn(signingCertificateStore).whenever(it).signingCertificateStore
             doReturn(crlCheckSoftFail).whenever(it).crlCheckSoftFail
         }
         serverConfig.configureWithDevSSLCertificate()
-        val nodeCert = serverConfig.recreateNodeCaAndTlsCertificates(nodeCrlDistPoint, tlsCrlDistPoint)
-        val serverTruststore = serverConfig.loadTrustStore().internal
-        val serverKeystore = serverConfig.loadSslKeyStore().internal
+        val nodeCert = (signingCertificateStore to p2pSslConfiguration).recreateNodeCaAndTlsCertificates(nodeCrlDistPoint, tlsCrlDistPoint)
+        val keyStore = serverConfig.p2pSslOptions.keyStore.get()
         val amqpConfig = object : AMQPConfiguration {
-            override val keyStore: KeyStore = serverKeystore
-            override val keyStorePrivateKeyPassword: CharArray = serverConfig.keyStorePassword.toCharArray()
-            override val trustStore: KeyStore = serverTruststore
+            override val keyStore = keyStore
+            override val trustStore = serverConfig.p2pSslOptions.trustStore.get()
             override val crlCheckSoftFail: Boolean = crlCheckSoftFail
             override val maxMessageSize: Int = maxMessageSize
         }
@@ -384,22 +391,28 @@ class CertificateRevocationListNodeTests {
                 amqpConfig), nodeCert)
     }
 
-    private fun SSLConfiguration.recreateNodeCaAndTlsCertificates(nodeCaCrlDistPoint: String, tlsCrlDistPoint: String?): X509Certificate {
-        val nodeKeyStore = loadNodeKeyStore()
-        val (nodeCert, nodeKeys) = nodeKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)
+    private fun Pair<CertificateStoreSupplier, MutualSslConfiguration>.recreateNodeCaAndTlsCertificates(nodeCaCrlDistPoint: String, tlsCrlDistPoint: String?): X509Certificate {
+
+        val signingCertificateStore = first
+        val p2pSslConfiguration = second
+        val nodeKeyStore = signingCertificateStore.get()
+        val (nodeCert, nodeKeys) = nodeKeyStore.query { getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA) }
         val newNodeCert = replaceCrlDistPointCaCertificate(nodeCert, CertificateType.NODE_CA, INTERMEDIATE_CA.keyPair, nodeCaCrlDistPoint)
-        val nodeCertChain = listOf(newNodeCert, INTERMEDIATE_CA.certificate, *nodeKeyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA).drop(2).toTypedArray())
-        nodeKeyStore.internal.deleteEntry(X509Utilities.CORDA_CLIENT_CA)
-        nodeKeyStore.save()
+        val nodeCertChain = listOf(newNodeCert, INTERMEDIATE_CA.certificate, *nodeKeyStore.query { getCertificateChain(X509Utilities.CORDA_CLIENT_CA) }.drop(2).toTypedArray())
+        nodeKeyStore.update {
+            internal.deleteEntry(X509Utilities.CORDA_CLIENT_CA)
+        }
         nodeKeyStore.update {
             setPrivateKey(X509Utilities.CORDA_CLIENT_CA, nodeKeys.private, nodeCertChain)
         }
-        val sslKeyStore = loadSslKeyStore()
-        val (tlsCert, tlsKeys) = sslKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_TLS)
+        val sslKeyStore = p2pSslConfiguration.keyStore.get()
+        val (tlsCert, tlsKeys) = sslKeyStore.query { getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_TLS) }
         val newTlsCert = replaceCrlDistPointCaCertificate(tlsCert, CertificateType.TLS, nodeKeys, tlsCrlDistPoint, X500Name.getInstance(ROOT_CA.certificate.subjectX500Principal.encoded))
-        val sslCertChain = listOf(newTlsCert, newNodeCert, INTERMEDIATE_CA.certificate, *sslKeyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).drop(3).toTypedArray())
-        sslKeyStore.internal.deleteEntry(X509Utilities.CORDA_CLIENT_TLS)
-        sslKeyStore.save()
+        val sslCertChain = listOf(newTlsCert, newNodeCert, INTERMEDIATE_CA.certificate, *sslKeyStore.query { getCertificateChain(X509Utilities.CORDA_CLIENT_TLS) }.drop(3).toTypedArray())
+
+        sslKeyStore.update {
+            internal.deleteEntry(X509Utilities.CORDA_CLIENT_TLS)
+        }
         sslKeyStore.update {
             setPrivateKey(X509Utilities.CORDA_CLIENT_TLS, tlsKeys.private, sslCertChain)
         }
diff --git a/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt b/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt
index fa83c8fd71..90b737b559 100644
--- a/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt
+++ b/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt
@@ -14,16 +14,18 @@ import net.corda.node.services.config.MutualExclusionConfiguration
 import net.corda.node.services.config.NodeConfiguration
 import net.corda.node.services.config.configureWithDevSSLCertificate
 import net.corda.node.services.messaging.ArtemisMessagingServer
-import net.corda.nodeapi.ArtemisTcpTransport.Companion.CIPHER_SUITES
 import net.corda.nodeapi.internal.ArtemisMessagingClient
 import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_PREFIX
-import net.corda.nodeapi.internal.config.SSLConfiguration
-import net.corda.nodeapi.internal.createDevKeyStores
+import net.corda.nodeapi.internal.InternalArtemisTcpTransport
+import net.corda.nodeapi.internal.config.MutualSslConfiguration
+import net.corda.nodeapi.internal.registerDevP2pCertificates
 import net.corda.nodeapi.internal.crypto.*
 import net.corda.nodeapi.internal.protonwrapper.messages.MessageStatus
 import net.corda.nodeapi.internal.protonwrapper.netty.AMQPClient
 import net.corda.nodeapi.internal.protonwrapper.netty.AMQPConfiguration
 import net.corda.nodeapi.internal.protonwrapper.netty.AMQPServer
+import net.corda.nodeapi.internal.protonwrapper.netty.init
+import net.corda.nodeapi.internal.registerDevSigningCertificates
 import net.corda.testing.core.ALICE_NAME
 import net.corda.testing.core.BOB_NAME
 import net.corda.testing.core.CHARLIE_NAME
@@ -31,13 +33,13 @@ import net.corda.testing.core.MAX_MESSAGE_SIZE
 import net.corda.testing.driver.PortAllocation
 import net.corda.testing.internal.createDevIntermediateCaCertPath
 import net.corda.testing.internal.rigorousMock
+import net.corda.testing.internal.stubs.CertificateStoreStubs
 import org.apache.activemq.artemis.api.core.RoutingType
 import org.assertj.core.api.Assertions.assertThatThrownBy
 import org.junit.Assert.assertArrayEquals
 import org.junit.Rule
 import org.junit.Test
 import org.junit.rules.TemporaryFolder
-import java.security.KeyStore
 import java.security.cert.X509Certificate
 import javax.net.ssl.*
 import kotlin.concurrent.thread
@@ -120,34 +122,30 @@ class ProtonWrapperTests {
         }
     }
 
-    private fun SSLConfiguration.createTrustStore(rootCert: X509Certificate) {
-        val trustStore = loadOrCreateKeyStore(trustStoreFile, trustStorePassword)
-        trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCert)
-        trustStore.save(trustStoreFile, trustStorePassword)
-    }
+    private fun MutualSslConfiguration.createTrustStore(rootCert: X509Certificate) {
 
+        trustStore.get(true)[X509Utilities.CORDA_ROOT_CA] = rootCert
+    }
 
     @Test
     fun `Test AMQP Client with invalid root certificate`() {
-        val sslConfig = object : SSLConfiguration {
-            override val certificatesDirectory = temporaryFolder.root.toPath()
-            override val keyStorePassword = "serverstorepass"
-            override val trustStorePassword = "trustpass"
-            override val crlCheckSoftFail: Boolean = true
-        }
+        val certificatesDirectory = temporaryFolder.root.toPath()
+        val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory, "serverstorepass")
+        val sslConfig = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory, keyStorePassword = "serverstorepass")
 
         val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
 
         // Generate server cert and private key and populate another keystore suitable for SSL
-        sslConfig.createDevKeyStores(ALICE_NAME, rootCa.certificate, intermediateCa)
+        signingCertificateStore.get(true).also { it.registerDevSigningCertificates(ALICE_NAME, rootCa.certificate, intermediateCa) }
+        sslConfig.keyStore.get(true).also { it.registerDevP2pCertificates(ALICE_NAME, rootCa.certificate, intermediateCa) }
         sslConfig.createTrustStore(rootCa.certificate)
 
-        val keyStore = loadKeyStore(sslConfig.sslKeystore, sslConfig.keyStorePassword)
-        val trustStore = loadKeyStore(sslConfig.trustStoreFile, sslConfig.trustStorePassword)
+        val keyStore = sslConfig.keyStore.get()
+        val trustStore = sslConfig.trustStore.get()
 
         val context = SSLContext.getInstance("TLS")
         val keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
-        keyManagerFactory.init(keyStore, sslConfig.keyStorePassword.toCharArray())
+        keyManagerFactory.init(keyStore)
         val keyManagers = keyManagerFactory.keyManagers
         val trustMgrFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
         trustMgrFactory.init(trustStore)
@@ -157,7 +155,7 @@ class ProtonWrapperTests {
         val serverSocketFactory = context.serverSocketFactory
 
         val serverSocket = serverSocketFactory.createServerSocket(serverPort) as SSLServerSocket
-        val serverParams = SSLParameters(CIPHER_SUITES.toTypedArray(),
+        val serverParams = SSLParameters(InternalArtemisTcpTransport.CIPHER_SUITES.toTypedArray(),
                 arrayOf("TLSv1.2"))
         serverParams.wantClientAuth = true
         serverParams.needClientAuth = true
@@ -406,11 +404,16 @@ class ProtonWrapperTests {
     }
 
     private fun createArtemisServerAndClient(maxMessageSize: Int = MAX_MESSAGE_SIZE): Pair<ArtemisMessagingServer, ArtemisMessagingClient> {
+        val baseDirectory = temporaryFolder.root.toPath() / "artemis"
+        val certificatesDirectory = baseDirectory / "certificates"
+        val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)
+        val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)
         val artemisConfig = rigorousMock<AbstractNodeConfiguration>().also {
-            doReturn(temporaryFolder.root.toPath() / "artemis").whenever(it).baseDirectory
+            doReturn(baseDirectory).whenever(it).baseDirectory
+            doReturn(certificatesDirectory).whenever(it).certificatesDirectory
             doReturn(CHARLIE_NAME).whenever(it).myLegalName
-            doReturn("trustpass").whenever(it).trustStorePassword
-            doReturn("cordacadevpass").whenever(it).keyStorePassword
+            doReturn(signingCertificateStore).whenever(it).signingCertificateStore
+            doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
             doReturn(NetworkHostAndPort("0.0.0.0", artemisPort)).whenever(it).p2pAddress
             doReturn(null).whenever(it).jmxMonitoringHttpPort
             doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000))).whenever(it).enterpriseConfiguration
@@ -419,28 +422,32 @@ class ProtonWrapperTests {
         artemisConfig.configureWithDevSSLCertificate()
 
         val server = ArtemisMessagingServer(artemisConfig, NetworkHostAndPort("0.0.0.0", artemisPort), maxMessageSize)
-        val client = ArtemisMessagingClient(artemisConfig, NetworkHostAndPort("localhost", artemisPort), maxMessageSize)
+        val client = ArtemisMessagingClient(artemisConfig.p2pSslOptions, NetworkHostAndPort("localhost", artemisPort), maxMessageSize)
         server.start()
         client.start()
         return Pair(server, client)
     }
 
     private fun createClient(maxMessageSize: Int = MAX_MESSAGE_SIZE): AMQPClient {
+        val baseDirectory = temporaryFolder.root.toPath() / "client"
+        val certificatesDirectory = baseDirectory / "certificates"
+        val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)
+        val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)
         val clientConfig = rigorousMock<AbstractNodeConfiguration>().also {
-            doReturn(temporaryFolder.root.toPath() / "client").whenever(it).baseDirectory
+            doReturn(baseDirectory).whenever(it).baseDirectory
+            doReturn(certificatesDirectory).whenever(it).certificatesDirectory
             doReturn(BOB_NAME).whenever(it).myLegalName
-            doReturn("trustpass").whenever(it).trustStorePassword
-            doReturn("cordacadevpass").whenever(it).keyStorePassword
+            doReturn(signingCertificateStore).whenever(it).signingCertificateStore
+            doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
             doReturn(true).whenever(it).crlCheckSoftFail
         }
         clientConfig.configureWithDevSSLCertificate()
 
-        val clientTruststore = clientConfig.loadTrustStore().internal
-        val clientKeystore = clientConfig.loadSslKeyStore().internal
+        val clientTruststore = clientConfig.p2pSslOptions.trustStore.get()
+        val clientKeystore = clientConfig.p2pSslOptions.keyStore.get()
         val amqpConfig = object : AMQPConfiguration {
-            override val keyStore: KeyStore = clientKeystore
-            override val keyStorePrivateKeyPassword: CharArray = clientConfig.keyStorePassword.toCharArray()
-            override val trustStore: KeyStore = clientTruststore
+            override val keyStore = clientKeystore
+            override val trustStore = clientTruststore
             override val trace: Boolean = true
             override val maxMessageSize: Int = maxMessageSize
         }
@@ -453,21 +460,25 @@ class ProtonWrapperTests {
     }
 
     private fun createSharedThreadsClient(sharedEventGroup: EventLoopGroup, id: Int, maxMessageSize: Int = MAX_MESSAGE_SIZE): AMQPClient {
+        val baseDirectory = temporaryFolder.root.toPath() / "client_%$id"
+        val certificatesDirectory = baseDirectory / "certificates"
+        val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)
+        val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)
         val clientConfig = rigorousMock<AbstractNodeConfiguration>().also {
-            doReturn(temporaryFolder.root.toPath() / "client_%$id").whenever(it).baseDirectory
+            doReturn(baseDirectory).whenever(it).baseDirectory
+            doReturn(certificatesDirectory).whenever(it).certificatesDirectory
             doReturn(CordaX500Name(null, "client $id", "Corda", "London", null, "GB")).whenever(it).myLegalName
-            doReturn("trustpass").whenever(it).trustStorePassword
-            doReturn("cordacadevpass").whenever(it).keyStorePassword
+            doReturn(signingCertificateStore).whenever(it).signingCertificateStore
+            doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
             doReturn(true).whenever(it).crlCheckSoftFail
         }
         clientConfig.configureWithDevSSLCertificate()
 
-        val clientTruststore = clientConfig.loadTrustStore().internal
-        val clientKeystore = clientConfig.loadSslKeyStore().internal
+        val clientTruststore = clientConfig.p2pSslOptions.trustStore.get()
+        val clientKeystore = clientConfig.p2pSslOptions.keyStore.get()
         val amqpConfig = object : AMQPConfiguration {
-            override val keyStore: KeyStore = clientKeystore
-            override val keyStorePrivateKeyPassword: CharArray = clientConfig.keyStorePassword.toCharArray()
-            override val trustStore: KeyStore = clientTruststore
+            override val keyStore = clientKeystore
+            override val trustStore = clientTruststore
             override val trace: Boolean = true
             override val maxMessageSize: Int = maxMessageSize
         }
@@ -478,25 +489,26 @@ class ProtonWrapperTests {
                 sharedThreadPool = sharedEventGroup)
     }
 
-
-    private fun createServer(port: Int, name: CordaX500Name = ALICE_NAME,  maxMessageSize: Int = MAX_MESSAGE_SIZE,
-                             crlCheckSoftFail: Boolean = true
-    ): AMQPServer {
+    private fun createServer(port: Int, name: CordaX500Name = ALICE_NAME, maxMessageSize: Int = MAX_MESSAGE_SIZE, crlCheckSoftFail: Boolean = true): AMQPServer {
+        val baseDirectory = temporaryFolder.root.toPath() / "server"
+        val certificatesDirectory = baseDirectory / "certificates"
+        val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)
+        val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)
         val serverConfig = rigorousMock<AbstractNodeConfiguration>().also {
-            doReturn(temporaryFolder.root.toPath() / "server").whenever(it).baseDirectory
+            doReturn(baseDirectory).whenever(it).baseDirectory
+            doReturn(certificatesDirectory).whenever(it).certificatesDirectory
             doReturn(name).whenever(it).myLegalName
-            doReturn("trustpass").whenever(it).trustStorePassword
-            doReturn("cordacadevpass").whenever(it).keyStorePassword
+            doReturn(signingCertificateStore).whenever(it).signingCertificateStore
+            doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
             doReturn(crlCheckSoftFail).whenever(it).crlCheckSoftFail
         }
         serverConfig.configureWithDevSSLCertificate()
 
-        val serverTruststore = serverConfig.loadTrustStore().internal
-        val serverKeystore = serverConfig.loadSslKeyStore().internal
+        val serverTruststore = serverConfig.p2pSslOptions.trustStore.get()
+        val serverKeystore = serverConfig.p2pSslOptions.keyStore.get()
         val amqpConfig = object : AMQPConfiguration {
-            override val keyStore: KeyStore = serverKeystore
-            override val keyStorePrivateKeyPassword: CharArray = serverConfig.keyStorePassword.toCharArray()
-            override val trustStore: KeyStore = serverTruststore
+            override val keyStore = serverKeystore
+            override val trustStore = serverTruststore
             override val trace: Boolean = true
             override val maxMessageSize: Int = maxMessageSize
         }
diff --git a/node/src/integration-test/kotlin/net/corda/node/amqp/SocksTests.kt b/node/src/integration-test/kotlin/net/corda/node/amqp/SocksTests.kt
index 3c5f69db3a..006ee4c50c 100644
--- a/node/src/integration-test/kotlin/net/corda/node/amqp/SocksTests.kt
+++ b/node/src/integration-test/kotlin/net/corda/node/amqp/SocksTests.kt
@@ -26,6 +26,7 @@ import net.corda.nodeapi.internal.protonwrapper.netty.*
 import net.corda.testing.core.*
 import net.corda.testing.driver.PortAllocation
 import net.corda.testing.internal.rigorousMock
+import net.corda.testing.internal.stubs.CertificateStoreStubs
 import org.apache.activemq.artemis.api.core.RoutingType
 import org.junit.After
 import org.junit.Assert.assertArrayEquals
@@ -33,7 +34,6 @@ import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.rules.TemporaryFolder
-import java.security.KeyStore
 import kotlin.test.assertEquals
 
 class SocksTests {
@@ -266,11 +266,17 @@ class SocksTests {
     }
 
     private fun createArtemisServerAndClient(): Pair<ArtemisMessagingServer, ArtemisMessagingClient> {
+        val baseDirectory = temporaryFolder.root.toPath() / "artemis"
+        val certificatesDirectory = baseDirectory / "certificates"
+        val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)
+        val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)
+
         val artemisConfig = rigorousMock<AbstractNodeConfiguration>().also {
-            doReturn(temporaryFolder.root.toPath() / "artemis").whenever(it).baseDirectory
+            doReturn(baseDirectory).whenever(it).baseDirectory
+            doReturn(certificatesDirectory).whenever(it).certificatesDirectory
             doReturn(CHARLIE_NAME).whenever(it).myLegalName
-            doReturn("trustpass").whenever(it).trustStorePassword
-            doReturn("cordacadevpass").whenever(it).keyStorePassword
+            doReturn(signingCertificateStore).whenever(it).signingCertificateStore
+            doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
             doReturn(NetworkHostAndPort("0.0.0.0", artemisPort)).whenever(it).p2pAddress
             doReturn(null).whenever(it).jmxMonitoringHttpPort
             doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000))).whenever(it).enterpriseConfiguration
@@ -278,27 +284,32 @@ class SocksTests {
         artemisConfig.configureWithDevSSLCertificate()
 
         val server = ArtemisMessagingServer(artemisConfig, NetworkHostAndPort("0.0.0.0", artemisPort), MAX_MESSAGE_SIZE)
-        val client = ArtemisMessagingClient(artemisConfig, NetworkHostAndPort("localhost", artemisPort), MAX_MESSAGE_SIZE)
+        val client = ArtemisMessagingClient(artemisConfig.p2pSslOptions, NetworkHostAndPort("localhost", artemisPort), MAX_MESSAGE_SIZE)
         server.start()
         client.start()
         return Pair(server, client)
     }
 
     private fun createClient(): AMQPClient {
+        val baseDirectory = temporaryFolder.root.toPath() / "client"
+        val certificatesDirectory = baseDirectory / "certificates"
+        val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)
+        val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)
+
         val clientConfig = rigorousMock<AbstractNodeConfiguration>().also {
             doReturn(temporaryFolder.root.toPath() / "client").whenever(it).baseDirectory
+            doReturn(certificatesDirectory).whenever(it).certificatesDirectory
             doReturn(BOB_NAME).whenever(it).myLegalName
-            doReturn("trustpass").whenever(it).trustStorePassword
-            doReturn("cordacadevpass").whenever(it).keyStorePassword
+            doReturn(signingCertificateStore).whenever(it).signingCertificateStore
+            doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
         }
         clientConfig.configureWithDevSSLCertificate()
 
-        val clientTruststore = clientConfig.loadTrustStore().internal
-        val clientKeystore = clientConfig.loadSslKeyStore().internal
+        val clientTruststore = clientConfig.p2pSslOptions.trustStore.get()
+        val clientKeystore = clientConfig.p2pSslOptions.keyStore.get()
         val amqpConfig = object : AMQPConfiguration {
-            override val keyStore: KeyStore = clientKeystore
-            override val keyStorePrivateKeyPassword: CharArray = clientConfig.keyStorePassword.toCharArray()
-            override val trustStore: KeyStore = clientTruststore
+            override val keyStore = clientKeystore
+            override val trustStore = clientTruststore
             override val trace: Boolean = true
             override val maxMessageSize: Int = MAX_MESSAGE_SIZE
             override val socksProxyConfig: SocksProxyConfig? = SocksProxyConfig(SocksProxyVersion.SOCKS5, NetworkHostAndPort("127.0.0.1", socksPort), null, null)
@@ -312,20 +323,25 @@ class SocksTests {
     }
 
     private fun createSharedThreadsClient(sharedEventGroup: EventLoopGroup, id: Int): AMQPClient {
+        val baseDirectory = temporaryFolder.root.toPath() / "client_%$id"
+        val certificatesDirectory = baseDirectory / "certificates"
+        val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)
+        val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)
+
         val clientConfig = rigorousMock<AbstractNodeConfiguration>().also {
-            doReturn(temporaryFolder.root.toPath() / "client_%$id").whenever(it).baseDirectory
+            doReturn(baseDirectory).whenever(it).baseDirectory
+            doReturn(certificatesDirectory).whenever(it).certificatesDirectory
             doReturn(CordaX500Name(null, "client $id", "Corda", "London", null, "GB")).whenever(it).myLegalName
-            doReturn("trustpass").whenever(it).trustStorePassword
-            doReturn("cordacadevpass").whenever(it).keyStorePassword
+            doReturn(signingCertificateStore).whenever(it).signingCertificateStore
+            doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
         }
         clientConfig.configureWithDevSSLCertificate()
 
-        val clientTruststore = clientConfig.loadTrustStore().internal
-        val clientKeystore = clientConfig.loadSslKeyStore().internal
+        val clientTruststore = clientConfig.p2pSslOptions.trustStore.get()
+        val clientKeystore = clientConfig.p2pSslOptions.keyStore.get()
         val amqpConfig = object : AMQPConfiguration {
-            override val keyStore: KeyStore = clientKeystore
-            override val keyStorePrivateKeyPassword: CharArray = clientConfig.keyStorePassword.toCharArray()
-            override val trustStore: KeyStore = clientTruststore
+            override val keyStore = clientKeystore
+            override val trustStore = clientTruststore
             override val trace: Boolean = true
             override val maxMessageSize: Int = MAX_MESSAGE_SIZE
             override val socksProxyConfig: SocksProxyConfig? = SocksProxyConfig(SocksProxyVersion.SOCKS5, NetworkHostAndPort("127.0.0.1", socksPort), null, null)
@@ -339,20 +355,25 @@ class SocksTests {
     }
 
     private fun createServer(port: Int, name: CordaX500Name = ALICE_NAME): AMQPServer {
+        val baseDirectory = temporaryFolder.root.toPath() / "server"
+        val certificatesDirectory = baseDirectory / "certificates"
+        val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)
+        val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)
+
         val serverConfig = rigorousMock<AbstractNodeConfiguration>().also {
-            doReturn(temporaryFolder.root.toPath() / "server").whenever(it).baseDirectory
+            doReturn(baseDirectory).whenever(it).baseDirectory
+            doReturn(certificatesDirectory).whenever(it).certificatesDirectory
             doReturn(name).whenever(it).myLegalName
-            doReturn("trustpass").whenever(it).trustStorePassword
-            doReturn("cordacadevpass").whenever(it).keyStorePassword
+            doReturn(signingCertificateStore).whenever(it).signingCertificateStore
+            doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
         }
         serverConfig.configureWithDevSSLCertificate()
 
-        val serverTruststore = serverConfig.loadTrustStore().internal
-        val serverKeystore = serverConfig.loadSslKeyStore().internal
+        val serverTruststore = serverConfig.p2pSslOptions.trustStore.get()
+        val serverKeystore = serverConfig.p2pSslOptions.keyStore.get()
         val amqpConfig = object : AMQPConfiguration {
-            override val keyStore: KeyStore = serverKeystore
-            override val keyStorePrivateKeyPassword: CharArray = serverConfig.keyStorePassword.toCharArray()
-            override val trustStore: KeyStore = serverTruststore
+            override val keyStore = serverKeystore
+            override val trustStore = serverTruststore
             override val trace: Boolean = true
             override val maxMessageSize: Int = MAX_MESSAGE_SIZE
         }
diff --git a/node/src/integration-test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTest.kt
index 82b5e96725..0ca24ab327 100644
--- a/node/src/integration-test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTest.kt
+++ b/node/src/integration-test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTest.kt
@@ -5,6 +5,7 @@ import com.nhaarman.mockito_kotlin.doReturn
 import com.nhaarman.mockito_kotlin.whenever
 import com.typesafe.config.ConfigFactory
 import net.corda.core.crypto.generateKeyPair
+import net.corda.core.internal.div
 import net.corda.core.utilities.NetworkHostAndPort
 import net.corda.core.utilities.seconds
 import net.corda.node.internal.configureDatabase
@@ -19,6 +20,7 @@ import net.corda.testing.core.ALICE_NAME
 import net.corda.testing.core.MAX_MESSAGE_SIZE
 import net.corda.testing.core.SerializationEnvironmentRule
 import net.corda.testing.driver.PortAllocation
+import net.corda.testing.internal.stubs.CertificateStoreStubs
 import net.corda.testing.internal.LogHelper
 import net.corda.testing.internal.rigorousMock
 import net.corda.testing.node.internal.MOCK_VERSION_INFO
@@ -69,11 +71,18 @@ class ArtemisMessagingTest {
     @Before
     fun setUp() {
         abstract class AbstractNodeConfiguration : NodeConfiguration
+
+        val baseDirectory = temporaryFolder.root.toPath()
+        val certificatesDirectory = baseDirectory / "certificates"
+        val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)
+        val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)
+
         config = rigorousMock<AbstractNodeConfiguration>().also {
             doReturn(temporaryFolder.root.toPath()).whenever(it).baseDirectory
             doReturn(ALICE_NAME).whenever(it).myLegalName
-            doReturn("trustpass").whenever(it).trustStorePassword
-            doReturn("cordacadevpass").whenever(it).keyStorePassword
+            doReturn(certificatesDirectory).whenever(it).certificatesDirectory
+            doReturn(signingCertificateStore).whenever(it).signingCertificateStore
+            doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
             doReturn(NetworkHostAndPort("0.0.0.0", serverPort)).whenever(it).p2pAddress
             doReturn(null).whenever(it).jmxMonitoringHttpPort
             doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000))).whenever(it).enterpriseConfiguration
diff --git a/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt
index b556a6f2c1..6a63669bce 100644
--- a/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt
+++ b/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt
@@ -1,7 +1,5 @@
 package net.corda.node.services.network
 
-import net.corda.cordform.CordformNode
-import net.corda.core.concurrent.CordaFuture
 import net.corda.core.crypto.random63BitValue
 import net.corda.core.internal.*
 import net.corda.core.internal.concurrent.transpose
@@ -10,9 +8,7 @@ import net.corda.core.node.NodeInfo
 import net.corda.core.serialization.serialize
 import net.corda.core.utilities.getOrThrow
 import net.corda.core.utilities.seconds
-import net.corda.node.services.config.configureDevKeyAndTrustStores
 import net.corda.nodeapi.internal.NODE_INFO_DIRECTORY
-import net.corda.nodeapi.internal.config.NodeSSLConfiguration
 import net.corda.nodeapi.internal.network.NETWORK_PARAMS_FILE_NAME
 import net.corda.nodeapi.internal.network.NETWORK_PARAMS_UPDATE_FILE_NAME
 import net.corda.nodeapi.internal.network.SignedNetworkParameters
@@ -26,7 +22,6 @@ import net.corda.testing.internal.IntegrationTestSchemas
 import net.corda.testing.internal.toDatabaseSchemaName
 import net.corda.testing.node.internal.*
 import net.corda.testing.node.internal.network.NetworkMapServer
-import net.corda.testing.node.internal.startNode
 import org.assertj.core.api.Assertions.assertThat
 import org.assertj.core.api.Assertions.assertThatThrownBy
 import org.junit.*
diff --git a/node/src/integration-test/kotlin/net/corda/node/services/rpc/ArtemisRpcTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/rpc/ArtemisRpcTests.kt
index 9a6192b4b1..c64a3773f5 100644
--- a/node/src/integration-test/kotlin/net/corda/node/services/rpc/ArtemisRpcTests.kt
+++ b/node/src/integration-test/kotlin/net/corda/node/services/rpc/ArtemisRpcTests.kt
@@ -17,13 +17,13 @@ import net.corda.node.services.messaging.RPCServerConfiguration
 import net.corda.node.utilities.createKeyPairAndSelfSignedTLSCertificate
 import net.corda.node.utilities.saveToKeyStore
 import net.corda.node.utilities.saveToTrustStore
-import net.corda.nodeapi.ArtemisTcpTransport.Companion.rpcConnectorTcpTransport
 import net.corda.nodeapi.BrokerRpcSslOptions
-import net.corda.nodeapi.internal.config.SSLConfiguration
+import net.corda.nodeapi.internal.InternalArtemisTcpTransport.Companion.rpcConnectorTcpTransport
+import net.corda.nodeapi.internal.config.MutualSslConfiguration
 import net.corda.nodeapi.internal.config.User
 import net.corda.testing.core.SerializationEnvironmentRule
 import net.corda.testing.driver.PortAllocation
-import net.corda.testing.internal.createNodeSslConfig
+import net.corda.testing.internal.p2pSslOptions
 import org.apache.activemq.artemis.api.core.ActiveMQConnectionTimedOutException
 import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl
 import org.assertj.core.api.Assertions.assertThat
@@ -58,12 +58,12 @@ class ArtemisRpcTests {
         val brokerSslOptions = BrokerRpcSslOptions(keyStorePath, "password")
         val trustStorePath = saveToTrustStore(tempFile("rpcTruststore.jks"), selfSignCert)
         val clientSslOptions = ClientRpcSslOptions(trustStorePath, "password")
-        testSslCommunication(createNodeSslConfig(tempFolder.root.toPath()), brokerSslOptions, true, clientSslOptions)
+        testSslCommunication(p2pSslOptions(tempFolder.root.toPath()), brokerSslOptions, true, clientSslOptions)
     }
 
     @Test
     fun rpc_with_ssl_disabled() {
-        testSslCommunication(createNodeSslConfig(tempFolder.root.toPath()), null, false, null)
+        testSslCommunication(p2pSslOptions(tempFolder.root.toPath()), null, false, null)
     }
 
     @Test
@@ -73,7 +73,7 @@ class ArtemisRpcTests {
         val brokerSslOptions = BrokerRpcSslOptions(keyStorePath, "password")
         // here client sslOptions are passed null (as in, do not use SSL)
         assertThatThrownBy {
-            testSslCommunication(createNodeSslConfig(tempFolder.root.toPath()), brokerSslOptions, true, null)
+            testSslCommunication(p2pSslOptions(tempFolder.root.toPath()), brokerSslOptions, true, null)
         }.isInstanceOf(ActiveMQConnectionTimedOutException::class.java)
     }
 
@@ -91,11 +91,11 @@ class ArtemisRpcTests {
         val clientSslOptions = ClientRpcSslOptions(trustStorePath, "password")
 
         assertThatThrownBy {
-            testSslCommunication(createNodeSslConfig(tempFolder.root.toPath()), brokerSslOptions, true, clientSslOptions)
+            testSslCommunication(p2pSslOptions(tempFolder.root.toPath()), brokerSslOptions, true, clientSslOptions)
         }.isInstanceOf(RPCException::class.java)
     }
 
-    private fun testSslCommunication(nodeSSlconfig: SSLConfiguration,
+    private fun testSslCommunication(nodeSSlconfig: MutualSslConfiguration,
                                      brokerSslOptions: BrokerRpcSslOptions?,
                                      useSslForBroker: Boolean,
                                      clientSslOptions: ClientRpcSslOptions?,
diff --git a/node/src/integration-test/kotlin/net/corda/node/services/vault/VaultQueryIntegrationTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/vault/VaultQueryIntegrationTests.kt
index da7322d334..0bf1291493 100644
--- a/node/src/integration-test/kotlin/net/corda/node/services/vault/VaultQueryIntegrationTests.kt
+++ b/node/src/integration-test/kotlin/net/corda/node/services/vault/VaultQueryIntegrationTests.kt
@@ -1,29 +1,29 @@
-package net.corda.node.services.vault
-
-
-import net.corda.core.identity.CordaX500Name
-import net.corda.testing.core.TestIdentity
-import net.corda.testing.internal.GlobalDatabaseRule
-import net.corda.testing.internal.toDatabaseSchemaName
-import org.junit.ClassRule
-import org.junit.Rule
-import org.junit.rules.RuleChain
-
-class VaultQueryIntegrationTests : VaultQueryTestsBase(), VaultQueryParties by vaultQueryTestRule {
-
-    companion object {
-        val MEGA_CORP = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).name
-        val databaseSchemas = listOf(Companion.MEGA_CORP.toDatabaseSchemaName())
-
-        val globalDatabaseRule = GlobalDatabaseRule(databaseSchemas)
-        val vaultQueryTestRule = VaultQueryTestRule()
-
-        @ClassRule @JvmField
-        val ruleChain = RuleChain.outerRule(globalDatabaseRule).around(vaultQueryTestRule)
-    }
-
-    @Suppress("LeakingThis")
-    @Rule
-    @JvmField
-    val transactionRule = VaultQueryRollbackRule(this)
-}
+//package net.corda.node.services.vault
+//
+//
+//import net.corda.core.identity.CordaX500Name
+//import net.corda.testing.core.TestIdentity
+//import net.corda.testing.internal.GlobalDatabaseRule
+//import net.corda.testing.internal.toDatabaseSchemaName
+//import org.junit.ClassRule
+//import org.junit.Rule
+//import org.junit.rules.RuleChain
+//
+//class VaultQueryIntegrationTests : VaultQueryTestsBase(), VaultQueryParties by vaultQueryTestRule {
+//
+//    companion object {
+//        val MEGA_CORP = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).name
+//        val databaseSchemas = listOf(Companion.MEGA_CORP.toDatabaseSchemaName())
+//
+//        val globalDatabaseRule = GlobalDatabaseRule(databaseSchemas)
+//        val vaultQueryTestRule = VaultQueryTestRule()
+//
+//        @ClassRule @JvmField
+//        val ruleChain = RuleChain.outerRule(globalDatabaseRule).around(vaultQueryTestRule)
+//    }
+//
+//    @Suppress("LeakingThis")
+//    @Rule
+//    @JvmField
+//    val transactionRule = VaultQueryRollbackRule(this)
+//}
diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt
index feda5b405f..7fb2986bc4 100644
--- a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt
+++ b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt
@@ -2,7 +2,6 @@ package net.corda.services.messaging
 
 import net.corda.core.crypto.Crypto
 import net.corda.core.identity.CordaX500Name
-import net.corda.core.internal.copyTo
 import net.corda.core.internal.createDirectories
 import net.corda.core.internal.exists
 import net.corda.core.internal.toX500Name
@@ -11,9 +10,10 @@ import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_P2P_U
 import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEER_USER
 import net.corda.nodeapi.internal.DEV_INTERMEDIATE_CA
 import net.corda.nodeapi.internal.DEV_ROOT_CA
-import net.corda.nodeapi.internal.config.SSLConfiguration
 import net.corda.nodeapi.internal.crypto.CertificateType
 import net.corda.nodeapi.internal.crypto.X509Utilities
+import net.corda.nodeapi.internal.loadDevCaTrustStore
+import net.corda.testing.internal.stubs.CertificateStoreStubs
 import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration
 import org.apache.activemq.artemis.api.core.ActiveMQClusterSecurityException
 import org.apache.activemq.artemis.api.core.ActiveMQNotConnectedException
@@ -84,57 +84,35 @@ class MQSecurityAsNodeTest : P2PMQSecurityTest() {
 
     @Test
     fun `login with invalid certificate chain`() {
-        val sslConfig = object : SSLConfiguration {
-            override val certificatesDirectory = Files.createTempDirectory("certs")
-            override val keyStorePassword: String get() = "cordacadevpass"
-            override val trustStorePassword: String get() = "trustpass"
-            override val crlCheckSoftFail: Boolean = true
+        val certsDir = Files.createTempDirectory("certs")
+        certsDir.createDirectories()
+        val signingCertStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certsDir)
+        val p2pSslConfig = CertificateStoreStubs.P2P.withCertificatesDirectory(certsDir)
 
-            init {
-                val legalName = CordaX500Name("MegaCorp", "London", "GB")
-                certificatesDirectory.createDirectories()
-                if (!trustStoreFile.exists()) {
-                    javaClass.classLoader.getResourceAsStream("certificates/cordatruststore.jks").use { it.copyTo(trustStoreFile) }
-                }
-
-                val clientKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
-                // Set name constrain to the legal name.
-                val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.toX500Name()))), arrayOf())
-                val clientCACert = X509Utilities.createCertificate(
-                        CertificateType.INTERMEDIATE_CA,
-                        DEV_INTERMEDIATE_CA.certificate,
-                        DEV_INTERMEDIATE_CA.keyPair,
-                        legalName.x500Principal,
-                        clientKeyPair.public,
-                        nameConstraints = nameConstraints)
-
-                val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
-                // Using different x500 name in the TLS cert which is not allowed in the name constraints.
-                val clientTLSCert = X509Utilities.createCertificate(
-                        CertificateType.TLS,
-                        clientCACert,
-                        clientKeyPair,
-                        CordaX500Name("MiniCorp", "London", "GB").x500Principal,
-                        tlsKeyPair.public)
-
-                loadNodeKeyStore(createNew = true).update {
-                    setPrivateKey(
-                            X509Utilities.CORDA_CLIENT_CA,
-                            clientKeyPair.private,
-                            listOf(clientCACert, DEV_INTERMEDIATE_CA.certificate, DEV_ROOT_CA.certificate))
-                }
-
-                loadSslKeyStore(createNew = true).update {
-                    setPrivateKey(
-                            X509Utilities.CORDA_CLIENT_TLS,
-                            tlsKeyPair.private,
-                            listOf(clientTLSCert, clientCACert, DEV_INTERMEDIATE_CA.certificate, DEV_ROOT_CA.certificate))
-                }
-            }
+        val legalName = CordaX500Name("MegaCorp", "London", "GB")
+        if (!p2pSslConfig.trustStore.path.exists()) {
+            val trustStore = p2pSslConfig.trustStore.get(true)
+            loadDevCaTrustStore().copyTo(trustStore)
         }
 
-        val attacker = clientTo(alice.node.configuration.p2pAddress, sslConfig)
+        val clientKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
+        // Set name constrain to the legal name.
+        val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.toX500Name()))), arrayOf())
+        val clientCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, DEV_INTERMEDIATE_CA.certificate, DEV_INTERMEDIATE_CA.keyPair, legalName.x500Principal, clientKeyPair.public, nameConstraints = nameConstraints)
 
+        val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
+        // Using different x500 name in the TLS cert which is not allowed in the name constraints.
+        val clientTLSCert = X509Utilities.createCertificate(CertificateType.TLS, clientCACert, clientKeyPair, CordaX500Name("MiniCorp", "London", "GB").x500Principal, tlsKeyPair.public)
+
+        signingCertStore.get(createNew = true).update {
+            setPrivateKey(X509Utilities.CORDA_CLIENT_CA, clientKeyPair.private, listOf(clientCACert, DEV_INTERMEDIATE_CA.certificate, DEV_ROOT_CA.certificate))
+        }
+
+        p2pSslConfig.keyStore.get(createNew = true).update {
+            setPrivateKey(X509Utilities.CORDA_CLIENT_TLS, tlsKeyPair.private, listOf(clientTLSCert, clientCACert, DEV_INTERMEDIATE_CA.certificate, DEV_ROOT_CA.certificate))
+        }
+
+        val attacker = clientTo(alice.node.configuration.p2pAddress, p2pSslConfig)
         assertThatExceptionOfType(ActiveMQNotConnectedException::class.java).isThrownBy {
             attacker.start(PEER_USER, PEER_USER)
         }
diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt
index 2ba6d4c917..437239e1ad 100644
--- a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt
+++ b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt
@@ -18,7 +18,7 @@ import net.corda.node.internal.NodeWithInfo
 import net.corda.nodeapi.RPCApi
 import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.INTERNAL_PREFIX
 import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NOTIFICATIONS_ADDRESS
-import net.corda.nodeapi.internal.config.SSLConfiguration
+import net.corda.nodeapi.internal.config.MutualSslConfiguration
 import net.corda.testing.core.ALICE_NAME
 import net.corda.testing.core.BOB_NAME
 import net.corda.testing.internal.IntegrationTestSchemas
@@ -103,7 +103,7 @@ abstract class MQSecurityTest : NodeBasedTest() {
         assertAllQueueCreationAttacksFail(randomQueue)
     }
 
-    fun clientTo(target: NetworkHostAndPort, sslConfiguration: SSLConfiguration? = configureTestSSL(CordaX500Name("MegaCorp", "London", "GB"))): SimpleMQClient {
+    fun clientTo(target: NetworkHostAndPort, sslConfiguration: MutualSslConfiguration? = configureTestSSL(CordaX500Name("MegaCorp", "London", "GB"))): SimpleMQClient {
         val client = SimpleMQClient(target, sslConfiguration)
         clients += client
         return client
diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/SimpleMQClient.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/SimpleMQClient.kt
index 58e04948c4..e70e77d6b5 100644
--- a/node/src/integration-test/kotlin/net/corda/services/messaging/SimpleMQClient.kt
+++ b/node/src/integration-test/kotlin/net/corda/services/messaging/SimpleMQClient.kt
@@ -3,8 +3,8 @@ package net.corda.services.messaging
 import net.corda.core.identity.CordaX500Name
 import net.corda.core.serialization.internal.nodeSerializationEnv
 import net.corda.core.utilities.NetworkHostAndPort
-import net.corda.nodeapi.ArtemisTcpTransport
-import net.corda.nodeapi.internal.config.SSLConfiguration
+import net.corda.nodeapi.internal.InternalArtemisTcpTransport.Companion.p2pConnectorTcpTransport
+import net.corda.nodeapi.internal.config.MutualSslConfiguration
 import net.corda.testing.internal.configureTestSSL
 import org.apache.activemq.artemis.api.core.client.*
 
@@ -12,7 +12,7 @@ import org.apache.activemq.artemis.api.core.client.*
  * As the name suggests this is a simple client for connecting to MQ brokers.
  */
 class SimpleMQClient(val target: NetworkHostAndPort,
-                     private val config: SSLConfiguration? = configureTestSSL(DEFAULT_MQ_LEGAL_NAME)) {
+                     private val config: MutualSslConfiguration? = configureTestSSL(DEFAULT_MQ_LEGAL_NAME)) {
     companion object {
         val DEFAULT_MQ_LEGAL_NAME = CordaX500Name(organisation = "SimpleMQClient", locality = "London", country = "GB")
     }
@@ -22,7 +22,7 @@ class SimpleMQClient(val target: NetworkHostAndPort,
     lateinit var producer: ClientProducer
 
     fun start(username: String? = null, password: String? = null, enableSSL: Boolean = true) {
-        val tcpTransport = ArtemisTcpTransport.p2pConnectorTcpTransport(target, config, enableSSL = enableSSL)
+        val tcpTransport = p2pConnectorTcpTransport(target, config, enableSSL = enableSSL)
         val locator = ActiveMQClient.createServerLocatorWithoutHA(tcpTransport).apply {
             isBlockOnNonDurableSend = true
             threadPoolMaxSize = 1
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 2c47696745..215ecc19e4 100644
--- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt
+++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt
@@ -46,6 +46,7 @@ import net.corda.node.services.FinalityHandler
 import net.corda.node.services.NotaryChangeHandler
 import net.corda.node.services.api.*
 import net.corda.node.services.config.*
+import net.corda.node.services.config.rpc.NodeRpcOptions
 import net.corda.node.services.config.shell.toShellConfig
 import net.corda.node.services.events.NodeSchedulerService
 import net.corda.node.services.events.ScheduledActivityObserver
@@ -257,7 +258,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
         val trustRoot = initKeyStore()
         val (identity, identityKeyPair) = obtainIdentity(notaryConfig = null)
         startDatabase()
-        val nodeCa = configuration.loadNodeKeyStore().getCertificate(X509Utilities.CORDA_CLIENT_CA)
+        val nodeCa = configuration.signingCertificateStore.get()[X509Utilities.CORDA_CLIENT_CA]
         identityService.start(trustRoot, listOf(identity.certificate, nodeCa))
         return database.use {
             it.transaction {
@@ -287,7 +288,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
         log.info("Node starting up ...")
 
         val trustRoot = initKeyStore()
-        val nodeCa = configuration.loadNodeKeyStore().getCertificate(X509Utilities.CORDA_CLIENT_CA)
+        val nodeCa = configuration.signingCertificateStore.get()[X509Utilities.CORDA_CLIENT_CA]
         initialiseJVMAgents()
 
         schemaService.mappedSchemasWarnings().forEach {
@@ -392,10 +393,10 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
     open fun startShell() {
         if (configuration.shouldInitCrashShell()) {
             val shellConfiguration = configuration.toShellConfig()
-            shellConfiguration.sshHostKeyDirectory?.let {
+            shellConfiguration.sshdPort?.let {
                 log.info("Binding Shell SSHD server on port $it.")
             }
-            InteractiveShell.startShellInternal(shellConfiguration, cordappLoader.appClassLoader)
+            InteractiveShell.startShell(shellConfiguration, cordappLoader.appClassLoader)
         }
     }
 
@@ -720,8 +721,8 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
     private fun validateKeyStore(): X509Certificate {
         val containCorrectKeys = try {
             // This will throw IOException if key file not found or KeyStoreException if keystore password is incorrect.
-            val sslKeystore = configuration.loadSslKeyStore()
-            val identitiesKeystore = configuration.loadNodeKeyStore()
+            val sslKeystore = configuration.p2pSslOptions.keyStore.get()
+            val identitiesKeystore = configuration.signingCertificateStore.get()
             X509Utilities.CORDA_CLIENT_TLS in sslKeystore && X509Utilities.CORDA_CLIENT_CA in identitiesKeystore
         } catch (e: KeyStoreException) {
             log.warn("Certificate key store found but key store password does not match configuration.")
@@ -738,15 +739,15 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
         }
 
         // Check all cert path chain to the trusted root
-        val sslCertChainRoot = configuration.loadSslKeyStore().getCertificateChain(X509Utilities.CORDA_CLIENT_TLS).last()
-        val nodeCaCertChainRoot = configuration.loadNodeKeyStore().getCertificateChain(X509Utilities.CORDA_CLIENT_CA).last()
-        val trustRoot = configuration.loadTrustStore().getCertificate(X509Utilities.CORDA_ROOT_CA)
+        val sslCertChainRoot = configuration.p2pSslOptions.keyStore.get().query { getCertificateChain(X509Utilities.CORDA_CLIENT_TLS) }.last()
+        val nodeCaCertChainRoot = configuration.signingCertificateStore.get().query { getCertificateChain(X509Utilities.CORDA_CLIENT_CA) }.last()
+        val trustRoot = configuration.p2pSslOptions.trustStore.get()[X509Utilities.CORDA_ROOT_CA]
 
         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))
+            val blacklisted = isCRLDistributionPointBlacklisted(configuration.signingCertificateStore.get().query { 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.")
             }
@@ -797,7 +798,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
         return notaryConfig.run {
             when {
                 raft != null -> {
-                    val uniquenessProvider = RaftUniquenessProvider(configuration, database, platformClock, monitoringService.metrics, raft)
+                    val uniquenessProvider = RaftUniquenessProvider(configuration.baseDirectory, configuration.p2pSslOptions, database, platformClock, monitoringService.metrics, raft)
                     (if (validating) ::RaftValidatingNotaryService else ::RaftNonValidatingNotaryService)(services, notaryKey, uniquenessProvider)
                 }
                 bftSMaRt != null -> {
@@ -844,7 +845,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
                                                  networkParameters: NetworkParameters)
 
     private fun obtainIdentity(notaryConfig: NotaryConfig?): Pair<PartyAndCertificate, KeyPair> {
-        val keyStore = configuration.loadNodeKeyStore()
+        val keyStore = configuration.signingCertificateStore.get()
 
         val (id, singleName) = if (notaryConfig == null || !notaryConfig.isClusterConfig) {
             // Node's main identity or if it's a single node notary
@@ -859,25 +860,25 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
         if (privateKeyAlias !in keyStore) {
             singleName ?: throw IllegalArgumentException(
                     "Unable to find in the key store the identity of the distributed notary the node is part of")
-            log.info("$privateKeyAlias not found in key store ${configuration.nodeKeystore}, generating fresh key!")
+            log.info("$privateKeyAlias not found in key store, generating fresh key!")
             // TODO This check shouldn't be needed
             check(singleName == configuration.myLegalName)
             keyStore.storeLegalIdentity(privateKeyAlias, generateKeyPair())
         }
 
-        val (x509Cert, keyPair) = keyStore.getCertificateAndKeyPair(privateKeyAlias)
+        val (x509Cert, keyPair) = keyStore.query { getCertificateAndKeyPair(privateKeyAlias) }
 
         // TODO: Use configuration to indicate composite key should be used instead of public key for the identity.
         val compositeKeyAlias = "$id-composite-key"
         val certificates = if (compositeKeyAlias in keyStore) {
             // Use composite key instead if it exists
-            val certificate = keyStore.getCertificate(compositeKeyAlias)
+            val certificate = keyStore[compositeKeyAlias]
             // We have to create the certificate chain for the composite key manually, this is because we don't have a keystore
             // provider that understand compositeKey-privateKey combo. The cert chain is created using the composite key certificate +
             // the tail of the private key certificates, as they are both signed by the same certificate chain.
-            listOf(certificate) + keyStore.getCertificateChain(privateKeyAlias).drop(1)
+            listOf(certificate) + keyStore.query { getCertificateChain(privateKeyAlias) }.drop(1)
         } else {
-            keyStore.getCertificateChain(privateKeyAlias).let {
+            keyStore.query { getCertificateChain(privateKeyAlias) }.let {
                 check(it[0] == x509Cert) { "Certificates from key store do not line up!" }
                 it
             }
@@ -1075,4 +1076,13 @@ fun CordaPersistence.startHikariPool(hikariProperties: Properties, databaseConfi
             else -> throw CouldNotCreateDataSourceException("Could not create the DataSource: ${ex.message}", ex)
         }
     }
+}
+
+fun clientSslOptionsCompatibleWith(nodeRpcOptions: NodeRpcOptions): ClientRpcSslOptions? {
+
+    if (!nodeRpcOptions.useSsl || nodeRpcOptions.sslConfig == null) {
+        return null
+    }
+    // Here we're using the node's RPC key store as the RPC client's trust store.
+    return ClientRpcSslOptions(trustStorePath = nodeRpcOptions.sslConfig!!.keyStorePath, trustStorePassword = nodeRpcOptions.sslConfig!!.keyStorePassword)
 }
\ No newline at end of file
diff --git a/node/src/main/kotlin/net/corda/node/internal/Node.kt b/node/src/main/kotlin/net/corda/node/internal/Node.kt
index 6757953e9a..51e84f2048 100644
--- a/node/src/main/kotlin/net/corda/node/internal/Node.kt
+++ b/node/src/main/kotlin/net/corda/node/internal/Node.kt
@@ -233,7 +233,7 @@ open class Node(configuration: NodeConfiguration,
 
         val externalBridge = configuration.enterpriseConfiguration.externalBridge
         val bridgeControlListener = if (externalBridge == null || !externalBridge) {
-            BridgeControlListener(configuration, network.serverAddress, networkParameters.maxMessageSize)
+            BridgeControlListener(configuration.p2pSslOptions, network.serverAddress, networkParameters.maxMessageSize)
         } else {
             null
         }
@@ -244,7 +244,7 @@ open class Node(configuration: NodeConfiguration,
                 rpcThreadPoolSize = configuration.enterpriseConfiguration.tuning.rpcThreadPoolSize
         )
         rpcServerAddresses?.let {
-            internalRpcMessagingClient = InternalRPCMessagingClient(configuration, it.admin, MAX_RPC_MESSAGE_SIZE, CordaX500Name.build(configuration.loadSslKeyStore().getCertificate(X509Utilities.CORDA_CLIENT_TLS).subjectX500Principal), rpcServerConfiguration)
+            internalRpcMessagingClient = InternalRPCMessagingClient(configuration.p2pSslOptions, it.admin, MAX_RPC_MESSAGE_SIZE, CordaX500Name.build(configuration.p2pSslOptions.keyStore.get()[X509Utilities.CORDA_CLIENT_TLS].subjectX500Principal), rpcServerConfiguration)
             printBasicNodeInfo("RPC connection address", it.primary.toString())
             printBasicNodeInfo("RPC admin connection address", it.admin.toString())
         }
@@ -284,9 +284,9 @@ open class Node(configuration: NodeConfiguration,
                 val rpcBrokerDirectory: Path = baseDirectory / "brokers" / "rpc"
                 with(rpcOptions) {
                     rpcBroker = if (useSsl) {
-                        ArtemisRpcBroker.withSsl(configuration, this.address, adminAddress, sslConfig!!, securityManager, MAX_RPC_MESSAGE_SIZE, jmxMonitoringHttpPort != null, rpcBrokerDirectory, shouldStartLocalShell())
+                        ArtemisRpcBroker.withSsl(configuration.p2pSslOptions, this.address, adminAddress, sslConfig!!, securityManager, MAX_RPC_MESSAGE_SIZE, jmxMonitoringHttpPort != null, rpcBrokerDirectory, shouldStartLocalShell())
                     } else {
-                        ArtemisRpcBroker.withoutSsl(configuration, this.address, adminAddress, securityManager, MAX_RPC_MESSAGE_SIZE, jmxMonitoringHttpPort != null, rpcBrokerDirectory, shouldStartLocalShell())
+                        ArtemisRpcBroker.withoutSsl(configuration.p2pSslOptions, this.address, adminAddress, securityManager, MAX_RPC_MESSAGE_SIZE, jmxMonitoringHttpPort != null, rpcBrokerDirectory, shouldStartLocalShell())
                     }
                 }
                 rpcBroker!!.closeOnStop()
diff --git a/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt
index 81c7992d13..96f0383fba 100644
--- a/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt
+++ b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt
@@ -10,11 +10,10 @@ import net.corda.core.internal.createDirectories
 import net.corda.core.internal.div
 import net.corda.core.internal.exists
 import net.corda.nodeapi.internal.*
-import net.corda.nodeapi.internal.config.SSLConfiguration
+import net.corda.nodeapi.internal.config.FileBasedCertificateStoreSupplier
+import net.corda.nodeapi.internal.config.MutualSslConfiguration
 import net.corda.nodeapi.internal.config.toProperties
 import net.corda.nodeapi.internal.crypto.X509KeyStore
-import net.corda.nodeapi.internal.crypto.loadKeyStore
-import net.corda.nodeapi.internal.crypto.save
 import org.slf4j.LoggerFactory
 import java.nio.file.Path
 import kotlin.math.min
@@ -83,22 +82,33 @@ object ConfigHelper {
  * the CA certs in Node resources. Then provision KeyStores into certificates folder under node path.
  */
 // TODO Move this to KeyStoreConfigHelpers
-fun NodeConfiguration.configureWithDevSSLCertificate() = configureDevKeyAndTrustStores(myLegalName)
+fun NodeConfiguration.configureWithDevSSLCertificate() = p2pSslOptions.configureDevKeyAndTrustStores(myLegalName, signingCertificateStore, certificatesDirectory)
 
 // TODO Move this to KeyStoreConfigHelpers
-fun SSLConfiguration.configureDevKeyAndTrustStores(myLegalName: CordaX500Name) {
+fun MutualSslConfiguration.configureDevKeyAndTrustStores(myLegalName: CordaX500Name, signingCertificateStore: FileBasedCertificateStoreSupplier, certificatesDirectory: Path) {
+
+    val specifiedTrustStore = trustStore.getOptional()
+
+    val specifiedKeyStore = keyStore.getOptional()
+    val specifiedSigningStore = signingCertificateStore.getOptional()
+
+    if (specifiedTrustStore != null && specifiedKeyStore != null && specifiedSigningStore != null) return
     certificatesDirectory.createDirectories()
-    if (!trustStoreFile.exists()) {
-        loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/$DEV_CA_TRUST_STORE_FILE"), DEV_CA_TRUST_STORE_PASS).save(trustStoreFile, trustStorePassword)
+
+    if (specifiedTrustStore == null) {
+        loadDevCaTrustStore().copyTo(trustStore.get(true))
     }
-    if (!sslKeystore.exists() || !nodeKeystore.exists()) {
-        val (nodeKeyStore) = createDevKeyStores(myLegalName)
+
+    if (keyStore.getOptional() == null || signingCertificateStore.getOptional() == null) {
+        val signingKeyStore = FileBasedCertificateStoreSupplier(signingCertificateStore.path, signingCertificateStore.password).get(true).also { it.registerDevSigningCertificates(myLegalName) }
+
+        FileBasedCertificateStoreSupplier(keyStore.path, keyStore.password).get(true).also { it.registerDevP2pCertificates(myLegalName) }
 
         // Move distributed service composite key (generated by IdentityGenerator.generateToDisk) to keystore if exists.
         val distributedServiceKeystore = certificatesDirectory / "distributedService.jks"
         if (distributedServiceKeystore.exists()) {
             val serviceKeystore = X509KeyStore.fromFile(distributedServiceKeystore, DEV_CA_KEY_STORE_PASS)
-            nodeKeyStore.update {
+            signingKeyStore.update {
                 serviceKeystore.aliases().forEach {
                     if (serviceKeystore.internal.isKeyEntry(it)) {
                         setPrivateKey(it, serviceKeystore.getPrivateKey(it, DEV_CA_PRIVATE_KEY_PASS), serviceKeystore.getCertificateChain(it))
diff --git a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt
index ddecc2fd7d..b8cd514f49 100644
--- a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt
+++ b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt
@@ -11,7 +11,9 @@ import net.corda.core.utilities.loggerFor
 import net.corda.core.utilities.seconds
 import net.corda.node.services.config.rpc.NodeRpcOptions
 import net.corda.nodeapi.BrokerRpcSslOptions
-import net.corda.nodeapi.internal.config.NodeSSLConfiguration
+import net.corda.nodeapi.internal.config.FileBasedCertificateStoreSupplier
+import net.corda.nodeapi.internal.config.SslConfiguration
+import net.corda.nodeapi.internal.config.MutualSslConfiguration
 import net.corda.nodeapi.internal.config.UnknownConfigKeysPolicy
 import net.corda.nodeapi.internal.config.User
 import net.corda.nodeapi.internal.config.parseAs
@@ -32,7 +34,7 @@ private val DEFAULT_FLOW_MONITOR_PERIOD_MILLIS: Duration = Duration.ofMinutes(1)
 private val DEFAULT_FLOW_MONITOR_SUSPENSION_LOGGING_THRESHOLD_MILLIS: Duration = Duration.ofMinutes(1)
 private const val CORDAPPS_DIR_NAME_DEFAULT = "cordapps"
 
-interface NodeConfiguration : NodeSSLConfiguration {
+interface NodeConfiguration {
     val myLegalName: CordaX500Name
     val emailAddress: String
     val jmxMonitoringHttpPort: Int?
@@ -75,8 +77,16 @@ interface NodeConfiguration : NodeSSLConfiguration {
     val effectiveH2Settings: NodeH2Settings?
     val flowMonitorPeriodMillis: Duration get() = DEFAULT_FLOW_MONITOR_PERIOD_MILLIS
     val flowMonitorSuspensionLoggingThresholdMillis: Duration get() = DEFAULT_FLOW_MONITOR_SUSPENSION_LOGGING_THRESHOLD_MILLIS
-    val cordappDirectories: List<Path> get() = listOf(baseDirectory / CORDAPPS_DIR_NAME_DEFAULT)
+    val crlCheckSoftFail: Boolean
     val jmxReporterType : JmxReporterType? get() = defaultJmxReporterType
+
+    val baseDirectory: Path
+    val certificatesDirectory: Path
+    val signingCertificateStore: FileBasedCertificateStoreSupplier
+    val p2pSslOptions: MutualSslConfiguration
+
+    val cordappDirectories: List<Path>
+
     fun validate(): List<String>
 
     companion object {
@@ -162,7 +172,7 @@ data class MySQLConfiguration(
          * further commit requests.
          */
         val maxQueueSize: Int = 100_000
-        ) {
+) {
     init {
         require(connectionRetries >= 0) { "connectionRetries cannot be negative" }
     }
@@ -220,8 +230,8 @@ data class NodeConfigurationImpl(
         override val myLegalName: CordaX500Name,
         override val jmxMonitoringHttpPort: Int? = null,
         override val emailAddress: String,
-        override val keyStorePassword: String,
-        override val trustStorePassword: String,
+        private val keyStorePassword: String,
+        private val trustStorePassword: String,
         override val crlCheckSoftFail: Boolean,
         override val dataSourceProperties: Properties,
         override val compatibilityZoneURL: URL? = null,
@@ -292,6 +302,17 @@ data class NodeConfigurationImpl(
         }
     }
 
+    override val certificatesDirectory = baseDirectory / "certificates"
+
+    private val signingCertificateStorePath = certificatesDirectory / "nodekeystore.jks"
+    override val signingCertificateStore = FileBasedCertificateStoreSupplier(signingCertificateStorePath, keyStorePassword)
+
+    private val p2pKeystorePath: Path get() = certificatesDirectory / "sslkeystore.jks"
+    private val p2pKeyStore = FileBasedCertificateStoreSupplier(p2pKeystorePath, keyStorePassword)
+    private val p2pTrustStoreFilePath: Path get() = certificatesDirectory / "truststore.jks"
+    private val p2pTrustStore = FileBasedCertificateStoreSupplier(p2pTrustStoreFilePath, trustStorePassword)
+    override val p2pSslOptions: MutualSslConfiguration = SslConfiguration.mutual(p2pKeyStore, p2pTrustStore)
+
     override val rpcOptions: NodeRpcOptions
         get() {
             return actualRpcSettings.asOptions()
@@ -430,8 +451,6 @@ data class NodeConfigurationImpl(
     }
 }
 
-
-
 data class NodeRpcSettings(
         val address: NetworkHostAndPort?,
         val adminAddress: NetworkHostAndPort?,
diff --git a/node/src/main/kotlin/net/corda/node/services/config/shell/ShellConfig.kt b/node/src/main/kotlin/net/corda/node/services/config/shell/ShellConfig.kt
index 5a0eaa4ac9..3138a29934 100644
--- a/node/src/main/kotlin/net/corda/node/services/config/shell/ShellConfig.kt
+++ b/node/src/main/kotlin/net/corda/node/services/config/shell/ShellConfig.kt
@@ -1,6 +1,7 @@
 package net.corda.node.services.config.shell
 
 import net.corda.core.internal.div
+import net.corda.node.internal.clientSslOptionsCompatibleWith
 import net.corda.node.services.config.NodeConfiguration
 import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.INTERNAL_SHELL_USER
 import net.corda.tools.shell.ShellConfiguration
@@ -14,8 +15,8 @@ fun NodeConfiguration.toShellConfig() = ShellConfiguration(
         cordappsDirectory = this.baseDirectory.toString() / CORDAPPS_DIR,
         user = INTERNAL_SHELL_USER,
         password = INTERNAL_SHELL_USER,
-        hostAndPort = this.rpcOptions.adminAddress,
-        nodeSslConfig = this,
+        hostAndPort = this.rpcOptions.address,
+        ssl = clientSslOptionsCompatibleWith(this.rpcOptions),
         sshdPort = this.sshd?.port,
         sshHostKeyDirectory = this.baseDirectory / SSHD_HOSTKEY_DIR,
         noLocalShell = this.noLocalShell)
diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt
index 11c6b8a37c..0569e71258 100644
--- a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt
+++ b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt
@@ -1,6 +1,5 @@
 package net.corda.node.services.messaging
 
-import io.netty.channel.unix.Errors
 import net.corda.core.internal.ThreadBox
 import net.corda.core.internal.div
 import net.corda.core.serialization.SingletonSerializeAsToken
@@ -12,13 +11,13 @@ import net.corda.node.internal.artemis.BrokerJaasLoginModule.Companion.NODE_P2P_
 import net.corda.node.internal.artemis.BrokerJaasLoginModule.Companion.PEER_ROLE
 import net.corda.core.internal.errors.AddressBindingException
 import net.corda.node.services.config.NodeConfiguration
-import net.corda.nodeapi.ArtemisTcpTransport.Companion.p2pAcceptorTcpTransport
 import net.corda.nodeapi.internal.AmqpMessageSizeChecksInterceptor
 import net.corda.nodeapi.internal.ArtemisMessageSizeChecksInterceptor
 import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.INTERNAL_PREFIX
 import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.JOURNAL_HEADER_SIZE
 import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NOTIFICATIONS_ADDRESS
 import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_PREFIX
+import net.corda.nodeapi.internal.InternalArtemisTcpTransport.Companion.p2pAcceptorTcpTransport
 import net.corda.nodeapi.internal.requireOnDefaultFileSystem
 import org.apache.activemq.artemis.api.core.SimpleString
 import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl
@@ -120,7 +119,7 @@ class ArtemisMessagingServer(private val config: NodeConfiguration,
         bindingsDirectory = (artemisDir / "bindings").toString()
         journalDirectory = (artemisDir / "journal").toString()
         largeMessagesDirectory = (artemisDir / "large-messages").toString()
-        acceptorConfigurations = mutableSetOf(p2pAcceptorTcpTransport(NetworkHostAndPort(messagingServerAddress.host, messagingServerAddress.port), config))
+        acceptorConfigurations = mutableSetOf(p2pAcceptorTcpTransport(NetworkHostAndPort(messagingServerAddress.host, messagingServerAddress.port), config.p2pSslOptions))
         // Enable built in message deduplication. Note we still have to do our own as the delayed commits
         // and our own definition of commit mean that the built in deduplication cannot remove all duplicates.
         idCacheSize = 2000 // Artemis Default duplicate cache size i.e. a guess
@@ -163,8 +162,8 @@ class ArtemisMessagingServer(private val config: NodeConfiguration,
 
     @Throws(IOException::class, KeyStoreException::class)
     private fun createArtemisSecurityManager(): ActiveMQJAASSecurityManager {
-        val keyStore = config.loadSslKeyStore().internal
-        val trustStore = config.loadTrustStore().internal
+        val keyStore = config.p2pSslOptions.keyStore.get().value.internal
+        val trustStore = config.p2pSslOptions.trustStore.get().value.internal
 
         val securityConfig = object : SecurityConfiguration() {
             // Override to make it work with our login module
diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/InternalRPCMessagingClient.kt b/node/src/main/kotlin/net/corda/node/services/messaging/InternalRPCMessagingClient.kt
index 9b5191b312..abb07763d1 100644
--- a/node/src/main/kotlin/net/corda/node/services/messaging/InternalRPCMessagingClient.kt
+++ b/node/src/main/kotlin/net/corda/node/services/messaging/InternalRPCMessagingClient.kt
@@ -6,9 +6,9 @@ import net.corda.core.serialization.SingletonSerializeAsToken
 import net.corda.core.serialization.internal.nodeSerializationEnv
 import net.corda.core.utilities.NetworkHostAndPort
 import net.corda.node.internal.security.RPCSecurityManager
-import net.corda.nodeapi.ArtemisTcpTransport
 import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_RPC_USER
-import net.corda.nodeapi.internal.config.SSLConfiguration
+import net.corda.nodeapi.internal.InternalArtemisTcpTransport
+import net.corda.nodeapi.internal.config.MutualSslConfiguration
 import org.apache.activemq.artemis.api.core.client.ActiveMQClient
 import org.apache.activemq.artemis.api.core.client.ServerLocator
 import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl
@@ -16,13 +16,13 @@ import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl
 /**
  * Used by the Node to communicate with the RPC broker.
  */
-class InternalRPCMessagingClient(val sslConfig: SSLConfiguration, val serverAddress: NetworkHostAndPort, val maxMessageSize: Int, val nodeName: CordaX500Name, val rpcServerConfiguration: RPCServerConfiguration) : SingletonSerializeAsToken(), AutoCloseable {
+class InternalRPCMessagingClient(val sslConfig: MutualSslConfiguration, val serverAddress: NetworkHostAndPort, val maxMessageSize: Int, val nodeName: CordaX500Name, val rpcServerConfiguration: RPCServerConfiguration) : SingletonSerializeAsToken(), AutoCloseable {
     private var locator: ServerLocator? = null
     private var rpcServer: RPCServer? = null
 
     fun init(rpcOps: RPCOps, securityManager: RPCSecurityManager) = synchronized(this) {
 
-        val tcpTransport = ArtemisTcpTransport.rpcInternalClientTcpTransport(serverAddress, sslConfig)
+        val tcpTransport = InternalArtemisTcpTransport.rpcInternalClientTcpTransport(serverAddress, sslConfig)
         locator = ActiveMQClient.createServerLocatorWithoutHA(tcpTransport).apply {
             // Never time out on our loopback Artemis connections. If we switch back to using the InVM transport this
             // would be the default and the two lines below can be deleted.
diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessagingClient.kt b/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessagingClient.kt
index 84d7a6185d..e022b46d0d 100644
--- a/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessagingClient.kt
+++ b/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessagingClient.kt
@@ -27,7 +27,6 @@ import net.corda.node.services.statemachine.DeduplicationId
 import net.corda.node.services.statemachine.ExternalEvent
 import net.corda.node.services.statemachine.SenderDeduplicationId
 import net.corda.node.utilities.AffinityExecutor
-import net.corda.nodeapi.ArtemisTcpTransport.Companion.p2pConnectorTcpTransport
 import net.corda.nodeapi.internal.ArtemisMessagingComponent
 import net.corda.nodeapi.internal.ArtemisMessagingComponent.*
 import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.BRIDGE_CONTROL
@@ -35,6 +34,7 @@ import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.BRIDGE_NOT
 import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.JOURNAL_HEADER_SIZE
 import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2PMessagingHeaders
 import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEERS_PREFIX
+import net.corda.nodeapi.internal.InternalArtemisTcpTransport.Companion.p2pConnectorTcpTransport
 import net.corda.nodeapi.internal.bridging.BridgeControl
 import net.corda.nodeapi.internal.bridging.BridgeEntry
 import net.corda.nodeapi.internal.persistence.CordaPersistence
@@ -152,7 +152,7 @@ class P2PMessagingClient(val config: NodeConfiguration,
             started = true
             log.info("Connecting to message broker: $serverAddress")
             // TODO Add broker CN to config for host verification in case the embedded broker isn't used
-            val tcpTransport = p2pConnectorTcpTransport(serverAddress, config)
+            val tcpTransport = p2pConnectorTcpTransport(serverAddress, config.p2pSslOptions)
             locator = ActiveMQClient.createServerLocatorWithoutHA(tcpTransport).apply {
                 // Never time out on our loopback Artemis connections. If we switch back to using the InVM transport this
                 // would be the default and the two lines below can be deleted.
diff --git a/node/src/main/kotlin/net/corda/node/services/rpc/ArtemisRpcBroker.kt b/node/src/main/kotlin/net/corda/node/services/rpc/ArtemisRpcBroker.kt
index 5fc3c98bc8..af6158e33a 100644
--- a/node/src/main/kotlin/net/corda/node/services/rpc/ArtemisRpcBroker.kt
+++ b/node/src/main/kotlin/net/corda/node/services/rpc/ArtemisRpcBroker.kt
@@ -8,7 +8,7 @@ import net.corda.node.internal.artemis.BrokerJaasLoginModule.Companion.NODE_SECU
 import net.corda.node.internal.artemis.BrokerJaasLoginModule.Companion.RPC_SECURITY_CONFIG
 import net.corda.node.internal.security.RPCSecurityManager
 import net.corda.nodeapi.BrokerRpcSslOptions
-import net.corda.nodeapi.internal.config.SSLConfiguration
+import net.corda.nodeapi.internal.config.MutualSslConfiguration
 import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl
 import org.apache.activemq.artemis.core.config.impl.SecurityConfiguration
 import org.apache.activemq.artemis.core.server.ActiveMQServer
@@ -28,17 +28,17 @@ class ArtemisRpcBroker internal constructor(
         private val maxMessageSize: Int,
         private val jmxEnabled: Boolean = false,
         private val baseDirectory: Path,
-        private val nodeConfiguration: SSLConfiguration,
+        private val nodeConfiguration: MutualSslConfiguration,
         private val shouldStartLocalShell: Boolean) : ArtemisBroker {
 
     companion object {
         private val logger = loggerFor<ArtemisRpcBroker>()
 
-        fun withSsl(configuration: SSLConfiguration, address: NetworkHostAndPort, adminAddress: NetworkHostAndPort, sslOptions: BrokerRpcSslOptions, securityManager: RPCSecurityManager, maxMessageSize: Int, jmxEnabled: Boolean, baseDirectory: Path, shouldStartLocalShell: Boolean): ArtemisBroker {
+        fun withSsl(configuration: MutualSslConfiguration, address: NetworkHostAndPort, adminAddress: NetworkHostAndPort, sslOptions: BrokerRpcSslOptions, securityManager: RPCSecurityManager, maxMessageSize: Int, jmxEnabled: Boolean, baseDirectory: Path, shouldStartLocalShell: Boolean): ArtemisBroker {
             return ArtemisRpcBroker(address, adminAddress, sslOptions, true, securityManager, maxMessageSize, jmxEnabled, baseDirectory, configuration, shouldStartLocalShell)
         }
 
-        fun withoutSsl(configuration: SSLConfiguration, address: NetworkHostAndPort, adminAddress: NetworkHostAndPort, securityManager: RPCSecurityManager, maxMessageSize: Int, jmxEnabled: Boolean, baseDirectory: Path, shouldStartLocalShell: Boolean): ArtemisBroker {
+        fun withoutSsl(configuration: MutualSslConfiguration, address: NetworkHostAndPort, adminAddress: NetworkHostAndPort, securityManager: RPCSecurityManager, maxMessageSize: Int, jmxEnabled: Boolean, baseDirectory: Path, shouldStartLocalShell: Boolean): ArtemisBroker {
             return ArtemisRpcBroker(address, adminAddress, null, false, securityManager, maxMessageSize, jmxEnabled, baseDirectory, configuration, shouldStartLocalShell)
         }
     }
@@ -83,14 +83,14 @@ class ArtemisRpcBroker internal constructor(
 
     @Throws(IOException::class, KeyStoreException::class)
     private fun createArtemisSecurityManager(loginListener: LoginListener): ActiveMQJAASSecurityManager {
-        val keyStore = nodeConfiguration.loadSslKeyStore().internal
-        val trustStore = nodeConfiguration.loadTrustStore().internal
+        val keyStore = nodeConfiguration.keyStore.get()
+        val trustStore = nodeConfiguration.trustStore.get()
 
         val securityConfig = object : SecurityConfiguration() {
             override fun getAppConfigurationEntry(name: String): Array<AppConfigurationEntry> {
                 val options = mapOf(
                         RPC_SECURITY_CONFIG to RPCJaasConfig(securityManager, loginListener, useSsl),
-                        NODE_SECURITY_CONFIG to NodeJaasConfig(keyStore, trustStore)
+                        NODE_SECURITY_CONFIG to NodeJaasConfig(keyStore.value.internal, trustStore.value.internal)
                 )
                 return arrayOf(AppConfigurationEntry(name, AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options))
             }
diff --git a/node/src/main/kotlin/net/corda/node/services/rpc/RpcBrokerConfiguration.kt b/node/src/main/kotlin/net/corda/node/services/rpc/RpcBrokerConfiguration.kt
index 3859e7cd8c..f17dcee8aa 100644
--- a/node/src/main/kotlin/net/corda/node/services/rpc/RpcBrokerConfiguration.kt
+++ b/node/src/main/kotlin/net/corda/node/services/rpc/RpcBrokerConfiguration.kt
@@ -4,12 +4,12 @@ import net.corda.core.internal.div
 import net.corda.core.utilities.NetworkHostAndPort
 import net.corda.node.internal.artemis.BrokerJaasLoginModule
 import net.corda.node.internal.artemis.SecureArtemisConfiguration
-import net.corda.nodeapi.ArtemisTcpTransport.Companion.rpcAcceptorTcpTransport
-import net.corda.nodeapi.ArtemisTcpTransport.Companion.rpcInternalAcceptorTcpTransport
 import net.corda.nodeapi.BrokerRpcSslOptions
 import net.corda.nodeapi.RPCApi
 import net.corda.nodeapi.internal.ArtemisMessagingComponent
-import net.corda.nodeapi.internal.config.SSLConfiguration
+import net.corda.nodeapi.internal.InternalArtemisTcpTransport.Companion.rpcAcceptorTcpTransport
+import net.corda.nodeapi.internal.InternalArtemisTcpTransport.Companion.rpcInternalAcceptorTcpTransport
+import net.corda.nodeapi.internal.config.MutualSslConfiguration
 import org.apache.activemq.artemis.api.core.SimpleString
 import org.apache.activemq.artemis.core.config.CoreQueueConfiguration
 import org.apache.activemq.artemis.core.security.Role
@@ -17,7 +17,7 @@ import org.apache.activemq.artemis.core.settings.impl.AddressFullMessagePolicy
 import org.apache.activemq.artemis.core.settings.impl.AddressSettings
 import java.nio.file.Path
 
-internal class RpcBrokerConfiguration(baseDirectory: Path, maxMessageSize: Int, jmxEnabled: Boolean, address: NetworkHostAndPort, adminAddress: NetworkHostAndPort?, sslOptions: BrokerRpcSslOptions?, useSsl: Boolean, nodeConfiguration: SSLConfiguration, shouldStartLocalShell: Boolean) : SecureArtemisConfiguration() {
+internal class RpcBrokerConfiguration(baseDirectory: Path, maxMessageSize: Int, jmxEnabled: Boolean, address: NetworkHostAndPort, adminAddress: NetworkHostAndPort?, sslOptions: BrokerRpcSslOptions?, useSsl: Boolean, nodeConfiguration: MutualSslConfiguration, shouldStartLocalShell: Boolean) : SecureArtemisConfiguration() {
     val loginListener: (String) -> Unit
 
     init {
diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/RaftUniquenessProvider.kt b/node/src/main/kotlin/net/corda/node/services/transactions/RaftUniquenessProvider.kt
index 1e5c46d28b..1265461d17 100644
--- a/node/src/main/kotlin/net/corda/node/services/transactions/RaftUniquenessProvider.kt
+++ b/node/src/main/kotlin/net/corda/node/services/transactions/RaftUniquenessProvider.kt
@@ -28,8 +28,7 @@ import net.corda.core.utilities.debug
 import net.corda.node.services.config.RaftConfig
 import net.corda.node.services.transactions.RaftTransactionCommitLog.Commands.CommitTransaction
 import net.corda.node.utilities.AppendOnlyPersistentMap
-import net.corda.nodeapi.internal.config.NodeSSLConfiguration
-import net.corda.nodeapi.internal.config.SSLConfiguration
+import net.corda.nodeapi.internal.config.MutualSslConfiguration
 import net.corda.nodeapi.internal.persistence.CordaPersistence
 import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
 import java.nio.file.Path
@@ -51,7 +50,8 @@ import javax.persistence.Table
  */
 @ThreadSafe
 class RaftUniquenessProvider(
-        private val transportConfiguration: NodeSSLConfiguration,
+        private val storagePath: Path,
+        private val transportConfiguration: MutualSslConfiguration,
         private val db: CordaPersistence,
         private val clock: Clock,
         private val metrics: MetricRegistry,
@@ -96,8 +96,6 @@ class RaftUniquenessProvider(
             var index: Long = 0
     )
 
-    /** Directory storing the Raft log and state machine snapshots */
-    private val storagePath: Path = transportConfiguration.baseDirectory
     private lateinit var _clientFuture: CompletableFuture<CopycatClient>
     private lateinit var server: CopycatServer
 
@@ -155,14 +153,14 @@ class RaftUniquenessProvider(
                 .build()
     }
 
-    private fun buildTransport(config: SSLConfiguration): Transport? {
+    private fun buildTransport(config: MutualSslConfiguration): Transport? {
         return NettyTransport.builder()
                 .withSsl()
                 .withSslProtocol(SslProtocol.TLSv1_2)
-                .withKeyStorePath(config.sslKeystore.toString())
-                .withKeyStorePassword(config.keyStorePassword)
-                .withTrustStorePath(config.trustStoreFile.toString())
-                .withTrustStorePassword(config.trustStorePassword)
+                .withKeyStorePath(config.keyStore.path.toString())
+                .withKeyStorePassword(config.keyStore.password)
+                .withTrustStorePath(config.trustStore.path.toString())
+                .withTrustStorePassword(config.trustStore.password)
                 .build()
     }
 
diff --git a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt
index 4ec5319c28..b3186b8d7b 100644
--- a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt
+++ b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt
@@ -6,7 +6,8 @@ import net.corda.core.internal.*
 import net.corda.core.utilities.contextLogger
 import net.corda.node.NodeRegistrationOption
 import net.corda.node.services.config.NodeConfiguration
-import net.corda.nodeapi.internal.config.SSLConfiguration
+import net.corda.nodeapi.internal.config.CertificateStore
+import net.corda.nodeapi.internal.config.CertificateStoreSupplier
 import net.corda.nodeapi.internal.crypto.CertificateType
 import net.corda.nodeapi.internal.crypto.X509KeyStore
 import net.corda.nodeapi.internal.crypto.X509Utilities
@@ -33,7 +34,8 @@ import javax.security.auth.x500.X500Principal
  * needed.
  */
 // TODO: Use content signer instead of keypairs.
-open class NetworkRegistrationHelper(private val config: SSLConfiguration,
+open class NetworkRegistrationHelper(private val certificatesDirectory: Path,
+                                     private val signingCertificateStore: CertificateStoreSupplier,
                                      private val myLegalName: CordaX500Name,
                                      private val emailAddress: String,
                                      private val certService: NetworkRegistrationService,
@@ -48,9 +50,7 @@ open class NetworkRegistrationHelper(private val config: SSLConfiguration,
         val logger = contextLogger()
     }
 
-    private val requestIdStore = config.certificatesDirectory / "certificate-request-id.txt"
-    // TODO: Use different password for private key.
-    private val privateKeyPassword = config.keyStorePassword
+    private val requestIdStore = certificatesDirectory / "certificate-request-id.txt"
     private val rootTrustStore: X509KeyStore
     protected val rootCert: X509Certificate
 
@@ -75,12 +75,14 @@ open class NetworkRegistrationHelper(private val config: SSLConfiguration,
      * @throws CertificateRequestException if the certificate retrieved by doorman is invalid.
      */
     fun buildKeystore() {
-        config.certificatesDirectory.createDirectories()
-        val nodeKeyStore = config.loadNodeKeyStore(createNew = true)
+        certificatesDirectory.createDirectories()
+        val nodeKeyStore = signingCertificateStore.get(createNew = true)
         if (keyAlias in nodeKeyStore) {
             println("Certificate already exists, Corda node will now terminate...")
             return
         }
+        // TODO: Use different password for private key.
+        val privateKeyPassword = nodeKeyStore.password
         val tlsCrlIssuerCert = validateAndGetTlsCrlIssuerCert()
         if (tlsCrlIssuerCert == null && isTlsCrlIssuerCertRequired()) {
             System.err.println("""tlsCrlIssuerCert config does not match the root certificate issuer and nor is there any other certificate in the trust store with a matching issuer.
@@ -89,7 +91,7 @@ open class NetworkRegistrationHelper(private val config: SSLConfiguration,
             throw IllegalArgumentException("TLS CRL issuer certificate not found in the trust store.")
         }
 
-        val keyPair = nodeKeyStore.loadOrCreateKeyPair(SELF_SIGNED_PRIVATE_KEY)
+        val keyPair = nodeKeyStore.loadOrCreateKeyPair(SELF_SIGNED_PRIVATE_KEY, privateKeyPassword)
 
         val requestId = try {
             submitOrResumeCertificateSigningRequest(keyPair)
@@ -110,7 +112,7 @@ open class NetworkRegistrationHelper(private val config: SSLConfiguration,
             throw certificateRequestException
         }
         validateCertificates(keyPair.public, certificates)
-        storePrivateKeyWithCertificates(nodeKeyStore, keyPair, certificates, keyAlias)
+        storePrivateKeyWithCertificates(nodeKeyStore, keyPair, certificates, keyAlias, privateKeyPassword)
         onSuccess(keyPair, certificates, tlsCrlIssuerCert?.subjectX500Principal?.toX500Name())
         // All done, clean up temp files.
         requestIdStore.deleteIfExists()
@@ -148,25 +150,29 @@ open class NetworkRegistrationHelper(private val config: SSLConfiguration,
         println("Certificate signing request approved, storing private key with the certificate chain.")
     }
 
-    private fun storePrivateKeyWithCertificates(nodeKeystore: X509KeyStore, keyPair: KeyPair, certificates: List<X509Certificate>, keyAlias: String) {
+    private fun storePrivateKeyWithCertificates(nodeKeystore: CertificateStore, keyPair: KeyPair, certificates: List<X509Certificate>, keyAlias: String, keyPassword: String) {
         // Save private key and certificate chain to the key store.
-        nodeKeystore.setPrivateKey(keyAlias, keyPair.private, certificates, keyPassword = config.keyStorePassword)
-        nodeKeystore.internal.deleteEntry(SELF_SIGNED_PRIVATE_KEY)
-        nodeKeystore.save()
-        println("Private key '$keyAlias' and certificate stored in ${config.nodeKeystore}.")
+        with(nodeKeystore.value) {
+            setPrivateKey(keyAlias, keyPair.private, certificates, keyPassword = keyPassword)
+            internal.deleteEntry(SELF_SIGNED_PRIVATE_KEY)
+            save()
+        }
+        println("Private key '$keyAlias' and certificate stored in node signing keystore.")
     }
 
-    private fun X509KeyStore.loadOrCreateKeyPair(alias: String): KeyPair {
+    private fun CertificateStore.loadOrCreateKeyPair(alias: String, privateKeyPassword: String = password): KeyPair {
         // Create or load self signed keypair from the key store.
         // We use the self sign certificate to store the key temporarily in the keystore while waiting for the request approval.
         if (alias !in this) {
             val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
             val selfSignCert = X509Utilities.createSelfSignedCACertificate(myLegalName.x500Principal, keyPair)
             // Save to the key store.
-            setPrivateKey(alias, keyPair.private, listOf(selfSignCert), keyPassword = privateKeyPassword)
-            save()
+            with(value) {
+                setPrivateKey(alias, keyPair.private, listOf(selfSignCert), keyPassword = privateKeyPassword)
+                save()
+            }
         }
-        return getCertificateAndKeyPair(alias, privateKeyPassword).keyPair
+        return query { getCertificateAndKeyPair(alias, privateKeyPassword) }.keyPair
     }
 
     /**
@@ -243,7 +249,9 @@ open class NetworkRegistrationHelper(private val config: SSLConfiguration,
 class NodeRegistrationException(cause: Throwable?) : IOException("Unable to contact node registration service", cause)
 
 class NodeRegistrationHelper(private val config: NodeConfiguration, certService: NetworkRegistrationService, regConfig: NodeRegistrationOption, computeNextIdleDoormanConnectionPollInterval: (Duration?) -> Duration? = FixedPeriodLimitedRetrialStrategy(10, Duration.ofMinutes(1))) :
-        NetworkRegistrationHelper(config,
+        NetworkRegistrationHelper(
+                config.certificatesDirectory,
+                config.signingCertificateStore,
                 config.myLegalName,
                 config.emailAddress,
                 certService,
@@ -263,7 +271,7 @@ class NodeRegistrationHelper(private val config: NodeConfiguration, certService:
     }
 
     private fun createSSLKeystore(nodeCAKeyPair: KeyPair, certificates: List<X509Certificate>, tlsCertCrlIssuer: X500Name?) {
-        config.loadSslKeyStore(createNew = true).update {
+        config.p2pSslOptions.keyStore.get(createNew = true).update {
             println("Generating SSL certificate for node messaging service.")
             val sslKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
             val sslCert = X509Utilities.createCertificate(
@@ -277,17 +285,17 @@ class NodeRegistrationHelper(private val config: NodeConfiguration, certService:
             logger.info("Generated TLS certificate: $sslCert")
             setPrivateKey(CORDA_CLIENT_TLS, sslKeyPair.private, listOf(sslCert) + certificates)
         }
-        println("SSL private key and certificate stored in ${config.sslKeystore}.")
+        println("SSL private key and certificate stored in ${config.p2pSslOptions.keyStore.path}.")
     }
 
     private fun createTruststore(rootCertificate: X509Certificate) {
         // Save root certificates to trust store.
-        config.loadTrustStore(createNew = true).update {
+        config.p2pSslOptions.trustStore.get(createNew = true).update {
             println("Generating trust store for corda node.")
             // Assumes certificate chain always starts with client certificate and end with root certificate.
             setCertificate(CORDA_ROOT_CA, rootCertificate)
         }
-        println("Node trust store stored in ${config.trustStoreFile}.")
+        println("Node trust store stored in ${config.p2pSslOptions.trustStore.path}.")
     }
 
     override fun validateAndGetTlsCrlIssuerCert(): X509Certificate? {
@@ -296,8 +304,9 @@ class NodeRegistrationHelper(private val config: NodeConfiguration, certService:
         if (principalMatchesCertificatePrincipal(tlsCertCrlIssuer, rootCert)) {
             return rootCert
         }
-        return if (config.trustStoreFile.exists()) {
-            findMatchingCertificate(tlsCertCrlIssuer, config.loadTrustStore())
+        val trustStore = config.p2pSslOptions.trustStore.getOptional()
+        return if (trustStore != null) {
+            findMatchingCertificate(tlsCertCrlIssuer, trustStore.value)
         } else {
             null
         }
diff --git a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt
index eaae962161..09d61e64a6 100644
--- a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt
+++ b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt
@@ -22,6 +22,7 @@ import net.corda.nodeapi.internal.crypto.CertificateType
 import net.corda.nodeapi.internal.crypto.X509KeyStore
 import net.corda.nodeapi.internal.crypto.X509Utilities
 import net.corda.testing.core.ALICE_NAME
+import net.corda.testing.internal.stubs.CertificateStoreStubs
 import net.corda.testing.internal.createDevIntermediateCaCertPath
 import net.corda.testing.internal.rigorousMock
 import org.assertj.core.api.Assertions.*
@@ -52,10 +53,12 @@ class NetworkRegistrationHelperTest {
         val baseDirectory = fs.getPath("/baseDir").createDirectories()
 
         abstract class AbstractNodeConfiguration : NodeConfiguration
+        val certificatesDirectory = baseDirectory / "certificates"
         config = rigorousMock<AbstractNodeConfiguration>().also {
             doReturn(baseDirectory).whenever(it).baseDirectory
-            doReturn("trustpass").whenever(it).trustStorePassword
-            doReturn("cordacadevpass").whenever(it).keyStorePassword
+            doReturn(certificatesDirectory).whenever(it).certificatesDirectory
+            doReturn(CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)).whenever(it).p2pSslOptions
+            doReturn(CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)).whenever(it).signingCertificateStore
             doReturn(nodeLegalName).whenever(it).myLegalName
             doReturn("").whenever(it).emailAddress
             doReturn(null).whenever(it).tlsCertCrlDistPoint
@@ -71,30 +74,30 @@ class NetworkRegistrationHelperTest {
 
     @Test
     fun `successful registration`() {
-        assertThat(config.nodeKeystore).doesNotExist()
-        assertThat(config.sslKeystore).doesNotExist()
-        assertThat(config.trustStoreFile).doesNotExist()
+        assertThat(config.signingCertificateStore.getOptional()).isNull()
+        assertThat(config.p2pSslOptions.keyStore.getOptional()).isNull()
+        assertThat(config.p2pSslOptions.trustStore.getOptional()).isNull()
 
         val rootAndIntermediateCA = createDevIntermediateCaCertPath().also { saveNetworkTrustStore(it.first.certificate) }
 
         createRegistrationHelper(rootAndIntermediateCA = rootAndIntermediateCA).buildKeystore()
 
-        val nodeKeystore = config.loadNodeKeyStore()
-        val sslKeystore = config.loadSslKeyStore()
-        val trustStore = config.loadTrustStore()
+        val nodeKeystore = config.signingCertificateStore.get()
+        val sslKeystore = config.p2pSslOptions.keyStore.get()
+        val trustStore = config.p2pSslOptions.trustStore.get()
 
         nodeKeystore.run {
             assertFalse(contains(X509Utilities.CORDA_INTERMEDIATE_CA))
             assertFalse(contains(X509Utilities.CORDA_ROOT_CA))
             assertFalse(contains(X509Utilities.CORDA_CLIENT_TLS))
-            assertThat(CertRole.extract(getCertificate(X509Utilities.CORDA_CLIENT_CA))).isEqualTo(CertRole.NODE_CA)
+            assertThat(CertRole.extract(this[X509Utilities.CORDA_CLIENT_CA])).isEqualTo(CertRole.NODE_CA)
         }
 
         sslKeystore.run {
             assertFalse(contains(X509Utilities.CORDA_CLIENT_CA))
             assertFalse(contains(X509Utilities.CORDA_INTERMEDIATE_CA))
             assertFalse(contains(X509Utilities.CORDA_ROOT_CA))
-            val nodeTlsCertChain = getCertificateChain(X509Utilities.CORDA_CLIENT_TLS)
+            val nodeTlsCertChain = query { getCertificateChain(X509Utilities.CORDA_CLIENT_TLS) }
             assertThat(nodeTlsCertChain).hasSize(4)
             // The TLS cert has the same subject as the node CA cert
             assertThat(CordaX500Name.build(nodeTlsCertChain[0].subjectX500Principal)).isEqualTo(nodeLegalName)
@@ -104,7 +107,7 @@ class NetworkRegistrationHelperTest {
         trustStore.run {
             assertFalse(contains(X509Utilities.CORDA_CLIENT_CA))
             assertFalse(contains(X509Utilities.CORDA_INTERMEDIATE_CA))
-            assertThat(getCertificate(X509Utilities.CORDA_ROOT_CA)).isEqualTo(rootAndIntermediateCA.first.certificate)
+            assertThat(this[X509Utilities.CORDA_ROOT_CA]).isEqualTo(rootAndIntermediateCA.first.certificate)
         }
     }
 
@@ -152,18 +155,18 @@ class NetworkRegistrationHelperTest {
 
     @Test
     fun `create service identity cert`() {
-        assertThat(config.nodeKeystore).doesNotExist()
-        assertThat(config.sslKeystore).doesNotExist()
-        assertThat(config.trustStoreFile).doesNotExist()
+        assertThat(config.signingCertificateStore.getOptional()).isNull()
+        assertThat(config.p2pSslOptions.keyStore.getOptional()).isNull()
+        assertThat(config.p2pSslOptions.trustStore.getOptional()).isNull()
 
         val rootAndIntermediateCA = createDevIntermediateCaCertPath().also { saveNetworkTrustStore(it.first.certificate) }
 
         createRegistrationHelper(CertRole.SERVICE_IDENTITY, rootAndIntermediateCA).buildKeystore()
 
-        val nodeKeystore = config.loadNodeKeyStore()
+        val nodeKeystore = config.signingCertificateStore.get()
 
-        assertThat(config.sslKeystore).doesNotExist()
-        assertThat(config.trustStoreFile).doesNotExist()
+        assertThat(config.p2pSslOptions.keyStore.getOptional()).isNull()
+        assertThat(config.p2pSslOptions.trustStore.getOptional()).isNull()
 
         val serviceIdentityAlias = "${DevIdentityGenerator.DISTRIBUTED_NOTARY_ALIAS_PREFIX}-private-key"
 
@@ -172,7 +175,7 @@ class NetworkRegistrationHelperTest {
             assertFalse(contains(X509Utilities.CORDA_ROOT_CA))
             assertFalse(contains(X509Utilities.CORDA_CLIENT_TLS))
             assertFalse(contains(X509Utilities.CORDA_CLIENT_CA))
-            assertThat(CertRole.extract(getCertificate(serviceIdentityAlias))).isEqualTo(CertRole.SERVICE_IDENTITY)
+            assertThat(CertRole.extract(this[serviceIdentityAlias])).isEqualTo(CertRole.SERVICE_IDENTITY)
         }
     }
 
@@ -223,7 +226,8 @@ class NetworkRegistrationHelperTest {
         return when (certRole) {
             CertRole.NODE_CA -> NodeRegistrationHelper(config, certService, NodeRegistrationOption(config.certificatesDirectory / networkRootTrustStoreFileName, networkRootTrustStorePassword))
             CertRole.SERVICE_IDENTITY -> NetworkRegistrationHelper(
-                    config,
+                    config.certificatesDirectory,
+                    config.signingCertificateStore,
                     config.myLegalName,
                     config.emailAddress,
                     certService,
diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt
index 504dac7262..69fab8f129 100644
--- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt
+++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt
@@ -5,7 +5,7 @@ import com.typesafe.config.Config
 import com.typesafe.config.ConfigFactory
 import com.typesafe.config.ConfigRenderOptions
 import com.typesafe.config.ConfigValueFactory
-import net.corda.client.rpc.internal.createCordaRPCClientWithInternalSslAndClassLoader
+import net.corda.client.rpc.internal.createCordaRPCClientWithSslAndClassLoader
 import net.corda.core.concurrent.CordaFuture
 import net.corda.core.concurrent.firstOf
 import net.corda.core.identity.CordaX500Name
@@ -20,6 +20,7 @@ import net.corda.node.NodeRegistrationOption
 import net.corda.node.VersionInfo
 import net.corda.node.internal.Node
 import net.corda.node.internal.NodeWithInfo
+import net.corda.node.internal.clientSslOptionsCompatibleWith
 import net.corda.node.services.Permissions
 import net.corda.node.services.config.*
 import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
@@ -27,9 +28,6 @@ import net.corda.node.utilities.registration.NodeRegistrationHelper
 import net.corda.nodeapi.internal.DevIdentityGenerator
 import net.corda.nodeapi.internal.SignedNodeInfo
 import net.corda.nodeapi.internal.addShutdownHook
-
-import net.corda.nodeapi.internal.config.NodeSSLConfiguration
-import net.corda.nodeapi.internal.config.parseAs
 import net.corda.nodeapi.internal.config.toConfig
 import net.corda.nodeapi.internal.crypto.X509KeyStore
 import net.corda.nodeapi.internal.crypto.X509Utilities
@@ -45,6 +43,7 @@ import net.corda.testing.driver.internal.InProcessImpl
 import net.corda.testing.driver.internal.NodeHandleInternal
 import net.corda.testing.driver.internal.OutOfProcessImpl
 import net.corda.testing.internal.setGlobalSerialization
+import net.corda.testing.internal.stubs.CertificateStoreStubs
 import net.corda.testing.node.ClusterSpec
 import net.corda.testing.node.NotarySpec
 import net.corda.testing.node.User
@@ -172,7 +171,8 @@ class DriverDSLImpl(
 
     private fun establishRpc(config: NodeConfig, processDeathFuture: CordaFuture<out Process>): CordaFuture<CordaRPCOps> {
         val rpcAddress = config.corda.rpcOptions.address
-        val client = createCordaRPCClientWithInternalSslAndClassLoader(config.corda.rpcOptions.adminAddress, sslConfiguration = config.corda)
+        val clientRpcSslOptions =  clientSslOptionsCompatibleWith(config.corda.rpcOptions)
+        val client = createCordaRPCClientWithSslAndClassLoader(rpcAddress, sslConfiguration = clientRpcSslOptions)
         val connectionFuture = poll(executorService, "RPC connection") {
             try {
                 config.corda.rpcUsers[0].run { client.start(username, password) }
@@ -842,8 +842,13 @@ class DriverDSLImpl(
             config += "rpcUsers" to configuration.toConfig().getValue("rpcUsers")
             config += "useHTTPS" to useHTTPS
             config += "baseDirectory" to configuration.baseDirectory.toAbsolutePath().toString()
-            config += "keyStorePassword" to configuration.keyStorePassword
-            config += "trustStorePassword" to configuration.trustStorePassword
+
+            config += "keyStorePath" to configuration.p2pSslOptions.keyStore.path.toString()
+            config += "keyStorePassword" to configuration.p2pSslOptions.keyStore.password
+
+            config += "trustStorePath" to configuration.p2pSslOptions.trustStore.path.toString()
+            config += "trustStorePassword" to configuration.p2pSslOptions.trustStore.password
+
             return config
         }
 
@@ -1165,20 +1170,18 @@ private fun Config.toNodeOnly(): Config {
     return if (hasPath("webAddress")) withoutPath("webAddress").withoutPath("useHTTPS") else this
 }
 
-internal fun DriverParameters.cordappsForAllNodes(): Set<TestCorDapp> = cordappsForAllNodes ?: cordappsInCurrentAndAdditionalPackages(extraCordappPackagesToScan)
+internal fun DriverParameters.cordappsForAllNodes(): Set<TestCorDapp> = cordappsForAllNodes
+        ?: cordappsInCurrentAndAdditionalPackages(extraCordappPackagesToScan)
 
 fun DriverDSL.startNode(providedName: CordaX500Name, devMode: Boolean, parameters: NodeParameters = NodeParameters()): CordaFuture<NodeHandle> {
     var customOverrides = emptyMap<String, String>()
     if (!devMode) {
         val nodeDir = baseDirectory(providedName)
-        val nodeSslConfig = object : NodeSSLConfiguration {
-            override val baseDirectory = nodeDir
-            override val keyStorePassword = "cordacadevpass"
-            override val trustStorePassword = "trustpass"
-            override val crlCheckSoftFail = true
-        }
-        nodeSslConfig.configureDevKeyAndTrustStores(providedName)
+        val certificatesDirectory = nodeDir / "certificates"
+        val signingCertStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)
+        val p2pSslConfig = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)
+        p2pSslConfig.configureDevKeyAndTrustStores(providedName, signingCertStore, certificatesDirectory)
         customOverrides = mapOf("devMode" to "false")
     }
     return startNode(parameters, providedName = providedName, customOverrides = customOverrides)
-}
+}
\ No newline at end of file
diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt
index 7214fd450c..a514351492 100644
--- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt
+++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt
@@ -54,6 +54,7 @@ import net.corda.nodeapi.internal.persistence.CordaPersistence
 import net.corda.nodeapi.internal.persistence.DatabaseConfig
 import net.corda.testing.common.internal.testNetworkParameters
 import net.corda.testing.driver.TestCorDapp
+import net.corda.testing.internal.stubs.CertificateStoreStubs
 import net.corda.testing.internal.rigorousMock
 import net.corda.testing.internal.setGlobalSerialization
 import net.corda.testing.internal.testThreadFactory
@@ -456,8 +457,11 @@ open class InternalMockNetwork(defaultParameters: MockNetworkParameters = MockNe
 
     private fun createNodeImpl(parameters: InternalMockNodeParameters, nodeFactory: (MockNodeArgs, CordappLoader?) -> MockNode, start: Boolean): MockNode {
         val id = parameters.forcedID ?: nextNodeId++
-        val config = mockNodeConfiguration().also {
-            doReturn(baseDirectory(id).createDirectories()).whenever(it).baseDirectory
+        val baseDirectory = baseDirectory(id)
+        val certificatesDirectory = baseDirectory / "certificates"
+        certificatesDirectory.createDirectories()
+        val config = mockNodeConfiguration(certificatesDirectory).also {
+            doReturn(baseDirectory).whenever(it).baseDirectory
             doReturn(parameters.legalName ?: CordaX500Name("Mock Company $id", "London", "GB")).whenever(it).myLegalName
             doReturn(makeInternalTestDataSourceProperties("node_$id", "net_$networkId")).whenever(it).dataSourceProperties
             doReturn(makeTestDatabaseProperties("node_$id")).whenever(it).database
@@ -562,12 +566,17 @@ abstract class MessagingServiceSpy {
     abstract fun send(message: Message, target: MessageRecipients, sequenceKey: Any)
 }
 
-private fun mockNodeConfiguration(): NodeConfiguration {
+private fun mockNodeConfiguration(certificatesDirectory: Path): NodeConfiguration {
     @DoNotImplement
     abstract class AbstractNodeConfiguration : NodeConfiguration
+
+    val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)
+    val p2pSslConfiguration = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)
+
     return rigorousMock<AbstractNodeConfiguration>().also {
-        doReturn("cordacadevpass").whenever(it).keyStorePassword
-        doReturn("trustpass").whenever(it).trustStorePassword
+        doReturn(certificatesDirectory.createDirectories()).whenever(it).certificatesDirectory
+        doReturn(p2pSslConfiguration).whenever(it).p2pSslOptions
+        doReturn(signingCertificateStore).whenever(it).signingCertificateStore
         doReturn(emptyList<User>()).whenever(it).rpcUsers
         doReturn(null).whenever(it).notary
         doReturn(DatabaseConfig()).whenever(it).database
diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt
index 8409d98d59..9a315bb2f1 100644
--- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt
+++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt
@@ -10,15 +10,15 @@ import net.corda.core.node.NodeInfo
 import net.corda.core.utilities.getOrThrow
 import net.corda.core.utilities.loggerFor
 import net.corda.node.VersionInfo
-import net.corda.node.internal.EnterpriseNode
 import net.corda.node.internal.NodeWithInfo
+import net.corda.node.internal.EnterpriseNode
 import net.corda.node.services.config.*
 import net.corda.nodeapi.internal.config.toConfig
 import net.corda.nodeapi.internal.network.NetworkParametersCopier
 import net.corda.testing.common.internal.testNetworkParameters
 import net.corda.testing.core.SerializationEnvironmentRule
-import net.corda.testing.driver.PortAllocation
 import net.corda.testing.internal.IntegrationTest
+import net.corda.testing.driver.PortAllocation
 import net.corda.testing.internal.testThreadFactory
 import net.corda.testing.node.User
 import org.apache.logging.log4j.Level
@@ -86,9 +86,9 @@ abstract class NodeBasedTest(private val cordappPackages: List<String> = emptyLi
 
     @JvmOverloads
     fun initNode(legalName: CordaX500Name,
-                  platformVersion: Int = 4,
-                  rpcUsers: List<User> = emptyList(),
-                  configOverrides: Map<String, Any> = emptyMap()): InProcessNode {
+                 platformVersion: Int = 4,
+                 rpcUsers: List<User> = emptyList(),
+                 configOverrides: Map<String, Any> = emptyMap()): InProcessNode {
         val baseDirectory = baseDirectory(legalName).createDirectories()
         val p2pAddress = configOverrides["p2pAddress"] ?: portAllocation.nextHostAndPort().toString()
         val config = ConfigHelper.loadConfig(
diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/RPCDriver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/RPCDriver.kt
index ceda937104..89d01a0a31 100644
--- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/RPCDriver.kt
+++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/RPCDriver.kt
@@ -21,8 +21,8 @@ import net.corda.core.utilities.seconds
 import net.corda.node.internal.security.RPCSecurityManagerImpl
 import net.corda.node.services.messaging.RPCServer
 import net.corda.node.services.messaging.RPCServerConfiguration
-import net.corda.nodeapi.ArtemisTcpTransport
 import net.corda.nodeapi.RPCApi
+import net.corda.nodeapi.internal.InternalArtemisTcpTransport
 import net.corda.serialization.internal.AMQP_RPC_CLIENT_CONTEXT
 import net.corda.testing.common.internal.testNetworkParameters
 import net.corda.testing.core.MAX_MESSAGE_SIZE
@@ -220,14 +220,14 @@ data class RPCDriverDSL(
                 bindingsDirectory = "$artemisDir/bindings"
                 journalDirectory = "$artemisDir/journal"
                 largeMessagesDirectory = "$artemisDir/large-messages"
-                acceptorConfigurations = setOf(ArtemisTcpTransport.rpcAcceptorTcpTransport(hostAndPort, null))
+                acceptorConfigurations = setOf(InternalArtemisTcpTransport.rpcAcceptorTcpTransport(hostAndPort, null))
                 configureCommonSettings(maxFileSize, maxBufferedBytesPerClient)
             }
         }
 
         val inVmClientTransportConfiguration = TransportConfiguration(InVMConnectorFactory::class.java.name)
         fun createNettyClientTransportConfiguration(hostAndPort: NetworkHostAndPort): TransportConfiguration {
-            return ArtemisTcpTransport.rpcConnectorTcpTransport(hostAndPort, null)
+            return InternalArtemisTcpTransport.rpcConnectorTcpTransport(hostAndPort, null)
         }
     }
 
@@ -339,7 +339,7 @@ data class RPCDriverDSL(
             configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT
     ): CordaFuture<I> {
         return driverDSL.executorService.fork {
-            val client = RPCClient<I>(ArtemisTcpTransport.rpcConnectorTcpTransport(rpcAddress, null), configuration)
+            val client = RPCClient<I>(InternalArtemisTcpTransport.rpcConnectorTcpTransport(rpcAddress, null), configuration)
             val connection = client.start(rpcOpsClass, username, password, externalTrace)
             driverDSL.shutdownManager.registerShutdown {
                 connection.close()
diff --git a/testing/qa/behave/tools/rpc-proxy/src/main/kotlin/net/corda/behave/service/proxy/CordaRPCProxyClient.kt b/testing/qa/behave/tools/rpc-proxy/src/main/kotlin/net/corda/behave/service/proxy/CordaRPCProxyClient.kt
index 008d035f2d..eae71e575e 100644
--- a/testing/qa/behave/tools/rpc-proxy/src/main/kotlin/net/corda/behave/service/proxy/CordaRPCProxyClient.kt
+++ b/testing/qa/behave/tools/rpc-proxy/src/main/kotlin/net/corda/behave/service/proxy/CordaRPCProxyClient.kt
@@ -29,15 +29,11 @@ import java.security.PublicKey
 import java.time.Instant
 import javax.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM
 
-// TODO: Make a shared implementation of CordaRPCOps where every method is unimplemented?
-
 class CordaRPCProxyClient(private val targetHostAndPort: NetworkHostAndPort) : CordaRPCOps {
     companion object {
         val log = contextLogger()
     }
 
-    override val protocolVersion: Int = 1000
-
     init {
         try {
             AMQPClientSerializationScheme.initialiseSerialization()
@@ -79,51 +75,51 @@ class CordaRPCProxyClient(private val targetHostAndPort: NetworkHostAndPort) : C
     }
 
     override fun stateMachinesSnapshot(): List<StateMachineInfo> {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun stateMachinesFeed(): DataFeed<List<StateMachineInfo>, StateMachineUpdate> {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun <T : ContractState> vaultQueryBy(criteria: QueryCriteria, paging: PageSpecification, sorting: Sort, contractStateType: Class<out T>): Vault.Page<T> {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun <T : ContractState> vaultQueryByCriteria(criteria: QueryCriteria, contractStateType: Class<out T>): Vault.Page<T> {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun <T : ContractState> vaultQueryByWithPagingSpec(contractStateType: Class<out T>, criteria: QueryCriteria, paging: PageSpecification): Vault.Page<T> {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun <T : ContractState> vaultQueryByWithSorting(contractStateType: Class<out T>, criteria: QueryCriteria, sorting: Sort): Vault.Page<T> {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun <T : ContractState> vaultTrackBy(criteria: QueryCriteria, paging: PageSpecification, sorting: Sort, contractStateType: Class<out T>): DataFeed<Vault.Page<T>, Vault.Update<T>> {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun <T : ContractState> vaultTrack(contractStateType: Class<out T>): DataFeed<Vault.Page<T>, Vault.Update<T>> {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun <T : ContractState> vaultTrackByCriteria(contractStateType: Class<out T>, criteria: QueryCriteria): DataFeed<Vault.Page<T>, Vault.Update<T>> {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun <T : ContractState> vaultTrackByWithPagingSpec(contractStateType: Class<out T>, criteria: QueryCriteria, paging: PageSpecification): DataFeed<Vault.Page<T>, Vault.Update<T>> {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun <T : ContractState> vaultTrackByWithSorting(contractStateType: Class<out T>, criteria: QueryCriteria, sorting: Sort): DataFeed<Vault.Page<T>, Vault.Update<T>> {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun internalVerifiedTransactionsSnapshot(): List<SignedTransaction> {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun internalFindVerifiedTransaction(txnId: SecureHash): SignedTransaction? {
@@ -131,107 +127,107 @@ class CordaRPCProxyClient(private val targetHostAndPort: NetworkHostAndPort) : C
     }
 
     override fun internalVerifiedTransactionsFeed(): DataFeed<List<SignedTransaction>, SignedTransaction> {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun stateMachineRecordedTransactionMappingSnapshot(): List<StateMachineTransactionMapping> {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun stateMachineRecordedTransactionMappingFeed(): DataFeed<List<StateMachineTransactionMapping>, StateMachineTransactionMapping> {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun networkMapFeed(): DataFeed<List<NodeInfo>, NetworkMapCache.MapChange> {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun networkParametersFeed(): DataFeed<ParametersUpdateInfo?, ParametersUpdateInfo> {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun acceptNewNetworkParameters(parametersHash: SecureHash) {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun <T> startTrackedFlowDynamic(logicType: Class<out FlowLogic<T>>, vararg args: Any?): FlowProgressHandle<T> {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun addVaultTransactionNote(txnId: SecureHash, txnNote: String) {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun getVaultTransactionNotes(txnId: SecureHash): Iterable<String> {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun attachmentExists(id: SecureHash): Boolean {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun openAttachment(id: SecureHash): InputStream {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun uploadAttachment(jar: InputStream): SecureHash {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun uploadAttachmentWithMetadata(jar: InputStream, uploader: String, filename: String): SecureHash {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun queryAttachments(query: AttachmentQueryCriteria, sorting: AttachmentSort?): List<AttachmentId> {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun currentNodeTime(): Instant {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun waitUntilNetworkReady(): CordaFuture<Void?> {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun wellKnownPartyFromAnonymous(party: AbstractParty): Party? {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun partyFromKey(key: PublicKey): Party? {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun wellKnownPartyFromX500Name(x500Name: CordaX500Name): Party? {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun notaryPartyFromX500Name(x500Name: CordaX500Name): Party? {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun nodeInfoFromParty(party: AbstractParty): NodeInfo? {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun clearNetworkMapCache() {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun setFlowsDrainingModeEnabled(enabled: Boolean) {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun isFlowsDrainingModeEnabled(): Boolean {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun shutdown() {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun killFlow(id: StateMachineRunId): Boolean {
-        TODO("not implemented") 
+        TODO("not implemented")
     }
 
     override fun refreshNetworkMapCache() {
diff --git a/testing/test-common/src/main/kotlin/net/corda/testing/common/internal/UnsafeCertificatesFactory.kt b/testing/test-common/src/main/kotlin/net/corda/testing/common/internal/UnsafeCertificatesFactory.kt
index 4ce15b78af..bce2152d57 100644
--- a/testing/test-common/src/main/kotlin/net/corda/testing/common/internal/UnsafeCertificatesFactory.kt
+++ b/testing/test-common/src/main/kotlin/net/corda/testing/common/internal/UnsafeCertificatesFactory.kt
@@ -4,7 +4,9 @@ import net.corda.core.identity.CordaX500Name
 import net.corda.core.internal.createFile
 import net.corda.core.internal.deleteIfExists
 import net.corda.core.internal.div
-import net.corda.nodeapi.internal.config.SSLConfiguration
+import net.corda.nodeapi.internal.config.FileBasedCertificateStoreSupplier
+import net.corda.nodeapi.internal.config.SslConfiguration
+import net.corda.nodeapi.internal.config.MutualSslConfiguration
 import net.corda.nodeapi.internal.crypto.*
 import org.apache.commons.io.FileUtils
 import sun.security.tools.keytool.CertAndKeyGen
@@ -65,7 +67,7 @@ class KeyStores(val keyStore: UnsafeKeyStore, val trustStore: UnsafeKeyStore) {
         val keyStoreFile = keyStore.toTemporaryFile("sslkeystore", directory = directory)
         val trustStoreFile = trustStore.toTemporaryFile("truststore", directory = directory)
 
-        val sslConfiguration = sslConfiguration(directory)
+        val sslConfiguration = sslConfiguration(keyStoreFile, trustStoreFile)
 
         return object : AutoClosableSSLConfiguration {
             override val value = sslConfiguration
@@ -77,16 +79,16 @@ class KeyStores(val keyStore: UnsafeKeyStore, val trustStore: UnsafeKeyStore) {
         }
     }
 
-    data class TestSslOptions(override val certificatesDirectory: Path,
-                              override val keyStorePassword: String,
-                              override val trustStorePassword: String,
-                              override val crlCheckSoftFail: Boolean) : SSLConfiguration
+    private fun sslConfiguration(keyStoreFile: TemporaryFile, trustStoreFile: TemporaryFile): MutualSslConfiguration {
 
-    private fun sslConfiguration(directory: Path) = TestSslOptions(directory, keyStore.password, trustStore.password, true)
+        val keyStore = FileBasedCertificateStoreSupplier(keyStoreFile.file, keyStore.password)
+        val trustStore = FileBasedCertificateStoreSupplier(trustStoreFile.file, trustStore.password)
+        return SslConfiguration.mutual(keyStore, trustStore)
+    }
 }
 
 interface AutoClosableSSLConfiguration : AutoCloseable {
-    val value: SSLConfiguration
+    val value: MutualSslConfiguration
 }
 
 typealias KeyStoreEntry = Pair<String, UnsafeCertificate>
@@ -189,7 +191,7 @@ private fun newKeyStore(type: String, password: String): KeyStore {
     return keyStore
 }
 
-fun withKeyStores(server: KeyStores, client: KeyStores, action: (brokerSslOptions: SSLConfiguration, clientSslOptions: SSLConfiguration) -> Unit) {
+fun withKeyStores(server: KeyStores, client: KeyStores, action: (brokerSslOptions: MutualSslConfiguration, clientSslOptions: MutualSslConfiguration) -> Unit) {
     val serverDir = Files.createTempDirectory(null)
     FileUtils.forceDeleteOnExit(serverDir.toFile())
 
diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt
index 5b4555e547..4ddca172af 100644
--- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt
+++ b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt
@@ -9,15 +9,15 @@ import net.corda.core.identity.Party
 import net.corda.core.identity.PartyAndCertificate
 import net.corda.core.node.NodeInfo
 import net.corda.core.transactions.WireTransaction
-import net.corda.core.utilities.NetworkHostAndPort
 import net.corda.core.utilities.loggerFor
-import net.corda.node.services.config.configureDevKeyAndTrustStores
 import net.corda.nodeapi.BrokerRpcSslOptions
-import net.corda.nodeapi.internal.config.SSLConfiguration
-import net.corda.nodeapi.internal.createDevKeyStores
+import net.corda.nodeapi.internal.config.MutualSslConfiguration
+import net.corda.nodeapi.internal.registerDevP2pCertificates
 import net.corda.nodeapi.internal.createDevNodeCa
 import net.corda.nodeapi.internal.crypto.*
+import net.corda.nodeapi.internal.loadDevCaTrustStore
 import net.corda.serialization.internal.amqp.AMQP_ENABLED
+import net.corda.testing.internal.stubs.CertificateStoreStubs
 import java.nio.file.Files
 import java.nio.file.Path
 import java.security.KeyPair
@@ -37,17 +37,17 @@ inline fun <reified T : Any> T.amqpSpecific(reason: String, function: () -> Unit
     loggerFor<T>().info("Ignoring AMQP specific test, reason: $reason")
 }
 
-fun configureTestSSL(legalName: CordaX500Name): SSLConfiguration {
-    return object : SSLConfiguration {
-        override val certificatesDirectory = Files.createTempDirectory("certs")
-        override val keyStorePassword: String get() = "cordacadevpass"
-        override val trustStorePassword: String get() = "trustpass"
-        override val crlCheckSoftFail: Boolean = true
+fun configureTestSSL(legalName: CordaX500Name): MutualSslConfiguration {
 
-        init {
-            configureDevKeyAndTrustStores(legalName)
-        }
+    val certificatesDirectory = Files.createTempDirectory("certs")
+    val config = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory)
+    if (config.trustStore.getOptional() == null) {
+        loadDevCaTrustStore().copyTo(config.trustStore.get(true))
     }
+    if (config.keyStore.getOptional() == null) {
+        config.keyStore.get(true).registerDevP2pCertificates(legalName)
+    }
+    return config
 }
 
 private val defaultRootCaName = X500Principal("CN=Corda Root CA,O=R3 Ltd,L=London,C=GB")
@@ -103,17 +103,6 @@ fun BrokerRpcSslOptions.useSslRpcOverrides(): Map<String, String> {
     )
 }
 
-fun SSLConfiguration.noSslRpcOverrides(rpcAdminAddress: NetworkHostAndPort): Map<String, Any> {
-    return mapOf(
-            "rpcSettings.adminAddress" to rpcAdminAddress.toString(),
-            "rpcSettings.useSsl" to "false",
-            "rpcSettings.ssl.certificatesDirectory" to certificatesDirectory.toString(),
-            "rpcSettings.ssl.keyStorePassword" to keyStorePassword,
-            "rpcSettings.ssl.trustStorePassword" to trustStorePassword,
-            "rpcSettings.ssl.crlCheckSoftFail" to true
-    )
-}
-
 /**
  * Until we have proper handling of multiple identities per node, for tests we use the first identity as special one.
  * TODO: Should be removed after multiple identities are introduced.
@@ -127,19 +116,12 @@ fun NodeInfo.chooseIdentityAndCert(): PartyAndCertificate = legalIdentitiesAndCe
  */
 fun NodeInfo.chooseIdentity(): Party = chooseIdentityAndCert().party
 
-fun createNodeSslConfig(path: Path, name: CordaX500Name = CordaX500Name("MegaCorp", "London", "GB")): SSLConfiguration {
-    val sslConfig = object : SSLConfiguration {
-        override val crlCheckSoftFail = true
-        override val certificatesDirectory = path
-        override val keyStorePassword = "serverstorepass"
-        override val trustStorePassword = "trustpass"
-    }
+fun p2pSslOptions(path: Path, name: CordaX500Name = CordaX500Name("MegaCorp", "London", "GB")): MutualSslConfiguration {
+    val sslConfig = CertificateStoreStubs.P2P.withCertificatesDirectory(path, keyStorePassword = "serverstorepass")
     val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
-    sslConfig.createDevKeyStores(name, rootCa.certificate, intermediateCa)
-    val trustStore = loadOrCreateKeyStore(sslConfig.trustStoreFile, sslConfig.trustStorePassword)
-    trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCa.certificate)
-    trustStore.save(sslConfig.trustStoreFile, sslConfig.trustStorePassword)
-
+    sslConfig.keyStore.get(true).registerDevP2pCertificates(name, rootCa.certificate, intermediateCa)
+    val trustStore = sslConfig.trustStore.get(true)
+    trustStore[X509Utilities.CORDA_ROOT_CA] = rootCa.certificate
     return sslConfig
 }
 
diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/stubs/CertificateStoreStubs.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/stubs/CertificateStoreStubs.kt
new file mode 100644
index 0000000000..1eba7b9aa5
--- /dev/null
+++ b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/stubs/CertificateStoreStubs.kt
@@ -0,0 +1,101 @@
+package net.corda.testing.internal.stubs
+
+import net.corda.core.internal.div
+import net.corda.nodeapi.internal.config.FileBasedCertificateStoreSupplier
+import net.corda.nodeapi.internal.config.SslConfiguration
+import net.corda.nodeapi.internal.config.MutualSslConfiguration
+import java.nio.file.Path
+
+class CertificateStoreStubs {
+
+    companion object {
+
+        const val DEFAULT_CERTIFICATES_DIRECTORY_NAME = "certificates"
+
+        @JvmStatic
+        fun withStoreAt(certificateStorePath: Path, password: String): FileBasedCertificateStoreSupplier = FileBasedCertificateStoreSupplier(certificateStorePath, password)
+    }
+
+    class Signing {
+
+        companion object {
+
+            const val DEFAULT_STORE_FILE_NAME = "nodekeystore.jks"
+            const val DEFAULT_STORE_PASSWORD = "cordacadevpass"
+
+            @JvmStatic
+            fun withCertificatesDirectory(certificatesDirectory: Path, password: String = DEFAULT_STORE_PASSWORD, certificateStoreFileName: String = DEFAULT_STORE_FILE_NAME): FileBasedCertificateStoreSupplier {
+
+                return FileBasedCertificateStoreSupplier(certificatesDirectory / certificateStoreFileName, password)
+            }
+
+            @JvmStatic
+            fun withBaseDirectory(baseDirectory: Path, password: String = DEFAULT_STORE_PASSWORD, certificatesDirectoryName: String = DEFAULT_CERTIFICATES_DIRECTORY_NAME, certificateStoreFileName: String = DEFAULT_STORE_FILE_NAME): FileBasedCertificateStoreSupplier {
+
+                return FileBasedCertificateStoreSupplier(baseDirectory / certificatesDirectoryName / certificateStoreFileName, password)
+            }
+        }
+    }
+
+    class P2P {
+
+        companion object {
+
+            @JvmStatic
+            fun withCertificatesDirectory(certificatesDirectory: Path, keyStoreFileName: String = KeyStore.DEFAULT_STORE_FILE_NAME, keyStorePassword: String = KeyStore.DEFAULT_STORE_PASSWORD, trustStoreFileName: String = TrustStore.DEFAULT_STORE_FILE_NAME, trustStorePassword: String = TrustStore.DEFAULT_STORE_PASSWORD): MutualSslConfiguration {
+
+                val keyStore = FileBasedCertificateStoreSupplier(certificatesDirectory / keyStoreFileName, keyStorePassword)
+                val trustStore = FileBasedCertificateStoreSupplier(certificatesDirectory / trustStoreFileName, trustStorePassword)
+                return SslConfiguration.mutual(keyStore, trustStore)
+            }
+
+            @JvmStatic
+            fun withBaseDirectory(baseDirectory: Path, certificatesDirectoryName: String = DEFAULT_CERTIFICATES_DIRECTORY_NAME, keyStoreFileName: String = KeyStore.DEFAULT_STORE_FILE_NAME, keyStorePassword: String = KeyStore.DEFAULT_STORE_PASSWORD, trustStoreFileName: String = TrustStore.DEFAULT_STORE_FILE_NAME, trustStorePassword: String = TrustStore.DEFAULT_STORE_PASSWORD): MutualSslConfiguration {
+
+                return withCertificatesDirectory(baseDirectory / certificatesDirectoryName, keyStorePassword, trustStorePassword, keyStoreFileName, trustStoreFileName)
+            }
+        }
+
+        class KeyStore {
+
+            companion object {
+
+                const val DEFAULT_STORE_FILE_NAME = "sslkeystore.jks"
+                const val DEFAULT_STORE_PASSWORD = "cordacadevpass"
+
+                @JvmStatic
+                fun withCertificatesDirectory(certificatesDirectory: Path, password: String = DEFAULT_STORE_PASSWORD, certificateStoreFileName: String = DEFAULT_STORE_FILE_NAME): FileBasedCertificateStoreSupplier {
+
+                    return FileBasedCertificateStoreSupplier(certificatesDirectory / certificateStoreFileName, password)
+                }
+
+                @JvmStatic
+                fun withBaseDirectory(baseDirectory: Path, password: String = DEFAULT_STORE_PASSWORD, certificatesDirectoryName: String = DEFAULT_CERTIFICATES_DIRECTORY_NAME, certificateStoreFileName: String = DEFAULT_STORE_FILE_NAME): FileBasedCertificateStoreSupplier {
+
+                    return FileBasedCertificateStoreSupplier(baseDirectory / certificatesDirectoryName / certificateStoreFileName, password)
+                }
+            }
+        }
+
+        class TrustStore {
+
+            companion object {
+
+                const val DEFAULT_STORE_FILE_NAME = "truststore.jks"
+                const val DEFAULT_STORE_PASSWORD = "trustpass"
+
+                @JvmStatic
+                fun withCertificatesDirectory(certificatesDirectory: Path, password: String = DEFAULT_STORE_PASSWORD, certificateStoreFileName: String = DEFAULT_STORE_FILE_NAME): FileBasedCertificateStoreSupplier {
+
+                    return FileBasedCertificateStoreSupplier(certificatesDirectory / certificateStoreFileName, password)
+                }
+
+                @JvmStatic
+                fun withBaseDirectory(baseDirectory: Path, password: String = DEFAULT_STORE_PASSWORD, certificatesDirectoryName: String = DEFAULT_CERTIFICATES_DIRECTORY_NAME, certificateStoreFileName: String = DEFAULT_STORE_FILE_NAME): FileBasedCertificateStoreSupplier {
+
+                    return FileBasedCertificateStoreSupplier(baseDirectory / certificatesDirectoryName / certificateStoreFileName, password)
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tools/shell/src/integration-test/kotlin/net/corda/tools/shell/InteractiveShellIntegrationTest.kt b/tools/shell/src/integration-test/kotlin/net/corda/tools/shell/InteractiveShellIntegrationTest.kt
index cf8882a6b5..dbae24f6ff 100644
--- a/tools/shell/src/integration-test/kotlin/net/corda/tools/shell/InteractiveShellIntegrationTest.kt
+++ b/tools/shell/src/integration-test/kotlin/net/corda/tools/shell/InteractiveShellIntegrationTest.kt
@@ -145,7 +145,7 @@ class InteractiveShellIntegrationTest : IntegrationTest() {
         driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList())) {
             startNode().getOrThrow().use { node ->
                 val conf = (node as NodeHandleInternal).configuration.toShellConfig()
-                InteractiveShell.startShellInternal(conf)
+                InteractiveShell.startShell(conf)
                 assertThatThrownBy { InteractiveShell.nodeInfo() }.isInstanceOf(ActiveMQSecurityException::class.java)
             }
         }
diff --git a/tools/shell/src/main/kotlin/net/corda/tools/shell/InteractiveShell.kt b/tools/shell/src/main/kotlin/net/corda/tools/shell/InteractiveShell.kt
index ed1b5e94cd..9b3f926c97 100644
--- a/tools/shell/src/main/kotlin/net/corda/tools/shell/InteractiveShell.kt
+++ b/tools/shell/src/main/kotlin/net/corda/tools/shell/InteractiveShell.kt
@@ -9,7 +9,6 @@ import net.corda.client.jackson.StringToMethodCallParser
 import net.corda.client.rpc.CordaRPCClientConfiguration
 import net.corda.client.rpc.CordaRPCConnection
 import net.corda.client.rpc.PermissionException
-import net.corda.client.rpc.internal.createCordaRPCClientWithInternalSslAndClassLoader
 import net.corda.client.rpc.internal.createCordaRPCClientWithSslAndClassLoader
 import net.corda.core.CordaException
 import net.corda.core.concurrent.CordaFuture
@@ -93,24 +92,6 @@ object InteractiveShell {
         _startShell(configuration, classLoader)
     }
 
-    /**
-     * Starts an interactive shell connected to the local terminal. This shell gives administrator access to the node
-     * internals.
-     */
-    fun startShellInternal(configuration: ShellConfiguration, classLoader: ClassLoader? = null) {
-        rpcOps = { username: String, credentials: String ->
-            val client = createCordaRPCClientWithInternalSslAndClassLoader(hostAndPort = configuration.hostAndPort,
-                    configuration = CordaRPCClientConfiguration.DEFAULT.copy(
-                            maxReconnectAttempts = 1
-                    ),
-                    sslConfiguration = configuration.nodeSslConfig,
-                    classLoader = classLoader)
-            this.connection = client.start(username, credentials)
-            connection.proxy
-        }
-        _startShell(configuration, classLoader)
-    }
-
     private fun _startShell(configuration: ShellConfiguration, classLoader: ClassLoader? = null) {
         shellConfiguration = configuration
         InteractiveShell.classLoader = classLoader
diff --git a/tools/shell/src/main/kotlin/net/corda/tools/shell/ShellConfiguration.kt b/tools/shell/src/main/kotlin/net/corda/tools/shell/ShellConfiguration.kt
index 90d5f8407c..4877b520cc 100644
--- a/tools/shell/src/main/kotlin/net/corda/tools/shell/ShellConfiguration.kt
+++ b/tools/shell/src/main/kotlin/net/corda/tools/shell/ShellConfiguration.kt
@@ -2,7 +2,6 @@ package net.corda.tools.shell
 
 import net.corda.core.utilities.NetworkHostAndPort
 import net.corda.core.messaging.ClientRpcSslOptions
-import net.corda.nodeapi.internal.config.SSLConfiguration
 import java.nio.file.Path
 
 data class ShellConfiguration(
@@ -12,7 +11,6 @@ data class ShellConfiguration(
         var password: String = "",
         val hostAndPort: NetworkHostAndPort,
         val ssl: ClientRpcSslOptions? = null,
-        val nodeSslConfig: SSLConfiguration? = null,
         val sshdPort: Int? = null,
         val sshHostKeyDirectory: Path? = null,
         val noLocalShell: Boolean = false) {
diff --git a/webserver/src/main/kotlin/net/corda/webserver/WebServerConfig.kt b/webserver/src/main/kotlin/net/corda/webserver/WebServerConfig.kt
index 5ba08b691d..69569e8dbf 100644
--- a/webserver/src/main/kotlin/net/corda/webserver/WebServerConfig.kt
+++ b/webserver/src/main/kotlin/net/corda/webserver/WebServerConfig.kt
@@ -1,8 +1,8 @@
 package net.corda.webserver
 
 import com.typesafe.config.Config
+import net.corda.core.internal.div
 import net.corda.core.utilities.NetworkHostAndPort
-import net.corda.nodeapi.internal.config.NodeSSLConfiguration
 import net.corda.nodeapi.internal.config.User
 import net.corda.nodeapi.internal.config.getValue
 import net.corda.nodeapi.internal.config.parseAs
@@ -11,10 +11,13 @@ import java.nio.file.Path
 /**
  * [baseDirectory] is not retrieved from the config file but rather from a command line argument.
  */
-class WebServerConfig(override val baseDirectory: Path, val config: Config) : NodeSSLConfiguration {
-    override val keyStorePassword: String by config
-    override val trustStorePassword: String by config
-    override val crlCheckSoftFail: Boolean by config
+class WebServerConfig(val baseDirectory: Path, val config: Config) {
+
+    val keyStorePath: String by config
+    val keyStorePassword: String by config
+    val trustStorePath: String by config
+    val trustStorePassword: String by config
+
     val useHTTPS: Boolean by config
     val myLegalName: String by config
     val rpcAddress: NetworkHostAndPort by lazy {
diff --git a/webserver/src/main/kotlin/net/corda/webserver/internal/NodeWebServer.kt b/webserver/src/main/kotlin/net/corda/webserver/internal/NodeWebServer.kt
index 5bf16e4a7a..9ca4e03546 100644
--- a/webserver/src/main/kotlin/net/corda/webserver/internal/NodeWebServer.kt
+++ b/webserver/src/main/kotlin/net/corda/webserver/internal/NodeWebServer.kt
@@ -66,10 +66,10 @@ class NodeWebServer(val config: WebServerConfig) {
             httpsConfiguration.outputBufferSize = 32768
             httpsConfiguration.addCustomizer(SecureRequestCustomizer())
             val sslContextFactory = SslContextFactory()
-            sslContextFactory.keyStorePath = config.sslKeystore.toString()
+            sslContextFactory.keyStorePath = config.keyStorePath
             sslContextFactory.setKeyStorePassword(config.keyStorePassword)
             sslContextFactory.setKeyManagerPassword(config.keyStorePassword)
-            sslContextFactory.setTrustStorePath(config.trustStoreFile.toString())
+            sslContextFactory.setTrustStorePath(config.trustStorePath)
             sslContextFactory.setTrustStorePassword(config.trustStorePassword)
             sslContextFactory.setExcludeProtocols("SSL.*", "TLSv1", "TLSv1.1")
             sslContextFactory.setIncludeProtocols("TLSv1.2")