Merge pull request #1495 from corda/vkolomeyko/os-ent-merge

OS->Ent merge
This commit is contained in:
Viktor Kolomeyko 2018-10-22 15:15:02 +01:00 committed by GitHub
commit 51e0822d59
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 165 additions and 327 deletions

View File

@ -130,7 +130,7 @@ class BridgeSmokeTest {
setPrivateKey(
X509Utilities.CORDA_CLIENT_TLS,
tlsKeyPair.private,
listOf(tlsCert, nodeCaCert, intermediateCa.certificate, rootCert))
listOf(tlsCert, nodeCaCert, intermediateCa.certificate, rootCert), sslKeyStore.entryPassword)
}
}

View File

@ -62,10 +62,10 @@ class AMQPListenerTest {
val trustStoreBytes = bridgeConfig.p2pSslOptions.trustStore.path.readAll()
// start listening
amqpListenerService.provisionKeysAndActivate(keyStoreBytes,
bridgeConfig.p2pSslOptions.keyStore.password.toCharArray(),
bridgeConfig.p2pSslOptions.keyStore.password.toCharArray(),
bridgeConfig.p2pSslOptions.keyStore.storePassword.toCharArray(),
bridgeConfig.p2pSslOptions.keyStore.entryPassword.toCharArray(),
trustStoreBytes,
bridgeConfig.p2pSslOptions.trustStore.password.toCharArray())
bridgeConfig.p2pSslOptions.trustStore.storePassword.toCharArray())
// Fire lots of activity to prove we are good
assertEquals(TestAuditService.AuditEvent.STATUS_CHANGE, auditFollower.next())
assertEquals(true, amqpListenerService.active)
@ -137,21 +137,21 @@ class AMQPListenerTest {
val trustStoreBytes = bridgeConfig.p2pSslOptions.trustStore.path.readAll()
// start listening
amqpListenerService.provisionKeysAndActivate(keyStoreBytes,
bridgeConfig.p2pSslOptions.keyStore.password.toCharArray(),
bridgeConfig.p2pSslOptions.keyStore.password.toCharArray(),
bridgeConfig.p2pSslOptions.keyStore.storePassword.toCharArray(),
bridgeConfig.p2pSslOptions.keyStore.entryPassword.toCharArray(),
trustStoreBytes,
bridgeConfig.p2pSslOptions.trustStore.password.toCharArray())
bridgeConfig.p2pSslOptions.trustStore.storePassword.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 clientKeyStoreRaw = X509KeyStore("password")
clientKeyStoreRaw.setPrivateKey("TLS_CERT", clientKeys.private, listOf(clientCert))
val clientKeyStore = CertificateStore.of(clientKeyStoreRaw, "password")
clientKeyStoreRaw.setPrivateKey("TLS_CERT", clientKeys.private, listOf(clientCert), "password")
val clientKeyStore = CertificateStore.of(clientKeyStoreRaw, "password", "password")
val clientTrustStoreRaw = X509KeyStore("password")
clientTrustStoreRaw.setCertificate("TLS_ROOT", clientCert)
val clientTrustStore = CertificateStore.of(clientTrustStoreRaw, "password")
val clientTrustStore = CertificateStore.of(clientTrustStoreRaw, "password", "password")
val amqpConfig = object : AMQPConfiguration {
override val keyStore = clientKeyStore
override val trustStore = clientTrustStore

View File

@ -22,8 +22,8 @@ data class BridgeSSLConfigurationImpl(private val sslKeystore: Path,
private val crlCheckSoftFail: Boolean,
override val useOpenSsl: Boolean = false) : BridgeSSLConfiguration {
override val keyStore = FileBasedCertificateStoreSupplier(sslKeystore, keyStorePassword)
override val trustStore = FileBasedCertificateStoreSupplier(trustStoreFile, trustStorePassword)
override val keyStore = FileBasedCertificateStoreSupplier(sslKeystore, keyStorePassword, keyStorePassword)
override val trustStore = FileBasedCertificateStoreSupplier(trustStoreFile, trustStorePassword, trustStorePassword)
}
data class BridgeOutboundConfigurationImpl(override val artemisBrokerAddress: NetworkHostAndPort,
@ -81,9 +81,9 @@ data class FirewallConfigurationImpl(
}
private val p2pKeystorePath = sslKeystore
private val p2pKeyStore = FileBasedCertificateStoreSupplier(p2pKeystorePath, keyStorePassword)
private val p2pKeyStore = FileBasedCertificateStoreSupplier(p2pKeystorePath, keyStorePassword, keyStorePassword)
private val p2pTrustStoreFilePath = trustStoreFile
private val p2pTrustStore = FileBasedCertificateStoreSupplier(p2pTrustStoreFilePath, trustStorePassword)
private val p2pTrustStore = FileBasedCertificateStoreSupplier(p2pTrustStoreFilePath, trustStorePassword, trustStorePassword)
override val p2pSslOptions: MutualSslConfiguration = SslConfiguration.mutual(p2pKeyStore, p2pTrustStore)
}

View File

@ -48,8 +48,8 @@ class BridgeAMQPListenerServiceImpl(val conf: FirewallConfiguration,
require(active) { "AuditService must be active" }
require(keyStorePassword !== keyStorePrivateKeyPassword) { "keyStorePassword and keyStorePrivateKeyPassword must reference distinct arrays!" }
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 keyStore = CertificateStore.of(loadKeyStore(keyStoreBytes, keyStorePassword), java.lang.String.valueOf(keyStorePrivateKeyPassword), java.lang.String.valueOf(keyStorePrivateKeyPassword)).also { wipeKeys(keyStoreBytes, keyStorePassword) }
val trustStore = CertificateStore.of(loadKeyStore(trustStoreBytes, trustStorePassword), java.lang.String.valueOf(trustStorePassword), java.lang.String.valueOf(trustStorePassword)).also { wipeKeys(trustStoreBytes, trustStorePassword) }
val bindAddress = conf.inboundConfig!!.listeningAddress
val amqpConfiguration = object : AMQPConfiguration {
override val keyStore = keyStore

View File

@ -35,10 +35,10 @@ class InProcessBridgeReceiverService(val conf: FirewallConfiguration,
val keyStoreBytes = sslConfiguration.keyStore.path.readAll()
val trustStoreBytes = sslConfiguration.trustStore.path.readAll()
amqpListenerService.provisionKeysAndActivate(keyStoreBytes,
sslConfiguration.keyStore.password.toCharArray(),
sslConfiguration.keyStore.password.toCharArray(),
sslConfiguration.keyStore.storePassword.toCharArray(),
sslConfiguration.keyStore.entryPassword.toCharArray(),
trustStoreBytes,
sslConfiguration.trustStore.password.toCharArray())
sslConfiguration.trustStore.storePassword.toCharArray())
} else {
if (amqpListenerService.running) {
amqpListenerService.wipeKeysAndDeactivate()

View File

@ -118,7 +118,7 @@ class TunnelingBridgeReceiverService(val conf: FirewallConfiguration,
freshKeyStorePassword,
freshKeyStoreKeyPassword,
trustStoreBytes,
floatListenerSSLConfiguration.trustStore.password.toCharArray())
floatListenerSSLConfiguration.trustStore.storePassword.toCharArray())
val amqpActivateMessage = amqpControlClient!!.createMessage(activateMessage.serialize(context = SerializationDefaults.P2P_CONTEXT).bytes,
FLOAT_CONTROL_TOPIC,
expectedCertificateSubject.toString(),
@ -141,7 +141,7 @@ class TunnelingBridgeReceiverService(val conf: FirewallConfiguration,
// Recode KeyStore to use a fresh random password for entries and overall
private fun recodeKeyStore(sslConfiguration: MutualSslConfiguration): Triple<CharArray, CharArray, ByteArray> {
val keyStoreOriginal = sslConfiguration.keyStore.get().value.internal
val originalKeyStorePassword = sslConfiguration.keyStore.password.toCharArray()
val originalKeyStorePassword = sslConfiguration.keyStore.storePassword.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()) {

View File

@ -76,7 +76,8 @@ fun MutualSslConfiguration.createBridgeKeyStores(legalName: CordaX500Name,
setPrivateKey(
X509Utilities.CORDA_CLIENT_TLS,
tlsKeyPair.private,
listOf(tlsCert, nodeCaCert, intermediateCa.certificate, rootCert))
listOf(tlsCert, nodeCaCert, intermediateCa.certificate, rootCert),
sslKeyStore.entryPassword)
}
}

View File

@ -73,13 +73,13 @@ class ConfigTest {
val config = createAndLoadConfigFromResource(tempFolder.root.toPath(), configResource)
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)
assertEquals("outboundkeypassword", config.outboundConfig!!.customSSLConfiguration!!.keyStore.storePassword)
assertEquals("outboundtrustpassword", config.outboundConfig!!.customSSLConfiguration!!.trustStore.storePassword)
assertNull(config.inboundConfig)
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)
assertEquals("tunnelkeypassword", config.bridgeInnerConfig!!.customSSLConfiguration!!.keyStore.storePassword)
assertEquals("tunneltrustpassword", config.bridgeInnerConfig!!.customSSLConfiguration!!.trustStore.storePassword)
assertNull(config.floatOuterConfig)
}
@ -89,13 +89,13 @@ class ConfigTest {
val config = createAndLoadConfigFromResource(tempFolder.root.toPath(), configResource)
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)
assertEquals("inboundkeypassword", config.inboundConfig!!.customSSLConfiguration!!.keyStore.storePassword)
assertEquals("inboundtrustpassword", config.inboundConfig!!.customSSLConfiguration!!.trustStore.storePassword)
assertNull(config.outboundConfig)
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)
assertEquals("tunnelkeypassword", config.floatOuterConfig!!.customSSLConfiguration!!.keyStore.storePassword)
assertEquals("tunneltrustpassword", config.floatOuterConfig!!.customSSLConfiguration!!.trustStore.storePassword)
assertNull(config.bridgeInnerConfig)
}

View File

@ -11,23 +11,31 @@ import org.bouncycastle.asn1.x509.GeneralSubtree
import org.bouncycastle.asn1.x509.NameConstraints
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.junit.Test
import java.security.UnrecoverableKeyException
import java.security.cert.CertPathValidator
import java.security.cert.CertPathValidatorException
import java.security.cert.PKIXParameters
import javax.security.auth.x500.X500Principal
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertTrue
class X509NameConstraintsTest {
companion object {
private const val storePassword = "storePassword"
private const val keyPassword = "entryPassword"
}
private fun makeKeyStores(subjectName: X500Name, nameConstraints: NameConstraints): Pair<X509KeyStore, X509KeyStore> {
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
val trustStore = X509KeyStore("password").apply {
val trustStore = X509KeyStore(storePassword).apply {
setCertificate(X509Utilities.CORDA_ROOT_CA, rootCa.certificate)
}
val keyStore = X509KeyStore("password").apply {
val keyStore = X509KeyStore(storePassword).apply {
val nodeCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val nodeCaCert = X509Utilities.createCertificate(
CertificateType.NODE_CA,
@ -43,7 +51,7 @@ class X509NameConstraintsTest {
nodeCaKeyPair,
X500Principal(subjectName.encoded),
tlsKeyPair.public)
setPrivateKey(X509Utilities.CORDA_CLIENT_TLS, tlsKeyPair.private, listOf(tlsCert, nodeCaCert, intermediateCa.certificate, rootCa.certificate))
setPrivateKey(X509Utilities.CORDA_CLIENT_TLS, tlsKeyPair.private, listOf(tlsCert, nodeCaCert, intermediateCa.certificate, rootCa.certificate), keyPassword)
}
return Pair(keyStore, trustStore)
@ -90,7 +98,6 @@ class X509NameConstraintsTest {
.map { GeneralSubtree(GeneralName(X500Name(it))) }.toTypedArray()
val nameConstraints = NameConstraints(acceptableNames, arrayOf())
Crypto.ECDSA_SECP256R1_SHA256
val pathValidator = CertPathValidator.getInstance("PKIX", BouncyCastleProvider.PROVIDER_NAME)
assertFailsWith(CertPathValidatorException::class) {
@ -127,4 +134,20 @@ class X509NameConstraintsTest {
true
}
}
@Test
fun `test private key retrieval`() {
val acceptableNames = listOf("CN=Bank A TLS, UID=", "O=Bank A")
.map { GeneralSubtree(GeneralName(X500Name(it))) }.toTypedArray()
val nameConstraints = NameConstraints(acceptableNames, arrayOf())
val (keystore, _) = makeKeyStores(X500Name("CN=Bank A"), nameConstraints)
val privateKey = keystore.getPrivateKey(X509Utilities.CORDA_CLIENT_TLS, keyPassword)
assertEquals(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME.algorithmName, privateKey.algorithm)
assertFailsWith(UnrecoverableKeyException::class) {
keystore.getPrivateKey(X509Utilities.CORDA_CLIENT_TLS, "gibberish")
}
}
}

View File

@ -161,9 +161,9 @@ class RaftUniquenessProvider(
.withSsl()
.withSslProtocol(SslProtocol.TLSv1_2)
.withKeyStorePath(config.keyStore.path.toString())
.withKeyStorePassword(config.keyStore.password)
.withKeyStorePassword(config.keyStore.storePassword)
.withTrustStorePath(config.trustStore.path.toString())
.withTrustStorePassword(config.trustStore.password)
.withTrustStorePassword(config.trustStore.storePassword)
.build()
}

Binary file not shown.

View File

@ -30,9 +30,9 @@ 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 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 signingCertStore = FileBasedCertificateStoreSupplier(certificatesDirectory / "nodekeystore.jks", DEV_CA_KEY_STORE_PASS, DEV_CA_KEY_STORE_PASS)
val p2pKeyStore = FileBasedCertificateStoreSupplier(certificatesDirectory / "sslkeystore.jks", DEV_CA_KEY_STORE_PASS, DEV_CA_KEY_STORE_PASS)
val p2pTrustStore = FileBasedCertificateStoreSupplier(certificatesDirectory / "truststore.jks", DEV_CA_TRUST_STORE_PASS, DEV_CA_TRUST_STORE_PRIVATE_KEY_PASS)
val p2pSslConfig = SslConfiguration.mutual(p2pKeyStore, p2pTrustStore)
certificatesDirectory.createDirectories()
@ -77,13 +77,16 @@ object DevIdentityGenerator {
publicKey)
}
val distServKeyStoreFile = (nodeDir / "certificates").createDirectories() / "distributedService.jks"
X509KeyStore.fromFile(distServKeyStoreFile, "cordacadevpass", createNew = true).update {
X509KeyStore.fromFile(distServKeyStoreFile, DEV_CA_KEY_STORE_PASS, createNew = true).update {
setCertificate("$DISTRIBUTED_NOTARY_ALIAS_PREFIX-composite-key", compositeKeyCert)
setPrivateKey(
"$DISTRIBUTED_NOTARY_ALIAS_PREFIX-private-key",
keyPair.private,
listOf(serviceKeyCert, DEV_INTERMEDIATE_CA.certificate, DEV_ROOT_CA.certificate),
"cordacadevkeypass")
DEV_CA_KEY_STORE_PASS // Unfortunately we have to use the same password for private key due to Artemis limitation, for more details please see:
// org.apache.activemq.artemis.core.remoting.impl.ssl.SSLSupport.loadKeyManagerFactory
// where it is calling `KeyManagerFactory.init()` with store password
/*DEV_CA_PRIVATE_KEY_PASS*/)
}
}
}

View File

@ -27,7 +27,8 @@ fun CertificateStore.registerDevSigningCertificates(legalName: CordaX500Name,
devNodeCa: CertificateAndKeyPair = createDevNodeCa(intermediateCa, legalName)) {
update {
setPrivateKey(X509Utilities.CORDA_CLIENT_CA, devNodeCa.keyPair.private, listOf(devNodeCa.certificate, intermediateCa.certificate, rootCert))
setPrivateKey(X509Utilities.CORDA_CLIENT_CA, devNodeCa.keyPair.private, listOf(devNodeCa.certificate, intermediateCa.certificate, rootCert),
this@registerDevSigningCertificates.entryPassword)
}
}
@ -39,7 +40,8 @@ fun CertificateStore.registerDevP2pCertificates(legalName: CordaX500Name,
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))
setPrivateKey(X509Utilities.CORDA_CLIENT_TLS, tlsKeyPair.private, listOf(tlsCert, devNodeCa.certificate, intermediateCa.certificate, rootCert),
this@registerDevP2pCertificates.entryPassword)
}
}
@ -47,15 +49,14 @@ fun CertificateStore.storeLegalIdentity(alias: String, keyPair: KeyPair = Crypto
val identityCertPath = query {
val nodeCaCertPath = getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
// Assume key password = store password.
val nodeCaCertAndKeyPair = getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)
val nodeCaCertAndKeyPair = getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, this@storeLegalIdentity.entryPassword)
// 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)
setPrivateKey(alias, keyPair.private, identityCertPath, this@storeLegalIdentity.entryPassword)
}
return PartyAndCertificate(X509Utilities.buildCertPath(identityCertPath))
}
@ -96,6 +97,7 @@ const val DEV_CA_KEY_STORE_FILE: String = "cordadevcakeys.jks"
const val DEV_CA_KEY_STORE_PASS: String = "cordacadevpass"
const val DEV_CA_TRUST_STORE_FILE: String = "cordatruststore.jks"
const val DEV_CA_TRUST_STORE_PASS: String = "trustpass"
const val DEV_CA_TRUST_STORE_PRIVATE_KEY_PASS: String = "trustpasskeypass"
// We need a class so that we can get hold of the class loader
internal object DevCaHelper {
@ -104,6 +106,8 @@ internal object DevCaHelper {
}
}
fun loadDevCaKeyStore(classLoader: ClassLoader = DevCaHelper::class.java.classLoader): CertificateStore = CertificateStore.fromResource("certificates/$DEV_CA_KEY_STORE_FILE", DEV_CA_KEY_STORE_PASS, classLoader)
fun loadDevCaKeyStore(classLoader: ClassLoader = DevCaHelper::class.java.classLoader): CertificateStore = CertificateStore.fromResource(
"certificates/$DEV_CA_KEY_STORE_FILE", DEV_CA_KEY_STORE_PASS, DEV_CA_PRIVATE_KEY_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)
fun loadDevCaTrustStore(classLoader: ClassLoader = DevCaHelper::class.java.classLoader): CertificateStore = CertificateStore.fromResource(
"certificates/$DEV_CA_TRUST_STORE_FILE", DEV_CA_TRUST_STORE_PASS, DEV_CA_TRUST_STORE_PRIVATE_KEY_PASS, classLoader)

