mirror of
https://github.com/corda/corda.git
synced 2025-06-17 14:48:16 +00:00
Moved KeyStoreUtilities out of core and into node
This commit is contained in:
@ -2,8 +2,11 @@ package net.corda.services.messaging
|
||||
|
||||
import net.corda.core.copyTo
|
||||
import net.corda.core.createDirectories
|
||||
import net.corda.core.crypto.*
|
||||
import net.corda.core.crypto.CertificateType
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.X509Utilities
|
||||
import net.corda.core.exists
|
||||
import net.corda.node.utilities.*
|
||||
import net.corda.nodeapi.ArtemisMessagingComponent.Companion.NODE_USER
|
||||
import net.corda.nodeapi.ArtemisMessagingComponent.Companion.PEER_USER
|
||||
import net.corda.nodeapi.RPCApi
|
||||
@ -94,7 +97,9 @@ class MQSecurityAsNodeTest : MQSecurityTest() {
|
||||
javaClass.classLoader.getResourceAsStream("net/corda/node/internal/certificates/cordatruststore.jks").copyTo(trustStoreFile)
|
||||
}
|
||||
|
||||
val caKeyStore = KeyStoreUtilities.loadKeyStore(javaClass.classLoader.getResourceAsStream("net/corda/node/internal/certificates/cordadevcakeys.jks"), "cordacadevpass")
|
||||
val caKeyStore = loadKeyStore(
|
||||
javaClass.classLoader.getResourceAsStream("net/corda/node/internal/certificates/cordadevcakeys.jks"),
|
||||
"cordacadevpass")
|
||||
|
||||
val rootCACert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA)
|
||||
val intermediateCA = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
|
||||
@ -102,12 +107,13 @@ class MQSecurityAsNodeTest : MQSecurityTest() {
|
||||
|
||||
// Set name constrain to the legal name.
|
||||
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName))), arrayOf())
|
||||
val clientCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, intermediateCA.certificate, intermediateCA.keyPair, legalName, clientKey.public, nameConstraints = nameConstraints)
|
||||
val clientCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, intermediateCA.certificate,
|
||||
intermediateCA.keyPair, legalName, clientKey.public, nameConstraints = nameConstraints)
|
||||
val tlsKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
// Using different x500 name in the TLS cert which is not allowed in the name constraints.
|
||||
val clientTLSCert = X509Utilities.createCertificate(CertificateType.TLS, clientCACert, clientKey, MINI_CORP.name, tlsKey.public)
|
||||
val keyPass = keyStorePassword.toCharArray()
|
||||
val clientCAKeystore = KeyStoreUtilities.loadOrCreateKeyStore(nodeKeystore, keyStorePassword)
|
||||
val clientCAKeystore = loadOrCreateKeyStore(nodeKeystore, keyStorePassword)
|
||||
clientCAKeystore.addOrReplaceKey(
|
||||
X509Utilities.CORDA_CLIENT_CA,
|
||||
clientKey.private,
|
||||
@ -115,7 +121,7 @@ class MQSecurityAsNodeTest : MQSecurityTest() {
|
||||
CertPath(arrayOf(clientCACert, intermediateCA.certificate, rootCACert)))
|
||||
clientCAKeystore.save(nodeKeystore, keyStorePassword)
|
||||
|
||||
val tlsKeystore = KeyStoreUtilities.loadOrCreateKeyStore(sslKeystore, keyStorePassword)
|
||||
val tlsKeystore = loadOrCreateKeyStore(sslKeystore, keyStorePassword)
|
||||
tlsKeystore.addOrReplaceKey(
|
||||
X509Utilities.CORDA_CLIENT_TLS,
|
||||
tlsKey.private,
|
||||
|
@ -59,10 +59,8 @@ import net.corda.node.services.vault.CashBalanceAsMetricsObserver
|
||||
import net.corda.node.services.vault.HibernateVaultQueryImpl
|
||||
import net.corda.node.services.vault.NodeVaultService
|
||||
import net.corda.node.services.vault.VaultSoftLockManager
|
||||
import net.corda.node.utilities.*
|
||||
import net.corda.node.utilities.AddOrRemove.ADD
|
||||
import net.corda.node.utilities.AffinityExecutor
|
||||
import net.corda.node.utilities.configureDatabase
|
||||
import net.corda.node.utilities.transaction
|
||||
import org.apache.activemq.artemis.utils.ReusableLatch
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import org.jetbrains.exposed.sql.Database
|
||||
@ -520,8 +518,8 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
||||
private fun validateKeystore() {
|
||||
val containCorrectKeys = try {
|
||||
// This will throw IOException if key file not found or KeyStoreException if keystore password is incorrect.
|
||||
val sslKeystore = KeyStoreUtilities.loadKeyStore(configuration.sslKeystore, configuration.keyStorePassword)
|
||||
val identitiesKeystore = KeyStoreUtilities.loadKeyStore(configuration.nodeKeystore, configuration.keyStorePassword)
|
||||
val sslKeystore = loadKeyStore(configuration.sslKeystore, configuration.keyStorePassword)
|
||||
val identitiesKeystore = loadKeyStore(configuration.nodeKeystore, configuration.keyStorePassword)
|
||||
sslKeystore.containsAlias(X509Utilities.CORDA_CLIENT_TLS) && identitiesKeystore.containsAlias(X509Utilities.CORDA_CLIENT_CA)
|
||||
} catch (e: KeyStoreException) {
|
||||
log.warn("Certificate key store found but key store password does not match configuration.")
|
||||
@ -535,7 +533,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
||||
"or if you don't have one yet, fill out the config file and run corda.jar --initial-registration. " +
|
||||
"Read more at: https://docs.corda.net/permissioning.html"
|
||||
}
|
||||
val identitiesKeystore = KeyStoreUtilities.loadKeyStore(configuration.sslKeystore, configuration.keyStorePassword)
|
||||
val identitiesKeystore = loadKeyStore(configuration.sslKeystore, configuration.keyStorePassword)
|
||||
val tlsIdentity = identitiesKeystore.getX509Certificate(X509Utilities.CORDA_CLIENT_TLS).subject
|
||||
|
||||
require(tlsIdentity == configuration.myLegalName) {
|
||||
@ -839,7 +837,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
||||
}
|
||||
|
||||
private class KeyStoreWrapper(val keyStore: KeyStore, val storePath: Path, private val storePassword: String) {
|
||||
constructor(storePath: Path, storePassword: String) : this(KeyStoreUtilities.loadKeyStore(storePath, storePassword), storePath, storePassword)
|
||||
constructor(storePath: Path, storePassword: String) : this(loadKeyStore(storePath, storePassword), storePath, storePassword)
|
||||
|
||||
fun certificateAndKeyPair(alias: String): CertificateAndKeyPair? {
|
||||
return if (keyStore.containsAlias(alias)) keyStore.getCertificateAndKeyPair(alias, storePassword) else null
|
||||
|
@ -1,6 +1,3 @@
|
||||
// TODO: Remove when configureTestSSL() is moved.
|
||||
@file:JvmName("ConfigUtilities")
|
||||
|
||||
package net.corda.node.services.config
|
||||
|
||||
import com.typesafe.config.Config
|
||||
@ -9,17 +6,21 @@ import com.typesafe.config.ConfigParseOptions
|
||||
import com.typesafe.config.ConfigRenderOptions
|
||||
import net.corda.core.copyTo
|
||||
import net.corda.core.createDirectories
|
||||
import net.corda.core.crypto.KeyStoreUtilities
|
||||
import net.corda.core.crypto.X509Utilities
|
||||
import net.corda.core.crypto.*
|
||||
import net.corda.core.div
|
||||
import net.corda.core.exists
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.node.utilities.*
|
||||
import net.corda.nodeapi.config.SSLConfiguration
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import org.bouncycastle.asn1.x509.GeneralName
|
||||
import org.bouncycastle.asn1.x509.GeneralSubtree
|
||||
import org.bouncycastle.asn1.x509.NameConstraints
|
||||
import java.nio.file.Path
|
||||
import java.security.KeyStore
|
||||
|
||||
fun configOf(vararg pairs: Pair<String, Any?>) = ConfigFactory.parseMap(mapOf(*pairs))
|
||||
operator fun Config.plus(overrides: Map<String, Any?>) = ConfigFactory.parseMap(overrides).withFallback(this)
|
||||
fun configOf(vararg pairs: Pair<String, Any?>): Config = ConfigFactory.parseMap(mapOf(*pairs))
|
||||
operator fun Config.plus(overrides: Map<String, Any?>): Config = ConfigFactory.parseMap(overrides).withFallback(this)
|
||||
|
||||
object ConfigHelper {
|
||||
private val log = loggerFor<ConfigHelper>()
|
||||
@ -55,7 +56,56 @@ fun SSLConfiguration.configureDevKeyAndTrustStores(myLegalName: X500Name) {
|
||||
javaClass.classLoader.getResourceAsStream("net/corda/node/internal/certificates/cordatruststore.jks").copyTo(trustStoreFile)
|
||||
}
|
||||
if (!sslKeystore.exists() || !nodeKeystore.exists()) {
|
||||
val caKeyStore = KeyStoreUtilities.loadKeyStore(javaClass.classLoader.getResourceAsStream("net/corda/node/internal/certificates/cordadevcakeys.jks"), "cordacadevpass")
|
||||
X509Utilities.createKeystoreForCordaNode(sslKeystore, nodeKeystore, keyStorePassword, keyStorePassword, caKeyStore, "cordacadevkeypass", myLegalName)
|
||||
val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("net/corda/node/internal/certificates/cordadevcakeys.jks"), "cordacadevpass")
|
||||
createKeystoreForCordaNode(sslKeystore, nodeKeystore, keyStorePassword, keyStorePassword, caKeyStore, "cordacadevkeypass", myLegalName)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An all in wrapper to manufacture a server certificate and keys all stored in a KeyStore suitable for running TLS on the local machine.
|
||||
* @param sslKeyStorePath KeyStore path to save ssl key and cert to.
|
||||
* @param clientCAKeystorePath KeyStore path to save client CA key and cert to.
|
||||
* @param storePassword access password for KeyStore.
|
||||
* @param keyPassword PrivateKey access password for the generated keys.
|
||||
* It is recommended that this is the same as the storePassword as most TLS libraries assume they are the same.
|
||||
* @param caKeyStore KeyStore containing CA keys generated by createCAKeyStoreAndTrustStore.
|
||||
* @param caKeyPassword password to unlock private keys in the CA KeyStore.
|
||||
* @return The KeyStore created containing a private key, certificate chain and root CA public cert for use in TLS applications.
|
||||
*/
|
||||
fun createKeystoreForCordaNode(sslKeyStorePath: Path,
|
||||
clientCAKeystorePath: Path,
|
||||
storePassword: String,
|
||||
keyPassword: String,
|
||||
caKeyStore: KeyStore,
|
||||
caKeyPassword: String,
|
||||
legalName: X500Name,
|
||||
signatureScheme: SignatureScheme = X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) {
|
||||
|
||||
val rootCACert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA)
|
||||
val (intermediateCACert, intermediateCAKeyPair) = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, caKeyPassword)
|
||||
|
||||
val clientKey = Crypto.generateKeyPair(signatureScheme)
|
||||
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName))), arrayOf())
|
||||
val clientCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, intermediateCACert, intermediateCAKeyPair, legalName, clientKey.public, nameConstraints = nameConstraints)
|
||||
|
||||
val tlsKey = Crypto.generateKeyPair(signatureScheme)
|
||||
val clientTLSCert = X509Utilities.createCertificate(CertificateType.TLS, clientCACert, clientKey, legalName, tlsKey.public)
|
||||
|
||||
val keyPass = keyPassword.toCharArray()
|
||||
|
||||
val clientCAKeystore = loadOrCreateKeyStore(clientCAKeystorePath, storePassword)
|
||||
clientCAKeystore.addOrReplaceKey(
|
||||
X509Utilities.CORDA_CLIENT_CA,
|
||||
clientKey.private,
|
||||
keyPass,
|
||||
org.bouncycastle.cert.path.CertPath(arrayOf(clientCACert, intermediateCACert, rootCACert)))
|
||||
clientCAKeystore.save(clientCAKeystorePath, storePassword)
|
||||
|
||||
val tlsKeystore = loadOrCreateKeyStore(sslKeyStorePath, storePassword)
|
||||
tlsKeystore.addOrReplaceKey(
|
||||
X509Utilities.CORDA_CLIENT_TLS,
|
||||
tlsKey.private,
|
||||
keyPass,
|
||||
org.bouncycastle.cert.path.CertPath(arrayOf(clientTLSCert, clientCACert, intermediateCACert, rootCACert)))
|
||||
tlsKeystore.save(sslKeyStorePath, storePassword)
|
||||
}
|
@ -20,6 +20,8 @@ import net.corda.node.services.messaging.NodeLoginModule.Companion.NODE_ROLE
|
||||
import net.corda.node.services.messaging.NodeLoginModule.Companion.PEER_ROLE
|
||||
import net.corda.node.services.messaging.NodeLoginModule.Companion.RPC_ROLE
|
||||
import net.corda.node.services.messaging.NodeLoginModule.Companion.VERIFIER_ROLE
|
||||
import net.corda.node.utilities.getX509Certificate
|
||||
import net.corda.node.utilities.loadKeyStore
|
||||
import net.corda.nodeapi.*
|
||||
import net.corda.nodeapi.ArtemisMessagingComponent.Companion.NODE_USER
|
||||
import net.corda.nodeapi.ArtemisMessagingComponent.Companion.PEER_USER
|
||||
@ -264,8 +266,8 @@ class ArtemisMessagingServer(override val config: NodeConfiguration,
|
||||
|
||||
@Throws(IOException::class, KeyStoreException::class)
|
||||
private fun createArtemisSecurityManager(): ActiveMQJAASSecurityManager {
|
||||
val keyStore = KeyStoreUtilities.loadKeyStore(config.sslKeystore, config.keyStorePassword)
|
||||
val trustStore = KeyStoreUtilities.loadKeyStore(config.trustStoreFile, config.trustStorePassword)
|
||||
val keyStore = loadKeyStore(config.sslKeystore, config.keyStorePassword)
|
||||
val trustStore = loadKeyStore(config.trustStoreFile, config.trustStorePassword)
|
||||
val ourCertificate = keyStore.getX509Certificate(CORDA_CLIENT_TLS)
|
||||
|
||||
// This is a sanity check and should not fail unless things have been misconfigured
|
||||
|
@ -0,0 +1,170 @@
|
||||
package net.corda.node.utilities
|
||||
|
||||
import net.corda.core.crypto.CertificateAndKeyPair
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.cert
|
||||
import net.corda.core.exists
|
||||
import net.corda.core.read
|
||||
import net.corda.core.write
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import org.bouncycastle.cert.path.CertPath
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import java.nio.file.Path
|
||||
import java.security.*
|
||||
import java.security.cert.Certificate
|
||||
|
||||
val KEYSTORE_TYPE = "JKS"
|
||||
|
||||
/**
|
||||
* Helper method to either open an existing keystore for modification, or create a new blank keystore.
|
||||
* @param keyStoreFilePath location of KeyStore file.
|
||||
* @param storePassword password to open the store. This does not have to be the same password as any keys stored,
|
||||
* but for SSL purposes this is recommended.
|
||||
* @return returns the KeyStore opened/created.
|
||||
*/
|
||||
fun loadOrCreateKeyStore(keyStoreFilePath: Path, storePassword: String): KeyStore {
|
||||
val pass = storePassword.toCharArray()
|
||||
val keyStore = KeyStore.getInstance(KEYSTORE_TYPE)
|
||||
if (keyStoreFilePath.exists()) {
|
||||
keyStoreFilePath.read { keyStore.load(it, pass) }
|
||||
} else {
|
||||
keyStore.load(null, pass)
|
||||
keyStoreFilePath.write { keyStore.store(it, pass) }
|
||||
}
|
||||
return keyStore
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to open an existing keystore for modification/read.
|
||||
* @param keyStoreFilePath location of KeyStore file which must exist, or this will throw FileNotFoundException.
|
||||
* @param storePassword password to open the store. This does not have to be the same password as any keys stored,
|
||||
* but for SSL purposes this is recommended.
|
||||
* @return returns the KeyStore opened.
|
||||
* @throws IOException if there was an error reading the key store from the file.
|
||||
* @throws KeyStoreException if the password is incorrect or the key store is damaged.
|
||||
*/
|
||||
@Throws(KeyStoreException::class, IOException::class)
|
||||
fun loadKeyStore(keyStoreFilePath: Path, storePassword: String): KeyStore {
|
||||
return keyStoreFilePath.read { loadKeyStore(it, storePassword) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to open an existing keystore for modification/read.
|
||||
* @param input stream containing a KeyStore e.g. loaded from a resource file.
|
||||
* @param storePassword password to open the store. This does not have to be the same password as any keys stored,
|
||||
* but for SSL purposes this is recommended.
|
||||
* @return returns the KeyStore opened.
|
||||
* @throws IOException if there was an error reading the key store from the stream.
|
||||
* @throws KeyStoreException if the password is incorrect or the key store is damaged.
|
||||
*/
|
||||
@Throws(KeyStoreException::class, IOException::class)
|
||||
fun loadKeyStore(input: InputStream, storePassword: String): KeyStore {
|
||||
val pass = storePassword.toCharArray()
|
||||
val keyStore = KeyStore.getInstance(KEYSTORE_TYPE)
|
||||
input.use {
|
||||
keyStore.load(input, pass)
|
||||
}
|
||||
return keyStore
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper extension method to add, or overwrite any key data in store.
|
||||
* @param alias name to record the private key and certificate chain under.
|
||||
* @param key cryptographic key to store.
|
||||
* @param password password for unlocking the key entry in the future. This does not have to be the same password as any keys stored,
|
||||
* but for SSL purposes this is recommended.
|
||||
* @param chain the sequence of certificates starting with the public key certificate for this key and extending to the root CA cert.
|
||||
*/
|
||||
fun KeyStore.addOrReplaceKey(alias: String, key: Key, password: CharArray, chain: CertPath) {
|
||||
addOrReplaceKey(alias, key, password, chain.certificates.map { it.cert }.toTypedArray<Certificate>())
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper extension method to add, or overwrite any key data in store.
|
||||
* @param alias name to record the private key and certificate chain under.
|
||||
* @param key cryptographic key to store.
|
||||
* @param password password for unlocking the key entry in the future. This does not have to be the same password as any keys stored,
|
||||
* but for SSL purposes this is recommended.
|
||||
* @param chain the sequence of certificates starting with the public key certificate for this key and extending to the root CA cert.
|
||||
*/
|
||||
fun KeyStore.addOrReplaceKey(alias: String, key: Key, password: CharArray, chain: Array<Certificate>) {
|
||||
if (containsAlias(alias)) {
|
||||
this.deleteEntry(alias)
|
||||
}
|
||||
this.setKeyEntry(alias, key, password, chain)
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper extension method to add, or overwrite any public certificate data in store.
|
||||
* @param alias name to record the public certificate under.
|
||||
* @param cert certificate to store.
|
||||
*/
|
||||
fun KeyStore.addOrReplaceCertificate(alias: String, cert: Certificate) {
|
||||
if (containsAlias(alias)) {
|
||||
this.deleteEntry(alias)
|
||||
}
|
||||
this.setCertificateEntry(alias, cert)
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method save KeyStore to storage.
|
||||
* @param keyStoreFilePath the file location to save to.
|
||||
* @param storePassword password to access the store in future. This does not have to be the same password as any keys stored,
|
||||
* but for SSL purposes this is recommended.
|
||||
*/
|
||||
fun KeyStore.save(keyStoreFilePath: Path, storePassword: String) = keyStoreFilePath.write { store(it, storePassword) }
|
||||
|
||||
fun KeyStore.store(out: OutputStream, password: String) = store(out, password.toCharArray())
|
||||
|
||||
/**
|
||||
* Extract public and private keys from a KeyStore file assuming storage alias is known.
|
||||
* @param alias The name to lookup the Key and Certificate chain from.
|
||||
* @param keyPassword Password to unlock the private key entries.
|
||||
* @return The KeyPair found in the KeyStore under the specified alias.
|
||||
*/
|
||||
fun KeyStore.getKeyPair(alias: String, keyPassword: String): KeyPair = getCertificateAndKeyPair(alias, keyPassword).keyPair
|
||||
|
||||
/**
|
||||
* Helper method to load a Certificate and KeyPair from their KeyStore.
|
||||
* The access details should match those of the createCAKeyStoreAndTrustStore call used to manufacture the keys.
|
||||
* @param alias The name to search for the data. Typically if generated with the methods here this will be one of
|
||||
* CERT_PRIVATE_KEY_ALIAS, ROOT_CA_CERT_PRIVATE_KEY_ALIAS, INTERMEDIATE_CA_PRIVATE_KEY_ALIAS defined above.
|
||||
* @param keyPassword The password for the PrivateKey (not the store access password).
|
||||
*/
|
||||
fun KeyStore.getCertificateAndKeyPair(alias: String, keyPassword: String): CertificateAndKeyPair {
|
||||
val cert = getX509Certificate(alias)
|
||||
val publicKey = Crypto.toSupportedPublicKey(cert.subjectPublicKeyInfo)
|
||||
return CertificateAndKeyPair(cert, KeyPair(publicKey, getSupportedKey(alias, keyPassword)))
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract public X509 certificate from a KeyStore file assuming storage alias is known.
|
||||
* @param alias The name to lookup the Key and Certificate chain from.
|
||||
* @return The X509Certificate found in the KeyStore under the specified alias.
|
||||
*/
|
||||
fun KeyStore.getX509Certificate(alias: String): X509CertificateHolder {
|
||||
val encoded = getCertificate(alias)?.encoded ?: throw IllegalArgumentException("No certificate under alias \"$alias\"")
|
||||
return X509CertificateHolder(encoded)
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a private key from a KeyStore file assuming storage alias is known.
|
||||
* By default, a JKS keystore returns PrivateKey implementations supported by the SUN provider.
|
||||
* For instance, if one imports a BouncyCastle ECC key, JKS will return a SUN ECC key implementation on getKey.
|
||||
* To convert to a supported implementation, an encode->decode method is applied to the keystore's returned object.
|
||||
* @param alias The name to lookup the Key.
|
||||
* @param keyPassword Password to unlock the private key entries.
|
||||
* @return the requested private key in supported type.
|
||||
* @throws KeyStoreException if the keystore has not been initialized.
|
||||
* @throws NoSuchAlgorithmException if the algorithm for recovering the key cannot be found (not supported from the Keystore provider).
|
||||
* @throws UnrecoverableKeyException if the key cannot be recovered (e.g., the given password is wrong).
|
||||
* @throws IllegalArgumentException on not supported scheme or if the given key specification
|
||||
* is inappropriate for a supported key factory to produce a private key.
|
||||
*/
|
||||
fun KeyStore.getSupportedKey(alias: String, keyPassword: String): PrivateKey {
|
||||
val keyPass = keyPassword.toCharArray()
|
||||
val key = getKey(alias, keyPass) as PrivateKey
|
||||
return Crypto.toSupportedPrivateKey(key)
|
||||
}
|
@ -1,24 +1,30 @@
|
||||
package net.corda.node.utilities.registration
|
||||
|
||||
import net.corda.core.*
|
||||
import net.corda.core.crypto.*
|
||||
import net.corda.core.crypto.CertificateType
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.X509Utilities
|
||||
import net.corda.core.crypto.X509Utilities.CORDA_CLIENT_CA
|
||||
import net.corda.core.crypto.X509Utilities.CORDA_CLIENT_TLS
|
||||
import net.corda.core.crypto.X509Utilities.CORDA_ROOT_CA
|
||||
import net.corda.core.crypto.cert
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.node.utilities.*
|
||||
import org.bouncycastle.cert.path.CertPath
|
||||
import org.bouncycastle.openssl.jcajce.JcaPEMWriter
|
||||
import org.bouncycastle.util.io.pem.PemObject
|
||||
import java.io.StringWriter
|
||||
import java.security.KeyPair
|
||||
import java.security.KeyStore
|
||||
import java.security.cert.Certificate
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
/**
|
||||
* This checks the config.certificatesDirectory field for certificates required to connect to a Corda network.
|
||||
* If the certificates are not found, a [org.bouncycastle.pkcs.PKCS10CertificationRequest] will be submitted to
|
||||
* Corda network permissioning server using [NetworkRegistrationService]. This process will enter a polling loop until the request has been approved, and then
|
||||
* the certificate chain will be downloaded and stored in [Keystore] reside in the certificates directory.
|
||||
* Corda network permissioning server using [NetworkRegistrationService]. This process will enter a polling loop until
|
||||
* the request has been approved, and then the certificate chain will be downloaded and stored in [KeyStore] reside in
|
||||
* the certificates directory.
|
||||
*/
|
||||
class NetworkRegistrationHelper(val config: NodeConfiguration, val certService: NetworkRegistrationService) {
|
||||
companion object {
|
||||
@ -33,7 +39,7 @@ class NetworkRegistrationHelper(val config: NodeConfiguration, val certService:
|
||||
|
||||
fun buildKeystore() {
|
||||
config.certificatesDirectory.createDirectories()
|
||||
val caKeyStore = KeyStoreUtilities.loadOrCreateKeyStore(config.nodeKeystore, keystorePassword)
|
||||
val caKeyStore = loadOrCreateKeyStore(config.nodeKeystore, keystorePassword)
|
||||
if (!caKeyStore.containsAlias(CORDA_CLIENT_CA)) {
|
||||
// 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.
|
||||
@ -64,7 +70,7 @@ class NetworkRegistrationHelper(val config: NodeConfiguration, val certService:
|
||||
caKeyStore.deleteEntry(SELF_SIGNED_PRIVATE_KEY)
|
||||
caKeyStore.save(config.nodeKeystore, keystorePassword)
|
||||
// Save root certificates to trust store.
|
||||
val trustStore = KeyStoreUtilities.loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword)
|
||||
val trustStore = loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword)
|
||||
// Assumes certificate chain always starts with client certificate and end with root certificate.
|
||||
trustStore.addOrReplaceCertificate(CORDA_ROOT_CA, certificates.last())
|
||||
trustStore.save(config.trustStoreFile, config.trustStorePassword)
|
||||
@ -74,7 +80,7 @@ class NetworkRegistrationHelper(val config: NodeConfiguration, val certService:
|
||||
val sslKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val caCert = caKeyStore.getX509Certificate(CORDA_CLIENT_CA)
|
||||
val sslCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, keyPair, caCert.subject, sslKey.public)
|
||||
val sslKeyStore = KeyStoreUtilities.loadOrCreateKeyStore(config.sslKeystore, keystorePassword)
|
||||
val sslKeyStore = loadOrCreateKeyStore(config.sslKeystore, keystorePassword)
|
||||
sslKeyStore.addOrReplaceKey(CORDA_CLIENT_TLS, sslKey.private, privateKeyPassword.toCharArray(),
|
||||
arrayOf(sslCert.cert, *certificates))
|
||||
sslKeyStore.save(config.sslKeystore, config.keyStorePassword)
|
||||
|
@ -6,6 +6,7 @@ import com.nhaarman.mockito_kotlin.mock
|
||||
import net.corda.core.crypto.*
|
||||
import net.corda.core.exists
|
||||
import net.corda.core.toTypedArray
|
||||
import net.corda.node.utilities.loadKeyStore
|
||||
import net.corda.testing.ALICE
|
||||
import net.corda.testing.getTestX509Name
|
||||
import net.corda.testing.testNodeConfiguration
|
||||
@ -52,10 +53,9 @@ class NetworkRegistrationHelperTest {
|
||||
assertTrue(config.sslKeystore.exists())
|
||||
assertTrue(config.trustStoreFile.exists())
|
||||
|
||||
val nodeKeystore = KeyStoreUtilities.loadKeyStore(config.nodeKeystore, config.keyStorePassword)
|
||||
val sslKeystore = KeyStoreUtilities.loadKeyStore(config.sslKeystore, config.keyStorePassword)
|
||||
val trustStore = KeyStoreUtilities.loadKeyStore(config.trustStoreFile, config.trustStorePassword)
|
||||
|
||||
val nodeKeystore = loadKeyStore(config.nodeKeystore, config.keyStorePassword)
|
||||
val sslKeystore = loadKeyStore(config.sslKeystore, config.keyStorePassword)
|
||||
val trustStore = loadKeyStore(config.trustStoreFile, config.trustStorePassword)
|
||||
|
||||
nodeKeystore.run {
|
||||
assertTrue(containsAlias(X509Utilities.CORDA_CLIENT_CA))
|
||||
|
Reference in New Issue
Block a user