ENT-2653: Add more options for providing distinct passwords for store and private keys. (#1580)

* ENT-2653: Add more options for providing distinct passwords for store and private keys.

* ENT-2653: Documentation update.
This commit is contained in:
Viktor Kolomeyko 2018-11-21 10:04:28 +00:00 committed by GitHub
parent 58056d4ea9
commit 1d511269f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 70 additions and 43 deletions

View File

@ -12,7 +12,7 @@ To run simply pass in the file or URL as the first parameter:
.. parsed-literal::
> java -jar |jar_name| <file or URL>
> java -jar |jar_name| <sub-command> <command line options>
..
@ -82,12 +82,13 @@ Command-line options
ha-utilities generate-internal-ssl-keystores [-hvV] [--logging-level=<loggingLevel>] [-b=<baseDirectory>] [-c=<country>] [-l=<locality>] [-o=<organization>] [-p=<password>]
* ``-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``, ``--password=<password>``: Default password for all generated keystore and private keys. Default: changeit
* ``--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
* ``-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.
* ``-V``, ``--version``: Print version information and exit.

View File

@ -24,8 +24,12 @@ class InternalKeystoreGenerator : CliWrapperBase("generate-internal-ssl-keystore
var baseDirectory: Path = Paths.get(".").toAbsolutePath().normalize()
// TODO: options to generate keys for different HA deployment mode?
@Option(names = ["-p", "--password"], description = ["Default password for all generated keystore and private keys."], defaultValue = DEFAULT_PASSWORD)
lateinit var password: String
@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)
lateinit var trustStorePassword: String
@Option(names = ["-o", "--organization"], description = ["X500Name's organization attribute."], defaultValue = "Corda")
lateinit var organization: String
@Option(names = ["-u", "--organization-unit"], description = ["X500Name's organization unit attribute."], required = false)
@ -38,42 +42,49 @@ class InternalKeystoreGenerator : CliWrapperBase("generate-internal-ssl-keystore
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").getCertificateAndKeyPair(X509Utilities.CORDA_ROOT_CA, password)
createTLSKeystore("float", tunnelRoot, tunnelCertDir / "float.jks")
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").getCertificateAndKeyPair(X509Utilities.CORDA_ROOT_CA, password)
createTLSKeystore("bridge", root, artemisCertDir / "bridge.jks")
createTLSKeystore("artemis", root, artemisCertDir / "artemis.jks")
createTLSKeystore("artemis-client", root, artemisCertDir / "artemis-client.jks")
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)
if (password == DEFAULT_PASSWORD) {
println("Password is defaulted to $DEFAULT_PASSWORD, please change the keystores password using java keytool.")
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.")
}
}
return ExitCodes.SUCCESS
}
private fun createRootKeystore(commonName: String, keystorePath: Path, trustStorePath: Path): X509KeyStore {
private 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, password, createNew = true)
val keystore = X509KeyStore.fromFile(keystorePath, storePassword, createNew = true)
keystore.update {
setPrivateKey(X509Utilities.CORDA_ROOT_CA, key.private, listOf(rootCert), password)
setPrivateKey(X509Utilities.CORDA_ROOT_CA, key.private, listOf(rootCert), entryPassword)
}
println("$commonName keystore created in $keystorePath.")
X509KeyStore.fromFile(trustStorePath, password, createNew = true).setCertificate(X509Utilities.CORDA_ROOT_CA, rootCert)
X509KeyStore.fromFile(trustStorePath, trustStorePassword, createNew = true).setCertificate(X509Utilities.CORDA_ROOT_CA, rootCert)
println("$commonName truststore created in $trustStorePath.")
return keystore
}
private fun createTLSKeystore(serviceName: String, root: CertificateAndKeyPair, keystorePath: Path) {
private 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, password, createNew = true).update {
setPrivateKey(X509Utilities.CORDA_CLIENT_TLS, key.private, listOf(cert, root.certificate), password)
X509KeyStore.fromFile(keystorePath, storePassword, createNew = true).update {
setPrivateKey(X509Utilities.CORDA_CLIENT_TLS, key.private, listOf(cert, root.certificate), entryPassword)
}
println("Internal TLS keystore for '$serviceName' created in $keystorePath.")
}

View File

