Add support for X509Certificate and CertPath serialization

This commit is contained in:
Ross Nicoll 2017-05-23 12:05:22 +01:00
parent b8755ccdb2
commit 36a091dd6a
5 changed files with 70 additions and 5 deletions

View File

@ -188,15 +188,15 @@ object X509Utilities {
* @param revocationEnabled whether revocation of certificates in the path should be checked.
*/
fun createCertificatePath(rootCertAndKey: CertificateAndKeyPair,
targetCertAndKey: CertificateAndKeyPair,
targetCertAndKey: X509Certificate,
revocationEnabled: Boolean): CertPathBuilderResult {
val intermediateCertificates = setOf(targetCertAndKey.certificate)
val intermediateCertificates = setOf(targetCertAndKey)
val certStore = CertStore.getInstance("Collection", CollectionCertStoreParameters(intermediateCertificates))
val certPathFactory = CertPathBuilder.getInstance("PKIX")
val trustAnchor = TrustAnchor(rootCertAndKey.certificate, null)
val certPathParameters = try {
PKIXBuilderParameters(setOf(trustAnchor), X509CertSelector().apply {
certificate = targetCertAndKey.certificate
certificate = targetCertAndKey
})
} catch (ex: InvalidAlgorithmParameterException) {
throw RuntimeException(ex)

View File

@ -26,9 +26,12 @@ import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PrivateKey
import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PublicKey
import org.objenesis.strategy.StdInstantiatorStrategy
import org.slf4j.Logger
import sun.security.provider.certpath.X509CertPath
import java.io.BufferedInputStream
import java.io.FileInputStream
import java.io.InputStream
import java.security.cert.CertPath
import java.security.cert.X509Certificate
import java.util.*
object DefaultKryoCustomizer {
@ -97,6 +100,12 @@ object DefaultKryoCustomizer {
// Note that return type should be specifically set to InputStream, otherwise it may not work, i.e. val aStream : InputStream = HashCheckingStream(...).
addDefaultSerializer(InputStream::class.java, InputStreamSerializer)
register(CertPath::class.java, CertPathSerializer)
register(X509CertPath::class.java, CertPathSerializer)
// TODO: We shouldn't need to serialize raw certificates, and if we do then we need a cleaner solution
// than this mess.
val x509CertObjectClazz = Class.forName("org.bouncycastle.jcajce.provider.asymmetric.x509.X509CertificateObject")
register(x509CertObjectClazz, X509CertificateSerializer)
register(X500Name::class.java, X500NameSerializer)
register(BCECPrivateKey::class.java, PrivateKeySerializer)

View File

@ -30,6 +30,9 @@ import java.nio.file.Files
import java.nio.file.Path
import java.security.PrivateKey
import java.security.PublicKey
import java.security.cert.CertPath
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
import java.security.spec.InvalidKeySpecException
import java.time.Instant
import java.util.*
@ -613,6 +616,36 @@ object X500NameSerializer : Serializer<X500Name>() {
}
}
/**
* For serialising an [CertPath] in an X.500 standard format.
*/
@ThreadSafe
object CertPathSerializer : Serializer<CertPath>() {
val factory = CertificateFactory.getInstance("X.509")
override fun read(kryo: Kryo, input: Input, type: Class<CertPath>): CertPath {
return factory.generateCertPath(input)
}
override fun write(kryo: Kryo, output: Output, obj: CertPath) {
output.writeBytes(obj.encoded)
}
}
/**
* For serialising an [CX509Certificate] in an X.500 standard format.
*/
@ThreadSafe
object X509CertificateSerializer : Serializer<X509Certificate>() {
val factory = CertificateFactory.getInstance("X.509")
override fun read(kryo: Kryo, input: Input, type: Class<X509Certificate>): X509Certificate {
return factory.generateCertificate(input) as X509Certificate
}
override fun write(kryo: Kryo, output: Output, obj: X509Certificate) {
output.writeBytes(obj.encoded)
}
}
class KryoPoolWithContext(val baseKryoPool: KryoPool, val contextKey: Any, val context: Any) : KryoPool {
override fun <T : Any?> run(callback: KryoCallback<T>): T {
val kryo = borrow()

View File

@ -3,8 +3,11 @@ package net.corda.core.serialization
import com.esotericsoftware.kryo.Kryo
import com.google.common.primitives.Ints
import net.corda.core.crypto.*
import net.corda.core.utilities.ALICE
import net.corda.core.utilities.BOB
import net.corda.node.services.messaging.Ack
import net.corda.node.services.persistence.NodeAttachmentService
import net.corda.testing.BOB_PUBKEY
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Before
@ -12,6 +15,8 @@ import org.junit.Test
import org.slf4j.LoggerFactory
import java.io.ByteArrayInputStream
import java.io.InputStream
import java.security.cert.CertPath
import java.security.cert.X509Certificate
import java.time.Instant
import java.util.*
import kotlin.test.assertEquals
@ -136,6 +141,24 @@ class KryoTests {
assertEquals(-1, readRubbishStream.read())
}
@Test
fun `serialize - deserialize X509Certififcate`() {
val expected = X509Utilities.createSelfSignedCACert(ALICE.name).certificate
val serialized = expected.serialize(kryo).bytes
val actual: X509Certificate = serialized.deserialize(kryo)
assertEquals(expected, actual)
}
@Test
fun `serialize - deserialize X509CertPath`() {
val rootCA = X509Utilities.createSelfSignedCACert(ALICE.name)
val certificate = X509Utilities.createTlsServerCert(BOB.name, BOB_PUBKEY, rootCA, emptyList(), emptyList())
val expected = X509Utilities.createCertificatePath(rootCA, certificate, false).certPath
val serialized = expected.serialize(kryo).bytes
val actual: CertPath = serialized.deserialize(kryo)
assertEquals(expected, actual)
}
@CordaSerializable
private data class Person(val name: String, val birthday: Instant?)

View File

@ -87,10 +87,10 @@ class InMemoryIdentityServiceTests {
fun `assert ownership`() {
val aliceRootCertAndKey = X509Utilities.createSelfSignedCACert(ALICE.name)
val aliceTxCertAndKey = X509Utilities.createIntermediateCert(ALICE.name, aliceRootCertAndKey)
val aliceCertPath = X509Utilities.createCertificatePath(aliceRootCertAndKey, aliceTxCertAndKey, false).certPath
val aliceCertPath = X509Utilities.createCertificatePath(aliceRootCertAndKey, aliceTxCertAndKey.certificate, false).certPath
val bobRootCertAndKey = X509Utilities.createSelfSignedCACert(BOB.name)
val bobTxCertAndKey = X509Utilities.createIntermediateCert(BOB.name, bobRootCertAndKey)
val bobCertPath = X509Utilities.createCertificatePath(bobRootCertAndKey, bobTxCertAndKey, false).certPath
val bobCertPath = X509Utilities.createCertificatePath(bobRootCertAndKey, bobTxCertAndKey.certificate, false).certPath
val service = InMemoryIdentityService()
val alice = Party(aliceRootCertAndKey)
val anonymousAlice = AnonymousParty(aliceTxCertAndKey.keyPair.public)