mirror of
https://github.com/corda/corda.git
synced 2025-06-10 19:31:46 +00:00
Store notaries's identity composite key in keystore (#1036)
* * Store composite key in keystore from file for notaries's identity. * Some refactoring. * * Addressed PR issues * * Remove unintended format changes * * Fixed failing test due to getting keys from wrong keystore
This commit is contained in:
parent
8a2074eeee
commit
264b9316e3
@ -22,7 +22,6 @@ import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
|||||||
import org.bouncycastle.asn1.x509.GeneralName
|
import org.bouncycastle.asn1.x509.GeneralName
|
||||||
import org.bouncycastle.asn1.x509.GeneralSubtree
|
import org.bouncycastle.asn1.x509.GeneralSubtree
|
||||||
import org.bouncycastle.asn1.x509.NameConstraints
|
import org.bouncycastle.asn1.x509.NameConstraints
|
||||||
import org.bouncycastle.cert.path.CertPath
|
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
|
|
||||||
@ -118,7 +117,7 @@ class MQSecurityAsNodeTest : MQSecurityTest() {
|
|||||||
X509Utilities.CORDA_CLIENT_CA,
|
X509Utilities.CORDA_CLIENT_CA,
|
||||||
clientKey.private,
|
clientKey.private,
|
||||||
keyPass,
|
keyPass,
|
||||||
CertPath(arrayOf(clientCACert, intermediateCA.certificate, rootCACert)))
|
arrayOf(clientCACert, intermediateCA.certificate, rootCACert))
|
||||||
clientCAKeystore.save(nodeKeystore, keyStorePassword)
|
clientCAKeystore.save(nodeKeystore, keyStorePassword)
|
||||||
|
|
||||||
val tlsKeystore = loadOrCreateKeyStore(sslKeystore, keyStorePassword)
|
val tlsKeystore = loadOrCreateKeyStore(sslKeystore, keyStorePassword)
|
||||||
@ -126,7 +125,7 @@ class MQSecurityAsNodeTest : MQSecurityTest() {
|
|||||||
X509Utilities.CORDA_CLIENT_TLS,
|
X509Utilities.CORDA_CLIENT_TLS,
|
||||||
tlsKey.private,
|
tlsKey.private,
|
||||||
keyPass,
|
keyPass,
|
||||||
CertPath(arrayOf(clientTLSCert, clientCACert, intermediateCA.certificate, rootCACert)))
|
arrayOf(clientTLSCert, clientCACert, intermediateCA.certificate, rootCACert))
|
||||||
tlsKeystore.save(sslKeystore, keyStorePassword)
|
tlsKeystore.save(sslKeystore, keyStorePassword)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,9 +8,9 @@ import com.google.common.util.concurrent.MoreExecutors
|
|||||||
import com.google.common.util.concurrent.SettableFuture
|
import com.google.common.util.concurrent.SettableFuture
|
||||||
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner
|
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner
|
||||||
import io.github.lukehutch.fastclasspathscanner.scanner.ScanResult
|
import io.github.lukehutch.fastclasspathscanner.scanner.ScanResult
|
||||||
import net.corda.core.*
|
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.crypto.composite.CompositeKey
|
import net.corda.core.crypto.composite.CompositeKey
|
||||||
|
import net.corda.core.flatMap
|
||||||
import net.corda.core.flows.*
|
import net.corda.core.flows.*
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.identity.PartyAndCertificate
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
@ -28,7 +28,10 @@ import net.corda.core.transactions.SignedTransaction
|
|||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.core.utilities.debug
|
import net.corda.core.utilities.debug
|
||||||
import net.corda.core.utilities.toNonEmptySet
|
import net.corda.core.utilities.toNonEmptySet
|
||||||
import net.corda.flows.*
|
import net.corda.flows.CashExitFlow
|
||||||
|
import net.corda.flows.CashIssueFlow
|
||||||
|
import net.corda.flows.CashPaymentFlow
|
||||||
|
import net.corda.flows.IssuerFlow
|
||||||
import net.corda.node.services.*
|
import net.corda.node.services.*
|
||||||
import net.corda.node.services.api.*
|
import net.corda.node.services.api.*
|
||||||
import net.corda.node.services.config.NodeConfiguration
|
import net.corda.node.services.config.NodeConfiguration
|
||||||
@ -69,15 +72,14 @@ import rx.Observable
|
|||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.lang.reflect.InvocationTargetException
|
import java.lang.reflect.InvocationTargetException
|
||||||
import java.lang.reflect.Modifier.*
|
import java.lang.reflect.Modifier.*
|
||||||
import java.math.BigInteger
|
|
||||||
import java.net.JarURLConnection
|
import java.net.JarURLConnection
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.KeyStore
|
|
||||||
import java.security.KeyStoreException
|
import java.security.KeyStoreException
|
||||||
import java.security.cert.*
|
import java.security.cert.CertificateFactory
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
import java.time.Clock
|
import java.time.Clock
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
@ -709,63 +711,62 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
|||||||
// the legal name is actually validated in some way.
|
// the legal name is actually validated in some way.
|
||||||
|
|
||||||
// TODO: Integrate with Key management service?
|
// TODO: Integrate with Key management service?
|
||||||
val certFactory = CertificateFactory.getInstance("X509")
|
|
||||||
val keyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword)
|
val keyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword)
|
||||||
val privateKeyAlias = "$serviceId-private-key"
|
val privateKeyAlias = "$serviceId-private-key"
|
||||||
val privKeyFile = configuration.baseDirectory / privateKeyAlias
|
val compositeKeyAlias = "$serviceId-composite-key"
|
||||||
val pubIdentityFile = configuration.baseDirectory / "$serviceId-public"
|
|
||||||
val certificateAndKeyPair = keyStore.certificateAndKeyPair(privateKeyAlias)
|
if (!keyStore.containsAlias(privateKeyAlias)) {
|
||||||
val identityCertPathAndKey: Pair<PartyAndCertificate, KeyPair> = if (certificateAndKeyPair != null) {
|
val privKeyFile = configuration.baseDirectory / privateKeyAlias
|
||||||
val clientCertPath = keyStore.keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
|
val pubIdentityFile = configuration.baseDirectory / "$serviceId-public"
|
||||||
val (cert, keyPair) = certificateAndKeyPair
|
// TODO: Remove use of [ServiceIdentityGenerator.generateToDisk].
|
||||||
// Get keys from keystore.
|
|
||||||
val loadedServiceName = cert.subject
|
|
||||||
if (loadedServiceName != serviceName) {
|
|
||||||
throw ConfigurationException("The legal name in the config file doesn't match the stored identity keystore:" +
|
|
||||||
"$serviceName vs $loadedServiceName")
|
|
||||||
}
|
|
||||||
val certPath = certFactory.generateCertPath(listOf(cert.cert) + clientCertPath)
|
|
||||||
Pair(PartyAndCertificate(loadedServiceName, keyPair.public, cert, certPath), keyPair)
|
|
||||||
} else if (privKeyFile.exists()) {
|
|
||||||
// Get keys from key file.
|
// Get keys from key file.
|
||||||
// TODO: this is here to smooth out the key storage transition, remove this in future release.
|
// TODO: this is here to smooth out the key storage transition, remove this migration in future release.
|
||||||
// Check that the identity in the config file matches the identity file we have stored to disk.
|
if (privKeyFile.exists()) {
|
||||||
// This is just a sanity check. It shouldn't fail unless the admin has fiddled with the files and messed
|
migrateKeysFromFile(keyStore, serviceName, pubIdentityFile, privKeyFile, privateKeyAlias, compositeKeyAlias)
|
||||||
// things up for us.
|
} else {
|
||||||
val myIdentity = pubIdentityFile.readAll().deserialize<Party>()
|
log.info("$privateKeyAlias not found in keystore ${configuration.nodeKeystore}, generating fresh key!")
|
||||||
if (myIdentity.name != serviceName)
|
keyStore.saveNewKeyPair(serviceName, privateKeyAlias, generateKeyPair())
|
||||||
throw ConfigurationException("The legal name in the config file doesn't match the stored identity file:" +
|
|
||||||
"$serviceName vs ${myIdentity.name}")
|
|
||||||
// Load the private key.
|
|
||||||
val keyPair = privKeyFile.readAll().deserialize<KeyPair>()
|
|
||||||
if (myIdentity.owningKey !is CompositeKey) { // TODO: Support case where owningKey is a composite key.
|
|
||||||
keyStore.save(serviceName, privateKeyAlias, keyPair)
|
|
||||||
}
|
}
|
||||||
val dummyCaKey = entropyToKeyPair(BigInteger.valueOf(111))
|
|
||||||
val dummyCa = CertificateAndKeyPair(
|
|
||||||
X509Utilities.createSelfSignedCACertificate(X500Name("CN=Dummy CA,OU=Corda,O=R3 Ltd,L=London,C=GB"), dummyCaKey),
|
|
||||||
dummyCaKey)
|
|
||||||
val partyAndCertificate = getTestPartyAndCertificate(myIdentity, dummyCa)
|
|
||||||
// Sanity check the certificate and path
|
|
||||||
val validatorParameters = PKIXParameters(setOf(TrustAnchor(dummyCa.certificate.cert, null)))
|
|
||||||
val validator = CertPathValidator.getInstance("PKIX")
|
|
||||||
validatorParameters.isRevocationEnabled = false
|
|
||||||
validator.validate(partyAndCertificate.certPath, validatorParameters) as PKIXCertPathValidatorResult
|
|
||||||
Pair(partyAndCertificate, keyPair)
|
|
||||||
} else {
|
|
||||||
val clientCertPath = keyStore.keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
|
|
||||||
val clientCA = keyStore.certificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)!!
|
|
||||||
// Create new keys and store in keystore.
|
|
||||||
log.info("Identity key not found, generating fresh key!")
|
|
||||||
val keyPair: KeyPair = generateKeyPair()
|
|
||||||
val cert = X509Utilities.createCertificate(CertificateType.IDENTITY, clientCA.certificate, clientCA.keyPair, serviceName, keyPair.public)
|
|
||||||
val certPath = certFactory.generateCertPath(listOf(cert.cert) + clientCertPath)
|
|
||||||
keyStore.save(serviceName, privateKeyAlias, keyPair)
|
|
||||||
require(certPath.certificates.isNotEmpty()) { "Certificate path cannot be empty" }
|
|
||||||
Pair(PartyAndCertificate(serviceName, keyPair.public, cert, certPath), keyPair)
|
|
||||||
}
|
}
|
||||||
partyKeys += identityCertPathAndKey.second
|
|
||||||
return identityCertPathAndKey
|
val (cert, keyPair) = keyStore.certificateAndKeyPair(privateKeyAlias)
|
||||||
|
|
||||||
|
// Get keys from keystore.
|
||||||
|
val loadedServiceName = cert.subject
|
||||||
|
if (loadedServiceName != serviceName)
|
||||||
|
throw ConfigurationException("The legal name in the config file doesn't match the stored identity keystore:$serviceName vs $loadedServiceName")
|
||||||
|
|
||||||
|
val certPath = CertificateFactory.getInstance("X509").generateCertPath(keyStore.getCertificateChain(privateKeyAlias).toList())
|
||||||
|
// Use composite key instead if exists
|
||||||
|
// TODO: Use configuration to indicate composite key should be used instead of public key for the identity.
|
||||||
|
val publicKey = if (keyStore.containsAlias(compositeKeyAlias)) {
|
||||||
|
Crypto.toSupportedPublicKey(keyStore.getCertificate(compositeKeyAlias).publicKey)
|
||||||
|
} else {
|
||||||
|
keyPair.public
|
||||||
|
}
|
||||||
|
|
||||||
|
partyKeys += keyPair
|
||||||
|
return Pair(PartyAndCertificate(loadedServiceName, publicKey, cert, certPath), keyPair)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun migrateKeysFromFile(keyStore: KeyStoreWrapper, serviceName: X500Name,
|
||||||
|
pubIdentityFile: Path, privKeyFile: Path,
|
||||||
|
privateKeyAlias: String, compositeKeyAlias: String) {
|
||||||
|
log.info("Migrating $privateKeyAlias from file to keystore...")
|
||||||
|
val myIdentity = pubIdentityFile.readAll().deserialize<Party>()
|
||||||
|
// Check that the identity in the config file matches the identity file we have stored to disk.
|
||||||
|
// This is just a sanity check. It shouldn't fail unless the admin has fiddled with the files and messed
|
||||||
|
// things up for us.
|
||||||
|
if (myIdentity.name != serviceName)
|
||||||
|
throw ConfigurationException("The legal name in the config file doesn't match the stored identity file:$serviceName vs ${myIdentity.name}")
|
||||||
|
// Load the private key.
|
||||||
|
val keyPair = privKeyFile.readAll().deserialize<KeyPair>()
|
||||||
|
keyStore.saveNewKeyPair(serviceName, privateKeyAlias, keyPair)
|
||||||
|
// Store composite key separately.
|
||||||
|
if (myIdentity.owningKey is CompositeKey) {
|
||||||
|
keyStore.savePublicKey(serviceName, compositeKeyAlias, myIdentity.owningKey)
|
||||||
|
}
|
||||||
|
log.info("Finish migrating $privateKeyAlias from file to keystore.")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getTestPartyAndCertificate(party: Party, trustRoot: CertificateAndKeyPair): PartyAndCertificate {
|
private fun getTestPartyAndCertificate(party: Party, trustRoot: CertificateAndKeyPair): PartyAndCertificate {
|
||||||
@ -801,10 +802,11 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
|||||||
override val keyManagementService by lazy { makeKeyManagementService(identityService) }
|
override val keyManagementService by lazy { makeKeyManagementService(identityService) }
|
||||||
override val schedulerService by lazy { NodeSchedulerService(this, unfinishedSchedules = busyNodeLatch) }
|
override val schedulerService by lazy { NodeSchedulerService(this, unfinishedSchedules = busyNodeLatch) }
|
||||||
override val identityService by lazy {
|
override val identityService by lazy {
|
||||||
val keyStoreWrapper = KeyStoreWrapper(configuration.trustStoreFile, configuration.trustStorePassword)
|
val trustStore = KeyStoreWrapper(configuration.trustStoreFile, configuration.trustStorePassword)
|
||||||
|
val caKeyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword)
|
||||||
makeIdentityService(
|
makeIdentityService(
|
||||||
keyStoreWrapper.keyStore.getCertificate(X509Utilities.CORDA_ROOT_CA)!! as X509Certificate,
|
trustStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA).cert,
|
||||||
keyStoreWrapper.certificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA),
|
caKeyStore.certificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA),
|
||||||
info.legalIdentityAndCert)
|
info.legalIdentityAndCert)
|
||||||
}
|
}
|
||||||
override val attachments: AttachmentStorage get() = this@AbstractNode.attachments
|
override val attachments: AttachmentStorage get() = this@AbstractNode.attachments
|
||||||
@ -836,18 +838,3 @@ 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(loadKeyStore(storePath, storePassword), storePath, storePassword)
|
|
||||||
|
|
||||||
fun certificateAndKeyPair(alias: String): CertificateAndKeyPair? {
|
|
||||||
return if (keyStore.containsAlias(alias)) keyStore.getCertificateAndKeyPair(alias, storePassword) else null
|
|
||||||
}
|
|
||||||
|
|
||||||
fun save(serviceName: X500Name, privateKeyAlias: String, keyPair: KeyPair) {
|
|
||||||
val clientCA = keyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, storePassword)
|
|
||||||
val cert = X509Utilities.createCertificate(CertificateType.IDENTITY, clientCA.certificate, clientCA.keyPair, serviceName, keyPair.public).cert
|
|
||||||
keyStore.addOrReplaceKey(privateKeyAlias, keyPair.private, storePassword.toCharArray(), arrayOf(cert, *keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA)))
|
|
||||||
keyStore.save(storePath, storePassword)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -98,7 +98,7 @@ fun createKeystoreForCordaNode(sslKeyStorePath: Path,
|
|||||||
X509Utilities.CORDA_CLIENT_CA,
|
X509Utilities.CORDA_CLIENT_CA,
|
||||||
clientKey.private,
|
clientKey.private,
|
||||||
keyPass,
|
keyPass,
|
||||||
org.bouncycastle.cert.path.CertPath(arrayOf(clientCACert, intermediateCACert, rootCACert)))
|
arrayOf(clientCACert, intermediateCACert, rootCACert))
|
||||||
clientCAKeystore.save(clientCAKeystorePath, storePassword)
|
clientCAKeystore.save(clientCAKeystorePath, storePassword)
|
||||||
|
|
||||||
val tlsKeystore = loadOrCreateKeyStore(sslKeyStorePath, storePassword)
|
val tlsKeystore = loadOrCreateKeyStore(sslKeyStorePath, storePassword)
|
||||||
@ -106,6 +106,6 @@ fun createKeystoreForCordaNode(sslKeyStorePath: Path,
|
|||||||
X509Utilities.CORDA_CLIENT_TLS,
|
X509Utilities.CORDA_CLIENT_TLS,
|
||||||
tlsKey.private,
|
tlsKey.private,
|
||||||
keyPass,
|
keyPass,
|
||||||
org.bouncycastle.cert.path.CertPath(arrayOf(clientTLSCert, clientCACert, intermediateCACert, rootCACert)))
|
arrayOf(clientTLSCert, clientCACert, intermediateCACert, rootCACert))
|
||||||
tlsKeystore.save(sslKeyStorePath, storePassword)
|
tlsKeystore.save(sslKeyStorePath, storePassword)
|
||||||
}
|
}
|
@ -1,19 +1,19 @@
|
|||||||
package net.corda.node.utilities
|
package net.corda.node.utilities
|
||||||
|
|
||||||
import net.corda.core.crypto.CertificateAndKeyPair
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.crypto.Crypto
|
|
||||||
import net.corda.core.crypto.cert
|
|
||||||
import net.corda.core.internal.exists
|
import net.corda.core.internal.exists
|
||||||
import net.corda.core.internal.read
|
import net.corda.core.internal.read
|
||||||
import net.corda.core.internal.write
|
import net.corda.core.internal.write
|
||||||
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
import org.bouncycastle.cert.X509CertificateHolder
|
||||||
import org.bouncycastle.cert.path.CertPath
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.security.*
|
import java.security.*
|
||||||
|
import java.security.cert.CertPath
|
||||||
import java.security.cert.Certificate
|
import java.security.cert.Certificate
|
||||||
|
import java.security.cert.CertificateFactory
|
||||||
|
|
||||||
val KEYSTORE_TYPE = "JKS"
|
val KEYSTORE_TYPE = "JKS"
|
||||||
|
|
||||||
@ -77,8 +77,8 @@ fun loadKeyStore(input: InputStream, storePassword: String): KeyStore {
|
|||||||
* but for SSL purposes this is recommended.
|
* 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.
|
* @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) {
|
fun KeyStore.addOrReplaceKey(alias: String, key: Key, password: CharArray, chain: Array<out X509CertificateHolder>) {
|
||||||
addOrReplaceKey(alias, key, password, chain.certificates.map { it.cert }.toTypedArray<Certificate>())
|
addOrReplaceKey(alias, key, password, chain.map { it.cert }.toTypedArray<Certificate>())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -89,7 +89,7 @@ fun KeyStore.addOrReplaceKey(alias: String, key: Key, password: CharArray, chain
|
|||||||
* but for SSL purposes this is recommended.
|
* 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.
|
* @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>) {
|
fun KeyStore.addOrReplaceKey(alias: String, key: Key, password: CharArray, chain: Array<out Certificate>) {
|
||||||
if (containsAlias(alias)) {
|
if (containsAlias(alias)) {
|
||||||
this.deleteEntry(alias)
|
this.deleteEntry(alias)
|
||||||
}
|
}
|
||||||
@ -168,3 +168,43 @@ fun KeyStore.getSupportedKey(alias: String, keyPassword: String): PrivateKey {
|
|||||||
val key = getKey(alias, keyPass) as PrivateKey
|
val key = getKey(alias, keyPass) as PrivateKey
|
||||||
return Crypto.toSupportedPrivateKey(key)
|
return Crypto.toSupportedPrivateKey(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class KeyStoreWrapper(private val storePath: Path, private val storePassword: String) {
|
||||||
|
private val keyStore = storePath.read { loadKeyStore(it, storePassword) }
|
||||||
|
|
||||||
|
private fun createCertificate(serviceName: X500Name, pubKey: PublicKey): CertPath {
|
||||||
|
val clientCertPath = keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
|
||||||
|
// Assume key password = store password.
|
||||||
|
val clientCA = certificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)
|
||||||
|
// Create new keys and store in keystore.
|
||||||
|
val cert = X509Utilities.createCertificate(CertificateType.IDENTITY, clientCA.certificate, clientCA.keyPair, serviceName, pubKey)
|
||||||
|
val certPath = CertificateFactory.getInstance("X509").generateCertPath(listOf(cert.cert) + clientCertPath)
|
||||||
|
require(certPath.certificates.isNotEmpty()) { "Certificate path cannot be empty" }
|
||||||
|
return certPath
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveNewKeyPair(serviceName: X500Name, privateKeyAlias: String, keyPair: KeyPair) {
|
||||||
|
val certPath = createCertificate(serviceName, keyPair.public)
|
||||||
|
// Assume key password = store password.
|
||||||
|
keyStore.addOrReplaceKey(privateKeyAlias, keyPair.private, storePassword.toCharArray(), certPath.certificates.toTypedArray())
|
||||||
|
keyStore.save(storePath, storePassword)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun savePublicKey(serviceName: X500Name, pubKeyAlias: String, pubKey: PublicKey) {
|
||||||
|
val certPath = createCertificate(serviceName, pubKey)
|
||||||
|
// Assume key password = store password.
|
||||||
|
keyStore.addOrReplaceCertificate(pubKeyAlias, certPath.certificates.first())
|
||||||
|
keyStore.save(storePath, storePassword)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delegate methods to keystore. Sadly keystore doesn't have an interface.
|
||||||
|
fun containsAlias(alias: String) = keyStore.containsAlias(alias)
|
||||||
|
|
||||||
|
fun getX509Certificate(alias: String) = keyStore.getX509Certificate(alias)
|
||||||
|
|
||||||
|
fun getCertificateChain(alias: String): Array<out Certificate> = keyStore.getCertificateChain(alias)
|
||||||
|
|
||||||
|
fun getCertificate(alias: String): Certificate = keyStore.getCertificate(alias)
|
||||||
|
|
||||||
|
fun certificateAndKeyPair(alias: String) = keyStore.getCertificateAndKeyPair(alias, storePassword)
|
||||||
|
}
|
||||||
|
@ -49,7 +49,7 @@ class NetworkRegistrationHelper(val config: NodeConfiguration, val certService:
|
|||||||
val selfSignCert = X509Utilities.createSelfSignedCACertificate(config.myLegalName, keyPair)
|
val selfSignCert = X509Utilities.createSelfSignedCACertificate(config.myLegalName, keyPair)
|
||||||
// Save to the key store.
|
// Save to the key store.
|
||||||
caKeyStore.addOrReplaceKey(SELF_SIGNED_PRIVATE_KEY, keyPair.private, privateKeyPassword.toCharArray(),
|
caKeyStore.addOrReplaceKey(SELF_SIGNED_PRIVATE_KEY, keyPair.private, privateKeyPassword.toCharArray(),
|
||||||
CertPath(arrayOf(selfSignCert)))
|
arrayOf(selfSignCert))
|
||||||
caKeyStore.save(config.nodeKeystore, keystorePassword)
|
caKeyStore.save(config.nodeKeystore, keystorePassword)
|
||||||
}
|
}
|
||||||
val keyPair = caKeyStore.getKeyPair(SELF_SIGNED_PRIVATE_KEY, privateKeyPassword)
|
val keyPair = caKeyStore.getKeyPair(SELF_SIGNED_PRIVATE_KEY, privateKeyPassword)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user