View File

@ -14,17 +14,18 @@ interface CertificateStore : Iterable<Pair<String, X509Certificate>> {
companion object {
fun of(store: X509KeyStore, password: String): CertificateStore = DelegatingCertificateStore(store, password)
fun of(store: X509KeyStore, password: String, entryPassword: String): CertificateStore = DelegatingCertificateStore(store, password, entryPassword)
fun fromFile(storePath: Path, password: String, createNew: Boolean): CertificateStore = DelegatingCertificateStore(X509KeyStore.fromFile(storePath, password, createNew), password)
fun fromFile(storePath: Path, password: String, entryPassword: String, createNew: Boolean): CertificateStore = DelegatingCertificateStore(X509KeyStore.fromFile(storePath, password, createNew), password, entryPassword)
fun fromInputStream(stream: InputStream, password: String): CertificateStore = DelegatingCertificateStore(X509KeyStore.fromInputStream(stream, password), password)
fun fromInputStream(stream: InputStream, password: String, entryPassword: String): CertificateStore = DelegatingCertificateStore(X509KeyStore.fromInputStream(stream, password), password, entryPassword)
fun fromResource(storeResourceName: String, password: String, classLoader: ClassLoader = Thread.currentThread().contextClassLoader): CertificateStore = fromInputStream(classLoader.getResourceAsStream(storeResourceName), password)
fun fromResource(storeResourceName: String, password: String, entryPassword: String, classLoader: ClassLoader = Thread.currentThread().contextClassLoader): CertificateStore = fromInputStream(classLoader.getResourceAsStream(storeResourceName), password, entryPassword)
}
val value: X509KeyStore
val password: String
val entryPassword: String
fun writeTo(stream: OutputStream) = value.internal.store(stream, password.toCharArray())
@ -80,4 +81,4 @@ interface CertificateStore : Iterable<Pair<String, X509Certificate>> {
}
}
private class DelegatingCertificateStore(override val value: X509KeyStore, override val password: String) : CertificateStore
private class DelegatingCertificateStore(override val value: X509KeyStore, override val password: String, override val entryPassword: String) : CertificateStore