@ -4,13 +4,16 @@ import net.corda.cliutils.CommonCliConstants.BASE_DIR
import net.corda.core.internal.div
import net.corda.core.internal.exists
import net.corda.nodeapi.internal.crypto.X509KeyStore
import net.corda.nodeapi.internal.crypto.X509Utilities
import org.assertj.core.api.Assertions
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_TLS
import net.corda.nodeapi.internal.crypto.getCertificateAndKeyPair
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import picocli.CommandLine
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
class InternalKeystoreGeneratorTest {
@ -23,40 +26,52 @@ class InternalKeystoreGeneratorTest {
@Test
fun `generate keystores correctly`() {
val workingDirectory = tempFolder.root.toPath()
CommandLine.populateCommand(generator, BASE_DIR, workingDirectory.toString())
Assertions.assertThat(generator.baseDirectory).isEqualTo(workingDirectory)
val keyStorePassword = "keyStorePassword"
val entryPassword = "entryPassword"
val trustStorePassword = "trustStorePassword"
CommandLine.populateCommand(generator,
BASE_DIR, workingDirectory.toString(),
"--keyStorePassword", keyStorePassword,
"--entryPassword", entryPassword,
"--trustStorePassword", trustStorePassword)
assertEquals(workingDirectory, generator.baseDirectory)
assertEquals(keyStorePassword, generator.keyStorePassword)
assertEquals(entryPassword, generator.entryPassword)
assertEquals(trustStorePassword, generator.trustStorePassword)
generator.runProgram()
listOf("float.jks").map { workingDirectory / "tunnel" / it }.forEach {
listOf("float.jks", "bridge.jks").map { workingDirectory / "tunnel" / it }.forEach {
assertTrue(it.exists())
assertTrue(X509KeyStore.fromFile(it, generator.password).contains(X509Utilities.CORDA_CLIENT_TLS))
assertTrue(X509KeyStore.fromFile(it, generator.password).internal.isKeyEntry(X509Utilities.CORDA_CLIENT_TLS))
assertTrue(X509KeyStore.fromFile(it, keyStorePassword).contains(CORDA_CLIENT_TLS))
assertTrue(X509KeyStore.fromFile(it, keyStorePassword).internal.isKeyEntry(CORDA_CLIENT_TLS))
assertNotNull(X509KeyStore.fromFile(it, keyStorePassword).internal.getCertificateAndKeyPair(CORDA_CLIENT_TLS, entryPassword))
}
X509KeyStore.fromFile(workingDirectory / "tunnel" / "tunnel-root.jks", generator.password).update {
assertTrue(contains(X509Utilities.CORDA_ROOT_CA))
assertTrue(internal.isKeyEntry(X509Utilities.CORDA_ROOT_CA))
X509KeyStore.fromFile(workingDirectory / "tunnel" / "tunnel-root.jks", keyStorePassword).update {
assertTrue(contains(CORDA_ROOT_CA))
assertTrue(internal.isKeyEntry(CORDA_ROOT_CA))
assertNotNull(internal.getCertificateAndKeyPair(CORDA_ROOT_CA, entryPassword))
}
X509KeyStore.fromFile(workingDirectory / "tunnel" / "tunnel-truststore.jks", generator.password).update {
assertTrue(contains(X509Utilities.CORDA_ROOT_CA))
assertFalse(internal.isKeyEntry(X509Utilities.CORDA_ROOT_CA))
X509KeyStore.fromFile(workingDirectory / "tunnel" / "tunnel-truststore.jks", trustStorePassword).update {
assertTrue(internal.isCertificateEntry(CORDA_ROOT_CA))
assertFalse(internal.isKeyEntry(CORDA_ROOT_CA))
}
listOf("bridge.jks", "artemis.jks", "artemis-client.jks").map { workingDirectory / "artemis" / it }.forEach {
assertTrue(it.exists())
assertTrue(X509KeyStore.fromFile(it, generator.password).contains(X509Utilities.CORDA_CLIENT_TLS))
assertTrue(X509KeyStore.fromFile(it, generator.password).internal.isKeyEntry(X509Utilities.CORDA_CLIENT_TLS))
assertTrue(X509KeyStore.fromFile(it, keyStorePassword).contains(CORDA_CLIENT_TLS))
assertTrue(X509KeyStore.fromFile(it, keyStorePassword).internal.isKeyEntry(CORDA_CLIENT_TLS))
}
X509KeyStore.fromFile(workingDirectory / "artemis" / "artemis-root.jks", generator.password).update {
assertTrue(contains(X509Utilities.CORDA_ROOT_CA))
assertTrue(internal.isKeyEntry(X509Utilities.CORDA_ROOT_CA))
X509KeyStore.fromFile(workingDirectory / "artemis" / "artemis-root.jks", keyStorePassword).update {
assertTrue(contains(CORDA_ROOT_CA))
assertTrue(internal.isKeyEntry(CORDA_ROOT_CA))
}
X509KeyStore.fromFile(workingDirectory / "artemis" / "artemis-truststore.jks", generator.password).update {
assertTrue(contains(X509Utilities.CORDA_ROOT_CA))
assertFalse(internal.isKeyEntry(X509Utilities.CORDA_ROOT_CA))
X509KeyStore.fromFile(workingDirectory / "artemis" / "artemis-truststore.jks", trustStorePassword).update {
assertTrue(contains(CORDA_ROOT_CA))
assertFalse(internal.isKeyEntry(CORDA_ROOT_CA))
}
}
}