mirror of
https://github.com/corda/corda.git
synced 2025-03-15 08:41:04 +00:00
ENT-2653: Split Artemis and Tunnel keys generation onto two separate sub-commands (#1584)
* ENT-2653: Introduce generation modes in InternalKeystoreGenerator So that it would be possible to produce Artemis and Tunnel keystores independently. * ENT-2653: Split Artemis certificates generation and Tunnel certificate generation onto two separate sub-commands. * ENT-2653: Minor documentation update
This commit is contained in:
parent
df2c313f23
commit
109bed8def
@ -23,7 +23,8 @@ Sub-commands
|
||||
|
||||
``node-registration``: Corda registration tool for registering 1 or more node with the corda network, using provided node configuration.
|
||||
``import-ssl-key``: Key copying tool for creating bridge SSL keystore or add new node SSL identity to existing bridge SSL keystore.
|
||||
``generate-internal-ssl-keystores``: Generate self-signed root and SSL certificates for bridge, external artemis broker and float, for internal communication between the services.
|
||||
``generate-internal-artemis-ssl-keystores``: Generate self-signed root and SSL certificates for internal communication between the services and external Artemis broker.
|
||||
``generate-internal-tunnel-ssl-keystores``: Generate self-signed root and SSL certificates for internal communication between Bridge and Float.
|
||||
``install-shell-extensions``: Install alias and autocompletion for bash and zsh. See :doc:`cli-application-shell-extensions` for more info.
|
||||
|
||||
|
||||
@ -70,22 +71,45 @@ Command-line options
|
||||
* ``-V``, ``--version`` :Print version information and exit.
|
||||
|
||||
|
||||
Self signed internal SSL keystore
|
||||
---------------------------------
|
||||
Self signed internal Artemis SSL keystore
|
||||
-----------------------------------------
|
||||
|
||||
TLS is used to ensure communications between firewall components are secured. This tool can be used to generate the required keystores if TLS cert signing infrastructure is not available within your organisation.
|
||||
TLS is used to ensure communications between various components connected to standalone Artemis. This tool can be used to generate the required keystores if TLS cert signing infrastructure is not available within your organisation.
|
||||
Please note that for Artemis to work correctly with keystores, the password for the store and the password for the private will be set to the same value.
|
||||
|
||||
Command-line options
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
.. code-block:: shell
|
||||
|
||||
ha-utilities generate-internal-ssl-keystores [-hvV] [--logging-level=<loggingLevel>] [-b=<baseDirectory>] [-c=<country>] [-l=<locality>] [-o=<organization>] [-p=<password>]
|
||||
ha-utilities generate-internal-artemis-ssl-keystores [-hvV] [--logging-level=<loggingLevel>] [-b=<baseDirectory>] [-c=<country>] [-l=<locality>] [-o=<organization>] [-p=<keyStorePassword>] [-t=<trustStorePassword>]
|
||||
|
||||
* ``-v``, ``--verbose``, ``--log-to-console``: If set, prints logging to the console as well as to a file.
|
||||
* ``--logging-level=<loggingLevel>``: Enable logging at this level and higher. Possible values: ERROR, WARN, INFO, DEBUG, TRACE. Default: INFO
|
||||
* ``-p``, ``--keyStorePassword=<keyStorePassword>``: Password for all generated keystores. Default: changeit
|
||||
* ``-t``, ``--trustStorePassword=<trustStorePassword>``: Password for the trust store. Default: changeit
|
||||
* ``-o``, ``--organization=<organization>``: X500Name's organization attribute. Default: Corda
|
||||
* ``-l``, ``--locality=<locality>``: X500Name's locality attribute. Default: London
|
||||
* ``-c``, ``--country=<country>``: X500Name's country attribute. Default: GB
|
||||
* ``-b``, ``--base-directory=<baseDirectory>``: The node working directory where all the files are kept.
|
||||
* ``-h``, ``--help``: Show this help message and exit.
|
||||
* ``-V``, ``--version``: Print version information and exit.
|
||||
|
||||
Self signed internal Tunnel SSL keystore
|
||||
-----------------------------------------
|
||||
|
||||
TLS is used to for communications between Bridge and Float components. This tool can be used to generate the required keystores if TLS cert signing infrastructure is not available within your organisation.
|
||||
|
||||
Command-line options
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
.. code-block:: shell
|
||||
|
||||
ha-utilities generate-internal-tunnel-ssl-keystores [-hvV] [--logging-level=<loggingLevel>] [-b=<baseDirectory>] [-c=<country>] [-l=<locality>] [-o=<organization>] [-p=<keyStorePassword>] [-e=<entryPassword>] [-t=<trustStorePassword>]
|
||||
|
||||
* ``-v``, ``--verbose``, ``--log-to-console``: If set, prints logging to the console as well as to a file.
|
||||
* ``--logging-level=<loggingLevel>``: Enable logging at this level and higher. Possible values: ERROR, WARN, INFO, DEBUG, TRACE. Default: INFO
|
||||
* ``-p``, ``--keyStorePassword=<keyStorePassword>``: Password for all generated keystores. Default: changeit
|
||||
* ``-e``, ``--entryPassword=<entryPassword>``: Password for all the keystores private keys. Default: changeit
|
||||
* ``-t``, ``--trustStorePassword=<trustStorePassword>``: Password for all the trust stores. Default: changeit
|
||||
* ``-t``, ``--trustStorePassword=<trustStorePassword>``: Password for the trust store. Default: changeit
|
||||
* ``-o``, ``--organization=<organization>``: X500Name's organization attribute. Default: Corda
|
||||
* ``-l``, ``--locality=<locality>``: X500Name's locality attribute. Default: London
|
||||
* ``-c``, ``--country=<country>``: X500Name's country attribute. Default: GB
|
||||
|
@ -15,20 +15,45 @@ import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import javax.security.auth.x500.X500Principal
|
||||
|
||||
class InternalKeystoreGenerator : CliWrapperBase("generate-internal-ssl-keystores", "Generate self-signed root and SSL certificates for bridge, external artemis broker and float, for internal communication between the services.") {
|
||||
companion object {
|
||||
private const val DEFAULT_PASSWORD = "changeit"
|
||||
private const val DEFAULT_PASSWORD = "changeit"
|
||||
|
||||
internal class InternalArtemisKeystoreGenerator : AbstractInternalKeystoreGenerator("generate-internal-artemis-ssl-keystores", "Generate self-signed root and SSL certificates for internal communication between the services and external Artemis broker.") {
|
||||
|
||||
override fun createKeyStores() {
|
||||
println("Generating Artemis keystores")
|
||||
val artemisCertDir = baseDirectory / "artemis"
|
||||
val root = createRootKeystore("Internal Artemis Root", artemisCertDir / "artemis-root.jks", artemisCertDir / "artemis-truststore.jks",
|
||||
keyStorePassword, keyStorePassword, trustStorePassword).getCertificateAndKeyPair(X509Utilities.CORDA_ROOT_CA, keyStorePassword)
|
||||
createTLSKeystore("bridge", root, artemisCertDir / "bridge.jks", keyStorePassword, keyStorePassword)
|
||||
createTLSKeystore("artemis", root, artemisCertDir / "artemis.jks", keyStorePassword, keyStorePassword)
|
||||
createTLSKeystore("artemis-client", root, artemisCertDir / "artemis-client.jks", keyStorePassword, keyStorePassword)
|
||||
}
|
||||
}
|
||||
|
||||
internal class InternalTunnelKeystoreGenerator : AbstractInternalKeystoreGenerator("generate-internal-tunnel-ssl-keystores", "Generate self-signed root and SSL certificates for internal communication between Bridge and Float.") {
|
||||
|
||||
@Option(names = ["-e", "--entryPassword"], description = ["Password for all the keystores private keys."], defaultValue = DEFAULT_PASSWORD)
|
||||
lateinit var entryPassword: String
|
||||
|
||||
override fun createKeyStores() {
|
||||
println("Generating Tunnel keystores")
|
||||
val tunnelCertDir = baseDirectory / "tunnel"
|
||||
val tunnelRoot = createRootKeystore("Internal Tunnel Root", tunnelCertDir / "tunnel-root.jks", tunnelCertDir / "tunnel-truststore.jks",
|
||||
keyStorePassword, entryPassword, trustStorePassword).getCertificateAndKeyPair(X509Utilities.CORDA_ROOT_CA, entryPassword)
|
||||
createTLSKeystore("float", tunnelRoot, tunnelCertDir / "float.jks", keyStorePassword, entryPassword)
|
||||
createTLSKeystore("bridge", tunnelRoot, tunnelCertDir / "bridge.jks", keyStorePassword, entryPassword)
|
||||
|
||||
warnOnDefaultPassword(entryPassword, "entryPassword")
|
||||
}
|
||||
}
|
||||
|
||||
internal abstract class AbstractInternalKeystoreGenerator(alias: String, description: String) : CliWrapperBase(alias, description) {
|
||||
|
||||
@Option(names = ["-b", BASE_DIR], description = ["The node working directory where all the files are kept."])
|
||||
var baseDirectory: Path = Paths.get(".").toAbsolutePath().normalize()
|
||||
|
||||
// TODO: options to generate keys for different HA deployment mode?
|
||||
@Option(names = ["-p", "--keyStorePassword"], description = ["Password for all generated keystores."], defaultValue = DEFAULT_PASSWORD)
|
||||
lateinit var keyStorePassword: String
|
||||
@Option(names = ["-e", "--entryPassword"], description = ["Password for all the keystores private keys."], defaultValue = DEFAULT_PASSWORD)
|
||||
lateinit var entryPassword: String
|
||||
@Option(names = ["-t", "--trustStorePassword"], description = ["Password for all the trust stores."], defaultValue = DEFAULT_PASSWORD)
|
||||
@Option(names = ["-t", "--trustStorePassword"], description = ["Password for the trust store."], defaultValue = DEFAULT_PASSWORD)
|
||||
lateinit var trustStorePassword: String
|
||||
@Option(names = ["-o", "--organization"], description = ["X500Name's organization attribute."], defaultValue = "Corda")
|
||||
lateinit var organization: String
|
||||
@ -39,33 +64,26 @@ class InternalKeystoreGenerator : CliWrapperBase("generate-internal-ssl-keystore
|
||||
@Option(names = ["-c", "--country"], description = ["X500Name's country attribute."], defaultValue = "GB")
|
||||
lateinit var country: String
|
||||
|
||||
protected abstract fun createKeyStores()
|
||||
|
||||
override fun runProgram(): Int {
|
||||
// Create tunnel certs
|
||||
val tunnelCertDir = baseDirectory / "tunnel"
|
||||
val tunnelRoot = createRootKeystore("Internal Tunnel Root", tunnelCertDir / "tunnel-root.jks", tunnelCertDir / "tunnel-truststore.jks",
|
||||
keyStorePassword, entryPassword, trustStorePassword).getCertificateAndKeyPair(X509Utilities.CORDA_ROOT_CA, entryPassword)
|
||||
createTLSKeystore("float", tunnelRoot, tunnelCertDir / "float.jks", keyStorePassword, entryPassword)
|
||||
createTLSKeystore("bridge", tunnelRoot, tunnelCertDir / "bridge.jks", keyStorePassword, entryPassword)
|
||||
|
||||
// Create artemis certs
|
||||
// NB: For Artemis store password must be the same as key password due to implementation restrictions.
|
||||
val artemisCertDir = baseDirectory / "artemis"
|
||||
val root = createRootKeystore("Internal Artemis Root", artemisCertDir / "artemis-root.jks", artemisCertDir / "artemis-truststore.jks",
|
||||
keyStorePassword, keyStorePassword, trustStorePassword).getCertificateAndKeyPair(X509Utilities.CORDA_ROOT_CA, keyStorePassword)
|
||||
createTLSKeystore("bridge", root, artemisCertDir / "bridge.jks", keyStorePassword, keyStorePassword)
|
||||
createTLSKeystore("artemis", root, artemisCertDir / "artemis.jks", keyStorePassword, keyStorePassword)
|
||||
createTLSKeystore("artemis-client", root, artemisCertDir / "artemis-client.jks", keyStorePassword, keyStorePassword)
|
||||
createKeyStores()
|
||||
|
||||
listOf(keyStorePassword to "keyStorePassword", entryPassword to "entryPassword", trustStorePassword to "trustStorePassword").forEach {
|
||||
if(it.first == DEFAULT_PASSWORD) {
|
||||
println("WARN: Password for '${it.second}' is defaulted to '$DEFAULT_PASSWORD'. Please consider changing the password using java keytool.")
|
||||
}
|
||||
listOf(keyStorePassword to "keyStorePassword", trustStorePassword to "trustStorePassword").forEach {
|
||||
warnOnDefaultPassword(it.first, it.second)
|
||||
}
|
||||
|
||||
return ExitCodes.SUCCESS
|
||||
}
|
||||
|
||||
private fun createRootKeystore(commonName: String, keystorePath: Path, trustStorePath: Path, storePassword: String, entryPassword: String, trustStorePassword: String): X509KeyStore {
|
||||
protected fun warnOnDefaultPassword(password: String, paramName: String) {
|
||||
if (password == DEFAULT_PASSWORD) {
|
||||
println("WARN: Password for '$paramName' is defaulted to '$DEFAULT_PASSWORD'. Please consider changing the password using java keytool.")
|
||||
}
|
||||
}
|
||||
|
||||
protected fun createRootKeystore(commonName: String, keystorePath: Path, trustStorePath: Path, storePassword: String, entryPassword: String, trustStorePassword: String): X509KeyStore {
|
||||
val key = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val rootCert = X509Utilities.createSelfSignedCACertificate(getX500Principal(commonName), key)
|
||||
val keystore = X509KeyStore.fromFile(keystorePath, storePassword, createNew = true)
|
||||
@ -80,7 +98,7 @@ class InternalKeystoreGenerator : CliWrapperBase("generate-internal-ssl-keystore
|
||||
return keystore
|
||||
}
|
||||
|
||||
private fun createTLSKeystore(serviceName: String, root: CertificateAndKeyPair, keystorePath: Path, storePassword: String, entryPassword: String) {
|
||||
protected fun createTLSKeystore(serviceName: String, root: CertificateAndKeyPair, keystorePath: Path, storePassword: String, entryPassword: String) {
|
||||
val key = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val cert = X509Utilities.createCertificate(CertificateType.TLS, root.certificate, root.keyPair, getX500Principal(serviceName), key.public)
|
||||
X509KeyStore.fromFile(keystorePath, storePassword, createNew = true).update {
|
||||
|
@ -9,7 +9,7 @@ fun main(args: Array<String>) {
|
||||
}
|
||||
|
||||
class HAUtilities : CordaCliWrapper("ha-utilities", "HA utilities contains tools to help setting up corda firewall services.") {
|
||||
override fun additionalSubCommands() = setOf(RegistrationTool(), BridgeSSLKeyTool(), InternalKeystoreGenerator())
|
||||
override fun additionalSubCommands() = setOf(RegistrationTool(), BridgeSSLKeyTool(), InternalArtemisKeystoreGenerator(), InternalTunnelKeystoreGenerator())
|
||||
|
||||
override fun runProgram(): Int {
|
||||
printHelp()
|
||||
|
@ -17,14 +17,14 @@ import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class InternalKeystoreGeneratorTest {
|
||||
private val generator = InternalKeystoreGenerator()
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
val tempFolder: TemporaryFolder = TemporaryFolder()
|
||||
|
||||
@Test
|
||||
fun `generate keystores correctly`() {
|
||||
fun `generate tunnel keystores correctly`() {
|
||||
val generator = InternalTunnelKeystoreGenerator()
|
||||
val workingDirectory = tempFolder.root.toPath()
|
||||
val keyStorePassword = "keyStorePassword"
|
||||
val entryPassword = "entryPassword"
|
||||
@ -57,6 +57,22 @@ class InternalKeystoreGeneratorTest {
|
||||
assertTrue(internal.isCertificateEntry(CORDA_ROOT_CA))
|
||||
assertFalse(internal.isKeyEntry(CORDA_ROOT_CA))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `generate Artemis keystores correctly`() {
|
||||
val generator = InternalArtemisKeystoreGenerator()
|
||||
val workingDirectory = tempFolder.root.toPath()
|
||||
val keyStorePassword = "keyStorePassword"
|
||||
val trustStorePassword = "trustStorePassword"
|
||||
CommandLine.populateCommand(generator,
|
||||
BASE_DIR, workingDirectory.toString(),
|
||||
"--keyStorePassword", keyStorePassword,
|
||||
"--trustStorePassword", trustStorePassword)
|
||||
assertEquals(workingDirectory, generator.baseDirectory)
|
||||
assertEquals(keyStorePassword, generator.keyStorePassword)
|
||||
assertEquals(trustStorePassword, generator.trustStorePassword)
|
||||
generator.runProgram()
|
||||
|
||||
listOf("bridge.jks", "artemis.jks", "artemis-client.jks").map { workingDirectory / "artemis" / it }.forEach {
|
||||
assertTrue(it.exists())
|
||||
|
Loading…
x
Reference in New Issue
Block a user