View File

@ -18,7 +18,7 @@ interface CertificateStoreSupplier {
}
// 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 {
class FileBasedCertificateStoreSupplier(val path: Path, val storePassword: String, val entryPassword: String) : CertificateStoreSupplier {
override fun get(createNew: Boolean) = CertificateStore.fromFile(path, password, createNew)
override fun get(createNew: Boolean) = CertificateStore.fromFile(path, storePassword, entryPassword, createNew)
}

View File

@ -151,7 +151,7 @@ private fun Config.getSingleValue(path: String, type: KType, onUnknownKeys: (Set
private fun ConfigException.Missing.relative(path: String, nestedPath: String?): ConfigException.Missing {
return when {
nestedPath != null -> throw ConfigException.Missing("$nestedPath.$path")
nestedPath != null -> throw ConfigException.Missing("$nestedPath.$path", this)
else -> this
}
}

View File

@ -53,17 +53,17 @@ class X509KeyStore private constructor(val internal: KeyStore, private val store
return uncheckedCast(certArray.asList())
}
fun getCertificateAndKeyPair(alias: String, keyPassword: String = storePassword): CertificateAndKeyPair {
fun getCertificateAndKeyPair(alias: String, keyPassword: String): CertificateAndKeyPair {
val cert = getCertificate(alias)
val publicKey = Crypto.toSupportedPublicKey(cert.publicKey)
return CertificateAndKeyPair(cert, KeyPair(publicKey, getPrivateKey(alias, keyPassword)))
}
fun getPrivateKey(alias: String, keyPassword: String = storePassword): PrivateKey {
fun getPrivateKey(alias: String, keyPassword: String): PrivateKey {
return internal.getSupportedKey(alias, keyPassword)
}
fun setPrivateKey(alias: String, key: PrivateKey, certificates: List<X509Certificate>, keyPassword: String = storePassword) {
fun setPrivateKey(alias: String, key: PrivateKey, certificates: List<X509Certificate>, keyPassword: String) {
internal.setKeyEntry(alias, key, keyPassword.toCharArray(), certificates.toTypedArray())
}

View File

@ -238,7 +238,9 @@ internal fun splitKeystore(config: AMQPConfiguration): Map<String, CertHoldingKe
}.toMap()
}
fun KeyManagerFactory.init(keyStore: CertificateStore) = init(keyStore.value.internal, keyStore.password.toCharArray())
// As per Javadoc in: https://docs.oracle.com/javase/8/docs/api/javax/net/ssl/KeyManagerFactory.html `init` method
// 2nd parameter `password` - the password for recovering keys in the KeyStore
fun KeyManagerFactory.init(keyStore: CertificateStore) = init(keyStore.value.internal, keyStore.entryPassword.toCharArray())
fun TrustManagerFactory.init(trustStore: CertificateStore) = init(trustStore.value.internal)

View File

@ -247,8 +247,9 @@ class X509UtilitiesTest {
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 = signingCertStore.get().value
val (serverCert, serverKeyPair) = serverKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)
val certStore = signingCertStore.get()
val serverKeyStore = certStore.value
val (serverCert, serverKeyPair) = serverKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, certStore.entryPassword)
serverCert.checkValidity()
serverCert.verify(intermediateCa.certificate.publicKey)
@ -256,7 +257,7 @@ class X509UtilitiesTest {
// Load back SSL certificate
val sslKeyStoreReloaded = p2pSslConfig.keyStore.get()
val (sslCert) = sslKeyStoreReloaded.query { getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_TLS, p2pSslConfig.keyStore.password) }
val (sslCert) = sslKeyStoreReloaded.query { getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_TLS, sslKeyStoreReloaded.entryPassword) }
sslCert.checkValidity()
sslCert.verify(serverCert.publicKey)

View File

@ -20,8 +20,10 @@ class SSLHelperTest {
val keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
keyManagerFactory.init(CertificateStore.fromFile(sslConfig.keyStore.path, sslConfig.keyStore.password, false))
trustManagerFactory.init(initialiseTrustStoreAndEnableCrlChecking(CertificateStore.fromFile(sslConfig.trustStore.path, sslConfig.trustStore.password, false), false))
val keyStore = sslConfig.keyStore
keyManagerFactory.init(CertificateStore.fromFile(keyStore.path, keyStore.storePassword, keyStore.entryPassword, false))
val trustStore = sslConfig.trustStore
trustManagerFactory.init(initialiseTrustStoreAndEnableCrlChecking(CertificateStore.fromFile(trustStore.path, trustStore.storePassword, trustStore.entryPassword, false), false))
val sslHandler = createClientSslHelper(NetworkHostAndPort("localhost", 1234), setOf(legalName), keyManagerFactory, trustManagerFactory)
val legalNameHash = SecureHash.sha256(legalName.toString()).toString().take(32).toLowerCase()

View File

@ -36,7 +36,7 @@ class NodeKeystoreCheckTest : IntegrationTest() {
}
@Test
fun `node should throw exception if cert path doesn't chain to the trust root`() {
fun `node should throw exception if cert path does not chain to the trust root`() {
driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList())) {
// Create keystores.
val keystorePassword = "password"
@ -60,9 +60,9 @@ class NodeKeystoreCheckTest : IntegrationTest() {
// Self signed root.
val badRootKeyPair = Crypto.generateKeyPair()
val badRoot = X509Utilities.createSelfSignedCACertificate(X500Principal("O=Bad Root,L=Lodnon,C=GB"), badRootKeyPair)
val nodeCA = getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)
val nodeCA = getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, signingCertStore.entryPassword)
val badNodeCACert = X509Utilities.createCertificate(CertificateType.NODE_CA, badRoot, badRootKeyPair, ALICE_NAME.x500Principal, nodeCA.keyPair.public)
setPrivateKey(X509Utilities.CORDA_CLIENT_CA, nodeCA.keyPair.private, listOf(badNodeCACert, badRoot))
setPrivateKey(X509Utilities.CORDA_CLIENT_CA, nodeCA.keyPair.private, listOf(badNodeCACert, badRoot), signingCertStore.entryPassword)
}
assertThatThrownBy {

View File

@ -423,17 +423,17 @@ class CertificateRevocationListNodeTests {
val signingCertificateStore = first
val p2pSslConfiguration = second
val nodeKeyStore = signingCertificateStore.get()
val (nodeCert, nodeKeys) = nodeKeyStore.query { getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA) }
val (nodeCert, nodeKeys) = nodeKeyStore.query { getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, nodeKeyStore.entryPassword) }
val newNodeCert = replaceCrlDistPointCaCertificate(nodeCert, CertificateType.NODE_CA, INTERMEDIATE_CA.keyPair, nodeCaCrlDistPoint)
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)
setPrivateKey(X509Utilities.CORDA_CLIENT_CA, nodeKeys.private, nodeCertChain, nodeKeyStore.entryPassword)
}
val sslKeyStore = p2pSslConfiguration.keyStore.get()
val (tlsCert, tlsKeys) = sslKeyStore.query { getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_TLS) }
val (tlsCert, tlsKeys) = sslKeyStore.query { getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_TLS, sslKeyStore.entryPassword) }
val newTlsCert = replaceCrlDistPointCaCertificate(tlsCert, CertificateType.TLS, nodeKeys, tlsCrlDistPoint, X500Name.getInstance(ROOT_CA.certificate.subjectX500Principal.encoded))
val sslCertChain = listOf(newTlsCert, newNodeCert, INTERMEDIATE_CA.certificate, *sslKeyStore.query { getCertificateChain(X509Utilities.CORDA_CLIENT_TLS) }.drop(3).toTypedArray())
@ -441,7 +441,7 @@ class CertificateRevocationListNodeTests {
internal.deleteEntry(X509Utilities.CORDA_CLIENT_TLS)
}
sslKeyStore.update {
setPrivateKey(X509Utilities.CORDA_CLIENT_TLS, tlsKeys.private, sslCertChain)
setPrivateKey(X509Utilities.CORDA_CLIENT_TLS, tlsKeys.private, sslCertChain, sslKeyStore.entryPassword)
}
return newNodeCert
}

