mirror of
https://github.com/corda/corda.git
synced 2025-01-30 16:14:39 +00:00
CORDA-2221: Fix clustered notary identity generation (#4230)
- Don't generate a composite key certificate for CFT notaries - Don't require a composite key certificate for CFT notaries on startup
This commit is contained in:
parent
eb9bd10da0
commit
e1e5d13941
@ -18,6 +18,8 @@ import org.slf4j.LoggerFactory
|
|||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains utility methods for generating identities for a node.
|
* Contains utility methods for generating identities for a node.
|
||||||
@ -43,43 +45,47 @@ object DevIdentityGenerator {
|
|||||||
return identity.party
|
return identity.party
|
||||||
}
|
}
|
||||||
|
|
||||||
fun generateDistributedNotaryCompositeIdentity(dirs: List<Path>, notaryName: CordaX500Name, threshold: Int = 1): Party {
|
/** Generates a CFT notary identity, where the entire cluster shares a key pair. */
|
||||||
require(dirs.isNotEmpty())
|
|
||||||
|
|
||||||
log.trace { "Generating composite identity \"$notaryName\" for nodes: ${dirs.joinToString()}" }
|
|
||||||
val keyPairs = (1..dirs.size).map { generateKeyPair() }
|
|
||||||
val notaryKey = CompositeKey.Builder().addKeys(keyPairs.map { it.public }).build(threshold)
|
|
||||||
keyPairs.zip(dirs) { keyPair, nodeDir ->
|
|
||||||
generateCertificates(keyPair, notaryKey, notaryName, nodeDir)
|
|
||||||
}
|
|
||||||
return Party(notaryName, notaryKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun generateDistributedNotarySingularIdentity(dirs: List<Path>, notaryName: CordaX500Name): Party {
|
fun generateDistributedNotarySingularIdentity(dirs: List<Path>, notaryName: CordaX500Name): Party {
|
||||||
require(dirs.isNotEmpty())
|
require(dirs.isNotEmpty())
|
||||||
|
|
||||||
log.trace { "Generating singular identity \"$notaryName\" for nodes: ${dirs.joinToString()}" }
|
log.trace { "Generating singular identity \"$notaryName\" for nodes: ${dirs.joinToString()}" }
|
||||||
|
|
||||||
val keyPair = generateKeyPair()
|
val keyPair = generateKeyPair()
|
||||||
val notaryKey = keyPair.public
|
val notaryKey = keyPair.public
|
||||||
dirs.forEach { dir ->
|
|
||||||
generateCertificates(keyPair, notaryKey, notaryName, dir)
|
dirs.forEach { nodeDir ->
|
||||||
|
val keyStore = getKeyStore(nodeDir)
|
||||||
|
setPrivateKey(keyStore, keyPair, notaryName.x500Principal)
|
||||||
}
|
}
|
||||||
return Party(notaryName, notaryKey)
|
return Party(notaryName, notaryKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateCertificates(keyPair: KeyPair, notaryKey: PublicKey, notaryName: CordaX500Name, nodeDir: Path) {
|
/** Generates a BFT notary identity: individual key pairs for each cluster member, and a shared composite key. */
|
||||||
val (serviceKeyCert, compositeKeyCert) = listOf(keyPair.public, notaryKey).map { publicKey ->
|
fun generateDistributedNotaryCompositeIdentity(dirs: List<Path>, notaryName: CordaX500Name, threshold: Int = 1): Party {
|
||||||
X509Utilities.createCertificate(
|
require(dirs.isNotEmpty())
|
||||||
CertificateType.SERVICE_IDENTITY,
|
|
||||||
DEV_INTERMEDIATE_CA.certificate,
|
log.trace { "Generating composite identity \"$notaryName\" for nodes: ${dirs.joinToString()}" }
|
||||||
DEV_INTERMEDIATE_CA.keyPair,
|
|
||||||
notaryName.x500Principal,
|
val keyPairs = (1..dirs.size).map { generateKeyPair() }
|
||||||
publicKey)
|
val notaryKey = CompositeKey.Builder().addKeys(keyPairs.map { it.public }).build(threshold)
|
||||||
|
|
||||||
|
keyPairs.zip(dirs) { keyPair, nodeDir ->
|
||||||
|
val keyStore = getKeyStore(nodeDir)
|
||||||
|
setPrivateKey(keyStore, keyPair, notaryName.x500Principal)
|
||||||
|
setCompositeKey(keyStore, notaryKey, notaryName.x500Principal)
|
||||||
}
|
}
|
||||||
val distServKeyStoreFile = (nodeDir / "certificates").createDirectories() / "distributedService.jks"
|
return Party(notaryName, notaryKey)
|
||||||
X509KeyStore.fromFile(distServKeyStoreFile, DEV_CA_KEY_STORE_PASS, createNew = true).update {
|
}
|
||||||
setCertificate("$DISTRIBUTED_NOTARY_ALIAS_PREFIX-composite-key", compositeKeyCert)
|
|
||||||
setPrivateKey(
|
private fun getKeyStore(nodeDir: Path): X509KeyStore {
|
||||||
|
val distServKeyStoreFile = nodeDir / "certificates/distributedService.jks"
|
||||||
|
return X509KeyStore.fromFile(distServKeyStoreFile, DEV_CA_KEY_STORE_PASS, createNew = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setPrivateKey(keyStore: X509KeyStore, keyPair: KeyPair, notaryPrincipal: X500Principal) {
|
||||||
|
val serviceKeyCert = createCertificate(keyPair.public, notaryPrincipal)
|
||||||
|
keyStore.setPrivateKey(
|
||||||
"$DISTRIBUTED_NOTARY_ALIAS_PREFIX-private-key",
|
"$DISTRIBUTED_NOTARY_ALIAS_PREFIX-private-key",
|
||||||
keyPair.private,
|
keyPair.private,
|
||||||
listOf(serviceKeyCert, DEV_INTERMEDIATE_CA.certificate, DEV_ROOT_CA.certificate),
|
listOf(serviceKeyCert, DEV_INTERMEDIATE_CA.certificate, DEV_ROOT_CA.certificate),
|
||||||
@ -88,5 +94,18 @@ object DevIdentityGenerator {
|
|||||||
// where it is calling `KeyManagerFactory.init()` with store password
|
// where it is calling `KeyManagerFactory.init()` with store password
|
||||||
/*DEV_CA_PRIVATE_KEY_PASS*/)
|
/*DEV_CA_PRIVATE_KEY_PASS*/)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setCompositeKey(keyStore: X509KeyStore, compositeKey: PublicKey, notaryPrincipal: X500Principal) {
|
||||||
|
val compositeKeyCert = createCertificate(compositeKey, notaryPrincipal)
|
||||||
|
keyStore.setCertificate("$DISTRIBUTED_NOTARY_ALIAS_PREFIX-composite-key", compositeKeyCert)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createCertificate(publicKey: PublicKey, principal: X500Principal): X509Certificate {
|
||||||
|
return X509Utilities.createCertificate(
|
||||||
|
CertificateType.SERVICE_IDENTITY,
|
||||||
|
DEV_INTERMEDIATE_CA.certificate,
|
||||||
|
DEV_INTERMEDIATE_CA.keyPair,
|
||||||
|
principal,
|
||||||
|
publicKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -864,13 +864,17 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
|||||||
val compositeKeyAlias = "$DISTRIBUTED_NOTARY_ALIAS_PREFIX-composite-key"
|
val compositeKeyAlias = "$DISTRIBUTED_NOTARY_ALIAS_PREFIX-composite-key"
|
||||||
|
|
||||||
val signingCertificateStore = configuration.signingCertificateStore.get()
|
val signingCertificateStore = configuration.signingCertificateStore.get()
|
||||||
|
// A composite key is only required for BFT notaries.
|
||||||
val certificates = if (cryptoService.containsKey(compositeKeyAlias)) {
|
val certificates = if (cryptoService.containsKey(compositeKeyAlias)) {
|
||||||
val certificate = signingCertificateStore[compositeKeyAlias]
|
val certificate = signingCertificateStore[compositeKeyAlias]
|
||||||
// We have to create the certificate chain for the composite key manually, this is because we don't have a keystore
|
// We have to create the certificate chain for the composite key manually, this is because we don't have a keystore
|
||||||
// provider that understand compositeKey-privateKey combo. The cert chain is created using the composite key certificate +
|
// provider that understand compositeKey-privateKey combo. The cert chain is created using the composite key certificate +
|
||||||
// the tail of the private key certificates, as they are both signed by the same certificate chain.
|
// the tail of the private key certificates, as they are both signed by the same certificate chain.
|
||||||
listOf(certificate) + signingCertificateStore.query { getCertificateChain(privateKeyAlias) }.drop(1)
|
listOf(certificate) + signingCertificateStore.query { getCertificateChain(privateKeyAlias) }.drop(1)
|
||||||
} else throw IllegalStateException("The identity public key for the notary service $serviceLegalName was not found in the key store.")
|
} else {
|
||||||
|
// We assume the notary is CFT, and each cluster member shares the same notary key pair.
|
||||||
|
signingCertificateStore.query { getCertificateChain(privateKeyAlias) }
|
||||||
|
}
|
||||||
|
|
||||||
val subject = CordaX500Name.build(certificates.first().subjectX500Principal)
|
val subject = CordaX500Name.build(certificates.first().subjectX500Principal)
|
||||||
if (subject != serviceLegalName) {
|
if (subject != serviceLegalName) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user