Add DER format encoding for CompositeKey

Add extremely rough DER format encoding for CompositeKey so that they can be used in X.509 certificates,
and switch service identity generator to using the proper identity cert for signing.
This commit is contained in:
Ross Nicoll 2017-06-20 14:58:26 +01:00
parent 155bb029da
commit b7bec90fae
2 changed files with 34 additions and 16 deletions

View File

@ -1,7 +1,9 @@
package net.corda.core.crypto package net.corda.core.crypto
import net.corda.core.crypto.CompositeKey.NodeAndWeight
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.serialize import org.bouncycastle.asn1.*
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
import java.security.PublicKey import java.security.PublicKey
/** /**
@ -35,20 +37,24 @@ class CompositeKey private constructor (val threshold: Int,
* Holds node - weight pairs for a CompositeKey. Ordered first by weight, then by node's hashCode. * Holds node - weight pairs for a CompositeKey. Ordered first by weight, then by node's hashCode.
*/ */
@CordaSerializable @CordaSerializable
data class NodeAndWeight(val node: PublicKey, val weight: Int): Comparable<NodeAndWeight> { data class NodeAndWeight(val node: PublicKey, val weight: Int): Comparable<NodeAndWeight>, ASN1Object() {
override fun compareTo(other: NodeAndWeight): Int { override fun compareTo(other: NodeAndWeight): Int {
if (weight == other.weight) { if (weight == other.weight) {
return node.hashCode().compareTo(other.node.hashCode()) return node.hashCode().compareTo(other.node.hashCode())
} }
else return weight.compareTo(other.weight) else return weight.compareTo(other.weight)
} }
override fun toASN1Primitive(): ASN1Primitive {
val vector = ASN1EncodableVector()
vector.add(DERBitString(node.encoded))
vector.add(ASN1Integer(weight.toLong()))
return DERSequence(vector)
}
} }
companion object { companion object {
// TODO: Get the design standardised and from there define a recognised name val ALGORITHM = CompositeSignature.ALGORITHM_IDENTIFIER.algorithm.toString()
val ALGORITHM = "X-Corda-CompositeKey"
// TODO: We should be using a well defined format.
val FORMAT = "X-Corda-Kryo"
} }
/** /**
@ -57,8 +63,17 @@ class CompositeKey private constructor (val threshold: Int,
fun isFulfilledBy(key: PublicKey) = isFulfilledBy(setOf(key)) fun isFulfilledBy(key: PublicKey) = isFulfilledBy(setOf(key))
override fun getAlgorithm() = ALGORITHM override fun getAlgorithm() = ALGORITHM
override fun getEncoded(): ByteArray = this.serialize().bytes override fun getEncoded(): ByteArray {
override fun getFormat() = FORMAT val keyVector = ASN1EncodableVector()
val childrenVector = ASN1EncodableVector()
children.forEach {
childrenVector.add(it.toASN1Primitive())
}
keyVector.add(ASN1Integer(threshold.toLong()))
keyVector.add(DERSequence(childrenVector))
return SubjectPublicKeyInfo(CompositeSignature.ALGORITHM_IDENTIFIER, DERSequence(keyVector)).encoded
}
override fun getFormat() = ASN1Encoding.DER
/** /**
* Function checks if the public keys corresponding to the signatures are matched against the leaves of the composite * Function checks if the public keys corresponding to the signatures are matched against the leaves of the composite

View File

@ -1,9 +1,6 @@
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.CompositeKey
import net.corda.core.crypto.X509Utilities
import net.corda.core.crypto.generateKeyPair
import net.corda.core.identity.PartyAndCertificate import net.corda.core.identity.PartyAndCertificate
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.serialization.storageKryo import net.corda.core.serialization.storageKryo
@ -12,6 +9,7 @@ import net.corda.core.utilities.trace
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.security.cert.*
object ServiceIdentityGenerator { object ServiceIdentityGenerator {
private val log = loggerFor<ServiceIdentityGenerator>() private val log = loggerFor<ServiceIdentityGenerator>()
@ -36,15 +34,20 @@ object ServiceIdentityGenerator {
log.trace { "Generating a group identity \"serviceName\" for nodes: ${dirs.joinToString()}" } log.trace { "Generating a group identity \"serviceName\" for nodes: ${dirs.joinToString()}" }
val keyPairs = (1..dirs.size).map { generateKeyPair() } val keyPairs = (1..dirs.size).map { generateKeyPair() }
val notaryKey = CompositeKey.Builder().addKeys(keyPairs.map { it.public }).build(threshold) val notaryKey = CompositeKey.Builder().addKeys(keyPairs.map { it.public }).build(threshold)
// TODO: This doesn't work until we have composite keys in X.509 certificates, so we make up a certificate that nothing checks val notaryCert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, serviceCa.certificate,
// val notaryCert = X509Utilities.createCertificate(CertificateType.IDENTITY, serviceCa.certificate, serviceCa.keyPair, serviceName, notaryKey)
// serviceCa.keyPair, serviceName, notaryKey)
val notaryCert = X509Utilities.createSelfSignedCACertificate(serviceName, generateKeyPair())
val notaryCertPath = X509Utilities.createCertificatePath(serviceCa.certificate, notaryCert, revocationEnabled = false) val notaryCertPath = X509Utilities.createCertificatePath(serviceCa.certificate, notaryCert, revocationEnabled = false)
val notaryParty = PartyAndCertificate(serviceName, notaryKey, notaryCert, notaryCertPath) val notaryParty = PartyAndCertificate(serviceName, notaryKey, notaryCert, notaryCertPath)
val notaryPartyBytes = notaryParty.serialize() val notaryPartyBytes = notaryParty.serialize()
val privateKeyFile = "$serviceId-private-key" val privateKeyFile = "$serviceId-private-key"
val publicKeyFile = "$serviceId-public" val publicKeyFile = "$serviceId-public"
// Sanity check the certificate and path
val validatorParameters = PKIXParameters(setOf(TrustAnchor(serviceCa.certificate.cert, null)))
val validator = CertPathValidator.getInstance("PKIX")
validatorParameters.isRevocationEnabled = false
validator.validate(notaryCertPath, validatorParameters) as PKIXCertPathValidatorResult
keyPairs.zip(dirs) { keyPair, dir -> keyPairs.zip(dirs) { keyPair, dir ->
Files.createDirectories(dir) Files.createDirectories(dir)
notaryPartyBytes.writeToFile(dir.resolve(publicKeyFile)) notaryPartyBytes.writeToFile(dir.resolve(publicKeyFile))