View File

@ -105,11 +105,11 @@ class MQSecurityAsNodeTest : P2PMQSecurityTest() {
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))
setPrivateKey(X509Utilities.CORDA_CLIENT_CA, clientKeyPair.private, listOf(clientCACert, DEV_INTERMEDIATE_CA.certificate, DEV_ROOT_CA.certificate), signingCertStore.entryPassword)
}
p2pSslConfig.keyStore.get(createNew = true).update {
setPrivateKey(X509Utilities.CORDA_CLIENT_TLS, tlsKeyPair.private, listOf(clientTLSCert, clientCACert, DEV_INTERMEDIATE_CA.certificate, DEV_ROOT_CA.certificate))
setPrivateKey(X509Utilities.CORDA_CLIENT_TLS, tlsKeyPair.private, listOf(clientTLSCert, clientCACert, DEV_INTERMEDIATE_CA.certificate, DEV_ROOT_CA.certificate), p2pSslConfig.keyStore.entryPassword)
}
val attacker = clientTo(alice.node.configuration.p2pAddress, p2pSslConfig)

View File

@ -892,7 +892,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
keyStore.storeLegalIdentity(privateKeyAlias, generateKeyPair())
}
val (x509Cert, keyPair) = keyStore.query { getCertificateAndKeyPair(privateKeyAlias) }
val (x509Cert, keyPair) = keyStore.query { getCertificateAndKeyPair(privateKeyAlias, keyStore.entryPassword) }
// TODO: Use configuration to indicate composite key should be used instead of public key for the identity.
val compositeKeyAlias = "$id-composite-key"

