diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/ConfigUtilities.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/ConfigUtilities.kt
index 94997d34b8..544f1c1f7e 100644
--- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/ConfigUtilities.kt
+++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/ConfigUtilities.kt
@@ -2,10 +2,7 @@
 
 package net.corda.nodeapi.internal.config
 
-import com.typesafe.config.Config
-import com.typesafe.config.ConfigFactory
-import com.typesafe.config.ConfigUtil
-import com.typesafe.config.ConfigValueFactory
+import com.typesafe.config.*
 import net.corda.core.identity.CordaX500Name
 import net.corda.core.internal.noneOrSingle
 import net.corda.core.internal.uncheckedCast
@@ -78,7 +75,12 @@ private fun Config.getSingleValue(path: String, type: KType): Any? {
         NetworkHostAndPort::class -> NetworkHostAndPort.parse(getString(path))
         Path::class -> Paths.get(getString(path))
         URL::class -> URL(getString(path))
-        CordaX500Name::class -> CordaX500Name.parse(getString(path))
+        CordaX500Name::class -> {
+            when (getValue(path).valueType()) {
+                ConfigValueType.OBJECT -> getConfig(path).parseAs()
+                else -> CordaX500Name.parse(getString(path))
+            }
+        }
         Properties::class -> getConfig(path).toProperties()
         Config::class -> getConfig(path)
         else -> if (typeClass.java.isEnum) {
diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt
index 5b103222a3..e7ff717bf2 100644
--- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt
+++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt
@@ -259,13 +259,17 @@ object X509Utilities {
     private fun createCertificateSigningRequest(subject: X500Principal,
                                                 email: String,
                                                 keyPair: KeyPair,
-                                                signatureScheme: SignatureScheme): PKCS10CertificationRequest {
+                                                signatureScheme: SignatureScheme,
+                                                certRole: CertRole): PKCS10CertificationRequest {
         val signer = ContentSignerBuilder.build(signatureScheme, keyPair.private, Crypto.findProvider(signatureScheme.providerName))
-        return JcaPKCS10CertificationRequestBuilder(subject, keyPair.public).addAttribute(BCStyle.E, DERUTF8String(email)).build(signer)
+        return JcaPKCS10CertificationRequestBuilder(subject, keyPair.public)
+                .addAttribute(BCStyle.E, DERUTF8String(email))
+                .addAttribute(ASN1ObjectIdentifier(CordaOID.X509_EXTENSION_CORDA_ROLE), certRole)
+                .build(signer)
     }
 
-    fun createCertificateSigningRequest(subject: X500Principal, email: String, keyPair: KeyPair): PKCS10CertificationRequest {
-        return createCertificateSigningRequest(subject, email, keyPair, DEFAULT_TLS_SIGNATURE_SCHEME)
+    fun createCertificateSigningRequest(subject: X500Principal, email: String, keyPair: KeyPair, certRole: CertRole = CertRole.NODE_CA): PKCS10CertificationRequest {
+        return createCertificateSigningRequest(subject, email, keyPair, DEFAULT_TLS_SIGNATURE_SCHEME, certRole)
     }
 
     fun buildCertPath(first: X509Certificate, remaining: List<X509Certificate>): CertPath {
@@ -284,19 +288,24 @@ object X509Utilities {
     }
 }
 
+// Assuming cert type to role is 1:1
+val CertRole.certificateType: CertificateType get() = CertificateType.values().first { it.role == this }
+
 /**
  * Convert a [X509Certificate] into Bouncycastle's [X509CertificateHolder].
  *
  * NOTE: To avoid unnecessary copying use [X509Certificate] where possible.
  */
 fun X509Certificate.toBc() = X509CertificateHolder(encoded)
+
 fun X509CertificateHolder.toJca(): X509Certificate = X509CertificateFactory().generateCertificate(encoded.inputStream())
 
-val CertPath.x509Certificates: List<X509Certificate> get() {
-    require(type == "X.509") { "Not an X.509 cert path: $this" }
-    // We're not mapping the list to avoid creating a new one.
-    return uncheckedCast(certificates)
-}
+val CertPath.x509Certificates: List<X509Certificate>
+    get() {
+        require(type == "X.509") { "Not an X.509 cert path: $this" }
+        // We're not mapping the list to avoid creating a new one.
+        return uncheckedCast(certificates)
+    }
 
 val Certificate.x509: X509Certificate get() = requireNotNull(this as? X509Certificate) { "Not an X.509 certificate: $this" }
 
diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/config/ConfigParsingTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/config/ConfigParsingTest.kt
index a6fa2ca169..7fbc481321 100644
--- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/config/ConfigParsingTest.kt
+++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/config/ConfigParsingTest.kt
@@ -87,10 +87,15 @@ class ConfigParsingTest {
 
     @Test
     fun CordaX500Name() {
+        val name1 = CordaX500Name(organisation = "Mock Party", locality = "London", country = "GB")
         testPropertyType<CordaX500NameData, CordaX500NameListData, CordaX500Name>(
-                CordaX500Name(organisation = "Mock Party", locality = "London", country = "GB"),
+                name1,
                 CordaX500Name(organisation = "Mock Party 2", locality = "London", country = "GB"),
                 valuesToString = true)
+
+        // Test with config object.
+        val config = config("value" to mapOf("organisation" to "Mock Party", "locality" to "London", "country" to "GB"))
+        assertThat(config.parseAs<CordaX500NameData>().value).isEqualTo(name1)
     }
 
     @Test
@@ -273,6 +278,7 @@ class ConfigParsingTest {
     data class OldData(
             @OldConfig("oldValue")
             val newValue: String)
+
     data class DataWithCompanion(val value: Int) {
         companion object {
             @Suppress("unused")
diff --git a/node/src/main/kotlin/net/corda/node/ArgsParser.kt b/node/src/main/kotlin/net/corda/node/ArgsParser.kt
index f42a922564..4f234a4fe8 100644
--- a/node/src/main/kotlin/net/corda/node/ArgsParser.kt
+++ b/node/src/main/kotlin/net/corda/node/ArgsParser.kt
@@ -3,7 +3,10 @@ package net.corda.node
 import com.typesafe.config.ConfigFactory
 import joptsimple.OptionParser
 import joptsimple.util.EnumConverter
+import joptsimple.util.PathConverter
+import net.corda.core.internal.CertRole
 import net.corda.core.internal.div
+import net.corda.core.internal.exists
 import net.corda.node.services.config.ConfigHelper
 import net.corda.node.services.config.NodeConfiguration
 import net.corda.node.services.config.parseAsNodeConfiguration
@@ -34,9 +37,11 @@ class ArgsParser {
     private val sshdServerArg = optionParser.accepts("sshd", "Enables SSHD server for node administration.")
     private val noLocalShellArg = optionParser.accepts("no-local-shell", "Do not start the embedded shell locally.")
     private val isRegistrationArg = optionParser.accepts("initial-registration", "Start initial node registration with Corda network to obtain certificate from the permissioning server.")
-    private val networkRootTruststorePathArg = optionParser.accepts("network-root-truststore", "Network root trust store obtained from network operator.")
+    private val networkRootTrustStorePathArg = optionParser.accepts("network-root-truststore", "Network root trust store obtained from network operator.")
             .withRequiredArg()
-    private val networkRootTruststorePasswordArg = optionParser.accepts("network-root-truststore-password", "Network root trust store password obtained from network operator.")
+            .withValuesConvertedBy(PathConverter())
+            .defaultsTo((Paths.get("certificates") / "network-root-truststore.jks"))
+    private val networkRootTrustStorePasswordArg = optionParser.accepts("network-root-truststore-password", "Network root trust store password obtained from network operator.")
             .withRequiredArg()
     private val isVersionArg = optionParser.accepts("version", "Print the version and exit")
     private val justGenerateNodeInfoArg = optionParser.accepts("just-generate-node-info",
@@ -60,16 +65,23 @@ class ArgsParser {
         val sshdServer = optionSet.has(sshdServerArg)
         val justGenerateNodeInfo = optionSet.has(justGenerateNodeInfoArg)
         val bootstrapRaftCluster = optionSet.has(bootstrapRaftClusterArg)
-        val networkRootTruststorePath = optionSet.valueOf(networkRootTruststorePathArg)?.let { Paths.get(it).normalize().toAbsolutePath() }
-        val networkRootTruststorePassword = optionSet.valueOf(networkRootTruststorePasswordArg)
+        val networkRootTrustStorePath = optionSet.valueOf(networkRootTrustStorePathArg)
+        val networkRootTrustStorePassword = optionSet.valueOf(networkRootTrustStorePasswordArg)
+
+        val registrationConfig = if (isRegistration) {
+            requireNotNull(networkRootTrustStorePassword) { "Network root trust store password must be provided in registration mode." }
+            require(networkRootTrustStorePath.exists()) { "Network root trust store path: '$networkRootTrustStorePath' doesn't exist" }
+            NodeRegistrationOption(networkRootTrustStorePath, networkRootTrustStorePassword)
+        } else {
+            null
+        }
+
         return CmdLineOptions(baseDirectory,
                 configFile,
                 help,
                 loggingLevel,
                 logToConsole,
-                isRegistration,
-                networkRootTruststorePath,
-                networkRootTruststorePassword,
+                registrationConfig,
                 isVersion,
                 noLocalShell,
                 sshdServer,
@@ -80,14 +92,14 @@ class ArgsParser {
     fun printHelp(sink: PrintStream) = optionParser.printHelpOn(sink)
 }
 
+data class NodeRegistrationOption(val networkRootTrustStorePath: Path, val networkRootTrustStorePassword: String)
+
 data class CmdLineOptions(val baseDirectory: Path,
                           val configFile: Path,
                           val help: Boolean,
                           val loggingLevel: Level,
                           val logToConsole: Boolean,
-                          val isRegistration: Boolean,
-                          val networkRootTruststorePath: Path?,
-                          val networkRootTruststorePassword: String?,
+                          val nodeRegistrationConfig: NodeRegistrationOption?,
                           val isVersion: Boolean,
                           val noLocalShell: Boolean,
                           val sshdServer: Boolean,
@@ -97,10 +109,8 @@ data class CmdLineOptions(val baseDirectory: Path,
         val config = ConfigHelper.loadConfig(baseDirectory, configFile, configOverrides = ConfigFactory.parseMap(
                 mapOf("noLocalShell" to this.noLocalShell)
         )).parseAsNodeConfiguration()
-        if (isRegistration) {
+        if (nodeRegistrationConfig != null) {
             requireNotNull(config.compatibilityZoneURL) { "Compatibility Zone Url must be provided in registration mode." }
-            requireNotNull(networkRootTruststorePath) { "Network root trust store path must be provided in registration mode." }
-            requireNotNull(networkRootTruststorePassword) { "Network root trust store password must be provided in registration mode." }
         }
         return config
     }
diff --git a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt
index 99fe31d1fc..17e980ee87 100644
--- a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt
+++ b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt
@@ -99,9 +99,9 @@ open class NodeStartup(val args: Array<String>) {
         try {
             banJavaSerialisation(conf)
             preNetworkRegistration(conf)
-            if (shouldRegisterWithNetwork(cmdlineOptions, conf)) {
+            if (cmdlineOptions.nodeRegistrationConfig != null) {
                 // Null checks for [compatibilityZoneURL], [rootTruststorePath] and [rootTruststorePassword] has been done in [CmdLineOptions.loadConfig]
-                registerWithNetwork(conf, cmdlineOptions.networkRootTruststorePath!!, cmdlineOptions.networkRootTruststorePassword!!)
+                registerWithNetwork(conf, cmdlineOptions.nodeRegistrationConfig)
                 return true
             }
             logStartupInfo(versionInfo, cmdlineOptions, conf)
@@ -184,12 +184,7 @@ open class NodeStartup(val args: Array<String>) {
         logger.info("Starting as node on ${conf.p2pAddress}")
     }
 
-    private fun shouldRegisterWithNetwork(cmdlineOptions: CmdLineOptions, conf: NodeConfiguration): Boolean {
-        val compatibilityZoneURL = conf.compatibilityZoneURL
-        return !(!cmdlineOptions.isRegistration || compatibilityZoneURL == null)
-    }
-
-    open protected fun registerWithNetwork(conf: NodeConfiguration, networkRootTruststorePath: Path, networkRootTruststorePassword: String) {
+    open protected fun registerWithNetwork(conf: NodeConfiguration, nodeRegistrationConfig: NodeRegistrationOption) {
         val compatibilityZoneURL = conf.compatibilityZoneURL!!
         println()
         println("******************************************************************")
@@ -197,7 +192,7 @@ open class NodeStartup(val args: Array<String>) {
         println("*       Registering as a new participant with Corda network      *")
         println("*                                                                *")
         println("******************************************************************")
-        NetworkRegistrationHelper(conf, HTTPNetworkRegistrationService(compatibilityZoneURL), networkRootTruststorePath, networkRootTruststorePassword).buildKeystore()
+        NetworkRegistrationHelper(conf, HTTPNetworkRegistrationService(compatibilityZoneURL), nodeRegistrationConfig).buildKeystore()
     }
 
     open protected fun loadConfigFile(cmdlineOptions: CmdLineOptions): NodeConfiguration = cmdlineOptions.loadConfig()
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 287f4b41c9..1a253a701a 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
@@ -3,7 +3,10 @@ package net.corda.node.utilities.registration
 import net.corda.core.crypto.Crypto
 import net.corda.core.identity.CordaX500Name
 import net.corda.core.internal.*
+import net.corda.node.NodeRegistrationOption
 import net.corda.node.services.config.NodeConfiguration
+import net.corda.nodeapi.internal.DevIdentityGenerator
+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
@@ -22,10 +25,18 @@ import java.security.cert.X509Certificate
  * Helper for managing the node registration process, which checks for any existing certificates and requests them if
  * needed.
  */
-class NetworkRegistrationHelper(private val config: NodeConfiguration,
+class NetworkRegistrationHelper(private val config: SSLConfiguration,
+                                private val myLegalName: CordaX500Name,
+                                private val emailAddress: String,
                                 private val certService: NetworkRegistrationService,
-                                networkRootTrustStorePath: Path,
-                                networkRootTruststorePassword: String) {
+                                private val networkRootTrustStorePath: Path,
+                                networkRootTrustStorePassword: String,
+                                private val certRole: CertRole) {
+
+    // Constructor for corda node, cert role is restricted to [CertRole.NODE_CA].
+    constructor(config: NodeConfiguration, certService: NetworkRegistrationService, regConfig: NodeRegistrationOption) :
+            this(config, config.myLegalName, config.emailAddress, certService, regConfig.networkRootTrustStorePath, regConfig.networkRootTrustStorePassword, CertRole.NODE_CA)
+
     private companion object {
         const val SELF_SIGNED_PRIVATE_KEY = "Self Signed Private Key"
     }
@@ -41,7 +52,7 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration,
             "$networkRootTrustStorePath does not exist. This file must contain the root CA cert of your compatibility zone. " +
                     "Please contact your CZ operator."
         }
-        rootTrustStore = X509KeyStore.fromFile(networkRootTrustStorePath, networkRootTruststorePassword)
+        rootTrustStore = X509KeyStore.fromFile(networkRootTrustStorePath, networkRootTrustStorePassword)
         rootCert = rootTrustStore.getCertificate(CORDA_ROOT_CA)
     }
 
@@ -68,7 +79,7 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration,
         // We use the self sign certificate to store the key temporarily in the keystore while waiting for the request approval.
         if (SELF_SIGNED_PRIVATE_KEY !in nodeKeyStore) {
             val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
-            val selfSignCert = X509Utilities.createSelfSignedCACertificate(config.myLegalName.x500Principal, keyPair)
+            val selfSignCert = X509Utilities.createSelfSignedCACertificate(myLegalName.x500Principal, keyPair)
             // Save to the key store.
             nodeKeyStore.setPrivateKey(SELF_SIGNED_PRIVATE_KEY, keyPair.private, listOf(selfSignCert), keyPassword = privateKeyPassword)
             nodeKeyStore.save()
@@ -87,36 +98,59 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration,
             throw certificateRequestException
         }
 
-        val nodeCaCert = certificates[0]
+        val certificate = certificates.first()
 
         val nodeCaSubject = try {
-            CordaX500Name.build(nodeCaCert.subjectX500Principal)
+            CordaX500Name.build(certificate.subjectX500Principal)
         } catch (e: IllegalArgumentException) {
             throw CertificateRequestException("Received node CA cert has invalid subject name: ${e.message}")
         }
-        if (nodeCaSubject != config.myLegalName) {
+        if (nodeCaSubject != myLegalName) {
             throw CertificateRequestException("Subject of received node CA cert doesn't match with node legal name: $nodeCaSubject")
         }
 
         val nodeCaCertRole = try {
-            CertRole.extract(nodeCaCert)
+            CertRole.extract(certificate)
         } catch (e: IllegalArgumentException) {
             throw CertificateRequestException("Unable to extract cert role from received node CA cert: ${e.message}")
         }
-        if (nodeCaCertRole != CertRole.NODE_CA) {
-            throw CertificateRequestException("Received node CA cert has invalid role: $nodeCaCertRole")
-        }
 
         // Validate certificate chain returned from the doorman with the root cert obtained via out-of-band process, to prevent MITM attack on doorman server.
         X509Utilities.validateCertificateChain(rootCert, certificates)
 
         println("Certificate signing request approved, storing private key with the certificate chain.")
-        // Save private key and certificate chain to the key store.
-        nodeKeyStore.setPrivateKey(CORDA_CLIENT_CA, keyPair.private, certificates, keyPassword = privateKeyPassword)
-        nodeKeyStore.internal.deleteEntry(SELF_SIGNED_PRIVATE_KEY)
-        nodeKeyStore.save()
-        println("Node private key and certificate stored in ${config.nodeKeystore}.")
 
+        when (nodeCaCertRole) {
+            CertRole.NODE_CA -> {
+                // Save private key and certificate chain to the key store.
+                nodeKeyStore.setPrivateKey(CORDA_CLIENT_CA, keyPair.private, certificates, keyPassword = privateKeyPassword)
+                nodeKeyStore.internal.deleteEntry(SELF_SIGNED_PRIVATE_KEY)
+                nodeKeyStore.save()
+                println("Node private key and certificate stored in ${config.nodeKeystore}.")
+
+                config.loadSslKeyStore(createNew = true).update {
+                    println("Generating SSL certificate for node messaging service.")
+                    val sslKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
+                    val sslCert = X509Utilities.createCertificate(
+                            CertificateType.TLS,
+                            certificate,
+                            keyPair,
+                            myLegalName.x500Principal,
+                            sslKeyPair.public)
+                    setPrivateKey(CORDA_CLIENT_TLS, sslKeyPair.private, listOf(sslCert) + certificates)
+                }
+                println("SSL private key and certificate stored in ${config.sslKeystore}.")
+            }
+            // TODO: Fix this, this is not needed in corda node.
+            CertRole.SERVICE_IDENTITY -> {
+                // Only create keystore containing notary's key for service identity role.
+                nodeKeyStore.setPrivateKey("${DevIdentityGenerator.DISTRIBUTED_NOTARY_ALIAS_PREFIX}-private-key", keyPair.private, certificates, keyPassword = privateKeyPassword)
+                nodeKeyStore.internal.deleteEntry(SELF_SIGNED_PRIVATE_KEY)
+                nodeKeyStore.save()
+                println("Service identity private key and certificate stored in ${config.nodeKeystore}.")
+            }
+            else -> throw CertificateRequestException("Received node CA cert has invalid role: $nodeCaCertRole")
+        }
         // Save root certificates to trust store.
         config.loadTrustStore(createNew = true).update {
             println("Generating trust store for corda node.")
@@ -124,20 +158,6 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration,
             setCertificate(CORDA_ROOT_CA, certificates.last())
         }
         println("Node trust store stored in ${config.trustStoreFile}.")
-
-        config.loadSslKeyStore(createNew = true).update {
-            println("Generating SSL certificate for node messaging service.")
-            val sslKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
-            val sslCert = X509Utilities.createCertificate(
-                    CertificateType.TLS,
-                    nodeCaCert,
-                    keyPair,
-                    config.myLegalName.x500Principal,
-                    sslKeyPair.public)
-            setPrivateKey(CORDA_CLIENT_TLS, sslKeyPair.private, listOf(sslCert) + certificates)
-        }
-        println("SSL private key and certificate stored in ${config.sslKeystore}.")
-
         // All done, clean up temp files.
         requestIdStore.deleteIfExists()
     }
@@ -169,15 +189,15 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration,
     private fun submitOrResumeCertificateSigningRequest(keyPair: KeyPair): String {
         // Retrieve request id from file if exists, else post a request to server.
         return if (!requestIdStore.exists()) {
-            val request = X509Utilities.createCertificateSigningRequest(config.myLegalName.x500Principal, config.emailAddress, keyPair)
+            val request = X509Utilities.createCertificateSigningRequest(myLegalName.x500Principal, emailAddress, keyPair, certRole)
             val writer = StringWriter()
             JcaPEMWriter(writer).use {
                 it.writeObject(PemObject("CERTIFICATE REQUEST", request.encoded))
             }
             println("Certificate signing request with the following information will be submitted to the Corda certificate signing server.")
             println()
-            println("Legal Name: ${config.myLegalName}")
-            println("Email: ${config.emailAddress}")
+            println("Legal Name: $myLegalName")
+            println("Email: $emailAddress")
             println()
             println("Public Key: ${keyPair.public}")
             println()
diff --git a/node/src/test/kotlin/net/corda/node/ArgsParserTest.kt b/node/src/test/kotlin/net/corda/node/ArgsParserTest.kt
index 96f98d8c33..8ddb9707e9 100644
--- a/node/src/test/kotlin/net/corda/node/ArgsParserTest.kt
+++ b/node/src/test/kotlin/net/corda/node/ArgsParserTest.kt
@@ -2,12 +2,14 @@ package net.corda.node
 
 import joptsimple.OptionException
 import net.corda.core.internal.div
+import net.corda.nodeapi.internal.crypto.X509KeyStore
 import org.assertj.core.api.Assertions.assertThat
 import org.assertj.core.api.Assertions.assertThatExceptionOfType
 import org.junit.Test
 import org.slf4j.event.Level
 import java.nio.file.Paths
 import kotlin.test.assertEquals
+import kotlin.test.assertNotNull
 
 class ArgsParserTest {
     private val parser = ArgsParser()
@@ -21,14 +23,12 @@ class ArgsParserTest {
                 help = false,
                 logToConsole = false,
                 loggingLevel = Level.INFO,
-                isRegistration = false,
+                nodeRegistrationConfig = null,
                 isVersion = false,
                 noLocalShell = false,
                 sshdServer = false,
                 justGenerateNodeInfo = false,
-                bootstrapRaftCluster = false,
-                networkRootTruststorePassword = null,
-                networkRootTruststorePath = null))
+                bootstrapRaftCluster = false))
     }
 
     @Test
@@ -113,11 +113,17 @@ class ArgsParserTest {
 
     @Test
     fun `initial-registration`() {
-        val truststorePath = Paths.get("truststore") / "file.jks"
+        val truststorePath = workingDirectory / "truststore" / "file.jks"
+        assertThatExceptionOfType(IllegalArgumentException::class.java).isThrownBy {
+            parser.parse("--initial-registration", "--network-root-truststore", "$truststorePath", "--network-root-truststore-password", "password-test")
+        }.withMessageContaining("Network root trust store path").withMessageContaining("doesn't exist")
+
+        X509KeyStore.fromFile(truststorePath, "dummy_password", createNew = true)
+
         val cmdLineOptions = parser.parse("--initial-registration", "--network-root-truststore", "$truststorePath", "--network-root-truststore-password", "password-test")
-        assertThat(cmdLineOptions.isRegistration).isTrue()
-        assertEquals(truststorePath.toAbsolutePath(), cmdLineOptions.networkRootTruststorePath)
-        assertEquals("password-test", cmdLineOptions.networkRootTruststorePassword)
+        assertNotNull(cmdLineOptions.nodeRegistrationConfig)
+        assertEquals(truststorePath.toAbsolutePath(), cmdLineOptions.nodeRegistrationConfig?.networkRootTrustStorePath)
+        assertEquals("password-test", cmdLineOptions.nodeRegistrationConfig?.networkRootTrustStorePassword)
     }
 
     @Test
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 2371ab4437..d77e61bcae 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
@@ -13,7 +13,9 @@ import net.corda.core.internal.createDirectories
 import net.corda.core.internal.div
 import net.corda.core.internal.x500Name
 import net.corda.core.utilities.seconds
+import net.corda.node.NodeRegistrationOption
 import net.corda.node.services.config.NodeConfiguration
+import net.corda.nodeapi.internal.DevIdentityGenerator
 import net.corda.nodeapi.internal.crypto.CertificateType
 import net.corda.nodeapi.internal.crypto.X509KeyStore
 import net.corda.nodeapi.internal.crypto.X509Utilities
@@ -141,6 +143,38 @@ class NetworkRegistrationHelperTest {
         }.isInstanceOf(CertPathValidatorException::class.java)
     }
 
+    @Test
+    fun `create service identity cert`() {
+        assertThat(config.nodeKeystore).doesNotExist()
+        assertThat(config.sslKeystore).doesNotExist()
+        assertThat(config.trustStoreFile).doesNotExist()
+
+        val serviceIdentityCertPath = createServiceIdentityCertPath()
+
+        saveNetworkTrustStore(serviceIdentityCertPath.last())
+        createRegistrationHelper(serviceIdentityCertPath).buildKeystore()
+
+        val nodeKeystore = config.loadNodeKeyStore()
+        val trustStore = config.loadTrustStore()
+        assertThat(config.sslKeystore).doesNotExist()
+
+        val serviceIdentityAlias = "${DevIdentityGenerator.DISTRIBUTED_NOTARY_ALIAS_PREFIX}-private-key"
+
+        nodeKeystore.run {
+            assertFalse(contains(X509Utilities.CORDA_INTERMEDIATE_CA))
+            assertFalse(contains(X509Utilities.CORDA_ROOT_CA))
+            assertFalse(contains(X509Utilities.CORDA_CLIENT_TLS))
+            assertFalse(contains(X509Utilities.CORDA_CLIENT_CA))
+            assertThat(getCertificateChain(serviceIdentityAlias)).containsExactlyElementsOf(serviceIdentityCertPath)
+        }
+
+        trustStore.run {
+            assertFalse(contains(X509Utilities.CORDA_CLIENT_CA))
+            assertFalse(contains(X509Utilities.CORDA_INTERMEDIATE_CA))
+            assertThat(getCertificate(X509Utilities.CORDA_ROOT_CA)).isEqualTo(serviceIdentityCertPath.last())
+        }
+    }
+
     private fun createNodeCaCertPath(type: CertificateType = CertificateType.NODE_CA,
                                      legalName: CordaX500Name = nodeLegalName): List<X509Certificate> {
         val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
@@ -156,12 +190,25 @@ class NetworkRegistrationHelperTest {
         return listOf(nodeCaCert, intermediateCa.certificate, rootCa.certificate)
     }
 
+    private fun createServiceIdentityCertPath(type: CertificateType = CertificateType.SERVICE_IDENTITY,
+                                              legalName: CordaX500Name = nodeLegalName): List<X509Certificate> {
+        val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
+        val keyPair = Crypto.generateKeyPair()
+        val serviceIdentityCert = X509Utilities.createCertificate(
+                type,
+                intermediateCa.certificate,
+                intermediateCa.keyPair,
+                legalName.x500Principal,
+                keyPair.public)
+        return listOf(serviceIdentityCert, intermediateCa.certificate, rootCa.certificate)
+    }
+
     private fun createRegistrationHelper(response: List<X509Certificate>): NetworkRegistrationHelper {
         val certService = rigorousMock<NetworkRegistrationService>().also {
             doReturn(requestId).whenever(it).submitRequest(any())
             doReturn(CertificateResponse(5.seconds, response)).whenever(it).retrieveCertificates(eq(requestId))
         }
-        return NetworkRegistrationHelper(config, certService, config.certificatesDirectory / networkRootTrustStoreFileName, networkRootTrustStorePassword)
+        return NetworkRegistrationHelper(config, certService, NodeRegistrationOption(config.certificatesDirectory / networkRootTrustStoreFileName, networkRootTrustStorePassword))
     }
 
     private fun saveNetworkTrustStore(rootCert: X509Certificate) {
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 fe45106959..e3879eef58 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
@@ -23,6 +23,7 @@ import net.corda.core.utilities.NetworkHostAndPort
 import net.corda.core.utilities.contextLogger
 import net.corda.core.utilities.getOrThrow
 import net.corda.core.utilities.millis
+import net.corda.node.NodeRegistrationOption
 import net.corda.node.internal.Node
 import net.corda.node.internal.NodeStartup
 import net.corda.node.internal.StartedNode
@@ -259,7 +260,7 @@ class DriverDSLImpl(
 
         return if (startNodesInProcess) {
             executorService.fork {
-                NetworkRegistrationHelper(config.corda, HTTPNetworkRegistrationService(compatibilityZoneURL), rootTruststorePath, rootTruststorePassword).buildKeystore()
+                NetworkRegistrationHelper(config.corda, HTTPNetworkRegistrationService(compatibilityZoneURL), NodeRegistrationOption(rootTruststorePath, rootTruststorePassword)).buildKeystore()
                 config
             }
         } else {