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
import net.corda.core.crypto.CompositeKey.NodeAndWeight
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
/**
@ -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.
*/
@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 {
if (weight == other.weight) {
return node.hashCode().compareTo(other.node.hashCode())
}
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 {
// TODO: Get the design standardised and from there define a recognised name
val ALGORITHM = "X-Corda-CompositeKey"
// TODO: We should be using a well defined format.
val FORMAT = "X-Corda-Kryo"
val ALGORITHM = CompositeSignature.ALGORITHM_IDENTIFIER.algorithm.toString()
}
/**
@ -57,8 +63,17 @@ class CompositeKey private constructor (val threshold: Int,
fun isFulfilledBy(key: PublicKey) = isFulfilledBy(setOf(key))
override fun getAlgorithm() = ALGORITHM
override fun getEncoded(): ByteArray = this.serialize().bytes
override fun getFormat() = FORMAT
override fun getEncoded(): ByteArray {
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

View File

@ -1,9 +1,6 @@
package net.corda.node.utilities
import net.corda.core.crypto.CertificateAndKeyPair
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.X509Utilities
import net.corda.core.crypto.generateKeyPair
import net.corda.core.crypto.*
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.serialization.serialize
import net.corda.core.serialization.storageKryo
@ -12,6 +9,7 @@ import net.corda.core.utilities.trace
import org.bouncycastle.asn1.x500.X500Name
import java.nio.file.Files
import java.nio.file.Path
import java.security.cert.*
object ServiceIdentityGenerator {
private val log = loggerFor<ServiceIdentityGenerator>()
@ -36,15 +34,20 @@ object ServiceIdentityGenerator {
log.trace { "Generating a group identity \"serviceName\" for nodes: ${dirs.joinToString()}" }
val keyPairs = (1..dirs.size).map { generateKeyPair() }
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.IDENTITY, serviceCa.certificate,
// serviceCa.keyPair, serviceName, notaryKey)
val notaryCert = X509Utilities.createSelfSignedCACertificate(serviceName, generateKeyPair())
val notaryCert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, serviceCa.certificate,
serviceCa.keyPair, serviceName, notaryKey)
val notaryCertPath = X509Utilities.createCertificatePath(serviceCa.certificate, notaryCert, revocationEnabled = false)
val notaryParty = PartyAndCertificate(serviceName, notaryKey, notaryCert, notaryCertPath)
val notaryPartyBytes = notaryParty.serialize()
val privateKeyFile = "$serviceId-private-key"
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 ->
Files.createDirectories(dir)
notaryPartyBytes.writeToFile(dir.resolve(publicKeyFile))