View File

@ -101,9 +101,9 @@ fun MutualSslConfiguration.configureDevKeyAndTrustStores(myLegalName: CordaX500N
}
if (keyStore.getOptional() == null || signingCertificateStore.getOptional() == null) {
val signingKeyStore = FileBasedCertificateStoreSupplier(signingCertificateStore.path, signingCertificateStore.password).get(true).also { it.registerDevSigningCertificates(myLegalName) }
val signingKeyStore = FileBasedCertificateStoreSupplier(signingCertificateStore.path, signingCertificateStore.storePassword, signingCertificateStore.entryPassword).get(true).also { it.registerDevSigningCertificates(myLegalName) }
FileBasedCertificateStoreSupplier(keyStore.path, keyStore.password).get(true).also { it.registerDevP2pCertificates(myLegalName) }
FileBasedCertificateStoreSupplier(keyStore.path, keyStore.storePassword, keyStore.entryPassword).get(true).also { it.registerDevP2pCertificates(myLegalName) }
// Move distributed service composite key (generated by IdentityGenerator.generateToDisk) to keystore if exists.
val distributedServiceKeystore = certificatesDirectory / "distributedService.jks"
@ -112,7 +112,7 @@ fun MutualSslConfiguration.configureDevKeyAndTrustStores(myLegalName: CordaX500N
signingKeyStore.update {
serviceKeystore.aliases().forEach {
if (serviceKeystore.internal.isKeyEntry(it)) {
setPrivateKey(it, serviceKeystore.getPrivateKey(it, DEV_CA_PRIVATE_KEY_PASS), serviceKeystore.getCertificateChain(it))
setPrivateKey(it, serviceKeystore.getPrivateKey(it, DEV_CA_KEY_STORE_PASS), serviceKeystore.getCertificateChain(it), signingKeyStore.entryPassword)
} else {
setCertificate(it, serviceKeystore.getCertificate(it))
}

View File

@ -316,12 +316,16 @@ 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)
// TODO: There are two implications here:
// 1. "signingCertificateStore" and "p2pKeyStore" have the same passwords. In the future we should re-visit this "rule" and see of they can be made different;
// 2. The passwords for store and for keys in this store are the same, this is due to limitations of Artemis.
override val signingCertificateStore = FileBasedCertificateStoreSupplier(signingCertificateStorePath, keyStorePassword, keyStorePassword)
private val p2pKeyStore = FileBasedCertificateStoreSupplier(p2pKeystorePath, keyStorePassword, keyStorePassword)
private val p2pTrustStoreFilePath: Path get() = certificatesDirectory / "truststore.jks"
private val p2pTrustStore = FileBasedCertificateStoreSupplier(p2pTrustStoreFilePath, trustStorePassword)
private val p2pTrustStore = FileBasedCertificateStoreSupplier(p2pTrustStoreFilePath, trustStorePassword, trustStorePassword)
override val p2pSslOptions: MutualSslConfiguration = SslConfiguration.mutual(p2pKeyStore, p2pTrustStore, useOpenSsl)
override val rpcOptions: NodeRpcOptions

View File

@ -161,7 +161,7 @@ open class NetworkRegistrationHelper(private val certificatesDirectory: Path,
println("Private key '$keyAlias' and certificate stored in node signing keystore.")
}
private fun CertificateStore.loadOrCreateKeyPair(alias: String, privateKeyPassword: String = password): KeyPair {
private fun CertificateStore.loadOrCreateKeyPair(alias: String, entryPassword: 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) {
@ -170,11 +170,11 @@ open class NetworkRegistrationHelper(private val certificatesDirectory: Path,
val selfSignCert = X509Utilities.createSelfSignedCACertificate(myLegalName.x500Principal, keyPair)
// Save to the key store.
with(value) {
setPrivateKey(alias, keyPair.private, listOf(selfSignCert), keyPassword = privateKeyPassword)
setPrivateKey(alias, keyPair.private, listOf(selfSignCert), keyPassword = entryPassword)
save()
}
}
return query { getCertificateAndKeyPair(alias, privateKeyPassword) }.keyPair
return query { getCertificateAndKeyPair(alias, entryPassword) }.keyPair
}
/**
@ -281,7 +281,9 @@ class NodeRegistrationHelper(
}
private fun createSSLKeystore(nodeCAKeyPair: KeyPair, certificates: List<X509Certificate>, tlsCertCrlIssuer: X500Name?) {
config.p2pSslOptions.keyStore.get(createNew = true).update {
val keyStore = config.p2pSslOptions.keyStore
val certificateStore = keyStore.get(createNew = true)
certificateStore.update {
println("Generating SSL certificate for node messaging service.")
val sslKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val sslCert = X509Utilities.createCertificate(
@ -293,9 +295,9 @@ class NodeRegistrationHelper(
crlDistPoint = config.tlsCertCrlDistPoint?.toString(),
crlIssuer = tlsCertCrlIssuer)
logger.info("Generated TLS certificate: $sslCert")
setPrivateKey(CORDA_CLIENT_TLS, sslKeyPair.private, listOf(sslCert) + certificates)
setPrivateKey(CORDA_CLIENT_TLS, sslKeyPair.private, listOf(sslCert) + certificates, certificateStore.entryPassword)
}
println("SSL private key and certificate stored in ${config.p2pSslOptions.keyStore.path}.")
println("SSL private key and certificate stored in ${keyStore.path}.")
}
private fun createTruststore(rootCertificate: X509Certificate) {

View File

@ -887,10 +887,10 @@ class DriverDSLImpl(
config += "baseDirectory" to configuration.baseDirectory.toAbsolutePath().toString()
config += "keyStorePath" to configuration.p2pSslOptions.keyStore.path.toString()
config += "keyStorePassword" to configuration.p2pSslOptions.keyStore.password
config += "keyStorePassword" to configuration.p2pSslOptions.keyStore.storePassword
config += "trustStorePath" to configuration.p2pSslOptions.trustStore.path.toString()
config += "trustStorePassword" to configuration.p2pSslOptions.trustStore.password
config += "trustStorePassword" to configuration.p2pSslOptions.trustStore.storePassword
return config
}

View File

@ -1,215 +0,0 @@
package net.corda.testing.common.internal
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.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
import sun.security.x509.X500Name
import java.nio.file.Files
import java.nio.file.Path
import java.security.KeyPair
import java.security.KeyStore
import java.security.PrivateKey
import java.security.cert.X509Certificate
import java.time.Duration
import java.time.Instant
import java.time.Instant.now
import java.time.temporal.ChronoUnit
import java.util.*
import javax.security.auth.x500.X500Principal
class UnsafeCertificatesFactory(
defaults: Defaults = defaults(),
private val keyType: String = defaults.keyType,
private val signatureAlgorithm: String = defaults.signatureAlgorithm,
private val keySize: Int = defaults.keySize,
private val certificatesValidityWindow: CertificateValidityWindow = defaults.certificatesValidityWindow,
private val keyStoreType: String = defaults.keyStoreType) {
companion object {
private const val KEY_TYPE_RSA = "RSA"
private const val SIG_ALG_SHA_RSA = "SHA1WithRSA"
private const val KEY_SIZE = 1024
private val DEFAULT_DURATION = Duration.of(365, ChronoUnit.DAYS)
private const val DEFAULT_KEYSTORE_TYPE = "JKS"
fun defaults() = Defaults(KEY_TYPE_RSA, SIG_ALG_SHA_RSA, KEY_SIZE, CertificateValidityWindow(now(), DEFAULT_DURATION), DEFAULT_KEYSTORE_TYPE)
}
data class Defaults(
val keyType: String,
val signatureAlgorithm: String,
val keySize: Int,
val certificatesValidityWindow: CertificateValidityWindow,
val keyStoreType: String)
fun createSelfSigned(name: X500Name): UnsafeCertificate = createSelfSigned(name, keyType, signatureAlgorithm, keySize, certificatesValidityWindow)
fun createSelfSigned(name: CordaX500Name) = createSelfSigned(name.asX500Name())
fun createSignedBy(subject: X500Principal, issuer: UnsafeCertificate): UnsafeCertificate = issuer.createSigned(subject, keyType, signatureAlgorithm, keySize, certificatesValidityWindow)
fun createSignedBy(name: CordaX500Name, issuer: UnsafeCertificate): UnsafeCertificate = issuer.createSigned(name, keyType, signatureAlgorithm, keySize, certificatesValidityWindow)
fun newKeyStore(password: String) = UnsafeKeyStore.create(keyStoreType, password)
fun newKeyStores(keyStorePassword: String, trustStorePassword: String): KeyStores = KeyStores(newKeyStore(keyStorePassword), newKeyStore(trustStorePassword))
}
class KeyStores(val keyStore: UnsafeKeyStore, val trustStore: UnsafeKeyStore) {
fun save(directory: Path = Files.createTempDirectory(null)): AutoClosableSSLConfiguration {
val keyStoreFile = keyStore.toTemporaryFile("sslkeystore", directory = directory)
val trustStoreFile = trustStore.toTemporaryFile("truststore", directory = directory)
val sslConfiguration = sslConfiguration(keyStoreFile, trustStoreFile)
return object : AutoClosableSSLConfiguration {
override val value = sslConfiguration
override fun close() {
keyStoreFile.close()
trustStoreFile.close()
}
}
}
private fun sslConfiguration(keyStoreFile: TemporaryFile, trustStoreFile: TemporaryFile): MutualSslConfiguration {
val keyStore = FileBasedCertificateStoreSupplier(keyStoreFile.file, keyStore.password)
val trustStore = FileBasedCertificateStoreSupplier(trustStoreFile.file, trustStore.password)
return SslConfiguration.mutual(keyStore, trustStore)
}
}
interface AutoClosableSSLConfiguration : AutoCloseable {
val value: MutualSslConfiguration
}
typealias KeyStoreEntry = Pair<String, UnsafeCertificate>
data class UnsafeKeyStore(private val delegate: KeyStore, val password: String) : Iterable<KeyStoreEntry> {
companion object {
private const val JKS_TYPE = "JKS"
fun create(type: String, password: String) = UnsafeKeyStore(newKeyStore(type, password), password)
fun createJKS(password: String) = create(JKS_TYPE, password)
}
operator fun plus(entry: KeyStoreEntry) = set(entry.first, entry.second)
override fun iterator(): Iterator<Pair<String, UnsafeCertificate>> = delegate.aliases().toList().map { alias -> alias to get(alias) }.iterator()
operator fun get(alias: String): UnsafeCertificate {
return when {
delegate.isKeyEntry(alias) -> delegate.getCertificateAndKeyPair(alias, password).unsafe()
else -> UnsafeCertificate(delegate.getX509Certificate(alias), null)
}
}
operator fun set(alias: String, certificate: UnsafeCertificate) {
delegate.setCertificateEntry(alias, certificate.value)
delegate.setKeyEntry(alias, certificate.privateKey, password.toCharArray(), arrayOf(certificate.value))
}
fun save(path: Path) = delegate.save(path, password)
fun toTemporaryFile(fileName: String, fileExtension: String? = delegate.type.toLowerCase(), directory: Path): TemporaryFile {
return TemporaryFile("$fileName.$fileExtension", directory).also { save(it.file) }
}
}
class TemporaryFile(fileName: String, val directory: Path) : AutoCloseable {
val file: Path = (directory / fileName).createFile().toAbsolutePath()
init {
file.toFile().deleteOnExit()
}
override fun close() {
file.deleteIfExists()
}
}
data class UnsafeCertificate(val value: X509Certificate, val privateKey: PrivateKey?) {
val keyPair = KeyPair(value.publicKey, privateKey)
val principal: X500Principal get() = value.subjectX500Principal
val issuer: X500Principal get() = value.issuerX500Principal
fun createSigned(subject: X500Principal, keyType: String, signatureAlgorithm: String, keySize: Int, certificatesValidityWindow: CertificateValidityWindow): UnsafeCertificate {
val keyGen = keyGen(keyType, signatureAlgorithm, keySize)
return UnsafeCertificate(X509Utilities.createCertificate(
certificateType = CertificateType.TLS,
issuer = value.subjectX500Principal,
issuerKeyPair = keyPair,
validityWindow = certificatesValidityWindow.datePair,
subject = subject,
subjectPublicKey = keyGen.publicKey
), keyGen.privateKey)
}
fun createSigned(name: CordaX500Name, keyType: String, signatureAlgorithm: String, keySize: Int, certificatesValidityWindow: CertificateValidityWindow) = createSigned(name.x500Principal, keyType, signatureAlgorithm, keySize, certificatesValidityWindow)
}
data class CertificateValidityWindow(val from: Instant, val to: Instant) {
constructor(from: Instant, duration: Duration) : this(from, from.plus(duration))
val duration = Duration.between(from, to)!!
val datePair = Date.from(from) to Date.from(to)
}
private fun createSelfSigned(name: X500Name, keyType: String, signatureAlgorithm: String, keySize: Int, certificatesValidityWindow: CertificateValidityWindow): UnsafeCertificate {
val keyGen = keyGen(keyType, signatureAlgorithm, keySize)
return UnsafeCertificate(keyGen.getSelfCertificate(name, certificatesValidityWindow.duration.toMillis()), keyGen.privateKey)
}
private fun CordaX500Name.asX500Name(): X500Name = X500Name.asX500Name(x500Principal)
private fun CertificateAndKeyPair.unsafe() = UnsafeCertificate(certificate, keyPair.private)
private fun keyGen(keyType: String, signatureAlgorithm: String, keySize: Int): CertAndKeyGen {
val keyGen = CertAndKeyGen(keyType, signatureAlgorithm)
keyGen.generate(keySize)
return keyGen
}
private fun newKeyStore(type: String, password: String): KeyStore {
val keyStore = KeyStore.getInstance(type)
// Loading creates the store, can't do anything with it until it's loaded
keyStore.load(null, password.toCharArray())
return keyStore
}
fun withKeyStores(server: KeyStores, client: KeyStores, action: (brokerSslOptions: MutualSslConfiguration, clientSslOptions: MutualSslConfiguration) -> Unit) {
val serverDir = Files.createTempDirectory(null)
FileUtils.forceDeleteOnExit(serverDir.toFile())
val clientDir = Files.createTempDirectory(null)
FileUtils.forceDeleteOnExit(clientDir.toFile())
server.save(serverDir).use { serverSslConfiguration ->
client.save(clientDir).use { clientSslConfiguration ->
action(serverSslConfiguration.value, clientSslConfiguration.value)
}
}
clientDir.deleteIfExists()
serverDir.deleteIfExists()
}
fun withCertificates(factoryDefaults: UnsafeCertificatesFactory.Defaults = UnsafeCertificatesFactory.defaults(), action: (server: KeyStores, client: KeyStores, createSelfSigned: (name: CordaX500Name) -> UnsafeCertificate, createSignedBy: (name: CordaX500Name, issuer: UnsafeCertificate) -> UnsafeCertificate) -> Unit) {
val factory = UnsafeCertificatesFactory(factoryDefaults)
val server = factory.newKeyStores("serverKeyStorePass", "serverTrustKeyStorePass")
val client = factory.newKeyStores("clientKeyStorePass", "clientTrustKeyStorePass")
action(server, client, factory::createSelfSigned, factory::createSignedBy)
}

View File

@ -1,6 +1,9 @@
package net.corda.testing.internal.stubs
import net.corda.core.internal.div
import net.corda.nodeapi.internal.DEV_CA_KEY_STORE_PASS
import net.corda.nodeapi.internal.DEV_CA_TRUST_STORE_PASS
import net.corda.nodeapi.internal.DEV_CA_TRUST_STORE_PRIVATE_KEY_PASS
import net.corda.nodeapi.internal.config.FileBasedCertificateStoreSupplier
import net.corda.nodeapi.internal.config.SslConfiguration
import net.corda.nodeapi.internal.config.MutualSslConfiguration
@ -11,28 +14,27 @@ 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"
private const val DEFAULT_STORE_FILE_NAME = "nodekeystore.jks"
private const val DEFAULT_STORE_PASSWORD = DEV_CA_KEY_STORE_PASS
@JvmStatic
fun withCertificatesDirectory(certificatesDirectory: Path, password: String = DEFAULT_STORE_PASSWORD, certificateStoreFileName: String = DEFAULT_STORE_FILE_NAME): FileBasedCertificateStoreSupplier {
fun withCertificatesDirectory(certificatesDirectory: Path, password: String = DEFAULT_STORE_PASSWORD,
keyPassword: String = password, certificateStoreFileName: String = DEFAULT_STORE_FILE_NAME): FileBasedCertificateStoreSupplier {
return FileBasedCertificateStoreSupplier(certificatesDirectory / certificateStoreFileName, password)
return FileBasedCertificateStoreSupplier(certificatesDirectory / certificateStoreFileName, password, keyPassword)
}
@JvmStatic
fun withBaseDirectory(baseDirectory: Path, password: String = DEFAULT_STORE_PASSWORD, certificatesDirectoryName: String = DEFAULT_CERTIFICATES_DIRECTORY_NAME, certificateStoreFileName: String = DEFAULT_STORE_FILE_NAME): FileBasedCertificateStoreSupplier {
fun withBaseDirectory(baseDirectory: Path, password: String = DEFAULT_STORE_PASSWORD, keyPassword: String = password,
certificatesDirectoryName: String = DEFAULT_CERTIFICATES_DIRECTORY_NAME, certificateStoreFileName: String = DEFAULT_STORE_FILE_NAME): FileBasedCertificateStoreSupplier {
return FileBasedCertificateStoreSupplier(baseDirectory / certificatesDirectoryName / certificateStoreFileName, password)
return FileBasedCertificateStoreSupplier(baseDirectory / certificatesDirectoryName / certificateStoreFileName, password, keyPassword)
}
}
}
@ -42,10 +44,13 @@ class CertificateStoreStubs {
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, useOpenSsl: Boolean = false): MutualSslConfiguration {
fun withCertificatesDirectory(certificatesDirectory: Path, keyStoreFileName: String = KeyStore.DEFAULT_STORE_FILE_NAME,
keyStorePassword: String = KeyStore.DEFAULT_STORE_PASSWORD, keyPassword: String = keyStorePassword,
trustStoreFileName: String = TrustStore.DEFAULT_STORE_FILE_NAME, trustStorePassword: String = TrustStore.DEFAULT_STORE_PASSWORD, trustStoreKeyPassword: String = TrustStore.DEFAULT_KEY_PASSWORD,
useOpenSsl: Boolean = false): MutualSslConfiguration {
val keyStore = FileBasedCertificateStoreSupplier(certificatesDirectory / keyStoreFileName, keyStorePassword)
val trustStore = FileBasedCertificateStoreSupplier(certificatesDirectory / trustStoreFileName, trustStorePassword)
val keyStore = FileBasedCertificateStoreSupplier(certificatesDirectory / keyStoreFileName, keyStorePassword, keyPassword)
val trustStore = FileBasedCertificateStoreSupplier(certificatesDirectory / trustStoreFileName, trustStorePassword, trustStoreKeyPassword)
return SslConfiguration.mutual(keyStore, trustStore, useOpenSsl)
}
@ -61,18 +66,20 @@ class CertificateStoreStubs {
companion object {
const val DEFAULT_STORE_FILE_NAME = "sslkeystore.jks"
const val DEFAULT_STORE_PASSWORD = "cordacadevpass"
const val DEFAULT_STORE_PASSWORD = DEV_CA_KEY_STORE_PASS
@JvmStatic
fun withCertificatesDirectory(certificatesDirectory: Path, password: String = DEFAULT_STORE_PASSWORD, certificateStoreFileName: String = DEFAULT_STORE_FILE_NAME): FileBasedCertificateStoreSupplier {
fun withCertificatesDirectory(certificatesDirectory: Path, password: String = DEFAULT_STORE_PASSWORD, keyPassword: String = password,
certificateStoreFileName: String = DEFAULT_STORE_FILE_NAME): FileBasedCertificateStoreSupplier {
return FileBasedCertificateStoreSupplier(certificatesDirectory / certificateStoreFileName, password)
return FileBasedCertificateStoreSupplier(certificatesDirectory / certificateStoreFileName, password, keyPassword)
}
@JvmStatic
fun withBaseDirectory(baseDirectory: Path, password: String = DEFAULT_STORE_PASSWORD, certificatesDirectoryName: String = DEFAULT_CERTIFICATES_DIRECTORY_NAME, certificateStoreFileName: String = DEFAULT_STORE_FILE_NAME): FileBasedCertificateStoreSupplier {
fun withBaseDirectory(baseDirectory: Path, password: String = DEFAULT_STORE_PASSWORD, keyPassword: String = password,
certificatesDirectoryName: String = DEFAULT_CERTIFICATES_DIRECTORY_NAME, certificateStoreFileName: String = DEFAULT_STORE_FILE_NAME): FileBasedCertificateStoreSupplier {
return FileBasedCertificateStoreSupplier(baseDirectory / certificatesDirectoryName / certificateStoreFileName, password)
return FileBasedCertificateStoreSupplier(baseDirectory / certificatesDirectoryName / certificateStoreFileName, password, keyPassword)
}
}
}
@ -82,18 +89,21 @@ class CertificateStoreStubs {
companion object {
const val DEFAULT_STORE_FILE_NAME = "truststore.jks"
const val DEFAULT_STORE_PASSWORD = "trustpass"
const val DEFAULT_STORE_PASSWORD = DEV_CA_TRUST_STORE_PASS
const val DEFAULT_KEY_PASSWORD = DEV_CA_TRUST_STORE_PRIVATE_KEY_PASS
@JvmStatic
fun withCertificatesDirectory(certificatesDirectory: Path, password: String = DEFAULT_STORE_PASSWORD, certificateStoreFileName: String = DEFAULT_STORE_FILE_NAME): FileBasedCertificateStoreSupplier {
fun withCertificatesDirectory(certificatesDirectory: Path, password: String = DEFAULT_STORE_PASSWORD,
keyPassword: String = DEFAULT_KEY_PASSWORD, certificateStoreFileName: String = DEFAULT_STORE_FILE_NAME): FileBasedCertificateStoreSupplier {
return FileBasedCertificateStoreSupplier(certificatesDirectory / certificateStoreFileName, password)
return FileBasedCertificateStoreSupplier(certificatesDirectory / certificateStoreFileName, password, keyPassword)
}
@JvmStatic
fun withBaseDirectory(baseDirectory: Path, password: String = DEFAULT_STORE_PASSWORD, certificatesDirectoryName: String = DEFAULT_CERTIFICATES_DIRECTORY_NAME, certificateStoreFileName: String = DEFAULT_STORE_FILE_NAME): FileBasedCertificateStoreSupplier {
fun withBaseDirectory(baseDirectory: Path, password: String = DEFAULT_STORE_PASSWORD, keyPassword: String = DEFAULT_KEY_PASSWORD,
certificatesDirectoryName: String = DEFAULT_CERTIFICATES_DIRECTORY_NAME, certificateStoreFileName: String = DEFAULT_STORE_FILE_NAME): FileBasedCertificateStoreSupplier {
return FileBasedCertificateStoreSupplier(baseDirectory / certificatesDirectoryName / certificateStoreFileName, password)
return FileBasedCertificateStoreSupplier(baseDirectory / certificatesDirectoryName / certificateStoreFileName, password, keyPassword)
}
}
}